值班巡查

This commit is contained in:
hebo 2025-10-26 22:48:14 +08:00
parent 2dbf09df94
commit 8bc7961919
8 changed files with 2195 additions and 0 deletions

View File

@ -55,3 +55,10 @@ export const getXcCourseListApi = async (params: any) => {
export const getKyXcCourseListApi = async (params: any) => {
return await get("/api/pb/getKyXcCourseList", params);
};
/**
* -
*/
export const getZbXcListApi = async (params: any) => {
return await get("/api/pbZb/getZbXcList", params);
};

View File

@ -41,3 +41,10 @@ export const xcXmFindAllApi = async () => {
export const xcXmFindByXcLxApi = async (xcLx: string) => {
return await get("/api/xcXm/findByXcLx", { xcLx });
};
/**
* ID查询项目列表
*/
export const xcXmFindByPbLxIdApi = async (pbLxId: string) => {
return await get("/api/xcXm/findByPbLxId", { pbLxId });
};

37
src/api/base/zbXcApi.ts Normal file
View File

@ -0,0 +1,37 @@
import { get, post } from "@/utils/request";
/**
*
*/
export const zbXcFindPageApi = async (params: any) => {
return await get("/api/zbXc/findPage", params);
};
/**
* /
*/
export const zbXcSaveApi = async (params: any) => {
return await post("/api/zbXc/save", params);
};
/**
*
*/
export const zbXcLogicDeleteApi = async (params: any) => {
return await post("/api/zbXc/logicDelete", params);
};
/**
* id查询值周巡查记录
*/
export const zbXcFindByIdApi = async (params: any) => {
return await get("/api/zbXc/findById", params);
};
/**
*
*/
export const zbXcFindAllApi = async () => {
return await get("/api/zbXc/findAll");
};

View File

@ -871,6 +871,27 @@
"navigationBarTitleText": "课业巡查记录"
}
},
{
"path": "pages/view/routine/kefuxuncha/zbList",
"style": {
"navigationBarTitleText": "值周巡查",
"enablePullDownRefresh": false
}
},
{
"path": "pages/view/routine/kefuxuncha/zbDetail",
"style": {
"navigationBarTitleText": "值周巡查详情",
"enablePullDownRefresh": false
}
},
{
"path": "pages/view/routine/kefuxuncha/zbRecord",
"style": {
"navigationBarTitleText": "值周巡查记录",
"enablePullDownRefresh": false
}
},
{
"path": "pages/base/xs/qj/sp",
"style": {

View File

@ -264,6 +264,11 @@ const goXc = (pb: any) => {
uni.navigateTo({
url: `/pages/view/routine/kefuxuncha/kyXkList`,
});
} else if (pb.xclx === 'C') {
//
uni.navigateTo({
url: `/pages/view/routine/kefuxuncha/zbList`,
});
} else {
uni.showToast({
title: '未知的巡查类型',

View File

@ -0,0 +1,977 @@
<template>
<BasicLayout>
<!-- 待巡查内容 -->
<view class="pending-inspection">
<!-- 值周信息卡片 -->
<view class="duty-card mx-15 my-15 bg-white white-bg-color r-md p-15">
<view class="flex-row items-center mb-15">
<view class="duty-icon flex-center mr-10">
<u-icon name="account" color="#4080ff" size="20"></u-icon>
</view>
<text class="font-16 font-bold">{{ zb.jsxm || '值周教师' }}</text>
<text class="font-14 cor-999 ml-10">{{ todayInfo.weekName }}</text>
</view>
<!-- 值周详细信息 -->
<view class="duty-time-info">
<view class="time-item">
<view class="time-label">值周周次</view>
<view class="time-value">{{ zb.zbzc }}</view>
</view>
<view class="time-item">
<view class="time-label">值周星期</view>
<view class="time-value">{{ zb.zbxq || '全周' }}</view>
</view>
<view class="time-item">
<view class="time-label">值周区域</view>
<view class="time-value">{{ zb.zbqy || '暂无' }}</view>
</view>
<view class="time-item">
<view class="time-label">值周位置</view>
<view class="time-value">{{ zb.zbwz || '暂无' }}</view>
</view>
<view class="time-item">
<view class="time-label">值周角色</view>
<view class="time-value">{{ zb.zbjs || '暂无' }}</view>
</view>
</view>
<!-- 值周时间信息 -->
<view class="inspection-time-info" v-if="zb.zbkstime && zb.zbjstime">
<view class="time-item">
<u-icon name="clock" color="#4080ff" size="16"></u-icon>
<text class="time-label">值周时间</text>
<text class="time-value">{{ formatTime(zb.zbkstime) }} - {{ formatTime(zb.zbjstime) }}</text>
</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.xcJg === 'A'"
color="#52c41a"
class="item-radio"
/>
<text class="ml-2">优点</text>
</label>
<label class="item-radio-label">
<radio
:value="'B'"
:checked="item.xcJg === 'B'"
color="#ff4d4f"
class="item-radio"
/>
<text class="ml-2">缺点</text>
</label>
</radio-group>
</view>
</view>
<!-- 显示输入的评价内容 -->
<view v-if="item.xcPj" class="item-comment" @click="editComment(item)">
<view class="comment-header">
<text class="comment-label">{{ item.xcJg === 'A' ? '优点' : '缺点' }}</text>
<text class="comment-edit-hint">点击编辑</text>
</view>
<text class="comment-text">{{ item.xcPj }}</text>
</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-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">
<ImageVideoUpload
v-model:image-list="imageList"
v-model:video-list="videoList"
:max-image-count="5"
:max-video-count="3"
:compress-config="compressConfig"
:upload-api="attachmentUpload"
@image-upload-success="onImageUploadSuccess"
@video-upload-success="onVideoUploadSuccess"
/>
</view>
</view>
</view>
</view>
<template #bottom>
<view
v-if="canInspect"
class="submit-btn-wrap py-10 px-20 bg-white"
>
<button
class="submit-btn"
:class="{ 'submit-btn-disabled': isSubmitting }"
:disabled="isSubmitting"
@click="submit"
>
{{ isSubmitting ? (xcRecordId ? '更新中...' : '提交中...') : (xcRecordId ? '更新巡查' : '提交巡查') }}
</button>
</view>
</template>
</BasicLayout>
<!-- 富文本输入弹窗 -->
<view v-if="showInputModal" class="input-modal-overlay" @click="closeInputModal">
<view class="input-modal-content" @click.stop>
<view class="input-modal-header">
<text class="input-modal-title">请输入{{ currentInputLabel }}</text>
<view class="input-modal-close" @click="closeInputModal">
<u-icon name="close" size="20" color="#666"></u-icon>
</view>
</view>
<view class="input-modal-body">
<textarea
v-model="inputContent"
class="input-textarea"
:placeholder="`请输入${currentInputLabel}内容`"
:maxlength="500"
:show-confirm-bar="false"
:auto-height="false"
/>
<view class="input-counter">{{ inputContent.length }}/500</view>
</view>
<view class="input-modal-footer">
<button class="input-modal-btn input-modal-btn-cancel" @click="closeInputModal">
取消
</button>
<button class="input-modal-btn input-modal-btn-confirm" @click="confirmInput">
确定
</button>
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { xcXmFindByPbLxIdApi } from "@/api/base/xcXmApi";
import { zbXcSaveApi } from "@/api/base/zbXcApi";
import { attachmentUpload } from "@/api/system/upload";
import BasicLayout from "@/components/BasicLayout/Layout.vue";
import { ImageVideoUpload } from "@/components/ImageVideoUpload";
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 zb = 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[]>([]);
// ID
const xcRecordId = ref('');
//
import { type ImageItem, type VideoItem, COMPRESS_PRESETS } from '@/components/ImageVideoUpload'
//
const compressConfig = ref(COMPRESS_PRESETS.high)
const imageList = ref<ImageItem[]>([]);
const videoList = ref<VideoItem[]>([]);
//
const inspectionStatusText = ref("");
const canInspect = ref(true);
//
const isSubmitting = ref(false);
//
const showInputModal = ref(false);
const inputContent = ref('');
const currentInputLabel = ref('');
const currentInputValue = ref('');
const currentItem = ref<any>(null);
//
const formatTime = (timestamp: string) => {
if (!timestamp) return '';
return dayjs(timestamp).format('MM-DD HH:mm');
};
//
const loadCheckItems = async () => {
try {
// ID
const pbLxId = zb.value.pbLxId;
if (!pbLxId) {
console.warn('未找到排班类型ID无法加载巡查项目');
checkItems.value = [];
return;
}
console.log('加载巡查项目pbLxId:', pbLxId);
const res = await xcXmFindByPbLxIdApi(pbLxId);
if (res && res.resultCode === 1) {
checkItems.value = (res.result || []).map((item: any) => {
return {
...item,
xcJg: '', // A-B-
xcPj: '', //
};
});
console.log('巡查项目列表:', checkItems.value);
//
if (zb.value.xcRecord) {
restoreXcRecord(zb.value.xcRecord);
}
} else {
checkItems.value = [];
}
} catch (error) {
console.error("加载巡查项目失败:", error);
checkItems.value = [];
}
};
//
const restoreXcRecord = (xcRecord: any) => {
try {
console.log('========== 开始回显巡查记录 ==========');
console.log('巡查记录ID:', xcRecord.id);
console.log('巡查记录 zbXcXmList:', xcRecord.zbXcXmList);
console.log('当前巡查项目列表:', checkItems.value.map(item => ({
id: item.id,
xcMc: item.xcMc
})));
// ID
xcRecordId.value = xcRecord.id || '';
console.log('保存的巡查记录ID:', xcRecordId.value);
//
if (xcRecord.zbXcXmList && xcRecord.zbXcXmList.length > 0) {
console.log('开始回显', xcRecord.zbXcXmList.length, '个巡查项目');
let matchedCount = 0;
xcRecord.zbXcXmList.forEach((xcXm: any, index: number) => {
console.log(`\n[${index + 1}] 尝试匹配项目:`);
console.log(' xcXm.id:', xcXm.id);
console.log(' xcXm.xcXmId:', xcXm.xcXmId);
console.log(' xcXm.xcMc:', xcXm.xcMc);
console.log(' xcXm.xcJg:', xcXm.xcJg);
console.log(' xcXm.xcPj:', xcXm.xcPj);
const checkItem = checkItems.value.find(item => item.id === xcXm.xcXmId);
if (checkItem) {
console.log(' ✓ 找到匹配项目:', checkItem.xcMc);
checkItem.xcJg = xcXm.xcJg || '';
checkItem.xcPj = xcXm.xcPj || '';
checkItem.xcXmRecordId = xcXm.id || ''; // ID
matchedCount++;
console.log(' 设置后 - xcJg:', checkItem.xcJg, ', xcPj:', checkItem.xcPj);
} else {
console.log(' ✗ 未找到匹配项目!');
console.log(' 可用的项目ID:', checkItems.value.map(item => item.id).join(', '));
}
});
console.log(`\n成功匹配 ${matchedCount}/${xcRecord.zbXcXmList.length} 个巡查项目`);
} else {
console.log('⚠️ 没有巡查项目记录需要回显zbXcXmList 为空或不存在)');
}
//
if (xcRecord.zp) {
const imageUrls = xcRecord.zp.split(',').map((url: string) => url.trim()).filter((url: string) => url);
imageList.value = imageUrls.map((url: string) => ({
url: url,
path: imagUrl(url),
uploaded: true
}));
console.log('✓ 回显图片:', imageList.value.length, '张');
}
//
if (xcRecord.sp) {
const videoUrls = xcRecord.sp.split(',').map((url: string) => url.trim()).filter((url: string) => url);
videoList.value = videoUrls.map((url: string) => ({
url: url,
path: imagUrl(url),
uploaded: true
}));
console.log('✓ 回显视频:', videoList.value.length, '个');
}
console.log('\n回显完成最终巡查项目状态:');
checkItems.value.forEach((item, index) => {
console.log(` [${index + 1}] ${item.xcMc} - xcJg: ${item.xcJg || '未选择'}, xcPj: ${item.xcPj || '无'}`);
});
console.log('========== 回显完成 ==========\n');
} catch (error) {
console.error('❌ 回显巡查记录失败:', error);
}
};
const onCheckItemChange = (e: any, item: any) => {
const value = e.detail.value; // 'A' 'B'
const label = value === 'A' ? '优点' : '缺点';
//
currentItem.value = item;
currentInputValue.value = value;
currentInputLabel.value = label;
inputContent.value = item.xcPj || '';
showInputModal.value = true;
};
//
const closeInputModal = () => {
showInputModal.value = false;
//
if (currentItem.value && !currentItem.value.xcPj) {
currentItem.value.xcJg = '';
currentItem.value.xcPj = '';
}
inputContent.value = '';
currentItem.value = null;
};
//
const confirmInput = () => {
const value = inputContent.value.trim();
if (!value) {
uni.showToast({
title: `请输入${currentInputLabel.value}内容`,
icon: 'none',
duration: 2000
});
return;
}
if (currentItem.value) {
currentItem.value.xcJg = currentInputValue.value;
currentItem.value.xcPj = value;
}
showInputModal.value = false;
inputContent.value = '';
currentItem.value = null;
};
//
const editComment = (item: any) => {
const label = item.xcJg === 'A' ? '优点' : '缺点';
currentItem.value = item;
currentInputValue.value = item.xcJg;
currentInputLabel.value = label;
inputContent.value = item.xcPj || '';
showInputModal.value = true;
};
//
const onImageUploadSuccess = (image: ImageItem, index: number) => {
console.log('图片上传成功:', image, index);
};
const onVideoUploadSuccess = (video: VideoItem, index: number) => {
console.log('视频上传成功:', video, index);
};
//
const checkInspectionTime = () => {
const zbkstime = zb.value.zbkstime;
const zbjstime = zb.value.zbjstime;
//
if (!zbkstime || !zbjstime) {
canInspect.value = true;
inspectionStatusText.value = "可以巡查";
return;
}
const startTime = dayjs(zbkstime);
const endTime = dayjs(zbjstime);
//
const currentDate = now.format('YYYY-MM-DD');
const startDate = startTime.format('YYYY-MM-DD');
const endDate = endTime.format('YYYY-MM-DD');
//
if (currentDate < startDate) {
canInspect.value = false;
inspectionStatusText.value = `还未到值周时间,无法巡查(值周时间:${formatTime(zbkstime)} - ${formatTime(zbjstime)}`;
} else if (currentDate > endDate) {
canInspect.value = false;
inspectionStatusText.value = `值周时间已结束,无法巡查(值周时间:${formatTime(zbkstime)} - ${formatTime(zbjstime)}`;
} else {
canInspect.value = true;
inspectionStatusText.value = `可以巡查(值周时间:${formatTime(zbkstime)} - ${formatTime(zbjstime)}`;
}
};
//
const submit = async () => {
//
if (isSubmitting.value) {
uni.showToast({
title: "正在提交中,请勿重复点击",
icon: "none",
duration: 1500,
});
return;
}
//
checkInspectionTime();
if (!canInspect.value) {
uni.showToast({
title: inspectionStatusText.value,
icon: "none",
duration: 2000,
});
return;
}
//
const hasCheckedItems = checkItems.value.some(item => item.xcJg && item.xcPj);
if (!hasCheckedItems) {
uni.showToast({
title: "请至少选择一个巡查项目并填写评价",
icon: "none",
duration: 2000,
});
return;
}
isSubmitting.value = true;
try {
//
const zbXcXmList = checkItems.value
.filter((item: any) => item.xcJg && item.xcPj)
.map((item: any) => {
const newItem = {
...item,
xcXmId: item.id,
xcJg: item.xcJg, // A-B-
xcPj: item.xcPj, //
zbXcId: xcRecordId.value || "", // ID使
};
// IDID
if (xcRecordId.value && item.xcXmRecordId) {
newItem.id = item.xcXmRecordId;
} else {
newItem.id = "";
}
return newItem;
});
const submitData: any = {
jsId: js.value.id,
jsxm: js.value.xm || js.value.jsxm,
pbZbId: zb.value.id, // ID
pbLxId: zb.value.pbLxId, // ID
xctime: now.format("YYYY-MM-DD HH:mm:ss"),
zp: getImageUrls(),
sp: getVideoUrls(),
zbXcXmList: zbXcXmList,
};
// ID
if (xcRecordId.value) {
submitData.id = xcRecordId.value;
}
console.log('提交值周巡查数据:', submitData);
const res = await zbXcSaveApi(submitData);
if (res && res.resultCode === 1) {
uni.showToast({
title: xcRecordId.value ? "更新成功" : "提交成功",
icon: "success",
});
setTimeout(() => {
uni.navigateBack({
success: () => {
//
uni.$emit('refreshZbList');
}
});
}, 1500);
} else {
uni.showToast({
title: xcRecordId.value ? "更新失败" : "提交失败",
icon: "none",
});
}
} catch (error) {
console.error('提交值周巡查失败:', error);
uni.showToast({
title: xcRecordId.value ? "更新失败" : "提交失败",
icon: "none",
});
} finally {
isSubmitting.value = false;
}
};
// URL
const getImageUrls = () => {
const urls = imageList.value
.filter(img => img.url)
.map(img => img.url)
.filter((url): url is string => !!url);
return urls.length > 0 ? urls.join(',') : '';
};
// URL
const getVideoUrls = () => {
const urls = videoList.value
.filter(video => video.url)
.map(video => video.url)
.filter((url): url is string => !!url);
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;
}
.duty-card {
border-radius: 8px;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05);
}
.duty-icon {
width: 30px;
height: 30px;
border-radius: 4px;
background-color: rgba(64, 128, 255, 0.1);
}
.duty-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: 8px;
&:last-child {
margin-bottom: 0;
}
.time-label {
font-size: 14px;
color: #666;
}
.time-value {
font-size: 14px;
font-weight: bold;
color: #333;
}
}
}
.inspection-time-info {
margin-top: 15px;
padding: 10px 15px;
background-color: #f0f8ff;
border-radius: 4px;
border: 1px solid #d6e4ff;
.time-item {
display: flex;
align-items: center;
.time-label {
font-size: 14px;
color: #666;
margin-left: 5px;
margin-right: 5px;
}
.time-value {
font-size: 14px;
color: #4080ff;
font-weight: 500;
}
}
}
.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;
}
.item-comment {
margin-top: 10px;
padding: 10px 12px;
background-color: #f9f9f9;
border-radius: 6px;
border-left: 3px solid #4080ff;
cursor: pointer;
transition: background-color 0.3s;
&:active {
background-color: #f0f0f0;
}
.comment-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 4px;
}
.comment-label {
font-size: 13px;
color: #666;
font-weight: 500;
}
.comment-edit-hint {
font-size: 12px;
color: #4080ff;
}
.comment-text {
font-size: 13px;
color: #333;
line-height: 1.8;
white-space: pre-wrap;
word-break: break-word;
}
}
}
.upload-card {
background-color: white;
}
.submit-btn {
background-color: #4080ff;
color: #fff;
height: 44px;
border-radius: 22px;
font-size: 16px;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s ease;
}
.submit-btn-disabled {
background-color: #d9d9d9 !important;
color: #999 !important;
cursor: not-allowed;
}
.cor-999 {
color: #999;
}
/* 输入弹窗样式 */
.input-modal-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 9999;
padding: 20px;
}
.input-modal-content {
background-color: #fff;
border-radius: 12px;
width: 100%;
max-width: 600px;
max-height: 80vh;
display: flex;
flex-direction: column;
overflow: hidden;
}
.input-modal-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 16px 20px;
border-bottom: 1px solid #eee;
.input-modal-title {
font-size: 17px;
font-weight: bold;
color: #333;
}
.input-modal-close {
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
border-radius: 50%;
transition: background-color 0.3s;
&:active {
background-color: #f5f5f5;
}
}
}
.input-modal-body {
padding: 20px;
flex: 1;
overflow: hidden;
display: flex;
flex-direction: column;
.input-textarea {
width: 100%;
min-height: 200px;
max-height: 400px;
padding: 12px;
border: 1px solid #ddd;
border-radius: 8px;
font-size: 15px;
line-height: 1.6;
color: #333;
background-color: #fafafa;
resize: none;
box-sizing: border-box;
&:focus {
border-color: #4080ff;
background-color: #fff;
outline: none;
}
}
.input-counter {
margin-top: 8px;
text-align: right;
font-size: 12px;
color: #999;
}
}
.input-modal-footer {
display: flex;
padding: 12px 20px;
border-top: 1px solid #eee;
gap: 12px;
.input-modal-btn {
flex: 1;
height: 44px;
border-radius: 22px;
font-size: 16px;
display: flex;
align-items: center;
justify-content: center;
border: none;
cursor: pointer;
transition: all 0.3s;
&.input-modal-btn-cancel {
background-color: #f5f5f5;
color: #666;
&:active {
background-color: #e8e8e8;
}
}
&.input-modal-btn-confirm {
background-color: #4080ff;
color: #fff;
&:active {
background-color: #3070ef;
}
}
}
}
</style>

