ywyonui fc70ba71b1 1、调整请假
2、修复点名按钮的时间判断
2025-08-30 09:10:44 +08:00

690 lines
17 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="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="dm-btn" @click.stop="goDm(xkkc)">点名</view>
<view class="record-btn" @click.stop="goRecord(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 { dmBeforeMinuteApi } from "@/api/system/config/index";
import dayjs from "dayjs";
const { getJs } = useUserStore();
const { getData, setData } = useDataStore();
const wdNameList = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"];
// 课程列表数据
const xkkcList = ref<any[]>([]);
const dmBeforeMinute = ref<number>(0);
onMounted(async () => {
uni.showLoading({
title: "加载中...",
});
try {
await loadCourseList();
await loadDmBeforeMinute();
} catch (error) {
console.error('页面初始化失败:', error);
uni.showToast({
title: '页面加载失败',
icon: 'none'
});
} finally {
uni.hideLoading();
}
});
// 加载课程列表
const loadCourseList = async () => {
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.message || '获取课程列表失败',
icon: 'none'
});
}
} catch (error) {
console.error('加载课程列表失败:', error);
xkkcList.value = [];
uni.showToast({
title: '加载课程列表失败',
icon: 'none'
});
}
};
// 处理课程周期显示
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 loadDmBeforeMinute = async () => {
const res = await dmBeforeMinuteApi();
if (res.resultCode == 1) {
// 将res.result从字符串转换成int的number赋给dmBeforeMinute
dmBeforeMinute.value = parseInt(res.result);
}
}
// 跳转到点名
const goDm = (xkkc: any) => {
const now = dayjs();
let wDay = now.day();
if (wDay === 0) {
wDay = 7;
}
let mDay = now.date();
const strDate = now.format('YYYY-MM-DD') + ' ';
let dmFlag = false;
let msg = "";
// 判断周期
switch (xkkc.skzqlx) {
case '每天':
dmFlag = true;
break;
case '每周':
const daysOfWeek = xkkc.skzq.split(',').map(Number);
dmFlag = daysOfWeek.includes(wDay);
// 从wdNameList读取daysOfWeek对应的周几
xkkc.skzqmc = daysOfWeek.map((day: number) => wdNameList[day - 1]).join(',');
break;
case '每月':
const daysOfMonth = xkkc.skzq.split(',').map(Number);
dmFlag = daysOfMonth.includes(mDay);
// 从根据编号加
xkkc.skzqmc = daysOfMonth.map((day: number) => day + "号").join(',');
break;
}
// 判断日期是否合格
if (dmFlag) {
// xkkc.skkstime开始时间向前dmBeforeMinute分钟
const startTime = dayjs(strDate + xkkc.skkstime).subtract(dmBeforeMinute.value, 'minute').format('YYYY-MM-DD HH:mm:ss');
const endTime = dayjs(strDate + xkkc.skjstime, 'YYYY-MM-DD HH:mm:ss');
dmFlag = now.isBefore(endTime) && now.isAfter(startTime)
} else {
msg = "上课时间未到,无法点名";
}
if (dmFlag) {
setData(xkkc);
uni.navigateTo({
url: `/pages/view/routine/xk/dm`,
});
} else {
if (msg === "") {
msg = "上课时间未到,无法点名";
}
uni.showToast({
title: msg,
icon: 'none',
duration: 2000
});
}
};
// 跳转到点名记录
const goRecord = (xkkc: any) => {
setData(xkkc);
uni.navigateTo({
url: `/pages/view/routine/xk/dmList`,
});
};
// 页面卸载前清除定时器
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;
}
&.selected {
border: 2px solid #3fbf72;
background-color: rgba(63, 191, 114, 0.02);
box-shadow: 0 4px 20px rgba(63, 191, 114, 0.15);
}
.course-name {
font-size: 17px;
font-weight: 600;
color: #1a1a1a;
margin-bottom: 15px;
line-height: 1.4;
animation: fadeInLeft 0.5s ease-out 0.1s both;
}
.course-btn-group {
display: flex;
justify-content: flex-end;
gap: 10px; // 按钮之间的间距
.dm-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);
}
}
.record-btn {
display: inline-block;
color: #4CAF50; /* A green color for record */
font-size: 14px;
font-weight: 600;
padding: 8px 18px;
border-radius: 8px;
background: linear-gradient(135deg, rgba(76, 175, 80, 0.1), rgba(76, 175, 80, 0.05));
border: 1px solid #4CAF50;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
box-shadow: 0 2px 8px rgba(76, 175, 80, 0.15);
animation: fadeInUp 0.5s ease-out 0.4s both;
&:hover {
background: linear-gradient(135deg, rgba(76, 175, 80, 0.15), rgba(76, 175, 80, 0.1));
transform: translateY(-1px);
box-shadow: 0 4px 15px rgba(76, 175, 80, 0.25);
}
&:active {
background: linear-gradient(135deg, rgba(76, 175, 80, 0.2), rgba(76, 175, 80, 0.15));
transform: translateY(0);
box-shadow: 0 2px 8px rgba(76, 175, 80, 0.2);
}
}
.photo-btn {
display: inline-block;
color: #ff6b35;
font-size: 14px;
font-weight: 600;
padding: 8px 18px;
border-radius: 8px;
background: linear-gradient(135deg, rgba(255, 107, 53, 0.1), rgba(255, 107, 53, 0.05));
border: 1px solid #ff6b35;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
box-shadow: 0 2px 8px rgba(255, 107, 53, 0.15);
animation: fadeInUp 0.5s ease-out 0.4s both;
&:hover {
background: linear-gradient(135deg, rgba(255, 107, 53, 0.15), rgba(255, 107, 53, 0.1));
transform: translateY(-1px);
box-shadow: 0 4px 15px rgba(255, 107, 53, 0.25);
}
&:active {
background: linear-gradient(135deg, rgba(255, 107, 53, 0.2), rgba(255, 107, 53, 0.15));
transform: translateY(0);
box-shadow: 0 2px 8px rgba(255, 107, 53, 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;
}
/* 俱乐部选择弹窗样式 */
.xk-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;
}
}
.xk-list {
padding: 0 15px;
.xk-item {
display: flex;
align-items: center;
padding: 15px 0;
border-bottom: 1px solid #f2f2f2;
transition: all 0.3s ease;
&:last-child {
border-bottom: none;
}
&-active {
background-color: rgba(64, 158, 255, 0.05);
}
&:hover {
background-color: rgba(64, 158, 255, 0.02);
}
.xk-info {
flex: 1;
margin-left: 12px;
.xk-name {
font-size: 15px;
font-weight: 500;
color: #303133;
margin-bottom: 4px;
}
.xk-type {
font-size: 13px;
color: #303133;
margin-bottom: 4px;
}
.xk-class {
font-size: 13px;
color: #606266;
}
}
}
}
}
</style>