调整完善选课巡查

This commit is contained in:
ywyonui 2025-07-31 23:42:12 +08:00
parent f8c27fd710
commit 658403dabd
5 changed files with 344 additions and 168 deletions

43
src/api/base/xcXmApi.ts Normal file
View File

@ -0,0 +1,43 @@
import { get, post } from "@/utils/request";
/**
*
*/
export const xcXmFindPageApi = async (params: any) => {
return await get("/api/xcXm/findPage", params);
};
/**
* /
*/
export const xcXmSaveApi = async (params: any) => {
return await post("/api/xcXm/save", params);
};
/**
*
*/
export const xcXmLogicDeleteApi = async (params: any) => {
return await post("/api/xcXm/logicDelete", params);
};
/**
* id查询
*/
export const xcXmFindByIdApi = async (params: any) => {
return await get("/api/xcXm/findById", params);
};
/**
*
*/
export const xcXmFindAllApi = async () => {
return await get("/api/xcXm/findAll");
};
/**
*
*/
export const xcXmFindByXcLxApi = async (xcLx: string) => {
return await get("/api/xcXm/findByXcLx", { xcLx });
};

36
src/api/base/xkXcApi.ts Normal file
View File

@ -0,0 +1,36 @@
import { get, post } from "@/utils/request";
/**
*
*/
export const xkXcFindPageApi = async (params: any) => {
return await get("/api/xkXc/findPage", params);
};
/**
* /
*/
export const xkXcSaveApi = async (params: any) => {
return await post("/api/xkXc/save", params);
};
/**
*
*/
export const xkXcLogicDeleteApi = async (params: any) => {
return await post("/api/xkXc/logicDelete", params);
};
/**
* id查询
*/
export const xkXcFindByIdApi = async (params: any) => {
return await get("/api/xkXc/findById", params);
};
/**
*
*/
export const xkXcFindAllApi = async () => {
return await get("/api/xkXc/findAll");
};

36
src/api/base/xkXcXmApi.ts Normal file
View File

@ -0,0 +1,36 @@
import { get, post } from "@/utils/request";
/**
*
*/
export const xkXcXmFindPageApi = async (params: any) => {
return await get("/api/xkXcXm/findPage", params);
};
/**
* /
*/
export const xkXcXmSaveApi = async (params: any) => {
return await post("/api/xkXcXm/save", params);
};
/**
*
*/
export const xkXcXmLogicDeleteApi = async (params: any) => {
return await post("/api/xkXcXm/logicDelete", params);
};
/**
* id查询
*/
export const xkXcXmFindByIdApi = async (params: any) => {
return await get("/api/xkXcXm/findById", params);
};
/**
*
*/
export const xkXcXmFindAllApi = async () => {
return await get("/api/xkXcXm/findAll");
};

View File

@ -1,29 +0,0 @@
import { get, post } from "@/utils/request";
/**
*
*/
export const xkscFindPageApi = async (params: any) => {
return await get("/api/xksc/findPage", params);
};
/**
* /
*/
export const xkscSaveApi = async (params: any) => {
return await post("/api/xksc/save", params);
};
/**
* id查询巡查记录
*/
export const xkscFindByIdApi = async (params: any) => {
return await get("/api/xksc/findById", params);
};
/**
*
*/
export const xkscLogicDeleteApi = async (params: any) => {
return await post("/api/xksc/logicDelete", params);
};

View File

