525 lines
12 KiB
Vue
525 lines
12 KiB
Vue
|
|
<template>
|
|||
|
|
<view class="interest-course">
|
|||
|
|
<!-- 选课排班信息头部 - 固定部分 -->
|
|||
|
|
<view class="selection-header">
|
|||
|
|
<view class="header-content">
|
|||
|
|
<view class="title-section">
|
|||
|
|
<view class="title">
|
|||
|
|
<text v-if="pbList && pbList.length > 0">
|
|||
|
|
{{ getCurrentSemesterName() }} - 选课排班
|
|||
|
|
</text>
|
|||
|
|
<text v-else>暂无排班数据</text>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<!-- 可滚动的内容区域 -->
|
|||
|
|
<view class="scrollable-content">
|
|||
|
|
<!-- 排班列表 -->
|
|||
|
|
<view class="course-list" v-if="pbList && pbList.length > 0">
|
|||
|
|
<view v-for="(pb, index) in pbList" :key="pb.id || index" class="course-item">
|
|||
|
|
<view class="status-badge" :class="getStatusClass(pb)">
|
|||
|
|
{{ getStatusText(pb) }}
|
|||
|
|
</view>
|
|||
|
|
<view class="course-name">{{ pb.xcbt }}</view>
|
|||
|
|
<view class="course-info-item">
|
|||
|
|
<view class="info-label">巡查类型:</view>
|
|||
|
|
<view class="info-data">{{ getXclxText(pb.xclx) }}</view>
|
|||
|
|
</view>
|
|||
|
|
<view class="course-info-item">
|
|||
|
|
<view class="info-label">当前学期:</view>
|
|||
|
|
<view class="info-data">{{ pb.xqmc || '暂无' }}</view>
|
|||
|
|
</view>
|
|||
|
|
<view class="course-info-item" v-if="pb.remark">
|
|||
|
|
<view class="info-label">备注:</view>
|
|||
|
|
<view class="info-data">{{ pb.remark }}</view>
|
|||
|
|
</view>
|
|||
|
|
<view class="separator-line"></view>
|
|||
|
|
<view class="course-btn-group">
|
|||
|
|
<view class="detail-btn" @click.stop="goXc(pb)">巡查</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 class="load-more" v-if="hasMore && pbList.length > 0">
|
|||
|
|
<u-loading-icon v-if="loading" mode="spinner" size="20"></u-loading-icon>
|
|||
|
|
<text v-else @click="loadMore">加载更多</text>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
</view>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<script setup lang="ts">
|
|||
|
|
import {
|
|||
|
|
ref,
|
|||
|
|
onBeforeUnmount,
|
|||
|
|
onMounted,
|
|||
|
|
reactive,
|
|||
|
|
} from "vue";
|
|||
|
|
import { useUserStore } from "@/store/modules/user";
|
|||
|
|
import { useDataStore } from "@/store/modules/data";
|
|||
|
|
import { getXkPbPageApi } from "@/api/base/xkPbApi";
|
|||
|
|
import dayjs from "dayjs";
|
|||
|
|
|
|||
|
|
const { getJs } = useUserStore();
|
|||
|
|
const { getData, setData } = useDataStore();
|
|||
|
|
|
|||
|
|
// 排班列表数据
|
|||
|
|
const pbList = ref<any[]>([]);
|
|||
|
|
const loading = ref(false);
|
|||
|
|
const hasMore = ref(true);
|
|||
|
|
|
|||
|
|
// 分页参数
|
|||
|
|
const pagination = reactive({
|
|||
|
|
page: 1,
|
|||
|
|
pageSize: 10,
|
|||
|
|
total: 0,
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
onMounted(() => {
|
|||
|
|
loadPbList();
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 加载排班列表
|
|||
|
|
const loadPbList = async (isRefresh = false) => {
|
|||
|
|
if (loading.value) return;
|
|||
|
|
|
|||
|
|
loading.value = true;
|
|||
|
|
|
|||
|
|
if (isRefresh) {
|
|||
|
|
pagination.page = 1;
|
|||
|
|
pbList.value = [];
|
|||
|
|
hasMore.value = true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
const params = {
|
|||
|
|
page: pagination.page,
|
|||
|
|
rows: pagination.pageSize,
|
|||
|
|
// 可以根据需要添加筛选条件
|
|||
|
|
// xcbt: '', // 排班标题
|
|||
|
|
// xclx: '', // 排班类型
|
|||
|
|
// xkId: '', // 选课ID
|
|||
|
|
// xqId: '', // 学期ID
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const res: any = await getXkPbPageApi(params);
|
|||
|
|
|
|||
|
|
// 根据实际API响应结构判断成功条件
|
|||
|
|
if (res && (res.resultCode == 1 || res.rows || res.result)) {
|
|||
|
|
// 根据实际API响应结构调整数据获取逻辑
|
|||
|
|
const list = res.rows || res.result?.rows || res.result?.list || [];
|
|||
|
|
|
|||
|
|
if (isRefresh) {
|
|||
|
|
pbList.value = list;
|
|||
|
|
} else {
|
|||
|
|
pbList.value.push(...list);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 使用records字段作为总记录数,如果没有则使用total
|
|||
|
|
pagination.total = res.records || res.total || res.result?.total || 0;
|
|||
|
|
|
|||
|
|
// 判断是否还有更多数据
|
|||
|
|
hasMore.value = pbList.value.length < pagination.total;
|
|||
|
|
|
|||
|
|
if (list.length < pagination.pageSize) {
|
|||
|
|
hasMore.value = false;
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
uni.showToast({
|
|||
|
|
title: res.resultMessage || res.message || '获取排班列表失败',
|
|||
|
|
icon: 'none'
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('加载排班列表失败:', error);
|
|||
|
|
uni.showToast({
|
|||
|
|
title: '加载排班列表失败',
|
|||
|
|
icon: 'none'
|
|||
|
|
});
|
|||
|
|
} finally {
|
|||
|
|
loading.value = false;
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 加载更多
|
|||
|
|
const loadMore = () => {
|
|||
|
|
if (hasMore.value && !loading.value) {
|
|||
|
|
pagination.page++;
|
|||
|
|
loadPbList();
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 下拉刷新
|
|||
|
|
const onRefresh = () => {
|
|||
|
|
loadPbList(true);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 获取当前学期名称
|
|||
|
|
const getCurrentSemesterName = () => {
|
|||
|
|
if (pbList.value && pbList.value.length > 0) {
|
|||
|
|
// 从第一个排班获取学期名称
|
|||
|
|
return pbList.value[0].xqmc || '当前学期';
|
|||
|
|
}
|
|||
|
|
return '当前学期';
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 格式化日期时间
|
|||
|
|
const formatDateTime = (dateTime: string) => {
|
|||
|
|
if (!dateTime) {
|
|||
|
|
return '暂无';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
return dayjs(dateTime).format('YYYY-MM-DD HH:mm:ss');
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('时间格式化错误:', error);
|
|||
|
|
return dateTime;
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 获取状态文本
|
|||
|
|
const getStatusText = (pb: any) => {
|
|||
|
|
if (!pb.status || pb.status === 'A') {
|
|||
|
|
return '正常';
|
|||
|
|
}
|
|||
|
|
return '已停用';
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 获取状态样式类
|
|||
|
|
const getStatusClass = (pb: any) => {
|
|||
|
|
if (!pb.status || pb.status === 'A') {
|
|||
|
|
return 'normal';
|
|||
|
|
}
|
|||
|
|
return 'disabled';
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 获取巡查类型文本
|
|||
|
|
const getXclxText = (xclx: string) => {
|
|||
|
|
if (xclx === 'A') {
|
|||
|
|
return '课程巡查';
|
|||
|
|
} else if (xclx === 'B') {
|
|||
|
|
return '课业辅导巡查';
|
|||
|
|
}
|
|||
|
|
return '暂无';
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 跳转到巡查
|
|||
|
|
const goXc = (pb: any) => {
|
|||
|
|
// 添加调试信息
|
|||
|
|
console.log('排班数据pb:', pb);
|
|||
|
|
console.log('pb.id:', pb?.id);
|
|||
|
|
console.log('pb的所有属性:', Object.keys(pb || {}));
|
|||
|
|
|
|||
|
|
setData(pb);
|
|||
|
|
if (pb.xclx === 'A') {
|
|||
|
|
// 课程巡查,跳转到课程巡查列表
|
|||
|
|
uni.navigateTo({
|
|||
|
|
url: `/pages/view/routine/kefuxuncha/xcXkList`,
|
|||
|
|
});
|
|||
|
|
} else if (pb.xclx === 'B') {
|
|||
|
|
// 课业辅导巡查,跳转到课业辅导巡查列表
|
|||
|
|
uni.navigateTo({
|
|||
|
|
url: `/pages/view/routine/kefuxuncha/kyXkList`,
|
|||
|
|
});
|
|||
|
|
} else {
|
|||
|
|
uni.showToast({
|
|||
|
|
title: '未知的巡查类型',
|
|||
|
|
icon: 'none'
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 页面卸载前清除定时器
|
|||
|
|
onBeforeUnmount(() => {
|
|||
|
|
// 清理工作
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 暴露方法给父组件
|
|||
|
|
defineExpose({
|
|||
|
|
loadPbList,
|
|||
|
|
onRefresh,
|
|||
|
|
});
|
|||
|
|
</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;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.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;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.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.normal {
|
|||
|
|
color: #67c23a;
|
|||
|
|
border-color: #e1f3d8;
|
|||
|
|
background-color: #f0f9ff;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.status-badge.disabled {
|
|||
|
|
color: #f56c6c;
|
|||
|
|
border-color: #fde2e2;
|
|||
|
|
background-color: #fef0f0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.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;
|
|||
|
|
gap: 10px;
|
|||
|
|
|
|||
|
|
.detail-btn {
|
|||
|
|
display: inline-block;
|
|||
|
|
font-size: 14px;
|
|||
|
|
font-weight: 600;
|
|||
|
|
padding: 8px 18px;
|
|||
|
|
border-radius: 8px;
|
|||
|
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|||
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
|||
|
|
animation: fadeInUp 0.5s ease-out 0.3s both;
|
|||
|
|
color: #2879ff;
|
|||
|
|
background: linear-gradient(135deg, rgba(40, 121, 255, 0.1), rgba(40, 121, 255, 0.05));
|
|||
|
|
border: 1px solid #2879ff;
|
|||
|
|
|
|||
|
|
&: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);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.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;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 加载更多样式
|
|||
|
|
.load-more {
|
|||
|
|
display: flex;
|
|||
|
|
justify-content: center;
|
|||
|
|
align-items: center;
|
|||
|
|
padding: 20px;
|
|||
|
|
color: #666;
|
|||
|
|
font-size: 14px;
|
|||
|
|
|
|||
|
|
text {
|
|||
|
|
color: #2879ff;
|
|||
|
|
cursor: pointer;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 添加动画关键帧
|
|||
|
|
@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;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 暂无数据样式
|
|||
|
|
.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;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
</style>
|