View File

@ -0,0 +1,720 @@
<template>
<view class="duty-inspection">
<!-- 值周信息头部 - 固定部分 -->
<view class="selection-header">
<view class="header-content">
<view class="title-section">
<view class="title">
<text v-if="pbData && pbData.xcbt">{{ pbData.xcbt }}</text>
<text v-else>值周巡查</text>
</view>
</view>
</view>
</view>
<!-- 可滚动的内容区域 -->
<view class="scrollable-content">
<!-- 值周列表 -->
<view class="duty-list" v-if="zbList && zbList.length > 0">
<view
v-for="(zb, index) in zbList"
:key="zb.id || index"
class="duty-item"
>
<!-- 巡查状态标识 -->
<view class="duty-status" :class="zb.sfxc === '是' ? 'status-done' : 'status-pending'">
{{ zb.sfxc === '是' ? '已巡查' : '待巡查' }}
</view>
<view class="duty-name">{{ getDutyDisplayName(zb) }}</view>
<!-- 值周信息 -->
<view class="duty-info-row">
<view class="duty-info-item">
<view class="info-label">值周周次</view>
<view class="info-data">{{ zb.zbzc }}</view>
</view>
<view class="duty-info-item">
<view class="info-label">值周星期</view>
<view class="info-data">{{ zb.zbxq || '全周' }}</view>
</view>
</view>
<view class="duty-info-row">
<view class="duty-info-item">
<view class="info-label">值周区域</view>
<view class="info-data">{{ zb.zbqy || '暂无' }}</view>
</view>
<view class="duty-info-item">
<view class="info-label">值周位置</view>
<view class="info-data">{{ zb.zbwz || '暂无' }}</view>
</view>
</view>
<view class="duty-info-row">
<view class="duty-info-item">
<view class="info-label">值周时间</view>
<view class="info-data time">{{ formatDutyTime(zb) }}</view>
</view>
</view>
<view class="separator-line"></view>
<view class="duty-btn-group">
<view class="xc-btn" @click.stop="goXc(zb)">巡查</view>
<view class="record-btn" @click.stop="goRecord(zb)">巡查记录</view>
</view>
</view>
</view>
<!-- 暂无数据提示 -->
<view v-else class="empty-duty-list">
<view class="empty-icon">
<u-icon name="list" size="50" color="#C8C9CC"></u-icon>
</view>
<view class="empty-text">暂无值周数据</view>
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { getZbXcListApi } from "@/api/base/pbApi";
import { zbXcFindPageApi } from "@/api/base/zbXcApi";
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 dataStore = useDataStore();
//
const zbList = ref<any[]>([]);
//
const pbData = ref<any>(null);
//
const wdNameList = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"];
//
const isCurrentTimeMatch = (zb: any) => {
const now = dayjs();
//
if (zb.zbkstime && zb.zbjstime) {
const startTime = dayjs(zb.zbkstime);
const endTime = dayjs(zb.zbjstime);
//
const currentDate = now.format('YYYY-MM-DD');
const startDate = startTime.format('YYYY-MM-DD');
const endDate = endTime.format('YYYY-MM-DD');
//
if (currentDate >= startDate && currentDate <= endDate) {
return true;
}
}
return false;
};
//
const getDutyDisplayName = (zb: any) => {
let displayName = '';
if (zb.jsxm) {
displayName = zb.jsxm;
}
if (zb.zbjs) {
displayName += ` - ${zb.zbjs}`;
}
return displayName || '值周教师';
};
//
const formatDutyTime = (zb: any) => {
if (zb.zbkstime && zb.zbjstime) {
const startTime = dayjs(zb.zbkstime).format('MM-DD HH:mm');
const endTime = dayjs(zb.zbjstime).format('MM-DD HH:mm');
return `${startTime} ~ ${endTime}`;
}
return '暂无';
};
onMounted(async () => {
uni.showLoading({
title: "加载中...",
});
// data
let tempPbData = dataStore.getData;
console.log('初始获取的data数据:', tempPbData);
// dataglobal
if (!tempPbData || !tempPbData.xcbt) {
tempPbData = dataStore.getGlobal;
console.log('从global获取的数据:', tempPbData);
}
//
if (!tempPbData || !tempPbData.xcbt) {
uni.showToast({
title: '数据异常,请重新选择排班',
icon: 'none'
});
uni.navigateBack();
return;
}
// pbId
const pbId = tempPbData.pbId || tempPbData.id;
const pbDataWithPbId = {
...tempPbData,
pbId: pbId,
pbLxId: tempPbData.pbLxId || ''
};
delete pbDataWithPbId.id;
dataStore.setGlobal(pbDataWithPbId);
pbData.value = pbDataWithPbId;
console.log('排班数据检查:', {
pbData: pbDataWithPbId,
pbId: pbId,
pbLxId: pbDataWithPbId.pbLxId
});
await loadZbXcList(pbDataWithPbId);
//
uni.$on('refreshZbList', async () => {
console.log('收到刷新事件,重新加载值周列表');
await refreshZbList();
});
uni.hideLoading();
});
//
const loadZbXcList = async (pbData: any) => {
try {
// pbData
if (!pbData || !pbData.pbId) {
uni.showToast({
title: '排班数据无效,请重新选择',
icon: 'none'
});
return;
}
const pbId = pbData.pbId;
console.log('API调用参数:', {
jsId: getJs.id,
pbId: pbId,
pbData: pbData
});
const res = await getZbXcListApi({
jsId: getJs.id,
pbId: pbId
});
if (res && res.resultCode == 1) {
const list = res.result || [];
//
let mappedList = list.map((item: any) => ({
id: item.id, // ID
pbId: pbId, // ID
jsId: item.jsId, // ID
jsxm: item.jsxm, //
zbzc: item.zbzc, //
zbxq: item.zbxq, //
zbqy: item.zbqy, //
zbwz: item.zbwz, //
zbjs: item.zbjs, //
zbkstime: item.zbkstime, //
zbjstime: item.zbjstime, //
pbLxId: item.pbLxId || pbData.pbLxId, // ID
sfxc: item.sfxc || '否' //
}));
//
zbList.value = mappedList.filter((zb: any) => isCurrentTimeMatch(zb));
console.log('值周列表数据:', zbList.value);
} else {
zbList.value = [];
uni.showToast({
title: (res as any).resultMessage || '获取值周巡查数据失败',
icon: 'none'
});
}
} catch (error) {
console.error('加载值周巡查数据失败:', error);
zbList.value = [];
uni.showToast({
title: '加载值周巡查数据失败',
icon: 'none'
});
}
};
//
const refreshZbList = async () => {
try {
uni.showLoading({
title: "刷新中...",
});
const pbData = dataStore.getGlobal;
if (pbData && pbData.pbId) {
await loadZbXcList(pbData);
}
uni.hideLoading();
uni.showToast({
title: "刷新成功",
icon: "success",
duration: 1000
});
} catch (error) {
console.error('刷新值周列表失败:', error);
uni.hideLoading();
uni.showToast({
title: "刷新失败",
icon: "none"
});
}
};
//
const goXc = async (zb: any) => {
const pbData = dataStore.getGlobal;
//
if (!pbData || !pbData.xcbt) {
uni.showToast({
title: '数据异常,请重新选择排班',
icon: 'none'
});
return;
}
//
const combinedData = {
...zb,
pbId: pbData.pbId,
pbLxId: zb.pbLxId || pbData.pbLxId,
xclx: pbData.xclx,
xcbt: pbData.xcbt,
xqmc: pbData.xqmc
};
//
if (zb.sfxc === '是') {
try {
uni.showLoading({
title: '加载中...'
});
console.log('========== 查询已巡查记录 ==========');
console.log('查询参数 pbZbId:', zb.id);
const res = await zbXcFindPageApi({
rows: 10,
page: 1,
pbZbId: zb.id
});
console.log('查询结果完整数据:', res);
console.log('查询结果 resultCode:', res?.resultCode);
console.log('查询结果 result 数量:', res?.result?.length);
console.log('查询结果 rows:', res?.rows);
console.log('查询结果 rows 数量:', res?.rows?.length);
uni.hideLoading();
//
let xcRecordList = res?.result || res?.rows || [];
if (res && (res.resultCode === 1 || res.resultCode === '1' || xcRecordList.length > 0)) {
if (xcRecordList.length > 0) {
//
const xcRecord = xcRecordList[0];
combinedData.xcRecord = xcRecord;
console.log('✓ 获取到巡查记录ID:', xcRecord.id);
console.log(' - jsxm:', xcRecord.jsxm);
console.log(' - xctime:', xcRecord.xctime);
console.log(' - zbXcXmList 数量:', xcRecord.zbXcXmList?.length || 0);
if (xcRecord.zbXcXmList && xcRecord.zbXcXmList.length > 0) {
console.log(' - zbXcXmList 详情:', xcRecord.zbXcXmList.map((xm: any) => ({
xcXmId: xm.xcXmId,
xcMc: xm.xcMc,
xcJg: xm.xcJg,
xcPj: xm.xcPj
})));
} else {
console.log(' ⚠️ zbXcXmList 为空!');
}
console.log('========== 查询完成 ==========');
} else {
console.log('❌ 返回数据为空');
}
} else {
console.log('❌ 未查询到巡查记录或查询失败');
console.log(' 返回数据结构:', Object.keys(res || {}));
}
} catch (error) {
uni.hideLoading();
console.error('查询巡查记录失败:', error);
}
} else {
console.log('该值周记录尚未巡查,无需回显');
}
console.log('点击巡查,传递数据:', combinedData);
dataStore.setData(combinedData);
uni.navigateTo({
url: `/pages/view/routine/kefuxuncha/zbDetail`,
});
};
//
const goRecord = (zb: any) => {
const pbData = dataStore.getGlobal;
//
if (!pbData || !pbData.xcbt) {
uni.showToast({
title: '数据异常,请重新选择排班',
icon: 'none'
});
return;
}
//
const combinedData = {
...zb,
pbId: pbData.pbId,
pbLxId: zb.pbLxId || pbData.pbLxId,
xclx: pbData.xclx,
xcbt: pbData.xcbt,
xqmc: pbData.xqmc
};
dataStore.setData(combinedData);
uni.navigateTo({
url: `/pages/view/routine/kefuxuncha/zbRecord`,
});
};
//
onBeforeUnmount(() => {
uni.$off('refreshZbList');
});
</script>
<style lang="scss" scoped>
.duty-inspection {
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;
.title {
font-size: 20px;
font-weight: 700;
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;
}
.duty-list {
padding: 15px 15px 0 15px;
.duty-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;
}
.duty-status {
position: absolute;
top: 15px;
right: 15px;
padding: 4px 12px;
border-radius: 12px;
font-size: 12px;
font-weight: 600;
z-index: 2;
animation: fadeInRight 0.5s ease-out 0.2s both;
&.status-done {
background: linear-gradient(135deg, #67c23a, #85ce61);
color: #fff;
box-shadow: 0 2px 8px rgba(103, 194, 58, 0.3);
}
&.status-pending {
background: linear-gradient(135deg, #e6a23c, #f0c78a);
color: #fff;
box-shadow: 0 2px 8px rgba(230, 162, 60, 0.3);
}
}
.duty-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;
padding-right: 80px;
}
.duty-btn-group {
display: flex;
justify-content: flex-end;
gap: 10px;
.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);
}
}
}
.duty-info-row {
display: flex;
margin-bottom: 14px;
gap: 20px;
animation: fadeInUp 0.5s ease-out 0.15s both;
}
.duty-info-item {
display: flex;
flex: 1;
font-size: 13px;
align-items: center;
.info-label {
color: #666;
flex: 0 0 70px;
font-weight: 500;
margin-right: 0px;
}
.info-data {
flex: 1;
color: #333;
font-weight: 400;
word-break: break-all;
&.time {
white-space: normal;
word-break: break-word;
line-height: 1.5;
}
}
}
.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;
}
}
.empty-duty-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