@ -62,25 +62,55 @@
<text class="title-text">巡查项目</text> <text class="title-text">巡查项目</text>
</view> </view>
<view class="check-card bg-white r-md p-15"> <view class="check-card bg-white r-md p-15">
<checkbox-group class="check-list"> <template v-if="checkItems && checkItems.length > 0">
<label <view class="check-list">
v-for="item in checkItems" <view
v-for="(item, index) in checkItems"
:key="item.id" :key="item.id"
class="check-item" class="check-item"
> >
<view class="item-info"> <view class="item-info flex-1">
<text class="item-text">{{ item.id }}{{ item.text }}</text> <!-- 项目名称单独一行 -->
<text class="item-deduction">{{ item.deduction }}</text> <text class="item-text">{{ index + 1 }}{{ item.xcMc }}</text>
</view> <!-- 分值和结果同一行 -->
<checkbox <view class="item-score-result">
:value="String(item.id)" <text class="item-deduction mr-20">分值{{ item.xmFz }}</text>
:checked="item.checked" <view class="item-result">
@click="item.checked = !item.checked" <radio-group
:name="'result_' + item.id"
@change="e => { item.checked = e.detail.value === 'A'; }"
class="item-radio-group"
>
<label class="item-radio-label mr-10">
<radio
:value="'A'"
:checked="item.checked === true"
color="#4080ff" color="#4080ff"
style="transform: scale(0.8)" class="item-radio"
/> />
<text class="ml-2"></text>
</label> </label>
</checkbox-group> <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> </view>
@ -128,15 +158,13 @@
<view v-else class="completed-inspection"> <view v-else class="completed-inspection">
<!-- 巡查记录列表 --> <!-- 巡查记录列表 -->
<view class="inspection-list"> <view class="inspection-list">
<view <BasicListLayout @register="registerInspection" style="position: absolute;">
v-for="(record, index) in inspectionRecords" <template v-slot="{ data, index }">
:key="record.id || index" <view class="inspection-record bg-white r-md p-15 mb-15">
class="inspection-record bg-white r-md p-15 mb-15 mx-15"
>
<view class="record-header"> <view class="record-header">
<view class="record-time"> <view class="record-time">
<u-icon name="clock" color="#666" size="14"></u-icon> <u-icon name="clock" color="#666" size="14"></u-icon>
<text class="time-text">{{ formatTime(record.xctime) }}</text> <text class="time-text">{{ formatTime(data.xctime) }}</text>
</view> </view>
<view class="record-status"> <view class="record-status">
<text class="status-text">已巡查</text> <text class="status-text">已巡查</text>
@ -145,39 +173,80 @@
<view class="record-content"> <view class="record-content">
<view class="content-item"> <view class="content-item">
<text class="item-label">巡查教师</text> <text class="item-label">巡查教师</text>
<text class="item-value">{{ record.jsxm }}</text> <text class="item-value">{{ data.jsxm }}</text>
</view>
<view class="content-item flex-col">
<text class="item-label" style="flex: 0 0 25px;">巡查项目</text>
<view class="item-value" style="width:100%">
<template v-if="data.xkXcXmList && data.xkXcXmList.length > 0">
<view v-for="(xm, idx) in data.xkXcXmList" :key="xm.xcXmId" style="margin-bottom: 4px;">
<view>
<text>{{ idx + 1 }}{{ xm.xcMc }}</text>
<view style="display: flex; justify-content: space-between; margin: 4px 0;">
<text>分值{{ xm.xmFz }}</text>
<text>巡查结果{{ xm.xcJg === 'A' ? '有' : '无' }}</text>
</view>
</view>
</view>
</template>
<template v-else>
无巡查项目
</template>
</view>
</view> </view>
<view <view
class="content-item" class="content-item"
v-if="record.xcItems && record.xcItems.length > 0" v-if="data.images && data.images.length > 0"
>
<text class="item-label">巡查项目</text>
<text class="item-value">{{ record.xcItems.join(", ") }}</text>
</view>
<view
class="content-item"
v-if="record.images && record.images.length > 0"
> >
<text class="item-label">巡查图片</text> <text class="item-label">巡查图片</text>
<text class="item-value">{{ record.images.length }}</text> <view class="item-value" style="display: flex; flex-wrap: wrap; gap: 8px;">
<image
v-for="(img, imgIdx) in data.images"
:key="imgIdx"
:src="img"
mode="aspectFill"
style="width: 60px; height: 60px; border-radius: 4px; border: 1px solid #eee; cursor: pointer;"
@click="uni.previewImage({ current: img, urls: data.images })"
/>
</view>
</view> </view>
<view <view
class="content-item" class="content-item"
v-if="record.videos && record.videos.length > 0" v-if="data.videos && data.videos.length > 0"
> >
<text class="item-label">巡查视频</text> <text class="item-label">巡查视频</text>
<text class="item-value">{{ record.videos.length }}</text> <view class="item-value" style="display: flex; flex-wrap: wrap; gap: 8px;">
<view
v-for="(video, vIdx) in data.videos"
:key="vIdx"
style="width: 80px; height: 60px; position: relative; border-radius: 4px; overflow: hidden; border: 1px solid #eee; cursor: pointer; display: flex; align-items: center; justify-content: center; background: #000;"
@click="uni.previewMedia ? uni.previewMedia({ sources: data.videos.map(v => ({ url: v, type: 'video' })), current: vIdx }) : uni.showToast({ title: '当前平台不支持视频预览', icon: 'none' })"
>
<video
:src="video"
style="width: 100%; height: 100%; object-fit: cover;"
: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 style="position: absolute;left: 0;top: 0;width: 100%;height: 100%;display: flex;align-items: center;justify-content: center;">
<u-icon name="play-right-fill" color="#fff" size="28"></u-icon>
</view> </view>
</view> </view>
</view> </view>
</view> </view>
<!-- 暂无记录提示 -->
<view v-if="inspectionRecords.length === 0" class="empty-records">
<view class="empty-icon">
<u-icon name="list" size="50" color="#C8C9CC"></u-icon>
</view> </view>
<view class="empty-text">暂无巡查记录</view> </view>
</template>
</BasicListLayout>
</view> </view>
</view> </view>
@ -203,9 +272,11 @@
<script setup lang="ts"> <script setup lang="ts">
import { jsdXkXsListApi } from "@/api/base/server"; import { jsdXkXsListApi } from "@/api/base/server";
import { xkscFindPageApi, xkscSaveApi } from "@/api/base/xkscApi"; import { xkXcFindPageApi, xkXcSaveApi } from "@/api/base/xkXcApi";
import { xcXmFindByXcLxApi } from "@/api/base/xcXmApi";
import { xcBeforeMinuteApi } from "@/api/system/config/index"; import { xcBeforeMinuteApi } from "@/api/system/config/index";
import BasicLayout from "@/components/BasicLayout/Layout.vue"; import BasicLayout from "@/components/BasicLayout/Layout.vue";
import { useLayout } from "@/components/BasicListLayout/hooks/useLayout";
import { useDataStore } from "@/store/modules/data"; import { useDataStore } from "@/store/modules/data";
import { useDicStore } from "@/store/modules/dic"; import { useDicStore } from "@/store/modules/dic";
import { useUserStore } from "@/store/modules/user"; import { useUserStore } from "@/store/modules/user";
@ -250,13 +321,7 @@ const curXs = ref<any>(null);
const defSel = ref<any>([]); const defSel = ref<any>([]);
// //
const checkItems = ref<any[]>([ const checkItems = ref<any[]>([]);
{ id: 1, text: "未按时上课", deduction: 1, checked: false },
{ id: 2, text: "上课迟到", deduction: 2, checked: false },
{ id: 3, text: "上课内容与教学计划不一致", deduction: 1, checked: false },
{ id: 4, text: "课堂纪律差", deduction: 2, checked: false },
{ id: 5, text: "教学设备使用不当", deduction: 1, checked: false },
]);
// //
const uploadedImages = ref([]); const uploadedImages = ref([]);
@ -269,20 +334,39 @@ const inspectionStatusText = ref("");
const canInspect = ref(true); // const canInspect = ref(true); //
const xcBeforeMinute = ref<number>(0); const xcBeforeMinute = ref<number>(0);
// //
const inspectionRecords = ref<any[]>([]); let inspectionParams = ref({
rows: 10,
xkkcId: xkkc.value.id,
jsId: js.value.id
});
// //
const getStatusClass = (status: string) => { const [registerInspection, { reload }] = useLayout({
switch (status) { api: xkXcFindPageApi,
case "正常": componentProps: {},
return "status-normal"; param: inspectionParams.value
case "请假": });
return "status-leave";
case "缺勤": //
return "status-absent"; const loadCheckItems = async () => {
default: try {
return "status-normal"; const res = await xcXmFindByXcLxApi("选课巡查");
if (res && res.resultCode === 1) {
checkItems.value = res.result.map((item: any) => {
return {
...item,
checked: false
}
});
} else {
// API使
checkItems.value = [];
}
} catch (error) {
console.error("加载巡查项目失败:", error);
// 使
checkItems.value = [];
} }
}; };
@ -350,18 +434,6 @@ const rebuildNumInfo = () => {
}; };
}; };
//
const openStatusPicker = (xs: any) => {
curXs.value = xs;
for (let i = 0; i < statusOptions.value.length; i++) {
if (statusOptions.value[i].text === xs.xszt) {
defSel.value = [i];
break;
}
}
statusPickerVisible.value = true;
};
// //
const confirmStatus = (e: any) => { const confirmStatus = (e: any) => {
if (curXs.value && e.value && e.value[0]) { if (curXs.value && e.value && e.value[0]) {
@ -392,25 +464,8 @@ const handleVideoUploadSuccess = (res: any) => {
const onTabClick = (e: any) => { const onTabClick = (e: any) => {
currentTab.value = e.currentIndex; currentTab.value = e.currentIndex;
if (currentTab.value === 1) { if (currentTab.value === 1) {
loadInspectionRecords(); // tab
} reload();
};
//
const loadInspectionRecords = async () => {
try {
const res = await xkscFindPageApi({
xkkcId: xkkc.value.id,
jsId: js.value.id,
pageSize: 20,
pageNum: 1,
});
if (res && res.resultCode === 1) {
inspectionRecords.value = res.result?.rows || [];
}
} catch (error) {
console.error("加载巡查记录失败:", error);
inspectionRecords.value = [];
} }
}; };
@ -421,6 +476,8 @@ const formatTime = (timestamp: string) => {
// //
const checkInspectionTime = () => { const checkInspectionTime = () => {
canInspect.value = true;
return;
const currentTime = now; const currentTime = now;
let wDay = currentTime.day(); let wDay = currentTime.day();
if (wDay === 0) { if (wDay === 0) {
@ -482,26 +539,36 @@ const loadXcBeforeMinute = async () => {
// //
const submit = async () => { const submit = async () => {
if (!canInspect.value) { // if (!canInspect.value) {
uni.showToast({ // uni.showToast({
title: inspectionStatusText.value, // title: inspectionStatusText.value,
icon: "none", // icon: "none",
duration: 2000, // duration: 2000,
}); // });
return; // return;
} // }
try { try {
const res = await xkscSaveApi({ //
const xkXcXmList = checkItems.value.map((item: any) => {
const newItem = {
...item,
xcXmId: item.id,
xcJg: item.checked ? "A" : "B",
xkXcId: "", //
};
newItem.id = "";
return newItem;
});
const res = await xkXcSaveApi({
jsId: js.value.id, jsId: js.value.id,
jsxm: js.value.xm || js.value.jsxm, //
xkkcId: xkkc.value.id, xkkcId: xkkc.value.id,
xctime: now.format("YYYY-MM-DD HH:mm:ss"), xctime: now.format("YYYY-MM-DD HH:mm:ss"),
xkxcList: xsList.value, images: uploadedImages.value || "",
xcItems: checkItems.value videos: uploadedVideos.value || "",
.filter((item) => item.checked) xkXcXmList: xkXcXmList, //
.map((item) => item.text),
images: uploadedImages.value,
videos: uploadedVideos.value,
}); });
if (res && res.resultCode === 1) { if (res && res.resultCode === 1) {
@ -530,8 +597,7 @@ const submit = async () => {
// //
onMounted(async () => { onMounted(async () => {
// await loadXcBeforeMinute(); // await loadXcBeforeMinute();
await loadXsList(); await loadCheckItems(); //
await loadStatusOptions();
checkInspectionTime(); checkInspectionTime();
}); });
</script> </script>
@ -542,13 +608,16 @@ onMounted(async () => {
background-color: #f5f5f5; background-color: #f5f5f5;
} }
.bg-white {
background-color: white;
}
.header { .header {
height: 44px; height: 44px;
background-color: #fff; background-color: #fff;
} }
.inspection-tabs { .inspection-tabs {
padding: 15px 15px 0 15px;
background-color: #fff; background-color: #fff;
margin-bottom: 10px; margin-bottom: 10px;
position: sticky; position: sticky;
@ -731,8 +800,19 @@ onMounted(async () => {
padding: 12px 0; padding: 12px 0;
border-bottom: 1px solid #eee; border-bottom: 1px solid #eee;
.item-score-result {
display: flex;
align-items: center;
justify-content: space-between;;
}
&:first-child {
padding-top: 0;
}
&:last-child { &:last-child {
border-bottom: none; border-bottom: none;
padding-bottom: 0;
} }
} }
@ -761,6 +841,9 @@ onMounted(async () => {
} }
.inspection-list { .inspection-list {
position: relative;
height: calc(100vh - 50px);
.inspection-record { .inspection-record {
.record-header { .record-header {
display: flex; display: flex;
@ -794,18 +877,25 @@ onMounted(async () => {
.record-content { .record-content {
.content-item { .content-item {
display: flex; display: flex;
justify-content: space-between;
margin-bottom: 5px; margin-bottom: 5px;
font-size: 14px; font-size: 14px;
color: #333; color: #333;
.item-label { .item-label {
font-weight: bold; font-weight: bold;
margin-right: 10px; flex: 0 0 80px;
} }
} }
} }
} }
::v-deep .zp-loading-fixed {
position: absolute;
}
::v-deep .d-load-main {
position: absolute;
}
} }
.empty-records { .empty-records {