547 lines
13 KiB
Vue
Raw Normal View History

2025-05-21 02:44:13 +08:00
<template>
2025-06-20 09:51:43 +08:00
<view class="interest-course">
<!-- 选课信息头部 - 固定部分 -->
<view class="selection-header">
<view class="header-content">
2025-08-11 21:11:19 +08:00
<view class="title-section">
2025-06-20 09:51:43 +08:00
<view class="title">
2025-08-11 21:11:19 +08:00
<text v-if="xkkcList && xkkcList.length > 0">
{{ getCurrentSemesterName() }} - 我的课程
</text>
<text v-else>暂无课程</text>
2025-06-20 09:51:43 +08:00
</view>
</view>
</view>
</view>
<!-- 可滚动的内容区域 -->
<view class="scrollable-content">
<!-- 课程网格列表 -->
<view class="course-list" v-if="xkkcList && xkkcList.length > 0">
<view v-for="(xkkc, index) in xkkcList" :key="xkkc.id || index" class="course-item">
2025-08-11 21:11:19 +08:00
<view class="status-badge" :class="getStatusClass(xkkc)">
{{ getStatusText(xkkc) }}
2025-06-20 09:51:43 +08:00
</view>
2025-08-11 21:11:19 +08:00
<view class="course-name">{{ xkkc.kcmc }}</view>
2025-06-20 09:51:43 +08:00
<view class="course-info-item">
<view class="info-label">上课周期</view>
<view class="info-data">{{ xkkc.skzqmc }}</view>
</view>
<view class="course-info-item">
2025-08-11 21:11:19 +08:00
<view class="info-label">上课时间</view>
<view class="info-data">{{ formatClassTime(xkkc.skkstime, xkkc.skjstime) }}</view>
2025-06-20 09:51:43 +08:00
</view>
<view class="course-info-item">
<view class="info-label">开课地点</view>
<view class="info-data">{{ xkkc.kcdd }}</view>
</view>
<view class="course-info-item">
<view class="info-label">上课人数</view>
<view class="info-data">{{ xkkc.hasNum || 0 }} | {{ xkkc.maxNum || 0 }}</view>
</view>
2025-08-11 21:11:19 +08:00
<view class="separator-line"></view>
2025-06-20 09:51:43 +08:00
<view class="course-btn-group">
2025-08-11 21:11:19 +08:00
<view class="detail-btn" @click.stop="goDetail(xkkc)">填报</view>
2025-06-20 09:51:43 +08:00
</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>
2025-05-21 02:44:13 +08:00
</view>
</template>
<script setup lang="ts">
2025-06-20 09:51:43 +08:00
import {
ref,
onBeforeUnmount,
onMounted,
} from "vue";
import { useUserStore } from "@/store/modules/user";
import { useDataStore } from "@/store/modules/data";
2025-08-18 20:47:50 +08:00
import { xkkcListByJsIdApi } from "@/api/base/xkApi";
2025-06-20 09:51:43 +08:00
import dayjs from "dayjs";
const { getJs } = useUserStore();
const { getData, setData } = useDataStore();
const wdNameList = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"];
// 课程列表数据
const xkkcList = ref<any[]>([]);
onMounted(() => {
loadCourseList();
2025-05-21 02:44:13 +08:00
});
2025-06-20 09:51:43 +08:00
// 加载课程列表
2025-06-20 18:38:22 +08:00
const loadCourseList = async () => {
2025-06-20 09:51:43 +08:00
uni.showLoading({
title: "加载中...",
});
2025-08-11 21:11:19 +08:00
try {
2025-08-18 20:47:50 +08:00
const res:any = await xkkcListByJsIdApi({ jsId: getJs.id });
2025-08-11 21:11:19 +08:00
if (res.resultCode == 1) {
if (res.result && res.result.length) {
xkkcList.value = res.result;
// 处理课程周期显示
processCoursePeriods();
} else {
xkkcList.value = [];
}
} else {
xkkcList.value = [];
uni.showToast({
title: res.resultMessage || '获取课程列表失败',
icon: 'none'
});
}
} catch (error) {
console.error('加载课程列表失败:', error);
xkkcList.value = [];
uni.showToast({
title: '加载课程列表失败',
icon: 'none'
});
} finally {
uni.hideLoading();
}
2025-06-20 09:51:43 +08:00
};
2025-08-11 21:11:19 +08:00
// 处理课程周期显示
const processCoursePeriods = () => {
for (let i = 0; i < xkkcList.value.length; i++) {
let xkkc = xkkcList.value[i];
// 判断周期
switch (xkkc.skzqlx) {
case '每天':
xkkc.skzqmc = "每天";
break;
case '每周':
const daysOfWeek = xkkc.skzq.split(',').map(Number);
// 从wdNameList读取daysOfWeek对应的周几
xkkc.skzqmc = daysOfWeek.map((day: number) => wdNameList[day - 1]).join(',');
break;
case '每月':
const daysOfMonth = xkkc.skzq.split(',').map(Number);
// 从根据编号加
xkkc.skzqmc = daysOfMonth.map((day: number) => day + "号").join(',');
break;
}
2025-06-20 09:51:43 +08:00
}
2025-08-11 21:11:19 +08:00
};
2025-06-20 09:51:43 +08:00
2025-08-11 21:11:19 +08:00
// 获取当前学期名称
const getCurrentSemesterName = () => {
if (xkkcList.value && xkkcList.value.length > 0) {
// 从第一个课程获取学期名称
return xkkcList.value[0].xqmc || '当前学期';
}
return '当前学期';
};
// 格式化上课时间
const formatClassTime = (startTime: string, endTime: string) => {
if (!startTime || !endTime) {
return '';
}
try {
// 尝试解析时间,支持多种格式
let start, end;
// 如果是时间格式HH:mm:ss 或 HH:mm
if (startTime.includes(':') && !startTime.includes('-') && !startTime.includes('/')) {
start = startTime;
end = endTime;
} else {
// 尝试用 dayjs 解析
const startDate = dayjs(startTime);
const endDate = dayjs(endTime);
if (startDate.isValid() && endDate.isValid()) {
start = startDate.format('HH:mm:ss');
end = endDate.format('HH:mm:ss');
} else {
// 如果解析失败,直接返回原始值
return `${startTime}~${endTime}`;
}
}
return `${start}~${end}`;
} catch (error) {
console.error('时间格式化错误:', error);
// 如果出错,返回原始值
return `${startTime}~${endTime}`;
}
};
// 获取状态文本
const getStatusText = (xkkc: any) => {
if (!xkkc.kcjsms && !xkkc.jxll) {
return '待填报';
}
return '已填报';
};
// 获取状态样式类
const getStatusClass = (xkkc: any) => {
if (!xkkc.kcjsms && !xkkc.jxll) {
return 'pending';
}
return 'completed';
};
2025-06-20 09:51:43 +08:00
// 查看课程详情
const goDetail = (xkkc: any) => {
setData(xkkc);
2025-05-21 02:44:13 +08:00
uni.navigateTo({
2025-08-18 20:47:50 +08:00
url: `/pages/view/routine/xk/xkkcDetail`,
2025-05-21 02:44:13 +08:00
});
};
2025-06-20 09:51:43 +08:00
// 页面卸载前清除定时器
onBeforeUnmount(() => {
});
2025-05-21 02:44:13 +08:00
</script>
2025-06-20 09:51:43 +08:00
2025-05-21 02:44:13 +08:00
<style lang="scss" scoped>
2025-06-20 09:51:43 +08:00
.interest-course {
min-height: 100%;
2025-08-11 21:11:19 +08:00
background: linear-gradient(180deg, #f8fafc 0%, #f1f5f9 100%);
2025-06-20 09:51:43 +08:00
display: flex;
flex-direction: column;
height: 100%;
overflow: hidden;
}
.nav-bar {
display: flex;
align-items: center;
justify-content: space-between;
padding: 15px;
height: 44px;
background-color: #fff;
.nav-left {
width: 40px;
height: 40px;
display: flex;
align-items: center;
}
.nav-title {
font-size: 18px;
font-weight: 500;
color: #333;
}
.nav-right {
width: 40px;
display: flex;
justify-content: flex-end;
}
}
.selection-header {
2025-08-11 21:11:19 +08:00
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
padding: 25px 20px;
2025-06-20 09:51:43 +08:00
color: #fff;
2025-08-11 21:11:19 +08:00
border-radius: 0 0 20px 20px;
box-shadow: 0 8px 32px rgba(102, 126, 234, 0.3);
2025-06-20 09:51:43 +08:00
position: sticky;
top: 0;
left: 0;
right: 0;
z-index: 10;
2025-08-11 21:11:19 +08:00
position: relative;
overflow: hidden;
&::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(45deg, rgba(255, 255, 255, 0.1) 0%, transparent 50%, rgba(255, 255, 255, 0.05) 100%);
pointer-events: none;
}
2025-06-20 09:51:43 +08:00
.header-content {
display: flex;
flex-direction: column;
gap: 15px;
2025-08-11 21:11:19 +08:00
position: relative;
z-index: 1;
2025-06-20 09:51:43 +08:00
.title-section {
display: flex;
align-items: center;
2025-08-11 21:11:19 +08:00
justify-content: center;
2025-06-20 09:51:43 +08:00
.title {
2025-08-11 21:11:19 +08:00
font-size: 20px;
font-weight: 700;
text-align: center;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
letter-spacing: 0.5px;
2025-06-20 09:51:43 +08:00
}
}
}
}
// 可滚动内容区域样式
.scrollable-content {
flex: 1;
overflow-y: auto;
-webkit-overflow-scrolling: touch; // 增强iOS滚动体验
}
.course-list {
padding: 15px 15px 0 15px;
.course-item {
position: relative;
width: 100%;
2025-08-11 21:11:19 +08:00
margin-bottom: 20px;
2025-06-20 09:51:43 +08:00
background-color: #fff;
2025-08-11 21:11:19 +08:00
border-radius: 12px;
padding: 20px;
2025-06-20 09:51:43 +08:00
box-sizing: border-box;
2025-08-11 21:11:19 +08:00
border: 1px solid #f0f0f0;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
animation: fadeInUp 0.6s ease-out;
&:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.12);
border-color: #e8e8e8;
}
2025-06-20 09:51:43 +08:00
// &:nth-child(odd) {
// margin-right: 10px;
// }
// &:nth-child(even) {
// margin-left: 10px;
// }
&.selected {
2025-08-11 21:11:19 +08:00
border: 2px solid #3fbf72;
background-color: rgba(63, 191, 114, 0.02);
box-shadow: 0 4px 20px rgba(63, 191, 114, 0.15);
}
.status-badge {
position: absolute;
top: 15px;
right: 15px;
background-color: #fff;
border-radius: 20px;
padding: 6px 12px;
font-size: 11px;
font-weight: 600;
border: 1px solid #e0e0e0;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
letter-spacing: 0.5px;
animation: fadeInRight 0.5s ease-out 0.2s both;
}
.status-badge.pending {
color: #f56c6c;
border-color: #fde2e2;
background-color: #fef0f0;
}
.status-badge.completed {
color: #67c23a;
border-color: #e1f3d8;
background-color: #f0f9ff;
2025-06-20 09:51:43 +08:00
}
.course-name {
2025-08-11 21:11:19 +08:00
font-size: 17px;
font-weight: 600;
color: #1a1a1a;
margin-bottom: 15px;
padding-right: 90px;
line-height: 1.4;
animation: fadeInLeft 0.5s ease-out 0.1s both;
2025-06-20 09:51:43 +08:00
}
.course-btn-group {
display: flex;
2025-08-11 21:11:19 +08:00
justify-content: flex-end;
2025-06-20 09:51:43 +08:00
.detail-btn {
display: inline-block;
color: #2879ff;
font-size: 14px;
2025-08-11 21:11:19 +08:00
font-weight: 600;
padding: 8px 18px;
border-radius: 8px;
background: linear-gradient(135deg, rgba(40, 121, 255, 0.1), rgba(40, 121, 255, 0.05));
border: 1px solid #2879ff;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
box-shadow: 0 2px 8px rgba(40, 121, 255, 0.15);
animation: fadeInUp 0.5s ease-out 0.3s both;
&:hover {
background: linear-gradient(135deg, rgba(40, 121, 255, 0.15), rgba(40, 121, 255, 0.1));
transform: translateY(-1px);
box-shadow: 0 4px 15px rgba(40, 121, 255, 0.25);
}
&:active {
background: linear-gradient(135deg, rgba(40, 121, 255, 0.2), rgba(40, 121, 255, 0.15));
transform: translateY(0);
box-shadow: 0 2px 8px rgba(40, 121, 255, 0.2);
}
2025-06-20 09:51:43 +08:00
}
}
.course-info-item {
display: flex;
2025-08-11 21:11:19 +08:00
margin-bottom: 14px;
font-size: 13px;
align-items: center;
animation: fadeInUp 0.5s ease-out 0.15s both;
2025-06-20 09:51:43 +08:00
.info-label {
2025-08-11 21:11:19 +08:00
color: #666;
2025-06-20 09:51:43 +08:00
flex: 0 0 100px;
2025-08-11 21:11:19 +08:00
font-weight: 500;
2025-06-20 09:51:43 +08:00
}
.info-data {
flex: 1 0 1px;
2025-08-11 21:11:19 +08:00
color: #333;
font-weight: 400;
2025-06-20 09:51:43 +08:00
}
}
2025-08-11 21:11:19 +08:00
.separator-line {
height: 1px;
background: linear-gradient(90deg, transparent, #e8e8e8, transparent);
margin: 18px 0;
opacity: 0.8;
animation: fadeIn 0.5s ease-out 0.25s both;
2025-06-20 09:51:43 +08:00
}
}
2025-08-11 21:11:19 +08:00
}
2025-06-20 09:51:43 +08:00
2025-08-11 21:11:19 +08:00
// 添加动画关键帧
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
2025-06-20 09:51:43 +08:00
2025-08-11 21:11:19 +08:00
@keyframes fadeInLeft {
from {
opacity: 0;
transform: translateX(-20px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
2025-06-20 09:51:43 +08:00
2025-08-11 21:11:19 +08:00
@keyframes fadeInRight {
from {
opacity: 0;
transform: translateX(20px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
2025-06-20 09:51:43 +08:00
2025-08-11 21:11:19 +08:00
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
2025-06-20 09:51:43 +08:00
}
}
2025-08-11 21:11:19 +08:00
2025-06-20 09:51:43 +08:00
/* 全局图片样式 */
.w-full {
width: 100%;
}
.h-full {
height: 100%;
}
// 暂无数据样式
.empty-course-list {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
2025-08-11 21:11:19 +08:00
padding: 80px 20px;
2025-06-20 09:51:43 +08:00
text-align: center;
.empty-icon {
2025-08-11 21:11:19 +08:00
margin-bottom: 25px;
background: linear-gradient(135deg, #f1f5f9 0%, #e2e8f0 100%);
width: 90px;
height: 90px;
2025-06-20 09:51:43 +08:00
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
2025-08-11 21:11:19 +08:00
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
border: 2px solid rgba(255, 255, 255, 0.8);
2025-06-20 09:51:43 +08:00
}
.empty-text {
font-size: 18px;
2025-08-11 21:11:19 +08:00
font-weight: 600;
color: #475569;
2025-06-20 09:51:43 +08:00
margin-bottom: 8px;
2025-08-11 21:11:19 +08:00
letter-spacing: 0.3px;
2025-06-20 09:51:43 +08:00
}
.empty-desc {
font-size: 14px;
2025-08-11 21:11:19 +08:00
color: #64748b;
2025-06-20 09:51:43 +08:00
max-width: 80%;
2025-08-11 21:11:19 +08:00
line-height: 1.6;
2025-06-20 09:51:43 +08:00
}
}
/* 选课已结束样式 */
.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;
2025-05-21 02:44:13 +08:00
}
</style>