2025-08-20 10:36:48 +08:00

308 lines
7.1 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view>
<!-- 课程网格列表 -->
<view class="course-grid" v-if="xkkcList.length > 0">
<view
v-for="(xkkc, index) in xkkcList"
:key="xkkc.id || index"
class="course-item"
:class="{ selected: xkkc.isSelected }"
@click="toggleSelection(xkkc)"
>
<view class="course-header">
<view class="course-name">{{ xkkc.kcmc }}</view>
<view class="detail-btn" @click.stop="goToDetail(xkkc)">
<image src="/static/base/home/details.svg" class="detail-icon" />
</view>
</view>
<view class="register-info">
<text>报名情况</text>
<text class="register-count">{{ xkkc.hasNum || 0 }}</text>
<text> | {{ xkkc.maxNum || 0 }}</text>
</view>
<view class="study-time-info" v-if="xkkc.studyTime">
<text></text>
<text class="study-time">{{ xkkc.studyTime }}</text>
</view>
<view v-if="xkkc.isSelected" class="selected-mark">
<uni-icons
type="checkbox-filled"
color="#3FBF72"
size="22"
></uni-icons>
</view>
</view>
</view>
<!-- 暂无数据提示 -->
<view v-else class="empty-course-list">
<view class="empty-icon">
<u-icon name="list" size="50" color="#C8C9CC"></u-icon>
</view>
<view class="empty-text">暂无课程数据</view>
</view>
</view>
</template>
<script setup lang="ts">
import { ref, watch } from "vue";
import { useDataStore } from "@/store/modules/data";
const { setKcData } = useDataStore();
// 接收外部传入属性并设置默认值
const props = withDefaults(defineProps<{
xk: any,
canSelected: boolean,
multiple: boolean,
}>(), {
xk: () => ({}),
canSelected: false,
multiple: false,
});
// 定义一个上级传入的emit响应事件用于接收数据变更
const emit = defineEmits(['change'])
// 学生列表数据
const xkkcList = ref<any>([]);
// 切换选课课程
const toggleSelection = (xkkc: any) => {
if (!props.canSelected) {
return;
}
// 获取本地存储的已选课程ID数组
let selectedXkkcIds = uni.getStorageSync("selectedXkkcIds") || [];
if (xkkc.isSelected) {
xkkc.isSelected = false;
if (props.multiple) {
// 如果是多选,则从已选数组中移除
selectedXkkcIds = selectedXkkcIds.filter(
(id: string) => id !== xkkc.id
);
} else {
// 如果是单选,则清空已选数组
selectedXkkcIds = [];
}
// xkkc.hasNum--;
} else {
// 选择课程时的验证逻辑
const maxNum = xkkc.maxNum || 0;
const hasNum = xkkc.hasNum || 0;
if (maxNum <= hasNum) {
uni.showToast({
title: '课程名额已经被抢光,是否重新选课',
icon: 'none',
duration: 2000
});
return;
}
// 检查上课时间是否重复
if (xkkc.studyTime && props.multiple) {
const hasTimeConflict = xkkcList.value.some((item: any) => {
// 检查已选课程中是否有相同上课时间
return item.isSelected &&
item.id !== xkkc.id &&
item.studyTime === xkkc.studyTime;
});
if (hasTimeConflict) {
uni.showToast({
title: '上课时间重复,请重新选择!',
icon: 'none',
duration: 3000
});
return;
}
}
// 通过验证,可以选课
if (props.multiple) {
// 如果是多选,则添加到已选数组
if (!selectedXkkcIds.includes(xkkc.id)) {
selectedXkkcIds.push(xkkc.id);
}
} else {
// 如果是单选,则清空已选数组并添加当前课程
selectedXkkcIds = [xkkc.id];
// 清理选中状态
xkkcList.value.forEach((item: any) => {
item.isSelected = false;
});
}
xkkc.isSelected = true;
}
// 更新本地存储
uni.setStorageSync("selectedXkkcIds", selectedXkkcIds);
emit("change", selectedXkkcIds);
}
const goToDetail = (xkkc: any) => {
setKcData(xkkc);
uni.navigateTo({
url: `/pages/base/xk/detail`,
});
};
const switchXk = (xk: any) => {
xkkcList.value = xk.xkkcs;
if (!props.canSelected) {
return;
}
// 获取本地存储的已选课程ID数组
let selectedXkkcIds = uni.getStorageSync("selectedXkkcIds") || [];
let newSelectedXkkcIds: string[] = [];
for (let i = 0; i < xkkcList.value.length; i++) {
const xkkc = xkkcList.value[i];
// 只检查本地存储的已选课程
if (selectedXkkcIds.includes(xkkc.id)) {
xkkc.isSelected = true;
newSelectedXkkcIds.push(xkkc.id);
} else {
xkkc.isSelected = false;
}
}
uni.setStorageSync("selectedXkkcIds", newSelectedXkkcIds);
emit("change", newSelectedXkkcIds);
}
// 监听当前学生信息变更
watch(() => props.xk, (newVal) => {
if (newVal && newVal.xkkcs) {
switchXk(newVal);
}
});
// 初始化
if (props.xk && props.xk.xkkcs) {
switchXk(props.xk);
}
</script>
<style lang="scss" scoped>
.course-grid {
display: flex;
flex-wrap: wrap;
padding: 15px 15px 0 15px;
.course-item {
position: relative;
width: calc(50% - 10px);
margin-bottom: 15px;
background-color: #fff;
border-radius: 8px;
padding: 15px;
box-sizing: border-box;
border: 1px solid transparent;
transition: all 0.3s ease;
&:nth-child(odd) {
margin-right: 10px;
}
&:nth-child(even) {
margin-left: 10px;
}
&.selected {
border: 1px solid #3fbf72;
background-color: rgba(63, 191, 114, 0.05);
box-shadow: 0 2px 8px rgba(63, 191, 114, 0.15);
}
.course-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 10px;
.course-name {
font-size: 16px;
font-weight: 500;
color: #333;
flex: 1;
margin-right: 10px;
}
.detail-btn {
flex-shrink: 0;
padding: 4px;
.detail-icon {
width: 20px;
height: 20px;
}
}
}
.register-info {
font-size: 14px;
color: #666;
margin-bottom: 8px;
.register-count {
color: #2879ff;
}
}
.study-time-info {
font-size: 14px;
color: #666;
margin-bottom: 8px;
.study-time {
color: #ff6b35;
font-weight: 500;
}
}
.selected-mark {
position: absolute;
top: -6px;
right: -6px;
}
}
}
// 暂无数据样式
.empty-course-list {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 60px 20px;
text-align: center;
.empty-icon {
margin-bottom: 20px;
background-color: #f5f6f7;
width: 80px;
height: 80px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
.empty-text {
font-size: 18px;
font-weight: 500;
color: #303133;
margin-bottom: 8px;
}
.empty-desc {
font-size: 14px;
color: #909399;
max-width: 80%;
line-height: 1.5;
}
}
</style>