This commit is contained in:
ywyonui 2025-09-12 11:22:07 +08:00
commit 3fe5dc5b14
10 changed files with 2342 additions and 66 deletions

29
src/api/base/kyXcApi.ts Normal file
View File

@ -0,0 +1,29 @@
import { get, post } from "@/utils/request";
/**
*
*/
export const kyXcSaveApi = async (params: any) => {
return await post("/api/kyXc/save", params);
};
/**
*
*/
export const getKyXcPageApi = async (params: any) => {
return await get("/api/kyXc/findPage", params);
};
/**
* ID获取课业辅导巡查详情
*/
export const getKyXcByIdApi = async (id: string) => {
return await get(`/api/kyXc/${id}`);
};
/**
*
*/
export const deleteKyXcApi = async (id: string) => {
return await post(`/api/kyXc/delete/${id}`);
};

57
src/api/base/xkPbApi.ts Normal file
View File

@ -0,0 +1,57 @@
import { get, post } from "@/utils/request";
/**
*
*/
export const getXkPbPageApi = async (params: any) => {
return await get("/api/xkPb/findPage", params);
};
/**
* ID获取选课排班详情
*/
export const getXkPbByIdApi = async (id: string) => {
return await get(`/api/xkPb/${id}`);
};
/**
*
*/
export const createXkPbApi = async (params: any) => {
return await post("/api/xkPb", params);
};
/**
*
*/
export const updateXkPbApi = async (params: any) => {
return await post("/api/xkPb/update", params);
};
/**
*
*/
export const deleteXkPbApi = async (id: string) => {
return await post(`/api/xkPb/delete/${id}`);
};
/**
* ID获取排班列表
*/
export const getXkPbListByXkIdApi = async (params: any) => {
return await get("/api/xkPb/findByXkId", params);
};
/**
*
*/
export const getXcCourseListApi = async (params: any) => {
return await get("/api/xkPb/getXcCourseList", params);
};
/**
*
*/
export const getKyXcCourseListApi = async (params: any) => {
return await get("/api/xkPb/getKyXcCourseList", params);
};

View File

@ -147,3 +147,9 @@ const setDefaultValue = () => {
}
}
</style>

View File

