2025-08-18 20:47:50 +08:00

547 lines
13 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 class="interest-course">
<!-- 选课信息头部 - 固定部分 -->
<view class="selection-header">
<view class="header-content">
<view class="title-section">
<view class="title">
<text v-if="xkkcList && xkkcList.length > 0">
{{ getCurrentSemesterName() }} - 我的课程
</text>
<text v-else>暂无课程</text>
</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">
<view class="status-badge" :class="getStatusClass(xkkc)">
{{ getStatusText(xkkc) }}
</view>
<view class="course-name">{{ xkkc.kcmc }}</view>
<view class="course-info-item">
<view class="info-label">上课周期</view>
<view class="info-data">{{ xkkc.skzqmc }}</view>
</view>
<view class="course-info-item">
<view class="info-label">上课时间</view>
<view class="info-data">{{ formatClassTime(xkkc.skkstime, xkkc.skjstime) }}</view>
</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>
<view class="separator-line"></view>
<view class="course-btn-group">
<view class="detail-btn" @click.stop="goDetail(xkkc)">填报</view>
</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>
</view>
</template>
<script setup lang="ts">
import {
ref,
onBeforeUnmount,
onMounted,
} from "vue";
import { useUserStore } from "@/store/modules/user";
import { useDataStore } from "@/store/modules/data";
import { xkkcListByJsIdApi } from "@/api/base/xkApi";
import dayjs from "dayjs";
const { getJs } = useUserStore();
const { getData, setData } = useDataStore();
const wdNameList = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"];
// 课程列表数据
const xkkcList = ref<any[]>([]);
onMounted(() => {
loadCourseList();
});
// 加载课程列表
const loadCourseList = async () => {
uni.showLoading({
title: "加载中...",
});
try {
const res:any = await xkkcListByJsIdApi({ jsId: getJs.id });
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();
}
};
// 处理课程周期显示
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;
}
}
};
// 获取当前学期名称
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';
};
// 查看课程详情
const goDetail = (xkkc: any) => {
setData(xkkc);
uni.navigateTo({
url: `/pages/view/routine/xk/xkkcDetail`,
});
};
// 页面卸载前清除定时器
onBeforeUnmount(() => {
});
</script>
<style lang="scss" scoped>
.interest-course {
min-height: 100%;
background: linear-gradient(180deg, #f8fafc 0%, #f1f5f9 100%);
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 {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
padding: 25px 20px;
color: #fff;
border-radius: 0 0 20px 20px;
box-shadow: 0 8px 32px rgba(102, 126, 234, 0.3);
position: sticky;
top: 0;
left: 0;
right: 0;
z-index: 10;
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;
}
.header-content {
display: flex;
flex-direction: column;
gap: 15px;
position: relative;
z-index: 1;
.title-section {
display: flex;
align-items: center;
justify-content: center;
.title {
font-size: 20px;
font-weight: 700;
text-align: center;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
letter-spacing: 0.5px;
}
}
}
}
// 可滚动内容区域样式
.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%;
margin-bottom: 20px;
background-color: #fff;
border-radius: 12px;
padding: 20px;
box-sizing: border-box;
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;
}
// &:nth-child(odd) {
// margin-right: 10px;
// }
// &:nth-child(even) {
// margin-left: 10px;
// }
&.selected {
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;
}
.course-name {
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;
}
.course-btn-group {
display: flex;
justify-content: flex-end;
.detail-btn {
display: inline-block;
color: #2879ff;
font-size: 14px;
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);
}
}
}
.course-info-item {
display: flex;
margin-bottom: 14px;
font-size: 13px;
align-items: center;
animation: fadeInUp 0.5s ease-out 0.15s both;
.info-label {
color: #666;
flex: 0 0 100px;
font-weight: 500;
}
.info-data {
flex: 1 0 1px;
color: #333;
font-weight: 400;
}
}
.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;
}
}
}
// 添加动画关键帧
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes fadeInLeft {
from {
opacity: 0;
transform: translateX(-20px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
@keyframes fadeInRight {
from {
opacity: 0;
transform: translateX(20px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
/* 全局图片样式 */
.w-full {
width: 100%;
}
.h-full {
height: 100%;
}
// 暂无数据样式
.empty-course-list {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 80px 20px;
text-align: center;
.empty-icon {
margin-bottom: 25px;
background: linear-gradient(135deg, #f1f5f9 0%, #e2e8f0 100%);
width: 90px;
height: 90px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
border: 2px solid rgba(255, 255, 255, 0.8);
}
.empty-text {
font-size: 18px;
font-weight: 600;
color: #475569;
margin-bottom: 8px;
letter-spacing: 0.3px;
}
.empty-desc {
font-size: 14px;
color: #64748b;
max-width: 80%;
line-height: 1.6;
}
}
/* 选课已结束样式 */
.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>