2025-10-26 22:48:14 +08:00

575 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="pbList && pbList.length > 0">
{{ getCurrentSemesterName() }} - 教学巡查
</text>
<text v-else>暂无排班数据</text>
</view>
</view>
</view>
</view>
<!-- 可滚动的内容区域 -->
<scroll-view
class="scrollable-content"
scroll-y
@scrolltolower="onScrollToLower"
:refresher-enabled="true"
:refresher-triggered="refreshing"
@refresherrefresh="onRefresh"
refresher-background="#f8fafc"
>
<!-- 排班列表 -->
<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 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>加载更多</text>
</view>
<!-- 没有更多数据提示 -->
<view class="no-more" v-if="!hasMore && pbList.length > 0">
<text>没有更多数据了</text>
</view>
</scroll-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 { getPbPageApi } from "@/api/base/pbApi";
import dayjs from "dayjs";
const { getJs } = useUserStore();
const { getData, setData } = useDataStore();
// 排班列表数据
const pbList = ref<any[]>([]);
const loading = ref(false);
const hasMore = ref(true);
const refreshing = ref(false);
// 分页参数
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 getPbPageApi(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;
refreshing.value = false;
}
};
// 加载更多
const loadMore = () => {
if (hasMore.value && !loading.value) {
pagination.page++;
loadPbList();
}
};
// 滚动到底部时加载更多
const onScrollToLower = () => {
if (hasMore.value && !loading.value) {
pagination.page++;
loadPbList();
}
};
// 下拉刷新
const onRefresh = () => {
refreshing.value = true;
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 || {}));
// 确保数据一致性只使用pbId字段
const pbData = {
...pb,
pbId: pb.id, // 将id字段作为pbId字段
};
delete pbData.id; // 移除原始的id字段
setData(pbData);
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 if (pb.xclx === 'C') {
// 值周巡查,跳转到值周巡查列表
uni.navigateTo({
url: `/pages/view/routine/kefuxuncha/zbList`,
});
} 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;
}
}
// 没有更多数据样式
.no-more {
display: flex;
justify-content: center;
align-items: center;
padding: 20px;
color: #999;
font-size: 12px;
text {
color: #999;
}
}
// 添加动画关键帧
@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>