@ -250,6 +250,27 @@
},
{
"path": "pages/view/routine/kefuxuncha/xcXkList",
"style": {
"navigationBarTitleText": "课服清单",
"enablePullDownRefresh": false
}
},
{
"path": "pages/view/routine/kefuxuncha/kyXkList",
"style": {
"navigationBarTitleText": "课业辅导巡查",
"enablePullDownRefresh": false
}
},
{
"path": "pages/view/routine/kefuxuncha/kyXkkcDetail",
"style": {
"navigationBarTitleText": "课业辅导巡查执行",
"enablePullDownRefresh": false
}
},
{
"path": "pages/view/routine/kefuxuncha/xcPbList",
"style": {
"navigationBarTitleText": "课服巡查",
"enablePullDownRefresh": false

View File

@ -245,7 +245,7 @@ const sections = reactive<Section[]>([
text: "课服巡查",
show: true,
permissionKey: "routine-kfxc", //
path: "/pages/view/routine/kefuxuncha/xcXkList",
path: "/pages/view/routine/kefuxuncha/xcPbList",
},
{
id: "r5",

View File

@ -90,10 +90,10 @@
</view>
</view>
</view>
<!-- 更多图标 -->
<view class="more-icon-container" @click.stop="showMoreOptions(student)">
<!-- 箭头图标 -->
<view class="arrow-icon-container" @click.stop="viewStudentDetail(student)">
<image
class="more-icon"
class="arrow-icon"
src="/static/base/view/more.png"
mode="aspectFit"
></image>
@ -146,6 +146,9 @@ interface StudentInfo {
bjmc: string
jzIds?: string
jzxm?: string
xb?: string //
sfzh?: string //
cstime?: string //
}
interface ApiResponse {
@ -258,60 +261,7 @@ const getListTitle = () => {
}
}
//
const showMoreOptions = (student: StudentInfo) => {
console.log('显示更多选项:', student)
//
uni.showActionSheet({
itemList: ['查看详情', '编辑信息', '联系家长', '删除学生'],
success: (res) => {
const tapIndex = res.tapIndex
switch (tapIndex) {
case 0:
//
viewStudentDetail(student)
break
case 1:
//
uni.showToast({
title: '编辑功能开发中',
icon: 'none'
})
break
case 2:
//
if (student.jzxm) {
uni.showToast({
title: '联系家长功能开发中',
icon: 'none'
})
} else {
uni.showToast({
title: '该学生暂无家长信息',
icon: 'none'
})
}
break
case 3:
//
uni.showModal({
title: '确认删除',
content: `确定要删除学生 ${student.xsxm} 吗?`,
success: (modalRes) => {
if (modalRes.confirm) {
uni.showToast({
title: '删除功能开发中',
icon: 'none'
})
}
}
})
break
}
}
})
}
//
const getFollowStatusClass = (jzxm?: string) => {
@ -336,7 +286,10 @@ const viewStudentDetail = (student: StudentInfo) => {
bjId: student.bjId,
bjmc: student.bjmc,
jzIds: student.jzIds,
jzxm: student.jzxm
jzxm: student.jzxm,
xb: student.xb, //
sfzh: student.sfzh, //
cstime: student.cstime //
})
//
@ -542,7 +495,7 @@ onLoad(() => {
padding: 24rpx;
}
.more-icon-container {
.arrow-icon-container {
width: 40rpx;
height: 40rpx;
display: flex;
@ -563,14 +516,14 @@ onLoad(() => {
}
}
.more-icon {
.arrow-icon {
width: 35rpx;
height: 35rpx;
opacity: 0.6;
transition: opacity 0.2s ease;
}
.more-icon-container:hover .more-icon {
.arrow-icon-container:hover .arrow-icon {
opacity: 1;
}

View File

@ -0,0 +1,707 @@
<template>
<view class="interest-course">
<!-- 选课信息头部 - 固定部分 -->
<view class="selection-header">
<view class="header-content">
<view class="title-section" @click="clickShowXkSelector">
<view class="title">
<text v-if="xkData && xkData.xkmc">{{ xkData.xkmc }}</text>
<text v-else>巡查选课</text>
</view>
<view class="switch-btn" v-if="xkList.length > 1">切换</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.gradeName || '暂无' }}</view>
</view>
<view class="course-info-item" v-if="xkkc.bjmc">
<view class="info-label">班级</view>
<view class="info-data">{{ xkkc.bjmc || '暂无' }}</view>
</view>
<view class="separator-line"></view>
<view class="course-btn-group">
<view class="xc-btn" @click.stop="goXc(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>
<!-- 选课选择弹窗 -->
<u-popup
:show="showXkFlag"
@close="showXkFlag = false"
mode="bottom"
round="10"
>
<view class="xk-selector">
<view class="selector-header">
<text class="selector-title">选择俱乐部</text>
<u-icon name="close" size="20" @click="showXkFlag = false"></u-icon>
</view>
<view class="xk-list">
<view
v-for="(xk, index) in xkList"
:key="index"
class="xk-item"
:class="{
'xk-item-active': xkData.id === xk.id,
}"
@click="switchXk(xk)"
>
<view class="xk-info">
<text class="xk-name">{{ xk.xkmc }}</text>
<text class="xk-type">{{ xk.xkmc }}</text>
</view>
<u-icon
v-if="xkData.id === xk.id"
name="checkmark"
color="#409EFF"
size="20"
></u-icon>
</view>
</view>
</view>
</u-popup>
</view>
</view>
</template>
<script setup lang="ts">
import { getKyXcCourseListApi } from "@/api/base/xkPbApi";
import { useDataStore } from "@/store/modules/data";
import { useUserStore } from "@/store/modules/user";
import { onBeforeUnmount, onMounted, ref } from "vue";
const { getJs } = useUserStore();
const dataStore = useDataStore();
//
const showXkFlag = ref(false);
const xkList = ref<any>([]);
const xkData = ref();
//
const xkkcList = ref<any[]>([]);
const xcBeforeMinute = ref<number>(0);
//
const wdNameList = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"];
onMounted(async () => {
uni.showLoading({
title: "加载中...",
});
// globaldata
let pbData = dataStore.getGlobal;
if (!pbData || !pbData.xcbt || !pbData.xcbt.includes('课业辅导巡计划')) {
pbData = dataStore.getData;
}
//
if (!pbData || !pbData.xcbt || pbData.xcbt.includes('课业辅导巡计划') === false) {
uni.showToast({
title: '数据异常,请重新选择排班',
icon: 'none'
});
uni.navigateBack();
return;
}
// kcmc
if (pbData.kcmc) {
//
//
const reconstructedPbData = {
id: pbData.pbId, // 使pbIdID
status: 'A',
njIds: pbData.njIds || '',
bjIds: pbData.bjIds || '',
xcbt: pbData.xcbt,
xclx: pbData.xclx,
xqId: pbData.xqId,
xqmc: pbData.xqmc,
xcstime: pbData.xcstime,
xcjstime: pbData.xcjstime,
pk: pbData.pk
};
dataStore.setGlobal(reconstructedPbData);
pbData = reconstructedPbData;
} else {
//
dataStore.setGlobal(pbData);
}
await loadXcCourseList(pbData);
uni.hideLoading();
});
//
const loadXcCourseList = async (pbData: any) => {
try {
// pbData
if (!pbData || !pbData.id) {
uni.showToast({
title: '排班数据无效,请重新选择',
icon: 'none'
});
return;
}
const res = await getKyXcCourseListApi({
jsId: getJs.id,
pbId: pbData.id
});
if (res && res.resultCode == 1) {
const list = res.result || [];
xkkcList.value = list.map((item: any) => ({
id: item.id, // ID
pbId: item.pbId || pbData.id, // ID
kcmc: item.kmmc || '课业辅导',
gradeName: item.bc && item.njmc ? `${item.bc}(${item.njmc})` : (item.njmc || '暂无'),
bjmc: item.bjmc || '暂无',
// ID
njId: item.njId || '',
njmcId: item.njmcId || '',
bjId: item.bjId || ''
}));
} else {
xkkcList.value = [];
uni.showToast({
title: (res as any).resultMessage || '获取课业辅导巡查失败',
icon: 'none'
});
return;
}
} catch (error) {
console.error('加载巡查课程失败:', error);
xkkcList.value = [];
uni.showToast({
title: '加载巡查课程失败',
icon: 'none'
});
}
};
//
function clickShowXkSelector() {
if (xkList.value.length > 1) {
showXkFlag.value = true;
}
}
//
function switchXk(xk: any) {
xkData.value = xk;
xkkcList.value = xk.xkkcs;
showXkFlag.value = false;
for (let i = 0; i < xk.xkkcs.length; i++) {
let xkkc = xk.xkkcs[i];
//
switch (xkkc.skzqlx) {
case "每天":
xkkc.skzqmc = "每天";
break;
case "每周":
const daysOfWeek = xkkc.skzq.split(",").map(Number);
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;
}
}
//
uni.showToast({
title: `已切换到${xk.xkmc}`,
icon: "none",
});
}
//
const goXc = (xkkc: any) => {
// global
const pbData = dataStore.getGlobal;
//
if (!pbData || !pbData.xcbt || !pbData.xcbt.includes('课业辅导巡计划')) {
uni.showToast({
title: '数据异常,请重新选择排班',
icon: 'none'
});
return;
}
//
const combinedData = {
...xkkc,
id: xkkc.id, // ID
pbId: pbData.id, // ID - 使ID
xclx: pbData.xclx,
xcbt: pbData.xcbt,
xqmc: pbData.xqmc,
// ID
njId: xkkc.njId || '',
njmcId: xkkc.njmcId || '',
bjId: xkkc.bjId || ''
};
// 使
dataStore.setData(combinedData); //
// global
// combinedData
const originalPbData = {
id: pbData.id, // ID
status: pbData.status,
njIds: pbData.njIds,
bjIds: pbData.bjIds,
xcbt: pbData.xcbt,
xclx: pbData.xclx,
xqId: pbData.xqId,
xqmc: pbData.xqmc,
xcstime: pbData.xcstime,
xcjstime: pbData.xcjstime,
pk: pbData.pk
};
dataStore.setGlobal(originalPbData); // global
uni.navigateTo({
url: `/pages/view/routine/kefuxuncha/kyXkkcDetail`,
});
};
//
const goRecord = (xkkc: any) => {
// global
const pbData = dataStore.getGlobal;
//
if (!pbData || !pbData.xcbt || !pbData.xcbt.includes('课业辅导巡计划')) {
uni.showToast({
title: '数据异常,请重新选择排班',
icon: 'none'
});
return;
}
//
const combinedData = {
...xkkc,
id: xkkc.id, // ID
pbId: pbData.id, // ID - 使ID
xclx: pbData.xclx,
xcbt: pbData.xcbt,
xqmc: pbData.xqmc,
// ID
njId: xkkc.njId || '',
njmcId: xkkc.njmcId || '',
bjId: xkkc.bjId || ''
};
dataStore.setData(combinedData);
uni.navigateTo({
url: `/pages/view/routine/kefuxuncha/xcRecord`,
});
};
//
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;
}
.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: space-between;
cursor: pointer;
.title {
font-size: 20px;
font-weight: 700;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
letter-spacing: 0.5px;
}
.switch-btn {
padding: 8px 16px;
background-color: rgba(255, 255, 255, 0.2);
color: #fff;
border-radius: 20px;
font-size: 14px;
font-weight: 500;
transition: all 0.3s ease;
&:hover {
background-color: rgba(255, 255, 255, 0.3);
transform: translateY(-1px);
}
}
}
}
}
//
.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; // Added gap for spacing between buttons
.xc-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.3s 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);
}
}
.record-btn {
display: inline-block;
color: #409EFF;
font-size: 14px;
font-weight: 600;
padding: 8px 18px;
border-radius: 8px;
background: linear-gradient(135deg, rgba(64, 158, 255, 0.1), rgba(64, 158, 255, 0.05));
border: 1px solid #409EFF;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
box-shadow: 0 2px 8px rgba(64, 158, 255, 0.15);
animation: fadeInUp 0.5s ease-out 0.3s both;
&:hover {
background: linear-gradient(135deg, rgba(64, 158, 255, 0.15), rgba(64, 158, 255, 0.1));
transform: translateY(-1px);
box-shadow: 0 4px 15px rgba(64, 158, 255, 0.25);
}
&:active {
background: linear-gradient(135deg, rgba(64, 158, 255, 0.2), rgba(64, 158, 255, 0.15));
transform: translateY(0);
box-shadow: 0 2px 8px rgba(64, 158, 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;
}
}
/* 选课选择弹窗样式 */
.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>

View File

@ -0,0 +1,815 @@
<template>
<BasicLayout>
<!-- 待巡查内容 -->
<view class="pending-inspection">
<!-- 课程信息卡片 -->
<view class="course-card mx-15 my-15 bg-white white-bg-color r-md p-15">
<view class="flex-row items-center mb-15">
<view class="course-icon flex-center mr-10">
<u-icon name="calendar" color="#4080ff" size="20"></u-icon>
</view>
<text class="font-16 font-bold">{{ xkkc.kcmc }}</text>
<text class="font-14 cor-999 ml-10"
>{{ todayInfo.date }} ({{ todayInfo.weekName }})</text
>
</view>
<!-- 课业辅导信息 -->
<view class="course-time-info">
<view class="time-item">
<view class="time-label">年级</view>
<view class="time-value">{{ xkkc.gradeName || '暂无' }}</view>
</view>
<view class="time-item" v-if="xkkc.bjmc">
<view class="time-label">班级</view>
<view class="time-value">{{ xkkc.bjmc || '暂无' }}</view>
</view>
<view class="time-item">
<view class="time-label">巡查类型</view>
<view class="time-value">{{ xkkc.xclx === 'B' ? '课业辅导巡查' : '课程巡查' }}</view>
</view>
<view class="time-item">
<view class="time-label">排班标题</view>
<view class="time-value">{{ xkkc.xcbt || '暂无' }}</view>
</view>
<view class="time-item">
<view class="time-label">学期</view>
<view class="time-value">{{ xkkc.xqmc || '暂无' }}</view>
</view>
</view>
<!-- 巡查时间状态 -->
<view class="inspection-status" v-if="!canInspect">
<u-icon name="clock" color="#ff9900" size="16"></u-icon>
<text class="status-text">{{ inspectionStatusText }}</text>
</view>
</view>
<view v-if="canInspect">
<!-- 巡查项目 -->
<view class="section mx-15 mb-15">
<view class="section-title-bar">
<view class="decorator"></view>
<text class="title-text">巡查项目</text>
</view>
<view class="check-card bg-white r-md p-15">
<template v-if="checkItems && checkItems.length > 0">
<view class="check-list">
<view
v-for="(item, index) in checkItems"
:key="item.id"
class="check-item"
>
<view class="item-info flex-1">
<!-- 项目名称单独一行 -->
<text class="item-text"
>{{ index + 1 }}{{ item.xcMc }}</text
>
<!-- 分值和结果同一行 -->
<view class="item-score-result">
<text class="item-deduction mr-20">
分值{{ item.xmFz }}
</text>
<view class="item-result">
<radio-group
:name="'result_' + item.id"
class="item-radio-group"
@change="onCheckItemChange($event, item)"
>
<label class="item-radio-label mr-10">
<radio
:value="'A'"
:checked="item.checked === true"
color="#4080ff"
class="item-radio"
/>
<text class="ml-2"></text>
</label>
<label class="item-radio-label">
<radio
:value="'B'"
:checked="item.checked === false"
color="#4080ff"
class="item-radio"
/>
<text class="ml-2"></text>
</label>
</radio-group>
</view>
</view>
</view>
</view>
</view>
</template>
<template v-else>
<view
class="no-check-items"
style="text-align: center; color: #999; padding: 20px 0"
>
暂无巡查项目
</view>
</template>
</view>
</view>
<!-- 拍照上传 -->
<view class="section mx-15 mb-15">
<view class="section-title-bar">
<view class="decorator"></view>
<text class="title-text">拍照上传</text>
</view>
<view class="upload-card bg-white r-md p-15">
<view class="upload-section">
<view class="upload-list">
<view
v-for="(image, index) in imageList"
:key="index"
class="upload-item"
>
<image
:src="image.url ? imagUrl(image.url) : image.tempPath"
mode="aspectFill"
class="upload-image"
@click="previewImage(index)"
/>
<view class="upload-delete" @click="deleteImage(index)">
<text class="delete-icon">×</text>
</view>
</view>
<view
v-if="imageList.length < 5"
class="upload-add"
@click="chooseImage"
>
<text class="add-icon">+</text>
<text class="add-text">添加图片</text>
</view>
</view>
</view>
</view>
</view>
<!-- 拍视频上传 -->
<view class="section mx-15 mb-30">
<view class="section-title-bar">
<view class="decorator"></view>
<text class="title-text">拍视频上传</text>
</view>
<view class="upload-card bg-white r-md p-15">
<view class="upload-section">
<view class="upload-list">
<view
v-for="(video, index) in videoList"
:key="index"
class="upload-item"
>
<video
:src="video.url ? video.url : video.tempPath"
class="upload-video"
:controls="false"
:show-center-play-btn="false"
:show-play-btn="false"
:show-fullscreen-btn="false"
:show-progress="false"
:show-mute-btn="false"
:enable-progress-gesture="false"
:enable-play-gesture="false"
:loop="false"
:muted="true"
:poster="''"
></video>
<view class="video-play-icon">
<u-icon name="play-right-fill" color="#fff" size="20"></u-icon>
</view>
<view class="upload-delete" @click="deleteVideo(index)">
<text class="delete-icon">×</text>
</view>
</view>
<view
v-if="videoList.length < 3"
class="upload-add"
@click="chooseVideo"
>
<text class="add-icon">+</text>
<text class="add-text">添加视频</text>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
<template #bottom>
<view
v-if="canInspect"
class="submit-btn-wrap py-10 px-20 bg-white"
>
<button class="submit-btn" @click="submit">提交巡查</button>
</view>
</template>
</BasicLayout>
</template>
<script setup lang="ts">
import { xcXmFindByXcLxApi } from "@/api/base/xcXmApi";
import { kyXcSaveApi } from "@/api/base/kyXcApi";
import { attachmentUpload } from "@/api/system/upload";
import BasicLayout from "@/components/BasicLayout/Layout.vue";
import { useDataStore } from "@/store/modules/data";
import { useUserStore } from "@/store/modules/user";
import { computed, onMounted, ref } from "vue";
import dayjs from "dayjs";
import { imagUrl } from "@/utils";
import { showLoading, hideLoading, showToast } from "@/utils/uniapp";
const { getJs } = useUserStore();
const { getData } = useDataStore();
const js = computed(() => getJs);
const xkkc = computed(() => getData);
const now = dayjs();
let wDay = now.day();
if (wDay === 0) {
wDay = 7;
}
const wdNameList = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"];
const todayInfo = ref({
date: now.format("YYYY-MM-DD"),
weekName: wdNameList[wDay - 1],
});
//
const checkItems = ref<any[]>([]);
// -
interface ImageItem {
tempPath?: string; //
url?: string; //
name?: string; //
}
// -
interface VideoItem {
tempPath?: string; //
url?: string; //
name?: string; //
}
const imageList = ref<ImageItem[]>([]);
const videoList = ref<VideoItem[]>([]);
//
const inspectionStatusText = ref("");
const canInspect = ref(true); //
//
const loadCheckItems = async () => {
try {
const res = await xcXmFindByXcLxApi("课业辅导巡查");
if (res && res.resultCode === 1 && res.result && res.result.length > 0) {
checkItems.value = res.result.map((item: any) => {
return {
id: item.id,
xcMc: item.xcMc,
xmFz: item.xmFz,
xcLx: item.xcLx,
checked: false,
};
});
} else {
checkItems.value = [];
}
} catch (error) {
console.error("加载巡查项目失败:", error);
checkItems.value = [];
}
};
const onCheckItemChange = (e: any, item: any) => {
item.checked = e.detail.value === "A";
};
//
const chooseImage = () => {
uni.chooseImage({
count: 5 - imageList.value.length,
sizeType: ['compressed'],
sourceType: ['album', 'camera'],
success: async (res) => {
//
const tempFilePaths = res.tempFilePaths as string[];
const newImages = tempFilePaths.map((path: string) => ({
tempPath: path,
name: path.split('/').pop() || 'image.jpg'
}));
imageList.value = [...imageList.value, ...newImages];
//
await uploadImages(newImages);
}
});
};
//
const chooseVideo = () => {
uni.chooseVideo({
sourceType: ['album', 'camera'],
maxDuration: 60,
camera: 'back',
success: async (res) => {
//
const tempFilePath = res.tempFilePath;
const newVideo = {
tempPath: tempFilePath,
name: tempFilePath.split('/').pop() || 'video.mp4'
};
videoList.value = [...videoList.value, newVideo];
//
await uploadVideos([newVideo]);
}
});
};
//
const uploadImages = async (images: ImageItem[]) => {
try {
showLoading('上传图片中...');
for (let i = 0; i < images.length; i++) {
const image = images[i];
if (image.tempPath) {
try {
//
const uploadResult: any = await attachmentUpload(image.tempPath as any);
if (uploadResult && uploadResult.resultCode === 1 && uploadResult.result && uploadResult.result.length > 0) {
//
const serverPath = uploadResult.result[0].filePath;
//
const index = imageList.value.findIndex(img => img.tempPath === image.tempPath);
if (index !== -1) {
imageList.value[index].url = serverPath;
//
delete imageList.value[index].tempPath;
}
} else {
throw new Error('上传响应格式异常');
}
} catch (error) {
console.error('图片上传失败:', error);
showToast({ title: `${image.name || '图片'}上传失败`, icon: 'none' });
//
const index = imageList.value.findIndex(img => img.tempPath === image.tempPath);
if (index !== -1) {
imageList.value.splice(index, 1);
}
}
}
}
hideLoading();
showToast({ title: '图片上传完成', icon: 'success' });
} catch (error) {
hideLoading();
console.error('批量上传图片失败:', error);
showToast({ title: '图片上传失败,请重试', icon: 'none' });
}
};
//
const uploadVideos = async (videos: VideoItem[]) => {
try {
showLoading('上传视频中...');
for (let i = 0; i < videos.length; i++) {
const video = videos[i];
if (video.tempPath) {
try {
//
const uploadResult: any = await attachmentUpload(video.tempPath as any);
if (uploadResult && uploadResult.resultCode === 1 && uploadResult.result && uploadResult.result.length > 0) {
//
const serverPath = uploadResult.result[0].filePath;
//
const index = videoList.value.findIndex(v => v.tempPath === video.tempPath);
if (index !== -1) {
videoList.value[index].url = serverPath;
//
delete videoList.value[index].tempPath;
}
} else {
throw new Error('上传响应格式异常');
}
} catch (error) {
console.error('视频上传失败:', error);
showToast({ title: `${video.name || '视频'}上传失败`, icon: 'none' });
//
const index = videoList.value.findIndex(v => v.tempPath === video.tempPath);
if (index !== -1) {
videoList.value.splice(index, 1);
}
}
}
}
hideLoading();
showToast({ title: '视频上传完成', icon: 'success' });
} catch (error) {
hideLoading();
console.error('批量上传视频失败:', error);
showToast({ title: '视频上传失败,请重试', icon: 'none' });
}
};
//
const previewImage = (index: number) => {
const urls = imageList.value.map(img =>
img.url ? imagUrl(img.url) : img.tempPath
).filter((url): url is string => !!url);
uni.previewImage({
urls: urls,
current: index
});
};
//
const deleteImage = (index: number) => {
imageList.value.splice(index, 1);
};
//
const deleteVideo = (index: number) => {
videoList.value.splice(index, 1);
};
// -
const checkInspectionTime = () => {
//
canInspect.value = true;
inspectionStatusText.value = "可以巡查";
};
//
const submit = async () => {
if (!canInspect.value) {
uni.showToast({
title: inspectionStatusText.value,
icon: "none",
duration: 2000,
});
return;
}
//
const hasCheckedItems = checkItems.value.some(item => item.checked !== undefined);
if (!hasCheckedItems) {
uni.showToast({
title: "请至少选择一个巡查项目",
icon: "none",
duration: 2000,
});
return;
}
try {
//
const xkXcXmList = checkItems.value.map((item: any) => {
const newItem = {
...item,
xcXmId: item.id,
xcJg: item.checked ? "A" : "B",
xkXcId: "", //
};
newItem.id = "";
return newItem;
});
const submitData = {
jsId: js.value.id,
jsxm: js.value.xm || js.value.jsxm, //
njId: xkkc.value.njId || '', // ID
njmcId: xkkc.value.njmcId || '', // ID
bjId: xkkc.value.bjId || '', // ID
xctime: now.format("YYYY-MM-DD HH:mm:ss"),
zp: getImageUrls(),
sp: getVideoUrls(),
kyXcXmList: xkXcXmList, //
};
const res = await kyXcSaveApi(submitData);
if (res && res.resultCode === 1) {
uni.showToast({
title: "提交成功",
icon: "success",
});
setTimeout(() => {
uni.navigateBack();
}, 1500);
} else {
uni.showToast({
title: "提交失败",
icon: "none",
});
}
} catch (error) {
console.error('提交巡查失败:', error);
uni.showToast({
title: "提交失败",
icon: "none",
});
}
};
// URL -
const getImageUrls = () => {
const urls = imageList.value
.filter(img => img.url) // url
.map(img => img.url)
.filter((url): url is string => !!url); // undefinednull
//
return urls.length > 0 ? urls.join(',') : '';
};
// URL -
const getVideoUrls = () => {
const urls = videoList.value
.filter(video => video.url) // url
.map(video => video.url)
.filter((url): url is string => !!url); // undefinednull
//
return urls.length > 0 ? urls.join(',') : '';
};
//
onMounted(async () => {
await loadCheckItems(); //
checkInspectionTime();
});
</script>
<style scoped lang="scss">
.container {
min-height: 100vh;
background-color: #f5f5f5;
}
.bg-white {
background-color: white;
}
.pending-inspection {
padding-top: 0;
}
.course-card {
border-radius: 8px;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05);
}
.course-icon {
width: 30px;
height: 30px;
border-radius: 4px;
background-color: rgba(64, 128, 255, 0.1);
}
.course-time-info {
margin-top: 15px;
padding: 10px 15px;
background-color: #f9f9f9;
border-radius: 4px;
border: 1px solid #eee;
.time-item {
display: flex;
justify-content: space-between;
margin-bottom: 5px;
.time-label {
font-size: 14px;
color: #666;
}
.time-value {
font-size: 14px;
font-weight: bold;
color: #333;
}
}
}
.inspection-status {
display: flex;
align-items: center;
margin-top: 15px;
padding: 10px 15px;
background-color: #fffbe6;
border: 1px solid #ffe58f;
border-radius: 4px;
color: #faad14;
font-size: 14px;
.status-text {
margin-left: 5px;
}
}
.section {
.section-title-bar {
display: flex;
align-items: center;
margin-bottom: 10px;
.decorator {
width: 4px;
height: 16px;
background-color: #4080ff;
margin-right: 8px;
border-radius: 2px;
}
.title-text {
font-size: 16px;
font-weight: bold;
color: #333;
}
}
}
.check-card {
background-color: white;
.check-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 0;
border-bottom: 1px solid #eee;
.item-score-result {
display: flex;
align-items: center;
justify-content: space-between;
}
&:first-child {
padding-top: 0;
}
&:last-child {
border-bottom: none;
padding-bottom: 0;
}
}
.item-info {
display: flex;
flex-direction: column;
}
.item-text {
font-size: 14px;
color: #333;
margin-bottom: 4px;
}
.item-deduction {
font-size: 12px;
color: #999;
}
}
.upload-card {
background-color: white;
}
.upload-section {
.upload-list {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.upload-item {
position: relative;
width: 80px;
height: 80px;
border-radius: 8px;
overflow: hidden;
border: 1px solid #e9ecef;
.upload-image {
width: 100%;
height: 100%;
object-fit: cover;
}
.upload-video {
width: 100%;
height: 100%;
object-fit: cover;
}
.video-play-icon {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 32px;
height: 32px;
background-color: rgba(0, 0, 0, 0.6);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
.upload-delete {
position: absolute;
top: 4px;
right: 4px;
width: 20px;
height: 20px;
background-color: rgba(0, 0, 0, 0.6);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
.delete-icon {
color: #ffffff;
font-size: 14px;
font-weight: bold;
}
}
}
.upload-add {
width: 80px;
height: 80px;
border: 2px dashed #e9ecef;
border-radius: 8px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background-color: #f8f9fa;
cursor: pointer;
.add-icon {
font-size: 24px;
color: #6c757d;
margin-bottom: 4px;
}
.add-text {
font-size: 12px;
color: #6c757d;
}
}
}
.submit-btn {
background-color: #4080ff;
color: #fff;
height: 44px;
border-radius: 22px;
font-size: 16px;
display: flex;
align-items: center;
justify-content: center;
}
.cor-primary {
color: #4080ff;
}
.cor-warning {
color: #ff9900;
}
.cor-danger {
color: #ff4d4f;
}
.cor-666 {
color: #666;
}
.cor-999 {
color: #999;
}
</style>

View File

@ -0,0 +1,524 @@
<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>

View File

@ -35,6 +35,10 @@
<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.jsName || '暂无' }}</view>
</view>
<view class="course-info-item">
<view class="info-label">开课年级</view>
<view class="info-data">{{ xkkc.njname || '暂无' }}</view>
@ -105,13 +109,14 @@
<script setup lang="ts">
import { jsdXkListApi } from "@/api/base/server";
import { getXcCourseListApi } from "@/api/base/xkPbApi";
import { useDataStore } from "@/store/modules/data";
import { useUserStore } from "@/store/modules/user";
import { onBeforeUnmount, onMounted, ref } from "vue";
import dayjs from "dayjs";
const { getJs } = useUserStore();
const { getData, setData } = useDataStore();
const dataStore = useDataStore();
const wdNameList = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"];
@ -130,7 +135,39 @@ onMounted(async () => {
uni.showLoading({
title: "加载中...",
});
await loadCourseList();
//
const pbData = dataStore.getData;
if (pbData && pbData.id) {
// kcmc
if (pbData.kcmc) {
//
const reconstructedPbData = {
id: pbData.pbId, // 使pbIdID
status: 'A',
njIds: pbData.njIds || '',
bjIds: pbData.bjIds || '',
xcbt: pbData.xcbt,
xclx: pbData.xclx,
xqId: pbData.xqId,
xqmc: pbData.xqmc,
xcstime: pbData.xcstime,
xcjstime: pbData.xcjstime,
pk: pbData.pk
};
dataStore.setGlobal(reconstructedPbData);
await loadXcCourseList(reconstructedPbData);
} else {
// 使
dataStore.setGlobal(pbData);
await loadXcCourseList(pbData);
}
} else {
//
await loadCourseList();
}
uni.hideLoading();
});
@ -151,6 +188,89 @@ const loadCourseList = async () => {
}
};
//
const loadXcCourseList = async (pbData: any) => {
try {
const res = await getXcCourseListApi({
jsId: getJs.id,
pbId: pbData.id,
xclx: pbData.xclx
});
if (res && res.resultCode == 1) {
const list = res.result || [];
if (pbData.xclx === 'A') {
// A使
xkkcList.value = list.map((item: any) => ({
id: item.id,
kcmc: item.kcmc,
skzqmc: item.skzqmc || '每周',
skkstime: item.skkstime,
skjstime: item.skjstime,
kcdd: item.kcdd || '暂无',
njname: item.njname || '暂无',
hasNum: item.hasNum || 0,
maxNum: item.maxNum || 0,
skzqlx: item.skzqlx,
skzq: item.skzq,
jsName: item.jsName
}));
} else if (pbData.xclx === 'B') {
// B使
xkkcList.value = list.map((item: any) => ({
id: item.id,
kcmc: item.kcmc,
skzqmc: item.skzqmc || '每周',
skkstime: item.skkstime,
skjstime: item.skjstime,
kcdd: item.kcdd || '暂无',
njname: item.njname || '暂无',
hasNum: item.hasNum || 0,
maxNum: item.maxNum || 0,
skzqlx: item.skzqlx,
skzq: item.skzq,
jsName: item.jsName
}));
}
//
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);
// wdNameListdaysOfWeek
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;
}
}
} else {
xkkcList.value = [];
uni.showToast({
title: (res as any).resultMessage || '获取巡查课程失败',
icon: 'none'
});
}
} catch (error) {
console.error('加载巡查课程失败:', error);
xkkcList.value = [];
uni.showToast({
title: '加载巡查课程失败',
icon: 'none'
});
}
};
//
function clickShowXkSelector() {
if (xkList.value.length > 1) {
@ -191,7 +311,29 @@ function switchXk(xk: any) {
//
const goXc = (xkkc: any) => {
setData(xkkc);
// global
const pbData = dataStore.getGlobal;
//
if (!pbData || !pbData.xcbt || !pbData.xcbt.includes('排班计划')) {
uni.showToast({
title: '数据异常,请重新选择排班',
icon: 'none'
});
return;
}
//
const combinedData = {
...xkkc,
id: xkkc.id, // ID
pbId: pbData.id, // ID - 使ID
xclx: pbData.xclx,
xcbt: pbData.xcbt,
xqmc: pbData.xqmc
};
dataStore.setData(combinedData);
uni.navigateTo({
url: `/pages/view/routine/kefuxuncha/xcXkkcDetail`,
});
@ -199,7 +341,29 @@ const goXc = (xkkc: any) => {
//
const goRecord = (xkkc: any) => {
setData(xkkc);
// global
const pbData = dataStore.getGlobal;
//
if (!pbData || !pbData.xcbt || !pbData.xcbt.includes('排班计划')) {
uni.showToast({
title: '数据异常,请重新选择排班',
icon: 'none'
});
return;
}
//
const combinedData = {
...xkkc,
id: xkkc.id, // ID
pbId: pbData.id, // ID - 使ID
xclx: pbData.xclx,
xcbt: pbData.xcbt,
xqmc: pbData.xqmc
};
dataStore.setData(combinedData);
uni.navigateTo({
url: `/pages/view/routine/kefuxuncha/xcRecord`,
});