Merge branch 'master' of http://119.29.194.155:8894/zwq/zhxy-jsd
This commit is contained in:
commit
3fe5dc5b14
29
src/api/base/kyXcApi.ts
Normal file
29
src/api/base/kyXcApi.ts
Normal 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
57
src/api/base/xkPbApi.ts
Normal 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);
|
||||
};
|
||||
@ -147,3 +147,9 @@ const setDefaultValue = () => {
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
707
src/pages/view/routine/kefuxuncha/kyXkList.vue
Normal file
707
src/pages/view/routine/kefuxuncha/kyXkList.vue
Normal 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: "加载中...",
|
||||
});
|
||||
|
||||
// 优先从global获取排班数据,如果没有则从data获取
|
||||
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, // 使用pbId作为排班ID
|
||||
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>
|
||||
815
src/pages/view/routine/kefuxuncha/kyXkkcDetail.vue
Normal file
815
src/pages/view/routine/kefuxuncha/kyXkkcDetail.vue
Normal 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); // 过滤掉undefined和null
|
||||
|
||||
// 返回逗号分隔的字符串,如果没有图片则返回空字符串
|
||||
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); // 过滤掉undefined和null
|
||||
|
||||
// 返回逗号分隔的字符串,如果没有视频则返回空字符串
|
||||
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>
|
||||
|
||||
|
||||
524
src/pages/view/routine/kefuxuncha/xcPbList.vue
Normal file
524
src/pages/view/routine/kefuxuncha/xcPbList.vue
Normal 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>
|
||||
@ -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, // 使用pbId作为排班ID
|
||||
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);
|
||||
// 从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;
|
||||
}
|
||||
}
|
||||
} 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`,
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user