@ -0,0 +1,421 @@
<template>
<BasicLayout>
<!-- 巡查记录列表 -->
<view class="inspection-list">
<BasicListLayout
@register="registerInspection"
style="position: absolute"
>
<template v-slot="{ data, index }">
<view class="inspection-record bg-white r-md p-15 mb-15 timeline-item">
<!-- 时间轴连接线 -->
<view class="timeline-line" v-if="index < 10"></view>
<!-- 时间轴节点 -->
<view class="timeline-dot">
<view class="dot-inner"></view>
</view>
<view class="record-header">
<view class="record-time">
<u-icon name="clock" color="#4080ff" size="16"></u-icon>
<text class="time-text">{{ formatTime(data.xctime) }}</text>
</view>
<view class="record-status">
<text class="status-text">已巡查</text>
</view>
</view>
<view class="record-content">
<view class="content-item">
<text class="item-label">巡查教师</text>
<text class="item-value">{{ data.jsxm }}</text>
</view>
<view class="content-item">
<text class="item-label">值周周次</text>
<text class="item-value">{{ data.zbzc }}</text>
</view>
<view class="content-item">
<text class="item-label">值周星期</text>
<text class="item-value">{{ data.zbxq || '全周' }}</text>
</view>
<view class="content-item">
<text class="item-label">值周区域</text>
<text class="item-value">{{ data.zbqy || '暂无' }}</text>
</view>
<view class="content-item">
<text class="item-label">值周位置</text>
<text class="item-value">{{ data.zbwz || '暂无' }}</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.zbXcXmList && data.zbXcXmList.length > 0">
<view
v-for="(xm, idx) in data.zbXcXmList"
:key="xm.xcXmId"
style="margin-bottom: 4px"
>
<view>
<view style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 4px;">
<view style="display: flex; align-items: center; flex: 1;">
<text style="margin-right: 8px;">{{ idx + 1 }}{{ xm.xcMc }}</text>
<view style="display: flex; align-items: center;">
<u-icon
:name="xm.xcJg === 'B' ? 'checkmark-circle-fill' : 'close-circle-fill'"
:color="xm.xcJg === 'B' ? '#67c23a' : '#f56c6c'"
size="18"
></u-icon>
</view>
</view>
<view style="font-size: 12px; color: #666;">
<text v-if="xm.xcJg === 'A'" style="color: #f56c6c;">
扣分-{{ xm.xmFz }}
</text>
<text v-else style="color: #67c23a;">
不扣分
</text>
</view>
</view>
<view v-if="xm.xcPj" style="font-size: 12px; color: #666; margin-top: 4px; padding-left: 20px; line-height: 1.5;">
<text style="color: #999;">评价</text>
<text style="color: #666;">{{ xm.xcPj }}</text>
</view>
</view>
</view>
</template>
<template v-else> 无巡查项目 </template>
</view>
</view>
<view class="content-item" v-if="data.zp && data.zp.length > 0">
<text class="item-label">巡查图片</text>
<view class="item-value" style="display: flex; flex-wrap: wrap; gap: 8px">
<image
v-for="(img, imgIdx) in getImageArray(data.zp)"
:key="imgIdx"
:src="imagUrl(img)"
mode="aspectFill"
style="
width: 60px;
height: 60px;
border-radius: 4px;
border: 1px solid #eee;
cursor: pointer;
"
@click="handlePreviewImage(img, getImageArray(data.zp))"
/>
</view>
</view>
<view class="content-item" v-if="data.sp && data.sp.length > 0">
<text class="item-label">巡查视频</text>
<view class="item-value" style="display: flex; flex-wrap: wrap; gap: 8px">
<view
v-for="(video, vIdx) in getVideoArray(data.sp)"
: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="handlePreviewVideo(getVideoArray(data.sp), vIdx)"
>
<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>
</template>
</BasicListLayout>
</view>
</BasicLayout>
</template>
<script setup lang="ts">
import { zbXcFindPageApi } from "@/api/base/zbXcApi";
import BasicLayout from "@/components/BasicLayout/Layout.vue";
import { useLayout } from "@/components/BasicListLayout/hooks/useLayout";
import { useDataStore } from "@/store/modules/data";
import { useUserStore } from "@/store/modules/user";
import { computed, onMounted } from "vue";
import dayjs from "dayjs";
import { imagUrl } from "@/utils";
const { getJs } = useUserStore();
const { getData } = useDataStore();
const js = computed(() => getJs);
const zb = computed(() => getData);
//
let inspectionParams = {
rows: 10,
pbZbId: zb.value.id,
};
console.log('值周巡查记录查询参数:', inspectionParams);
//
const [registerInspection, { reload }] = useLayout({
api: zbXcFindPageApi,
componentProps: {},
param: inspectionParams,
});
//
const handlePreviewImage = (img: string, images: string[]) => {
const processedImages = images.map(image => imagUrl(image));
uni.previewImage({
current: imagUrl(img),
urls: processedImages,
});
};
//
const handlePreviewVideo = (videos: string[], index: number) => {
uni.previewMedia({
current: index,
sources: videos.map((url) => ({
url: imagUrl(url),
type: "video",
})),
});
};
//
const formatTime = (timestamp: string) => {
const date = dayjs(timestamp);
const weekDays = ['周日', '周一', '周二', '周三', '周四', '周五', '周六'];
const weekDay = weekDays[date.day()];
return `${weekDay} ${date.format("YYYY-MM-DD HH:mm")}`;
};
//
const getImageArray = (str: string) => {
if (!str) return [];
return str.split(",").map((item) => item.trim());
};
//
const getVideoArray = (str: string) => {
if (!str) return [];
return str.split(",").map((item) => item.trim());
};
//
onMounted(() => {
reload();
});
</script>
<style scoped lang="scss">
.inspection-list {
position: relative;
height: calc(100vh - 50px);
padding-left: 12px;
.inspection-record {
position: relative;
margin-left: 0;
background: linear-gradient(135deg, #ffffff 0%, #f8fafc 100%);
border-radius: 12px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
border: 1px solid #e8f4fd;
transition: all 0.3s ease;
overflow: hidden;
&:hover {
transform: translateY(-2px);
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12);
border-color: #4080ff;
}
.timeline-line {
position: absolute;
left: -12px;
top: 0;
width: 2px;
height: 100%;
background: linear-gradient(180deg, #4080ff 0%, #e8f4fd 100%);
z-index: 1;
}
.timeline-dot {
position: absolute;
left: -18px;
top: 20px;
width: 12px;
height: 12px;
background: #4080ff;
border-radius: 50%;
z-index: 2;
box-shadow: 0 0 0 4px #ffffff, 0 0 0 6px #e8f4fd;
.dot-inner {
width: 100%;
height: 100%;
background: linear-gradient(135deg, #4080ff 0%, #66b3ff 100%);
border-radius: 50%;
animation: pulse 2s infinite;
}
}
.record-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
padding: 12px 8px;
background: linear-gradient(135deg, #f0f7ff 0%, #e8f4fd 100%);
border-radius: 8px 8px 0 0;
border-bottom: 1px solid #d1e7ff;
.record-time {
display: flex;
align-items: center;
font-size: 15px;
color: #2c5aa0;
font-weight: 600;
.time-text {
margin-left: 8px;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
}
}
.record-status {
padding: 6px 12px;
border-radius: 20px;
background: linear-gradient(135deg, #4080ff 0%, #66b3ff 100%);
color: #ffffff;
font-size: 12px;
font-weight: 600;
box-shadow: 0 2px 8px rgba(64, 128, 255, 0.3);
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
}
}
.record-content {
padding: 0 8px 12px 8px;
.content-item {
display: flex;
margin-bottom: 6px;
font-size: 14px;
color: #333;
padding: 6px 8px;
background: #fafbfc;
border-radius: 6px;
border-left: 3px solid #e8f4fd;
transition: all 0.2s ease;
&:hover {
background: #f0f7ff;
border-left-color: #4080ff;
}
.item-label {
font-weight: 600;
flex: 0 0 80px;
color: #2c5aa0;
}
.item-value {
color: #4a5568;
}
}
}
}
.inspection-record:first-child {
.timeline-dot {
background: linear-gradient(135deg, #67c23a 0%, #85ce61 100%);
box-shadow: 0 0 0 4px #ffffff, 0 0 0 6px #f0f9ff;
}
}
.inspection-record:last-child {
.timeline-line {
display: none;
}
}
@keyframes pulse {
0% {
transform: scale(1);
opacity: 1;
}
50% {
transform: scale(1.1);
opacity: 0.8;
}
100% {
transform: scale(1);
opacity: 1;
}
}
.timeline-item {
animation: fadeInUp 0.6s ease-out;
}
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
::v-deep .zp-loading-fixed {
position: absolute;
}
::v-deep .d-load-main {
position: absolute;
}
}
</style>