调整选课使用公共组件
This commit is contained in:
parent
c12aa5c773
commit
829508ba57
@ -3,343 +3,37 @@
|
||||
<!-- 选课信息头部 - 固定部分 -->
|
||||
<view class="selection-header">
|
||||
<view class="header-content">
|
||||
<view class="title-section" @click="clickShowXkSelector">
|
||||
<view class="title">
|
||||
<text v-if="xkData && xkData.xkmc">{{ xkData.xkmc }}</text>
|
||||
<text v-else>选课信息</text>
|
||||
</view>
|
||||
<view class="switch-btn" v-if="xkList.length > 1">切换</view>
|
||||
</view>
|
||||
<!-- 选课类型选择部分 -->
|
||||
<XkPicker title="俱乐部信息" :is-xs="true" xklx-id="816059832" :xs-id="curXs.id" :nj-id="curXs.njId" @change="switchXk" />
|
||||
<!-- 学生选择部分 -->
|
||||
<view class="student-selector-bar" @click="clickShowXsSelector">
|
||||
<view class="user-avatar">
|
||||
<image
|
||||
:src="curXs.xstxUrl || '/static/base/home/11222.png'"
|
||||
class="w-full h-full"
|
||||
></image>
|
||||
</view>
|
||||
<view class="student-info">
|
||||
<text class="student-name">{{ curXs.xm }}</text>
|
||||
<text class="student-class"
|
||||
>{{ curXs.njmc }} {{ curXs.bjmc }}</text
|
||||
>
|
||||
</view>
|
||||
<view class="switch-btn" v-if="xsList.length > 1">切换</view>
|
||||
</view>
|
||||
<XsPicker :is-bar="true" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 可滚动的内容区域 -->
|
||||
<view class="scrollable-content">
|
||||
<!-- 课程网格列表 -->
|
||||
<view class="course-grid" v-if="courseListData.length > 0">
|
||||
<view
|
||||
v-for="(course, index) in courseListData"
|
||||
:key="course.id || index"
|
||||
class="course-item"
|
||||
:class="{ selected: course.isSelected }"
|
||||
>
|
||||
<view class="course-name">{{ course.kcmc }}</view>
|
||||
<view class="register-info">
|
||||
<text>上课人数:</text>
|
||||
<text class="register-count">{{ course.ybmr }}</text>
|
||||
</view>
|
||||
<view class="detail-btn" @click.stop="viewCourseDetail(course)"
|
||||
>详情</view
|
||||
>
|
||||
<view v-if="course.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>
|
||||
<XkkcList :xk="curXk" :can-selected="false" />
|
||||
</view>
|
||||
|
||||
<view>
|
||||
<!-- 学生选择弹窗 -->
|
||||
<u-popup
|
||||
:show="showXsFlag"
|
||||
@close="showXsFlag = false"
|
||||
mode="bottom"
|
||||
round="10"
|
||||
>
|
||||
<view class="student-selector">
|
||||
<view class="selector-header">
|
||||
<text class="selector-title">选择学生</text>
|
||||
<u-icon
|
||||
name="close"
|
||||
size="20"
|
||||
@click="showXsFlag = false"
|
||||
></u-icon>
|
||||
</view>
|
||||
<view class="student-list">
|
||||
<view
|
||||
v-for="(xs, index) in xsList"
|
||||
:key="index"
|
||||
class="student-item"
|
||||
:class="{
|
||||
'student-item-active': curXs.id === xs.id
|
||||
}"
|
||||
@click="switchXs(xs)"
|
||||
>
|
||||
<view class="student-avatar">
|
||||
<image
|
||||
:src="xs.xstxUrl || '/static/base/home/11222.png'"
|
||||
class="w-full h-full"
|
||||
></image>
|
||||
</view>
|
||||
<view class="student-info">
|
||||
<text class="student-name">{{ xs.xm }}</text>
|
||||
<text class="student-class"
|
||||
>{{ xs.njmc }} {{ xs.bjmc }}</text
|
||||
>
|
||||
</view>
|
||||
<u-icon
|
||||
v-if="curXs.id === xs.id"
|
||||
name="checkmark"
|
||||
color="#409EFF"
|
||||
size="20"
|
||||
></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
</view>
|
||||
|
||||
<view>
|
||||
<!-- 俱乐部选择弹窗 -->
|
||||
<u-popup
|
||||
:show="showXkFlag"
|
||||
@close="showXkFlag = false"
|
||||
mode="bottom"
|
||||
round="10"
|
||||
>
|
||||
<view class="student-selector">
|
||||
<view class="selector-header">
|
||||
<text class="selector-title">选择俱乐部</text>
|
||||
<u-icon
|
||||
name="close"
|
||||
size="20"
|
||||
@click="showXkFlag = false"
|
||||
></u-icon>
|
||||
</view>
|
||||
<view class="student-list">
|
||||
<view
|
||||
v-for="(xk, index) in xkList"
|
||||
:key="index"
|
||||
class="student-item"
|
||||
:class="{
|
||||
'student-item-active': xkData.id === xk.id
|
||||
}"
|
||||
@click="switchXk(xk)"
|
||||
>
|
||||
<view class="student-info">
|
||||
<text class="student-name">{{ xk.xkmc }}</text>
|
||||
</view>
|
||||
<u-icon
|
||||
v-if="xkData.id === xk.id"
|
||||
name="checkmark"
|
||||
color="#409EFF"
|
||||
size="20"
|
||||
></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
ref,
|
||||
computed,
|
||||
reactive,
|
||||
onBeforeUnmount,
|
||||
watch,
|
||||
onMounted,
|
||||
} from "vue";
|
||||
import XsPicker from "@/pages/base/components/XsPicker/index.vue"
|
||||
import XkPicker from "@/pages/base/components/XkPicker/index.vue"
|
||||
import XkkcList from "@/pages/base/components/XkkcList/index.vue"
|
||||
import { useUserStore } from "@/store/modules/user";
|
||||
import { useDataStore } from "@/store/modules/data";
|
||||
import { xkListApi, xsXkListApi } from "@/api/base/server";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
const { getUser, getCurXs } = useUserStore();
|
||||
const { getData, setKcData, setData } = useDataStore();
|
||||
const { sign_file } = getData;
|
||||
const { getCurXs } = useUserStore();
|
||||
|
||||
// 学生列表数据
|
||||
const xsList = computed(() => {
|
||||
return getUser.xsList;
|
||||
});
|
||||
const curXs = computed(() => getCurXs);
|
||||
|
||||
const curXs = ref<any>({});
|
||||
|
||||
// 控制选择器显示状态
|
||||
const showXsFlag = ref(false);
|
||||
const showXkFlag = ref(false);
|
||||
|
||||
if (xsList.value.length > 1) {
|
||||
showXsFlag.value = true;
|
||||
const curXk = ref<any>({});
|
||||
// 切换选课
|
||||
const switchXk = (xk: any) => {
|
||||
curXk.value = xk;
|
||||
}
|
||||
|
||||
const xkList = ref<any>([]);
|
||||
|
||||
const xkData = ref();
|
||||
const kcStatus = ref(false);
|
||||
|
||||
const courseInfo = ref<any>({});
|
||||
|
||||
onMounted(() => {
|
||||
curXs.value = getCurXs;
|
||||
// 先加载课程列表,不立即检查报名状态
|
||||
uni.showLoading({
|
||||
title: "加载中...",
|
||||
});
|
||||
loadCourseList(curXs.value);
|
||||
});
|
||||
|
||||
// 加载课程列表
|
||||
const loadCourseList = (xs: any) => {
|
||||
if (!xs) {
|
||||
uni.hideLoading();
|
||||
return;
|
||||
}
|
||||
|
||||
xsXkListApi({
|
||||
xsId: xs.id,
|
||||
njId: xs.njId,
|
||||
xklxId: "816059832",
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.resultCode == 1) {
|
||||
if (res.result && res.result.length) {
|
||||
xkList.value = res.result;
|
||||
xkData.value = res.result[0];
|
||||
} else {
|
||||
xkList.value = [];
|
||||
xkData.value = {};
|
||||
}
|
||||
uni.hideLoading();
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
uni.hideLoading();
|
||||
});
|
||||
};
|
||||
|
||||
// 显示学生选择器
|
||||
function clickShowXsSelector() {
|
||||
if (xsList.value.length > 1) {
|
||||
showXsFlag.value = true;
|
||||
}
|
||||
}
|
||||
|
||||
// 显示学生选择器
|
||||
function clickShowXkSelector() {
|
||||
if (xkList.value.length > 1) {
|
||||
showXkFlag.value = true;
|
||||
}
|
||||
}
|
||||
|
||||
// 切换学生
|
||||
function switchXs(xs: any) {
|
||||
curXs.value = xs;
|
||||
showXsFlag.value = false;
|
||||
|
||||
// 显示加载中
|
||||
uni.showLoading({
|
||||
title: "加载中...",
|
||||
});
|
||||
|
||||
// 显示切换成功提示
|
||||
uni.showToast({
|
||||
title: `已切换到${xs.xm}`,
|
||||
icon: "none",
|
||||
});
|
||||
|
||||
// 先加载当前学生的课程列表,课程数据加载完成后会自动检查报名状态
|
||||
loadCourseList(xs);
|
||||
}
|
||||
|
||||
// 切换学生
|
||||
function switchXk(xk: any) {
|
||||
xkData.value = xk;
|
||||
showXkFlag.value = false;
|
||||
|
||||
// 显示切换成功提示
|
||||
uni.showToast({
|
||||
title: `已切换到${xk.xkmc}`,
|
||||
icon: "none",
|
||||
});
|
||||
}
|
||||
|
||||
// 从课程数据中提取课程列表
|
||||
const displayCourseList = computed(() => {
|
||||
if (
|
||||
!xkData.value ||
|
||||
!xkData.value.xkkcs ||
|
||||
!Array.isArray(xkData.value.xkkcs)
|
||||
) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// 获取本地存储的已选课程ID数组
|
||||
const selectedCourseIds = uni.getStorageSync("selectedCourseIds") || [];
|
||||
|
||||
// 为课程添加选中状态属性
|
||||
return xkData.value.xkkcs.map((course: any) => ({
|
||||
...course,
|
||||
isSelected: selectedCourseIds.includes(course.id),
|
||||
}));
|
||||
});
|
||||
|
||||
// 可修改的课程列表数据
|
||||
const courseListData = ref<any[]>([]);
|
||||
|
||||
// 计算已选择的课程数量
|
||||
const selectedCoursesCount = computed(() => {
|
||||
return courseListData.value.filter((course: any) => course.isSelected).length;
|
||||
});
|
||||
|
||||
// 监听计算属性变化,更新可修改的数据
|
||||
watch(
|
||||
displayCourseList,
|
||||
(newVal) => {
|
||||
courseListData.value = JSON.parse(JSON.stringify(newVal));
|
||||
|
||||
// 初始化时,将已选课程的全部信息存入courseInfo
|
||||
const selectedCourseIds = uni.getStorageSync("selectedCourseIds") || [];
|
||||
if (selectedCourseIds.length > 0) {
|
||||
const selectedCourses = newVal.filter((course: any) =>
|
||||
selectedCourseIds.includes(course.id)
|
||||
);
|
||||
courseInfo.value = selectedCourses;
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
// 查看课程详情
|
||||
const viewCourseDetail = (course: any) => {
|
||||
setKcData(course);
|
||||
uni.navigateTo({
|
||||
url: `/pages/base/course-selection/detail`,
|
||||
});
|
||||
};
|
||||
|
||||
// 页面卸载前清除定时器
|
||||
onBeforeUnmount(() => {
|
||||
});
|
||||
@ -400,108 +94,6 @@ onBeforeUnmount(() => {
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
|
||||
.title-section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.title {
|
||||
flex: 1 0 1px;
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 14px;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.switch-btn {
|
||||
padding: 5px 15px;
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
color: #fff;
|
||||
border-radius: 15px;
|
||||
font-size: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
// 学生选择栏样式
|
||||
.student-selector-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 10px;
|
||||
padding: 10px;
|
||||
|
||||
.user-avatar {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
background-color: #fff;
|
||||
overflow: hidden;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.student-info {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.student-name {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.student-class {
|
||||
font-size: 12px;
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
.switch-btn {
|
||||
padding: 4px 12px;
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
color: #fff;
|
||||
border-radius: 20px;
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
|
||||
.countdown-section {
|
||||
.countdown-title {
|
||||
font-size: 14px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.countdown-timer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.time-block {
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
border-radius: 6px;
|
||||
min-width: 40px;
|
||||
padding: 5px 8px;
|
||||
text-align: center;
|
||||
|
||||
.time-value {
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.time-unit {
|
||||
font-size: 12px;
|
||||
opacity: 0.9;
|
||||
}
|
||||
}
|
||||
|
||||
.time-separator {
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
margin: 0 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -512,211 +104,4 @@ onBeforeUnmount(() => {
|
||||
-webkit-overflow-scrolling: touch; // 增强iOS滚动体验
|
||||
}
|
||||
|
||||
.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-name {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.register-info {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
margin-bottom: 12px;
|
||||
|
||||
.register-count {
|
||||
color: #2879ff;
|
||||
}
|
||||
}
|
||||
|
||||
.detail-btn {
|
||||
display: inline-block;
|
||||
color: #2879ff;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.selected-mark {
|
||||
position: absolute;
|
||||
top: -6px;
|
||||
right: -6px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.register-btn-container {
|
||||
position: sticky;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
padding: 15px;
|
||||
background-color: #fff;
|
||||
z-index: 10;
|
||||
box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.05);
|
||||
|
||||
.register-btn {
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
text-align: center;
|
||||
background-color: #2879ff;
|
||||
color: #fff;
|
||||
border-radius: 25px;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
/* 学生选择器弹窗样式 */
|
||||
.student-selector {
|
||||
background-color: #ffffff;
|
||||
border-top-left-radius: 12px;
|
||||
border-top-right-radius: 12px;
|
||||
padding-bottom: 20px;
|
||||
|
||||
.selector-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 15px;
|
||||
border-bottom: 1px solid #f2f2f2;
|
||||
|
||||
.selector-title {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #303133;
|
||||
}
|
||||
}
|
||||
|
||||
.student-list {
|
||||
padding: 0 15px;
|
||||
|
||||
.student-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 15px 0;
|
||||
border-bottom: 1px solid #f2f2f2;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
&-active {
|
||||
background-color: rgba(64, 158, 255, 0.05);
|
||||
}
|
||||
|
||||
.student-avatar {
|
||||
width: 45px;
|
||||
height: 45px;
|
||||
border-radius: 50%;
|
||||
background-color: #f0f0f0;
|
||||
overflow: hidden;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.student-info {
|
||||
flex: 1;
|
||||
margin-left: 12px;
|
||||
|
||||
.student-name {
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
color: #303133;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.student-class {
|
||||
font-size: 13px;
|
||||
color: #606266;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 全局图片样式 */
|
||||
.w-full {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.h-full {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
// 暂无数据样式
|
||||
.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;
|
||||
}
|
||||
}
|
||||
|
||||
/* 选课已结束样式 */
|
||||
.enrollment-ended {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
border-radius: 8px;
|
||||
padding: 12px 15px;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #fff;
|
||||
gap: 8px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -83,14 +83,13 @@ const startCountdown = (endTimeStamp: number) => {
|
||||
}
|
||||
|
||||
// 判断当前倒计时是选课开始还是选课结束
|
||||
if (!kcStatus.value && props.xk) {
|
||||
if (!kcStatus.value) {
|
||||
// 如果是选课开始倒计时结束,则切换到选课结束倒计时
|
||||
kcStatus.value = true; // 更新状态为选课已开始
|
||||
uni.showToast({
|
||||
title: "选课已开始",
|
||||
icon: "none",
|
||||
});
|
||||
|
||||
// 开始选课结束倒计时
|
||||
const xkjstime = dayjs(props.xk.xkjstime).valueOf();
|
||||
startCountdown(xkjstime);
|
||||
@ -103,7 +102,6 @@ const startCountdown = (endTimeStamp: number) => {
|
||||
icon: "none",
|
||||
});
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@ -162,10 +160,16 @@ onBeforeUnmount(() => {
|
||||
}
|
||||
});
|
||||
|
||||
// 监听变更
|
||||
watch(() => props.xk, (newVal) => {
|
||||
changeXk(newVal);
|
||||
});
|
||||
|
||||
// 初始化
|
||||
if (props.xk && props.xk.id) {
|
||||
changeXk(props.xk);
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@ -204,5 +208,20 @@ watch(() => props.xk, (newVal) => {
|
||||
margin: 0 5px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 选课已结束样式 */
|
||||
.enrollment-ended {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
border-radius: 8px;
|
||||
padding: 12px 15px;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #fff;
|
||||
gap: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
@ -3,7 +3,7 @@
|
||||
<view class="title-section" @click="showPicker">
|
||||
<view class="title">
|
||||
<text v-if="curXk && curXk.xkmc">{{ curXk.xkmc }}</text>
|
||||
<text v-else>选课信息</text>
|
||||
<text v-else>{{ title }}</text>
|
||||
</view>
|
||||
<view class="switch-btn" v-if="xkList.length > 1">切换</view>
|
||||
</view>
|
||||
@ -31,15 +31,17 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, watch } from "vue";
|
||||
import { xsXkkcListApi } from "@/api/base/server";
|
||||
import { xsXkListApi, xsXkkcListApi } from "@/api/base/server";
|
||||
import { useUserStore } from "@/store/modules/user";
|
||||
const { getCurXs } = useUserStore();
|
||||
|
||||
// 接收外部传入属性
|
||||
const props = defineProps<{
|
||||
isXs: boolean, // 是否是已经选的课程
|
||||
xsId: string,
|
||||
njId: string,
|
||||
xklxId: string; // 选课类型Id(962488654:兴趣课 / 816059832:俱乐部)
|
||||
xklxId: string, // 选课类型Id(962488654:兴趣课 / 816059832:俱乐部)
|
||||
title: string,
|
||||
}>();
|
||||
|
||||
// 定义一个上级传入的emit响应事件用于接收数据变更
|
||||
@ -63,15 +65,26 @@ const loadXkList = async () => {
|
||||
uni.showLoading({
|
||||
title: "加载中...",
|
||||
});
|
||||
xsXkkcListApi(props)
|
||||
const func = props.isXs ? xsXkListApi : xsXkkcListApi;
|
||||
func({
|
||||
xsId: props.xsId,
|
||||
njId: props.njId,
|
||||
xklxId: props.xklxId,
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.resultCode == 1) {
|
||||
if (res.result && res.result.length) {
|
||||
xkList.value = res.result;
|
||||
curXk.value = res.result[0];
|
||||
} else {
|
||||
xkList.value = [];
|
||||
curXk.value = {};
|
||||
if (props.isXs) {
|
||||
xkList.value = [];
|
||||
curXk.value = {};
|
||||
} else {
|
||||
uni.reLaunch({
|
||||
url: "/pages/base/course-selection/notopen",
|
||||
});
|
||||
}
|
||||
}
|
||||
switchXk(curXk.value);
|
||||
uni.hideLoading();
|
||||
@ -86,11 +99,13 @@ const loadXkList = async () => {
|
||||
function switchXk(xk: any) {
|
||||
curXk.value = xk;
|
||||
showFlag.value = false;
|
||||
// 显示切换成功提示
|
||||
uni.showToast({
|
||||
title: `已切换到${xk.xkmc}`,
|
||||
icon: "none",
|
||||
});
|
||||
if (xk && xk.id) {
|
||||
// 显示切换成功提示
|
||||
uni.showToast({
|
||||
title: `已切换到${xk.xkmc}`,
|
||||
icon: "none",
|
||||
});
|
||||
}
|
||||
emit("change", xk);
|
||||
}
|
||||
|
||||
|
||||
227
src/pages/base/components/XkkcList/index.vue
Normal file
227
src/pages/base/components/XkkcList/index.vue
Normal file
@ -0,0 +1,227 @@
|
||||
<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-name">{{ xkkc.kcmc }}</view>
|
||||
<view class="register-info">
|
||||
<text>报名情况:</text>
|
||||
<text class="register-count">{{ xkkc.hasNum || 0 }}</text>
|
||||
<text> | {{ xkkc.maxNum || 0 }}</text>
|
||||
</view>
|
||||
<view class="detail-btn" @click.stop="goToDetail(xkkc)"
|
||||
>详情</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 = defineProps<{
|
||||
xk: any,
|
||||
canSelected: boolean,
|
||||
}>();
|
||||
|
||||
// 定义一个上级传入的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) {
|
||||
// 从已选数组中移除
|
||||
selectedXkkcIds = selectedXkkcIds.filter(
|
||||
(id: string) => id !== xkkc.id
|
||||
);
|
||||
xkkc.isSelected = false;
|
||||
// xkkc.hasNum--;
|
||||
} else {
|
||||
// 如果是未选中,判断是否已选满
|
||||
const maxNum = xkkc.maxNum || 0;
|
||||
const hasNum = xkkc.hasNum || 0;
|
||||
if (maxNum > hasNum) {
|
||||
xkkc.isSelected = true;
|
||||
// 添加到已选数组
|
||||
if (!selectedXkkcIds.includes(xkkc.id)) {
|
||||
selectedXkkcIds.push(xkkc.id);
|
||||
// xkkc.hasNum++;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 更新本地存储
|
||||
uni.setStorageSync("selectedXkkcIds", selectedXkkcIds);
|
||||
emit("change", selectedXkkcIds);
|
||||
}
|
||||
|
||||
const goToDetail = (xkkc: any) => {
|
||||
setKcData(xkkc);
|
||||
uni.navigateTo({
|
||||
url: `/pages/base/course-selection/detail`,
|
||||
});
|
||||
};
|
||||
|
||||
const switchXk = (xk: any) => {
|
||||
xkkcList.value = xk.xkkcs;
|
||||
if (!props.canSelected) {
|
||||
return;
|
||||
}
|
||||
// 获取本地存储的已选课程ID数组
|
||||
let selectedXkkcIds = uni.getStorageSync("selectedXkkcIds") || [];
|
||||
let newSelectedXkkcIds = [];
|
||||
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);
|
||||
}
|
||||
|
||||
// 监听当前学生信息变更
|
||||
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-name {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.register-info {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
margin-bottom: 12px;
|
||||
|
||||
.register-count {
|
||||
color: #2879ff;
|
||||
}
|
||||
}
|
||||
|
||||
.detail-btn {
|
||||
display: inline-block;
|
||||
color: #2879ff;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.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>
|
||||
@ -94,7 +94,7 @@ const switchXs = (xs: any) => {
|
||||
}
|
||||
|
||||
// 如果是bar形式,则默认打开选择器
|
||||
if (props.isBar && getUser.xsList.length > 1) {
|
||||
if (props.isBar && getUser.xsList.length > 1 && (getCurXs === null || !getCurXs.id)) {
|
||||
showPicker();
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -4,51 +4,26 @@
|
||||
<view class="selection-header">
|
||||
<view class="header-content">
|
||||
<!-- 选课类型选择部分 -->
|
||||
<XkPicker xklx-id="816059832" :xs-id="curXs.id" :nj-id="curXs.njId" @change="switchXk" />
|
||||
<XkPicker title="俱乐部信息" :is-xs="false" xklx-id="816059832" :xs-id="curXs.id" :nj-id="curXs.njId" @change="switchXk" />
|
||||
<!-- 学生选择部分 -->
|
||||
<XsPicker :is-bar="true" />
|
||||
<!-- 倒计时-->
|
||||
<XkCountdown :xk="curXk" @over="xkTimeOver" />
|
||||
<XkCountdown :xk="curXk" @over="xkTimeOver" v-if="curXk && curXk.id" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 可滚动的内容区域 -->
|
||||
<view class="scrollable-content">
|
||||
<!-- 课程网格列表 -->
|
||||
<view class="course-grid" v-if="courseListData.length > 0">
|
||||
<view
|
||||
v-for="(course, index) in courseListData"
|
||||
:key="course.id || index"
|
||||
class="course-item"
|
||||
:class="{ selected: course.isSelected }"
|
||||
>
|
||||
<view class="course-name">{{ course.kcmc }}</view>
|
||||
<view class="register-info">
|
||||
<text>上课人数:</text>
|
||||
<text class="register-count">{{ course.ybmr }}</text>
|
||||
</view>
|
||||
<view class="detail-btn" @click.stop="viewCourseDetail(course)"
|
||||
>详情</view
|
||||
>
|
||||
<view v-if="course.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>
|
||||
<XkkcList :xk="curXk" :can-selected="true" @change="changeXkkc" />
|
||||
</view>
|
||||
|
||||
<!-- 底部报名按钮 - 固定部分 -->
|
||||
<view class="register-btn-container">
|
||||
<view class="selected-count-info" v-if="selectedXkkcIds && selectedXkkcIds.length > 0">
|
||||
已选 {{ selectedXkkcIds.length }} 门课程
|
||||
</view>
|
||||
<view class="register-btn" @click="submit">点击报名</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
@ -56,18 +31,14 @@
|
||||
import XsPicker from "@/pages/base/components/XsPicker/index.vue"
|
||||
import XkPicker from "@/pages/base/components/XkPicker/index.vue"
|
||||
import XkCountdown from "@/pages/base/components/XkCountdown/index.vue"
|
||||
import XkkcList from "@/pages/base/components/XkkcList/index.vue"
|
||||
import { useUserStore } from "@/store/modules/user";
|
||||
import { useDataStore } from "@/store/modules/data";
|
||||
|
||||
const { getCurXs } = useUserStore();
|
||||
const { setKcData } = useDataStore();
|
||||
|
||||
const curXs = computed(() => getCurXs);
|
||||
|
||||
const curXk = ref();
|
||||
const kcStatus = ref(false);
|
||||
|
||||
const courseInfo = ref<any>({});
|
||||
const curXk = ref<any>({});
|
||||
const selectedXkkcIds = ref<any>([]);
|
||||
// 切换选课
|
||||
const switchXk = (xk: any) => {
|
||||
curXk.value = xk;
|
||||
@ -78,63 +49,17 @@ const xkTimeOver = (val: any) => {
|
||||
console.log(val);
|
||||
}
|
||||
|
||||
// 从课程数据中提取课程列表
|
||||
const displayCourseList = computed(() => {
|
||||
if (
|
||||
!curXk.value ||
|
||||
!curXk.value.xkkcs ||
|
||||
!Array.isArray(curXk.value.xkkcs)
|
||||
) {
|
||||
return [];
|
||||
}
|
||||
// 选中课程
|
||||
const changeXkkc = (ids: any) => {
|
||||
console.log(ids);
|
||||
selectedXkkcIds.value = ids;
|
||||
}
|
||||
|
||||
// 获取本地存储的已选课程ID数组
|
||||
const selectedCourseIds = uni.getStorageSync("selectedCourseIds") || [];
|
||||
// 提交选课
|
||||
const submit = () => {
|
||||
console.log(selectedXkkcIds.value);
|
||||
}
|
||||
|
||||
// 为课程添加选中状态属性
|
||||
return curXk.value.xkkcs.map((course: any) => ({
|
||||
...course,
|
||||
isSelected: selectedCourseIds.includes(course.id),
|
||||
}));
|
||||
});
|
||||
|
||||
// 可修改的课程列表数据
|
||||
const courseListData = ref<any[]>([]);
|
||||
|
||||
// 计算已选择的课程数量
|
||||
const selectedCoursesCount = computed(() => {
|
||||
return courseListData.value.filter((course: any) => course.isSelected).length;
|
||||
});
|
||||
|
||||
// 监听计算属性变化,更新可修改的数据
|
||||
watch(
|
||||
displayCourseList,
|
||||
(newVal) => {
|
||||
courseListData.value = JSON.parse(JSON.stringify(newVal));
|
||||
|
||||
// 初始化时,将已选课程的全部信息存入courseInfo
|
||||
const selectedCourseIds = uni.getStorageSync("selectedCourseIds") || [];
|
||||
if (selectedCourseIds.length > 0) {
|
||||
const selectedCourses = newVal.filter((course: any) =>
|
||||
selectedCourseIds.includes(course.id)
|
||||
);
|
||||
courseInfo.value = selectedCourses;
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
// 查看课程详情
|
||||
const viewCourseDetail = (course: any) => {
|
||||
setKcData(course);
|
||||
uni.navigateTo({
|
||||
url: `/pages/base/course-selection/detail`,
|
||||
});
|
||||
};
|
||||
|
||||
// 页面卸载前清除定时器
|
||||
onBeforeUnmount(() => {
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@ -202,67 +127,6 @@ onBeforeUnmount(() => {
|
||||
-webkit-overflow-scrolling: touch; // 增强iOS滚动体验
|
||||
}
|
||||
|
||||
.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-name {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.register-info {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
margin-bottom: 12px;
|
||||
|
||||
.register-count {
|
||||
color: #2879ff;
|
||||
}
|
||||
}
|
||||
|
||||
.detail-btn {
|
||||
display: inline-block;
|
||||
color: #2879ff;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.selected-mark {
|
||||
position: absolute;
|
||||
top: -6px;
|
||||
right: -6px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.register-btn-container {
|
||||
position: sticky;
|
||||
bottom: 0;
|
||||
@ -270,9 +134,15 @@ onBeforeUnmount(() => {
|
||||
right: 0;
|
||||
padding: 15px;
|
||||
background-color: #fff;
|
||||
z-index: 10;
|
||||
z-index: 1;
|
||||
box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.05);
|
||||
|
||||
.selected-count-info {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.register-btn {
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
@ -285,52 +155,4 @@ onBeforeUnmount(() => {
|
||||
}
|
||||
}
|
||||
|
||||
// 暂无数据样式
|
||||
.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;
|
||||
}
|
||||
}
|
||||
|
||||
/* 选课已结束样式 */
|
||||
.enrollment-ended {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
border-radius: 8px;
|
||||
padding: 12px 15px;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #fff;
|
||||
gap: 8px;
|
||||
}
|
||||
</style>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -3,308 +3,37 @@
|
||||
<!-- 选课信息头部 - 固定部分 -->
|
||||
<view class="selection-header">
|
||||
<view class="header-content">
|
||||
<view class="title-section">
|
||||
<view class="title">
|
||||
<text v-if="xkData && xkData.xkmc">{{ xkData.xkmc }}</text>
|
||||
<text v-else>选课信息</text>
|
||||
</view>
|
||||
<!-- <view class="subtitle">互动兴趣课程</view> -->
|
||||
</view>
|
||||
<!-- 选课类型选择部分 -->
|
||||
<XkPicker title="兴趣课信息" :is-xs="true" xklx-id="962488654" :xs-id="curXs.id" :nj-id="curXs.njId" @change="switchXk" />
|
||||
<!-- 学生选择部分 -->
|
||||
<view class="student-selector-bar" @click="showStudentSelector">
|
||||
<view class="user-avatar">
|
||||
<image
|
||||
:src="currentStudent.avatar || '/static/base/home/11222.png'"
|
||||
class="w-full h-full"
|
||||
></image>
|
||||
</view>
|
||||
<view class="student-info">
|
||||
<text class="student-name">{{ currentStudent.xm }}</text>
|
||||
<text class="student-class"
|
||||
>{{ currentStudent.njmc }} {{ currentStudent.bjmc }}</text
|
||||
>
|
||||
</view>
|
||||
<view class="switch-btn" v-if="studentList.length > 1">切换</view>
|
||||
</view>
|
||||
<XsPicker :is-bar="true" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 可滚动的内容区域 -->
|
||||
<view class="scrollable-content">
|
||||
<!-- 课程网格列表 -->
|
||||
<view class="course-grid" v-if="courseListData.length > 0">
|
||||
<view
|
||||
v-for="(course, index) in courseListData"
|
||||
:key="course.id || index"
|
||||
class="course-item"
|
||||
:class="{ selected: course.isSelected }"
|
||||
>
|
||||
<view class="course-name">{{ course.kcmc }}</view>
|
||||
<view class="register-info">
|
||||
<text>上课人数:</text>
|
||||
<text class="register-count">{{ course.ybmr }}</text>
|
||||
</view>
|
||||
<view class="detail-btn" @click.stop="viewCourseDetail(course)"
|
||||
>详情</view
|
||||
>
|
||||
<view v-if="course.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>
|
||||
<XkkcList :xk="curXk" :can-selected="false" />
|
||||
</view>
|
||||
|
||||
<view>
|
||||
<!-- 学生选择弹窗 -->
|
||||
<u-popup
|
||||
:show="showSelector"
|
||||
@close="showSelector = false"
|
||||
mode="bottom"
|
||||
round="10"
|
||||
>
|
||||
<view class="student-selector">
|
||||
<view class="selector-header">
|
||||
<text class="selector-title">选择学生</text>
|
||||
<u-icon
|
||||
name="close"
|
||||
size="20"
|
||||
@click="showSelector = false"
|
||||
></u-icon>
|
||||
</view>
|
||||
<view class="student-list">
|
||||
<view
|
||||
v-for="(student, index) in studentList"
|
||||
:key="index"
|
||||
class="student-item"
|
||||
:class="{
|
||||
'student-item-active': currentStudent.id === student.id,
|
||||
}"
|
||||
@click="switchStudent(student)"
|
||||
>
|
||||
<view class="student-avatar">
|
||||
<image
|
||||
:src="student.avatar || '/static/base/home/11222.png'"
|
||||
class="w-full h-full"
|
||||
></image>
|
||||
</view>
|
||||
<view class="student-info">
|
||||
<text class="student-name">{{ student.xm }}</text>
|
||||
<text class="student-class"
|
||||
>{{ student.njmc }} {{ student.bjmc }}</text
|
||||
>
|
||||
</view>
|
||||
<u-icon
|
||||
v-if="currentStudent.id === student.id"
|
||||
name="checkmark"
|
||||
color="#409EFF"
|
||||
size="20"
|
||||
></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { navigateTo } from "@/utils/uniapp";
|
||||
import {
|
||||
ref,
|
||||
computed,
|
||||
reactive,
|
||||
onBeforeUnmount,
|
||||
watch,
|
||||
onMounted,
|
||||
} from "vue";
|
||||
import XsPicker from "@/pages/base/components/XsPicker/index.vue"
|
||||
import XkPicker from "@/pages/base/components/XkPicker/index.vue"
|
||||
import XkkcList from "@/pages/base/components/XkkcList/index.vue"
|
||||
import { useUserStore } from "@/store/modules/user";
|
||||
import { useDataStore } from "@/store/modules/data";
|
||||
import { xkListApi, xsXkListApi } from "@/api/base/server";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
const { getUser } = useUserStore();
|
||||
const { getData, setKcData, setData } = useDataStore();
|
||||
const { sign_file } = getData;
|
||||
const { getCurXs } = useUserStore();
|
||||
|
||||
// 学生列表数据
|
||||
const studentList = computed(() => {
|
||||
return getUser.xsList;
|
||||
});
|
||||
const curXs = computed(() => getCurXs);
|
||||
|
||||
const currentStudent = ref();
|
||||
|
||||
// 控制选择器显示状态
|
||||
const showSelector = ref(false);
|
||||
|
||||
if (studentList.value.length > 1) {
|
||||
showSelector.value = true;
|
||||
const curXk = ref<any>({});
|
||||
// 切换选课
|
||||
const switchXk = (xk: any) => {
|
||||
curXk.value = xk;
|
||||
}
|
||||
|
||||
const xkData = ref();
|
||||
const kcStatus = ref(false);
|
||||
|
||||
// 添加选课是否已结束的标记
|
||||
const isEnrollmentEnded = ref(false);
|
||||
|
||||
// 添加轮询定时器变量
|
||||
let pollTimer: number | null = null;
|
||||
|
||||
const courseInfo = ref({});
|
||||
|
||||
// 更新课程报名人数
|
||||
const updateEnrollmentCount = (
|
||||
data: Array<{ xkkcId: string; bmrs: number }>
|
||||
) => {
|
||||
if (!Array.isArray(courseListData.value) || courseListData.value.length === 0)
|
||||
return;
|
||||
|
||||
let hasUpdates = false;
|
||||
|
||||
courseListData.value.forEach((course) => {
|
||||
const newCount = data.find((item) => item.xkkcId === course.id);
|
||||
if (newCount && course.ybmr !== newCount.bmrs) {
|
||||
course.ybmr = newCount.bmrs;
|
||||
hasUpdates = true;
|
||||
}
|
||||
});
|
||||
|
||||
// 如果有更新,强制重新渲染列表
|
||||
if (hasUpdates) {
|
||||
courseListData.value = [...courseListData.value];
|
||||
}
|
||||
};
|
||||
|
||||
// 在组件挂载时开始轮询
|
||||
onMounted(() => {
|
||||
|
||||
});
|
||||
|
||||
// 加载课程列表
|
||||
const loadCourseList = (currentStudent: any) => {
|
||||
if (!currentStudent) {
|
||||
uni.hideLoading();
|
||||
return;
|
||||
}
|
||||
|
||||
xsXkListApi({
|
||||
xsId: currentStudent.id,
|
||||
njId: currentStudent.njId,
|
||||
xklxId: "962488654",
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.resultCode == 1) {
|
||||
if (res.result && res.result.length) {
|
||||
xkData.value = res.result[0];
|
||||
} else {
|
||||
xkData.value = [];
|
||||
}
|
||||
uni.hideLoading();
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
uni.hideLoading();
|
||||
});
|
||||
};
|
||||
|
||||
if (studentList.value.length > 0 && studentList.value.length === 1) {
|
||||
currentStudent.value = studentList.value[0];
|
||||
// 先加载课程列表,不立即检查报名状态
|
||||
uni.showLoading({
|
||||
title: "加载中...",
|
||||
});
|
||||
loadCourseList(currentStudent.value);
|
||||
}
|
||||
|
||||
// 显示学生选择器
|
||||
function showStudentSelector() {
|
||||
if (studentList.value.length > 1) {
|
||||
showSelector.value = true;
|
||||
}
|
||||
}
|
||||
|
||||
// 切换学生
|
||||
function switchStudent(student: any) {
|
||||
currentStudent.value = student;
|
||||
showSelector.value = false;
|
||||
|
||||
// 显示加载中
|
||||
uni.showLoading({
|
||||
title: "加载中...",
|
||||
});
|
||||
|
||||
// 显示切换成功提示
|
||||
uni.showToast({
|
||||
title: `已切换到${student.xm}`,
|
||||
icon: "none",
|
||||
});
|
||||
|
||||
// 先加载当前学生的课程列表,课程数据加载完成后会自动检查报名状态
|
||||
loadCourseList(student);
|
||||
}
|
||||
|
||||
// 从课程数据中提取课程列表
|
||||
const displayCourseList = computed(() => {
|
||||
if (
|
||||
!xkData.value ||
|
||||
!xkData.value.xkkcs ||
|
||||
!Array.isArray(xkData.value.xkkcs)
|
||||
) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// 获取本地存储的已选课程ID
|
||||
const selectedCourseId = uni.getStorageSync("selectedCourseId");
|
||||
|
||||
// 为课程添加选中状态属性
|
||||
return xkData.value.xkkcs.map((course: any) => ({
|
||||
...course,
|
||||
isSelected: selectedCourseId && course.id === selectedCourseId,
|
||||
}));
|
||||
});
|
||||
|
||||
// 可修改的课程列表数据
|
||||
const courseListData = ref<any[]>([]);
|
||||
|
||||
// 监听计算属性变化,更新可修改的数据
|
||||
watch(
|
||||
displayCourseList,
|
||||
(newVal) => {
|
||||
courseListData.value = JSON.parse(JSON.stringify(newVal));
|
||||
|
||||
// 初始化时,将已选课程的全部信息存入courseInfo
|
||||
const selectedCourseId = uni.getStorageSync("selectedCourseId");
|
||||
if (selectedCourseId) {
|
||||
const selectedCourse = newVal.find(
|
||||
(course: any) => course.id === selectedCourseId
|
||||
);
|
||||
if (selectedCourse) {
|
||||
courseInfo.value = selectedCourse;
|
||||
}
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
// 查看课程详情
|
||||
const viewCourseDetail = (course: any) => {
|
||||
setKcData(course);
|
||||
uni.navigateTo({
|
||||
url: `/pages/base/course-selection/detail`,
|
||||
});
|
||||
};
|
||||
|
||||
// 页面卸载前清除定时器
|
||||
onBeforeUnmount(() => {
|
||||
});
|
||||
@ -365,97 +94,6 @@ onBeforeUnmount(() => {
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
|
||||
.title-section {
|
||||
.title {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 14px;
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
// 学生选择栏样式
|
||||
.student-selector-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 10px;
|
||||
padding: 10px;
|
||||
|
||||
.user-avatar {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
background-color: #fff;
|
||||
overflow: hidden;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.student-info {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.student-name {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.student-class {
|
||||
font-size: 12px;
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
.switch-btn {
|
||||
padding: 4px 12px;
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
color: #fff;
|
||||
border-radius: 20px;
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
|
||||
.countdown-section {
|
||||
.countdown-title {
|
||||
font-size: 14px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.countdown-timer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.time-block {
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
border-radius: 6px;
|
||||
min-width: 40px;
|
||||
padding: 5px 8px;
|
||||
text-align: center;
|
||||
|
||||
.time-value {
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.time-unit {
|
||||
font-size: 12px;
|
||||
opacity: 0.9;
|
||||
}
|
||||
}
|
||||
|
||||
.time-separator {
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
margin: 0 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -466,211 +104,4 @@ onBeforeUnmount(() => {
|
||||
-webkit-overflow-scrolling: touch; // 增强iOS滚动体验
|
||||
}
|
||||
|
||||
.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-name {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.register-info {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
margin-bottom: 12px;
|
||||
|
||||
.register-count {
|
||||
color: #2879ff;
|
||||
}
|
||||
}
|
||||
|
||||
.detail-btn {
|
||||
display: inline-block;
|
||||
color: #2879ff;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.selected-mark {
|
||||
position: absolute;
|
||||
top: -6px;
|
||||
right: -6px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.register-btn-container {
|
||||
position: sticky;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
padding: 15px;
|
||||
background-color: #fff;
|
||||
z-index: 10;
|
||||
box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.05);
|
||||
|
||||
.register-btn {
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
text-align: center;
|
||||
background-color: #2879ff;
|
||||
color: #fff;
|
||||
border-radius: 25px;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
/* 学生选择器弹窗样式 */
|
||||
.student-selector {
|
||||
background-color: #ffffff;
|
||||
border-top-left-radius: 12px;
|
||||
border-top-right-radius: 12px;
|
||||
padding-bottom: 20px;
|
||||
|
||||
.selector-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 15px;
|
||||
border-bottom: 1px solid #f2f2f2;
|
||||
|
||||
.selector-title {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #303133;
|
||||
}
|
||||
}
|
||||
|
||||
.student-list {
|
||||
padding: 0 15px;
|
||||
|
||||
.student-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 15px 0;
|
||||
border-bottom: 1px solid #f2f2f2;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
&-active {
|
||||
background-color: rgba(64, 158, 255, 0.05);
|
||||
}
|
||||
|
||||
.student-avatar {
|
||||
width: 45px;
|
||||
height: 45px;
|
||||
border-radius: 50%;
|
||||
background-color: #f0f0f0;
|
||||
overflow: hidden;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.student-info {
|
||||
flex: 1;
|
||||
margin-left: 12px;
|
||||
|
||||
.student-name {
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
color: #303133;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.student-class {
|
||||
font-size: 13px;
|
||||
color: #606266;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 全局图片样式 */
|
||||
.w-full {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.h-full {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
// 暂无数据样式
|
||||
.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;
|
||||
}
|
||||
}
|
||||
|
||||
/* 选课已结束样式 */
|
||||
.enrollment-ended {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
border-radius: 8px;
|
||||
padding: 12px 15px;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #fff;
|
||||
gap: 8px;
|
||||
}
|
||||
</style>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user