From ec00255aab24865cd785e181704e502354b9c8f6 Mon Sep 17 00:00:00 2001
From: spring <2396852758@qq.com>
Date: 星期五, 13 三月 2026 13:52:49 +0800
Subject: [PATCH] Merge branch 'dev_KT' of http://114.132.189.42:9002/r/product-inventory-APP-before into dev_KT
---
src/pages/index.vue | 18
src/pages.json | 11
src/pages/inspectionManagement/components/qrCodeDia.vue | 132 ++++
src/pages/inspectionManagement/components/viewQrCodeFiles.vue | 169 +++++
src/pages/inspectionManagement/components/viewFiles.vue | 296 ++++++++++
src/pages/inspectionManagement/components/formDia.vue | 549 ++++++++++++++++++
src/pages/inspectionManagement/index.vue | 545 ++++++++++++++++++
7 files changed, 1,718 insertions(+), 2 deletions(-)
diff --git a/src/pages.json b/src/pages.json
index 09239ca..a6d3cf9 100644
--- a/src/pages.json
+++ b/src/pages.json
@@ -613,6 +613,15 @@
}
},
{
+ "path": "pages/inspectionManagement/index",
+ "style": {
+ "navigationBarTitleText": "宸℃浠诲姟绠$悊",
+ "navigationStyle": "custom",
+ "enablePullDownRefresh": true,
+ "backgroundColor": "#f8f8f8"
+ }
+ },
+ {
"path": "pages/equipmentManagement/faultAnalysis/index",
"style": {
"navigationBarTitleText": "鏁呴殰鍒嗘瀽杩芥函",
@@ -1148,4 +1157,4 @@
"navigationBarTitleText": "RuoYi",
"navigationBarBackgroundColor": "#FFFFFF"
}
-}
\ No newline at end of file
+}
diff --git a/src/pages/index.vue b/src/pages/index.vue
index ed56e44..127bcba 100644
--- a/src/pages/index.vue
+++ b/src/pages/index.vue
@@ -435,6 +435,10 @@
icon: "/static/images/icon/xunjianshangchuan@2x.png",
label: "宸℃绠$悊",
},
+ {
+ icon: "/static/images/icon/xunjianshangchuan@2x.png",
+ label: "宸℃浠诲姟绠$悊",
+ }
]);
// 澶勭悊甯哥敤鍔熻兘鐐瑰嚮
@@ -657,6 +661,11 @@
case "宸℃绠$悊":
uni.navigateTo({
url: "/pages/inspectionUpload/index",
+ });
+ break;
+ case "宸℃浠诲姟绠$悊":
+ uni.navigateTo({
+ url: "/pages/inspectionManagement/index",
});
break;
case "鍒嗘瀽杩芥函":
@@ -1033,10 +1042,17 @@
{ icon: "/static/images/icon/shbeibaoxiu@2x.png", label: "璁惧鎶ヤ慨" },
{ icon: "/static/images/icon/shbeibaoyang@2x.png", label: "璁惧淇濆吇" },
{ icon: "/static/images/icon/xunjianshangchuan@2x.png", label: "宸℃绠$悊" },
+ { icon: "/static/images/icon/xunjianshangchuan@2x.png", label: "宸℃浠诲姟绠$悊" },
];
const filteredEquipment = originalEquipment.filter(item => {
return allowedMenuTitles.has(item.label);
});
+ if (filteredEquipment.some(i => i.label === "宸℃绠$悊")) {
+ const task = originalEquipment.find(i => i.label === "宸℃浠诲姟绠$悊");
+ if (task && !filteredEquipment.some(i => i.label === "宸℃浠诲姟绠$悊")) {
+ filteredEquipment.push(task);
+ }
+ }
equipmentItems.splice(0, equipmentItems.length, ...filteredEquipment);
};
@@ -1796,4 +1812,4 @@
box-shadow: 0 0.375rem 1.25rem rgba(0, 0, 0, 0.4);
}
}
-</style>
\ No newline at end of file
+</style>
diff --git a/src/pages/inspectionManagement/components/formDia.vue b/src/pages/inspectionManagement/components/formDia.vue
new file mode 100644
index 0000000..e44d91e
--- /dev/null
+++ b/src/pages/inspectionManagement/components/formDia.vue
@@ -0,0 +1,549 @@
+<template>
+ <u-popup :show="dialogVisitable"
+ mode="center"
+ :round="12"
+ :zIndex="900"
+ @close="cancel">
+ <view class="popup-content">
+ <view class="popup-title">{{ operationType === "add" ? "鏂板宸℃浠诲姟" : "缂栬緫宸℃浠诲姟" }}</view>
+ <view class="form-body">
+ <view class="form-item">
+ <text class="label">璁惧鍚嶇О</text>
+ <picker mode="selector"
+ :range="deviceOptions"
+ range-key="deviceName"
+ :value="deviceIndex"
+ @change="onDeviceChange">
+ <view class="picker-value">{{ form.taskName || "璇烽�夋嫨璁惧" }}</view>
+ </picker>
+ </view>
+ <view class="form-item">
+ <text class="label">宸℃浜�</text>
+ <view class="picker-value inspector-picker"
+ @click="openInspectorPopup">
+ <text>{{ inspectorText || "璇烽�夋嫨宸℃浜�" }}</text>
+ <u-icon name="arrow-right"
+ size="14"
+ color="#999" />
+ </view>
+ <view class="inspector-tags"
+ v-if="form.inspector?.length">
+ <view v-for="userId in form.inspector"
+ :key="userId"
+ class="inspector-tag"
+ @click="toggleInspector(userId)">
+ <text>{{ getUserName(userId) }}</text>
+ <u-icon name="close"
+ size="11"
+ color="#1677ff" />
+ </view>
+ </view>
+ </view>
+ <view class="form-item">
+ <text class="label">澶囨敞</text>
+ <u-textarea v-model="form.remarks"
+ placeholder="璇疯緭鍏ュ娉�"
+ :height="80"
+ count />
+ </view>
+ <view class="form-item">
+ <text class="label">浠诲姟棰戠巼</text>
+ <picker mode="selector"
+ :range="frequencyOptions"
+ range-key="label"
+ :value="frequencyIndex"
+ @change="onFrequencyChange">
+ <view class="picker-value">{{ currentFrequencyLabel || "璇烽�夋嫨浠诲姟棰戠巼" }}</view>
+ </picker>
+ </view>
+ <view class="form-item"
+ v-if="form.frequencyType === 'DAILY'">
+ <text class="label">鏃堕棿</text>
+ <picker mode="time"
+ :value="form.frequencyDetail || '08:00'"
+ @change="onDailyTimeChange">
+ <view class="picker-value">{{ form.frequencyDetail || "璇烽�夋嫨鏃堕棿" }}</view>
+ </picker>
+ </view>
+ <view class="form-item"
+ v-if="form.frequencyType === 'WEEKLY'">
+ <text class="label">姣忓懆鏃ユ湡</text>
+ <picker mode="selector"
+ :range="weekOptions"
+ range-key="label"
+ :value="weekIndex"
+ @change="onWeekChange">
+ <view class="picker-value">{{ currentWeekLabel || "璇烽�夋嫨鏄熸湡" }}</view>
+ </picker>
+ </view>
+ <view class="form-item"
+ v-if="form.frequencyType === 'WEEKLY'">
+ <text class="label">姣忓懆鏃堕棿</text>
+ <picker mode="time"
+ :value="form.time || '08:00'"
+ @change="onWeekTimeChange">
+ <view class="picker-value">{{ form.time || "璇烽�夋嫨鏃堕棿" }}</view>
+ </picker>
+ </view>
+ <view class="form-item"
+ v-if="form.frequencyType === 'MONTHLY'">
+ <text class="label">姣忔湀鏃ユ湡涓庢椂闂�</text>
+ <picker mode="date"
+ fields="day"
+ :value="monthlyDate"
+ @change="onMonthlyDateChange">
+ <view class="picker-value">{{ monthlyDate || "璇烽�夋嫨鏃ユ湡" }}</view>
+ </picker>
+ <picker mode="time"
+ :value="monthlyTime"
+ @change="onMonthlyTimeChange">
+ <view class="picker-value">{{ monthlyTime || "璇烽�夋嫨鏃堕棿" }}</view>
+ </picker>
+ </view>
+ </view>
+ <view class="popup-footer">
+ <u-button @click="cancel">鍙栨秷</u-button>
+ <u-button type="primary"
+ @click="submitForm">淇濆瓨</u-button>
+ </view>
+ </view>
+ </u-popup>
+ <u-popup :show="showInspectorPopup"
+ mode="bottom"
+ :round="16"
+ :zIndex="1100"
+ @close="closeInspectorPopup">
+ <view class="inspector-popup">
+ <view class="inspector-header">
+ <text class="inspector-title">閫夋嫨宸℃浜�</text>
+ </view>
+ <scroll-view scroll-y
+ class="inspector-list">
+ <view v-for="item in userList"
+ :key="item.userId"
+ class="inspector-row"
+ @click="toggleInspector(item.userId)">
+ <text class="inspector-name">{{ item.nickName }}</text>
+ <u-icon v-if="form.inspector.includes(item.userId)"
+ name="checkmark-circle-fill"
+ color="#1677ff"
+ size="20" />
+ <u-icon v-else
+ name=""
+ color="#d5d8de"
+ size="20" />
+ </view>
+ </scroll-view>
+ <view class="inspector-footer">
+ <u-button @click="closeInspectorPopup">鍙栨秷</u-button>
+ <u-button type="primary"
+ @click="confirmInspector">纭畾</u-button>
+ </view>
+ </view>
+ </u-popup>
+</template>
+
+<script setup>
+ import { computed, reactive, ref } from "vue";
+ import useUserStore from "@/store/modules/user";
+ import { addOrEditTimingTask } from "@/api/inspectionManagement/index.js";
+ import { userListNoPageByTenantId } from "@/api/system/user.js";
+ import { getDeviceLedger } from "@/api/equipmentManagement/ledger";
+
+ const emit = defineEmits(["closeDia"]);
+ const userStore = useUserStore();
+ const dialogVisitable = ref(false);
+ const operationType = ref("add");
+ const deviceOptions = ref([]);
+ const userList = ref([]);
+ const showInspectorPopup = ref(false);
+
+ const defaultForm = () => ({
+ id: undefined,
+ taskId: undefined,
+ taskName: "",
+ inspector: [],
+ inspectorIds: "",
+ remarks: "",
+ frequencyType: "",
+ frequencyDetail: "",
+ week: "",
+ time: "",
+ registrantId: undefined,
+ });
+
+ const form = reactive(defaultForm());
+
+ const frequencyOptions = [
+ { label: "姣忔棩", value: "DAILY" },
+ { label: "姣忓懆", value: "WEEKLY" },
+ { label: "姣忔湀", value: "MONTHLY" },
+ ];
+
+ const weekOptions = [
+ { label: "鍛ㄤ竴", value: "MON" },
+ { label: "鍛ㄤ簩", value: "TUE" },
+ { label: "鍛ㄤ笁", value: "WED" },
+ { label: "鍛ㄥ洓", value: "THU" },
+ { label: "鍛ㄤ簲", value: "FRI" },
+ { label: "鍛ㄥ叚", value: "SAT" },
+ { label: "鍛ㄦ棩", value: "SUN" },
+ ];
+
+ const monthlyDate = ref("");
+ const monthlyTime = ref("");
+
+ const deviceIndex = computed(() => {
+ const index = deviceOptions.value.findIndex(item => item.id === form.taskId);
+ return index >= 0 ? index : 0;
+ });
+
+ const frequencyIndex = computed(() => {
+ const index = frequencyOptions.findIndex(item => item.value === form.frequencyType);
+ return index >= 0 ? index : 0;
+ });
+
+ const weekIndex = computed(() => {
+ const index = weekOptions.findIndex(item => item.value === form.week);
+ return index >= 0 ? index : 0;
+ });
+
+ const currentFrequencyLabel = computed(() => {
+ return frequencyOptions.find(item => item.value === form.frequencyType)?.label || "";
+ });
+
+ const currentWeekLabel = computed(() => {
+ return weekOptions.find(item => item.value === form.week)?.label || "";
+ });
+
+ const inspectorText = computed(() => {
+ if (!form.inspector?.length) return "";
+ const nameMap = new Map(userList.value.map(item => [item.userId, item.nickName]));
+ return form.inspector.map(id => nameMap.get(id)).filter(Boolean).join("銆�");
+ });
+
+ const resetForm = () => {
+ Object.assign(form, defaultForm());
+ monthlyDate.value = "";
+ monthlyTime.value = "";
+ showInspectorPopup.value = false;
+ };
+
+ const loadBaseData = async () => {
+ const [userRes, deviceRes] = await Promise.all([
+ userListNoPageByTenantId(),
+ getDeviceLedger(),
+ ]);
+ userList.value = userRes?.data || [];
+ deviceOptions.value = deviceRes?.data || [];
+ };
+
+ const parseWeeklyDetail = detail => {
+ if (!detail || typeof detail !== "string" || !detail.includes(",")) return;
+ const [week, time] = detail.split(",");
+ form.week = week || "";
+ form.time = time || "";
+ };
+
+ const parseMonthlyDetail = detail => {
+ if (!detail || typeof detail !== "string" || !detail.includes(",")) return;
+ const [day, time] = detail.split(",");
+ if (day) monthlyDate.value = `${new Date().getFullYear()}-${new Date().getMonth() + 1 < 10 ? `0${new Date().getMonth() + 1}` : new Date().getMonth() + 1}-${day.padStart(2, "0")}`;
+ if (time) monthlyTime.value = time;
+ };
+
+ const setDeviceModel = id => {
+ const matched = deviceOptions.value.find(item => item.id === id);
+ if (matched) {
+ form.taskId = matched.id;
+ form.taskName = matched.deviceName;
+ }
+ };
+
+ const openDialog = async (type, row) => {
+ operationType.value = type;
+ dialogVisitable.value = true;
+ resetForm();
+ try {
+ await loadBaseData();
+ if (type === "edit" && row) {
+ Object.assign(form, {
+ ...defaultForm(),
+ ...row,
+ inspector: String(row.inspectorIds || "")
+ .split(",")
+ .map(item => Number(item))
+ .filter(Boolean),
+ });
+ if (form.frequencyType === "WEEKLY") parseWeeklyDetail(form.frequencyDetail);
+ if (form.frequencyType === "MONTHLY") parseMonthlyDetail(form.frequencyDetail);
+ }
+ } catch (error) {
+ uni.showToast({
+ title: "鍒濆鍖栧け璐�",
+ icon: "none",
+ });
+ }
+ };
+
+ const onDeviceChange = e => {
+ const index = Number(e?.detail?.value || 0);
+ const selected = deviceOptions.value[index];
+ if (!selected) return;
+ setDeviceModel(selected.id);
+ };
+
+ const getUserName = userId => {
+ const user = userList.value.find(item => item.userId === userId);
+ return user?.nickName || "";
+ };
+
+ const openInspectorPopup = () => {
+ showInspectorPopup.value = true;
+ };
+
+ const closeInspectorPopup = () => {
+ showInspectorPopup.value = false;
+ };
+
+ const toggleInspector = userId => {
+ if (form.inspector.includes(userId)) {
+ form.inspector = form.inspector.filter(id => id !== userId);
+ } else {
+ form.inspector = [...form.inspector, userId];
+ }
+ };
+
+ const confirmInspector = () => {
+ showInspectorPopup.value = false;
+ };
+
+ const onFrequencyChange = e => {
+ const index = Number(e?.detail?.value || 0);
+ const selected = frequencyOptions[index];
+ form.frequencyType = selected?.value || "";
+ form.frequencyDetail = "";
+ form.week = "";
+ form.time = "";
+ monthlyDate.value = "";
+ monthlyTime.value = "";
+ };
+
+ const onDailyTimeChange = e => {
+ form.frequencyDetail = e?.detail?.value || "";
+ };
+
+ const onWeekChange = e => {
+ const index = Number(e?.detail?.value || 0);
+ const selected = weekOptions[index];
+ form.week = selected?.value || "";
+ };
+
+ const onWeekTimeChange = e => {
+ form.time = e?.detail?.value || "";
+ };
+
+ const onMonthlyDateChange = e => {
+ monthlyDate.value = e?.detail?.value || "";
+ };
+
+ const onMonthlyTimeChange = e => {
+ monthlyTime.value = e?.detail?.value || "";
+ };
+
+ const validateForm = () => {
+ if (!form.taskId) {
+ uni.showToast({ title: "璇烽�夋嫨璁惧", icon: "none" });
+ return false;
+ }
+ if (!form.inspector.length) {
+ uni.showToast({ title: "璇烽�夋嫨宸℃浜�", icon: "none" });
+ return false;
+ }
+ if (!form.frequencyType) {
+ uni.showToast({ title: "璇烽�夋嫨浠诲姟棰戠巼", icon: "none" });
+ return false;
+ }
+ if (form.frequencyType === "DAILY" && !form.frequencyDetail) {
+ uni.showToast({ title: "璇烽�夋嫨鏃堕棿", icon: "none" });
+ return false;
+ }
+ if (form.frequencyType === "WEEKLY" && (!form.week || !form.time)) {
+ uni.showToast({ title: "璇烽�夋嫨姣忓懆鏃ユ湡鍜屾椂闂�", icon: "none" });
+ return false;
+ }
+ if (form.frequencyType === "MONTHLY" && (!monthlyDate.value || !monthlyTime.value)) {
+ uni.showToast({ title: "璇烽�夋嫨姣忔湀鏃ユ湡鍜屾椂闂�", icon: "none" });
+ return false;
+ }
+ return true;
+ };
+
+ const buildFrequencyDetail = () => {
+ if (form.frequencyType === "WEEKLY") return `${form.week},${form.time}`;
+ if (form.frequencyType === "MONTHLY") {
+ const day = monthlyDate.value.split("-")[2] || "";
+ return `${day},${monthlyTime.value}`;
+ }
+ return form.frequencyDetail;
+ };
+
+ const submitForm = async () => {
+ if (!validateForm()) return;
+ try {
+ const userInfo = await userStore.getInfo();
+ const payload = {
+ ...form,
+ inspectorIds: form.inspector.join(","),
+ frequencyDetail: buildFrequencyDetail(),
+ registrantId: userInfo?.user?.userId,
+ };
+ delete payload.inspector;
+ delete payload.week;
+ delete payload.time;
+ await addOrEditTimingTask(payload);
+ uni.showToast({
+ title: "鎻愪氦鎴愬姛",
+ icon: "success",
+ });
+ cancel();
+ } catch (error) {
+ uni.showToast({
+ title: "鎻愪氦澶辫触锛岃閲嶈瘯",
+ icon: "none",
+ });
+ }
+ };
+
+ const cancel = () => {
+ dialogVisitable.value = false;
+ resetForm();
+ emit("closeDia");
+ };
+
+ defineExpose({ openDialog });
+</script>
+
+<style scoped lang="scss">
+ :deep(.uni-picker-container) {
+ z-index: 1200 !important;
+ }
+
+ .popup-content {
+ width: 88vw;
+ max-height: 80vh;
+ background: #fff;
+ border-radius: 20rpx;
+ overflow: hidden;
+ }
+
+ .popup-title {
+ text-align: center;
+ font-size: 32rpx;
+ color: #1f1f1f;
+ font-weight: 600;
+ padding: 24rpx 20rpx;
+ border-bottom: 1rpx solid #f0f0f0;
+ }
+
+ .form-body {
+ padding: 24rpx;
+ max-height: 56vh;
+ overflow-y: auto;
+ }
+
+ .form-item {
+ margin-bottom: 20rpx;
+ }
+
+ .label {
+ display: block;
+ margin-bottom: 10rpx;
+ font-size: 24rpx;
+ color: #666;
+ }
+
+ .picker-value {
+ min-height: 72rpx;
+ background: #f7f8fa;
+ border-radius: 12rpx;
+ padding: 0 20rpx;
+ display: flex;
+ align-items: center;
+ color: #333;
+ font-size: 26rpx;
+ }
+
+ .inspector-picker {
+ justify-content: space-between;
+ }
+
+ .inspector-tags {
+ display: flex;
+ flex-wrap: wrap;
+ margin-top: 10rpx;
+ gap: 10rpx;
+ }
+
+ .inspector-tag {
+ display: flex;
+ align-items: center;
+ gap: 8rpx;
+ background: #edf3ff;
+ color: #1677ff;
+ font-size: 22rpx;
+ border-radius: 999rpx;
+ padding: 6rpx 14rpx;
+ }
+
+ .popup-footer {
+ display: flex;
+ gap: 16rpx;
+ padding: 20rpx 24rpx 24rpx;
+ border-top: 1rpx solid #f0f0f0;
+ }
+
+ .inspector-popup {
+ background: #fff;
+ border-radius: 24rpx 24rpx 0 0;
+ overflow: hidden;
+ max-height: 70vh;
+ padding-bottom: env(safe-area-inset-bottom);
+ }
+
+ .inspector-header {
+ padding: 24rpx;
+ text-align: center;
+ border-bottom: 1rpx solid #f0f0f0;
+ }
+
+ .inspector-title {
+ color: #1f1f1f;
+ font-size: 30rpx;
+ font-weight: 600;
+ }
+
+ .inspector-list {
+ max-height: 46vh;
+ padding: 0 24rpx;
+ }
+
+ .inspector-row {
+ min-height: 82rpx;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ border-bottom: 1rpx solid #f5f5f5;
+ }
+
+ .inspector-name {
+ font-size: 26rpx;
+ color: #333;
+ }
+
+ .inspector-footer {
+ display: flex;
+ gap: 16rpx;
+ padding: 20rpx 24rpx 24rpx;
+ }
+</style>
diff --git a/src/pages/inspectionManagement/components/qrCodeDia.vue b/src/pages/inspectionManagement/components/qrCodeDia.vue
new file mode 100644
index 0000000..136c18c
--- /dev/null
+++ b/src/pages/inspectionManagement/components/qrCodeDia.vue
@@ -0,0 +1,132 @@
+<template>
+ <div>
+ <el-dialog :title="operationType === 'add' ? '鏂板浜岀淮鐮�' : '缂栬緫浜岀淮鐮�'"
+ v-model="dialogVisitable" width="500px" @close="cancel">
+ <el-form :model="form" :rules="rules" ref="formRef" label-width="120px">
+ <el-row>
+ <el-col :span="24">
+ <el-form-item label="璁惧鍚嶇О" prop="deviceName">
+ <el-input v-model="form.deviceName" placeholder="璇疯緭鍏ヨ澶囧悕绉�" maxlength="30" />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row>
+ <el-col :span="24">
+ <el-form-item label="鎵�鍦ㄤ綅缃弿杩�" prop="location">
+ <el-input v-model="form.location" placeholder="璇疯緭鍏ユ墍鍦ㄤ綅缃弿杩�" maxlength="30"/>
+ </el-form-item>
+ </el-col>
+ </el-row>
+ </el-form>
+ <div>
+ <el-button type="primary" @click="submitForm">鐢熸垚骞舵墦鍗颁簩缁寸爜</el-button>
+ </div>
+ <div v-if="isShowQrCode" class="print-section" ref="qrCodeContainer" id="qrCodeContainer">
+ <vue-qrcode :value="qrCodeValue" :width="qrCodeSize"></vue-qrcode>
+ </div>
+ </el-dialog>
+ </div>
+</template>
+
+<script setup>
+import useUserStore from "@/store/modules/user.js";
+import {reactive, ref} from "vue";
+import printJS from 'print-js';
+import {addOrEditQrCode} from "@/api/inspectionUpload/index.js";
+
+const { proxy } = getCurrentInstance()
+const emit = defineEmits()
+const userStore = useUserStore()
+const dialogVisitable = ref(false);
+const isShowQrCode = ref(false);
+const operationType = ref('add');
+
+const qrCodeValue = ref('');
+const qrCodeSize = ref(100);
+const data = reactive({
+ form: {
+ deviceName: '',
+ location: '',
+ qrCodeId: '',
+ id: ''
+ },
+ rules: {
+ deviceName: [{ required: true, message: '璇疯緭鍏ヨ澶囧悕绉�', trigger: 'blur' }],
+ location: [{ required: true, message: '璇疯緭鍏ュ湴鐐�', trigger: 'blur' }]
+ }
+})
+const { form, rules } = toRefs(data)
+
+
+// 鎵撳紑寮规
+const openDialog = async (type, row) => {
+ dialogVisitable.value = true
+ qrCodeValue.value = ''
+ isShowQrCode.value = false;
+ if (type === 'edit') {
+ form.value.id = row.id
+ form.value.qrCodeId = row.id
+ form.value.deviceName = row.deviceName
+ form.value.location = row.location
+ // 灏嗚〃鍗曟暟鎹浆涓� JSON 瀛楃涓蹭綔涓轰簩缁寸爜鍐呭
+ qrCodeValue.value = JSON.stringify(form.value);
+ isShowQrCode.value = true;
+ }
+}
+// 鎻愪氦鍚堝苟琛ㄥ崟
+const submitForm = () => {
+ proxy.$refs["formRef"].validate(valid => {
+ if (valid) {
+ addOrEditQrCode(form.value).then((res) => {
+ form.value.qrCodeId = res.data
+ })
+ // 灏嗚〃鍗曟暟鎹浆涓� JSON 瀛楃涓蹭綔涓轰簩缁寸爜鍐呭
+ qrCodeValue.value = JSON.stringify(form.value);
+ isShowQrCode.value = true;
+ showQrCode()
+ }
+ })
+}
+const showQrCode = () => {
+ // 寤惰繜鎵ц鎵撳嵃锛岄伩鍏� DOM 鏇存柊鍓嶅氨璋冪敤鎵撳嵃
+ setTimeout(() => {
+ printJS({
+ printable: 'qrCodeContainer',//椤甸潰
+ type: "html",//鏂囨。绫诲瀷
+ maxWidth: 360,
+ style: `@page {
+ margin:0;
+ size: 400px 75px collapse;
+ margin-top:3px;
+ &:first-of-type{
+ margin-top:0 !important;
+ }
+ }
+ html{
+ zoom:100%;
+ }
+ @media print{
+ width: 400px;
+ height: 75px;
+ margin:0;
+ }`,
+ targetStyles: ["*"], // 浣跨敤dom鐨勬墍鏈夋牱寮忥紝寰堥噸瑕�
+ font_size: '0.20cm',
+ });
+ }, 300);
+}
+// 鍏抽棴鍚堝苟琛ㄥ崟
+const cancel = () => {
+ proxy.resetForm("formRef")
+ dialogVisitable.value = false
+ emit('closeDia')
+}
+defineExpose({ openDialog })
+</script>
+
+<style scoped>
+.print-section {
+ text-align: center;
+ margin-top: 30px;
+}
+</style>
\ No newline at end of file
diff --git a/src/pages/inspectionManagement/components/viewFiles.vue b/src/pages/inspectionManagement/components/viewFiles.vue
new file mode 100644
index 0000000..f39d9a4
--- /dev/null
+++ b/src/pages/inspectionManagement/components/viewFiles.vue
@@ -0,0 +1,296 @@
+<template>
+ <u-popup :show="dialogVisitable"
+ mode="bottom"
+ :round="16"
+ @close="cancel">
+ <view class="popup-content">
+ <view class="popup-header">
+ <text class="popup-title">鏌ョ湅闄勪欢</text>
+ <view class="close-icon"
+ @click="cancel">
+ <u-icon name="close"
+ size="14"
+ color="#666" />
+ </view>
+ </view>
+ <view class="popup-body">
+ <view class="tabs">
+ <view v-for="tab in tabs"
+ :key="tab.key"
+ class="tab-item"
+ :class="{ active: currentType === tab.key }"
+ @click="currentType = tab.key">
+ {{ tab.label }} ({{ getCurrentList(tab.key).length }})
+ </view>
+ </view>
+ <view class="file-list"
+ v-if="getCurrentList(currentType).length">
+ <view v-for="(file, index) in getCurrentList(currentType)"
+ :key="index"
+ class="file-item"
+ @click="previewFile(file)">
+ <image v-if="isImageFile(file)"
+ :src="file.url"
+ class="thumb"
+ mode="aspectFill" />
+ <view v-else
+ class="video-thumb">
+ <u-icon name="video"
+ size="28"
+ color="#1677ff" />
+ <text class="video-text">瑙嗛</text>
+ </view>
+ <text class="name">{{ file.name || "闄勪欢" }}</text>
+ </view>
+ </view>
+ <view v-else
+ class="empty">
+ <text>鏆傛棤闄勪欢</text>
+ </view>
+ </view>
+ </view>
+ </u-popup>
+ <u-popup :show="showVideoPopup"
+ mode="center"
+ :round="10"
+ @close="closeVideoPopup">
+ <view class="video-container">
+ <video :src="videoUrl"
+ controls
+ autoplay
+ class="video-player" />
+ </view>
+ </u-popup>
+</template>
+
+<script setup>
+ import { ref } from "vue";
+ import config from "@/config";
+
+ const dialogVisitable = ref(false);
+ const currentType = ref("before");
+ const showVideoPopup = ref(false);
+ const videoUrl = ref("");
+ const filesMap = ref({
+ before: [],
+ after: [],
+ issue: [],
+ });
+
+ const tabs = [
+ { key: "before", label: "鐢熶骇鍓�" },
+ { key: "after", label: "鐢熶骇涓�" },
+ { key: "issue", label: "鐢熶骇鍚�" },
+ ];
+
+ const normalizeUrl = raw => {
+ if (!raw) return "";
+ const url = String(raw).trim();
+ if (!url) return "";
+ if (/^https?:\/\//i.test(url)) return url;
+ if (url.startsWith("/")) return `${config.fileUrl}${url}`;
+ if (/^[a-zA-Z]:\\/.test(url)) {
+ const normalized = url.replace(/\\/g, "/");
+ const idx = normalized.indexOf("/prod/");
+ if (idx >= 0) {
+ const relative = normalized.slice(idx + "/prod/".length);
+ return `${config.fileUrl}/${relative}`;
+ }
+ return normalized;
+ }
+ return `${config.fileUrl}/${url.replace(/^\//, "")}`;
+ };
+
+ const isImageFile = file => {
+ if (file?.contentType?.startsWith("image/")) return true;
+ const name = String(file?.name || file?.bucketFilename || "").toLowerCase();
+ return /\.(jpg|jpeg|png|gif|bmp|webp)$/.test(name);
+ };
+
+ const normalizeFile = (file, type) => ({
+ ...file,
+ type,
+ url: normalizeUrl(file?.url || file?.downloadUrl),
+ name: file?.originalFilename || file?.bucketFilename || file?.name,
+ });
+
+ const getCurrentList = type => filesMap.value[type] || [];
+
+ const previewFile = file => {
+ if (isImageFile(file)) {
+ const urls = getCurrentList(currentType.value)
+ .filter(item => isImageFile(item))
+ .map(item => item.url);
+ uni.previewImage({
+ urls,
+ current: file.url,
+ });
+ return;
+ }
+ videoUrl.value = file.url;
+ showVideoPopup.value = true;
+ };
+
+ const closeVideoPopup = () => {
+ showVideoPopup.value = false;
+ videoUrl.value = "";
+ };
+
+ const openDialog = row => {
+ const allList = Array.isArray(row?.commonFileList) ? row.commonFileList : [];
+ const beforeList = Array.isArray(row?.commonFileListBefore)
+ ? row.commonFileListBefore
+ : allList.filter(item => item?.type === 10);
+ const afterList = Array.isArray(row?.commonFileListAfter)
+ ? row.commonFileListAfter
+ : allList.filter(item => item?.type === 11);
+ const issueList = Array.isArray(row?.commonFileListIssue)
+ ? row.commonFileListIssue
+ : allList.filter(item => item?.type === 12);
+ filesMap.value = {
+ before: beforeList.map(item => normalizeFile(item, "before")).filter(item => item.url),
+ after: afterList.map(item => normalizeFile(item, "after")).filter(item => item.url),
+ issue: issueList.map(item => normalizeFile(item, "issue")).filter(item => item.url),
+ };
+ currentType.value = "before";
+ dialogVisitable.value = true;
+ };
+
+ const cancel = () => {
+ dialogVisitable.value = false;
+ closeVideoPopup();
+ filesMap.value = {
+ before: [],
+ after: [],
+ issue: [],
+ };
+ };
+
+ defineExpose({ openDialog });
+</script>
+
+<style scoped lang="scss">
+ .popup-content {
+ width: 100vw;
+ max-height: 82vh;
+ background: #fff;
+ border-radius: 24rpx 24rpx 0 0;
+ overflow: hidden;
+ padding-bottom: env(safe-area-inset-bottom);
+ }
+
+ .popup-header {
+ padding: 24rpx 20rpx;
+ border-bottom: 1rpx solid #f0f0f0;
+ text-align: center;
+ position: relative;
+ }
+
+ .popup-title {
+ font-size: 32rpx;
+ color: #1f1f1f;
+ font-weight: 600;
+ }
+
+ .popup-body {
+ padding: 20rpx 20rpx 26rpx;
+ max-height: 68vh;
+ overflow-y: auto;
+ }
+
+ .tabs {
+ display: flex;
+ background: #f4f5f8;
+ border-radius: 12rpx;
+ padding: 6rpx;
+ margin-bottom: 20rpx;
+ }
+
+ .tab-item {
+ flex: 1;
+ text-align: center;
+ padding: 12rpx 0;
+ color: #666;
+ font-size: 24rpx;
+ border-radius: 10rpx;
+ }
+
+ .tab-item.active {
+ background: #1677ff;
+ color: #fff;
+ font-weight: 600;
+ }
+
+ .file-list {
+ display: grid;
+ grid-template-columns: repeat(2, 1fr);
+ gap: 16rpx;
+ }
+
+ .file-item {
+ background: #fafafa;
+ border-radius: 12rpx;
+ padding: 10rpx;
+ }
+
+ .thumb {
+ width: 100%;
+ height: 180rpx;
+ border-radius: 8rpx;
+ }
+
+ .video-thumb {
+ width: 100%;
+ height: 180rpx;
+ border-radius: 8rpx;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ background: #edf3ff;
+ }
+
+ .video-text {
+ font-size: 22rpx;
+ color: #1677ff;
+ margin-top: 6rpx;
+ }
+
+ .name {
+ margin-top: 8rpx;
+ font-size: 22rpx;
+ color: #333;
+ display: block;
+ word-break: break-all;
+ }
+
+ .empty {
+ text-align: center;
+ color: #999;
+ padding: 40rpx 0;
+ }
+
+ .video-container {
+ width: 94vw;
+ background: #000;
+ }
+
+ .video-player {
+ width: 94vw;
+ height: 55vw;
+ }
+
+ .close-icon {
+ position: absolute;
+ right: 24rpx;
+ top: 50%;
+ transform: translateY(-50%);
+ width: 44rpx;
+ height: 44rpx;
+ border-radius: 50%;
+ background: #f5f5f5;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ }
+</style>
diff --git a/src/pages/inspectionManagement/components/viewQrCodeFiles.vue b/src/pages/inspectionManagement/components/viewQrCodeFiles.vue
new file mode 100644
index 0000000..f8e923a
--- /dev/null
+++ b/src/pages/inspectionManagement/components/viewQrCodeFiles.vue
@@ -0,0 +1,169 @@
+<template>
+ <div>
+ <el-dialog title="鏌ョ湅闄勪欢"
+ v-model="dialogVisitable" width="800px" @close="cancel">
+ <div class="upload-container">
+ <div class="form-container">
+ <div class="title">宸℃闄勪欢</div>
+ <!-- 鍥剧墖鍒楄〃 -->
+ <div style="display: flex; flex-wrap: wrap;">
+ <img v-for="(item, index) in beforeProductionImgs" :key="index"
+ @click="showMedia(beforeProductionImgs, index, 'image')"
+ :src="item" style="max-width: 100px; height: 100px; margin: 5px;" alt="">
+ </div>
+
+ <!-- 瑙嗛鍒楄〃 -->
+ <div style="display: flex; flex-wrap: wrap;">
+ <div
+ v-for="(videoUrl, index) in beforeProductionVideos"
+ :key="index"
+ @click="showMedia(beforeProductionVideos, index, 'video')"
+ style="position: relative; margin: 10px; cursor: pointer;"
+ >
+ <div style="width: 160px; height: 90px; background-color: #333; display: flex; align-items: center; justify-content: center;">
+ <img src="@/assets/images/video.png" alt="鎾斁" style="width: 30px; height: 30px; opacity: 0.8;" />
+ </div>
+ <div style="text-align: center; font-size: 12px; color: #666;">鐐瑰嚮鎾斁</div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </el-dialog>
+ <!-- 缁熶竴濯掍綋鏌ョ湅鍣� -->
+ <div v-if="isMediaViewerVisible" class="media-viewer-overlay" @click.self="closeMediaViewer">
+ <div class="media-viewer-content" @click.stop>
+ <!-- 鍥剧墖 -->
+ <vue-easy-lightbox
+ v-if="mediaType === 'image'"
+ :visible="isMediaViewerVisible"
+ :imgs="mediaList"
+ :index="currentMediaIndex"
+ @hide="closeMediaViewer"
+ ></vue-easy-lightbox>
+
+ <!-- 瑙嗛 -->
+ <div v-else-if="mediaType === 'video'" style="position: relative;">
+ <Video
+ :src="mediaList[currentMediaIndex]"
+ autoplay
+ controls
+ style="max-width: 90vw; max-height: 80vh;"
+ />
+ </div>
+ </div>
+ </div>
+ </div>
+</template>
+
+<script setup>
+// 鎺у埗寮圭獥鏄剧ず
+import VueEasyLightbox from "vue-easy-lightbox";
+
+const dialogVisitable = ref(false);
+// 鍥剧墖鏁扮粍
+const beforeProductionImgs = ref([]);
+// 瑙嗛鏁扮粍
+const beforeProductionVideos = ref([]);
+// 濯掍綋鏌ョ湅鍣ㄧ姸鎬�
+const isMediaViewerVisible = ref(false);
+const currentMediaIndex = ref(0);
+const mediaList = ref([]); // 瀛樺偍褰撳墠瑕佹煡鐪嬬殑濯掍綋鍒楄〃锛堝惈鍥剧墖鍜岃棰戝璞★級
+const mediaType = ref('image'); // image | video
+
+// 鎵撳紑寮圭獥骞跺姞杞芥暟鎹�
+const openDialog = async (row) => {
+ const { images: beforeImgs, videos: beforeVids } = processItems(row.storageBlobDTO);
+
+ beforeProductionImgs.value = beforeImgs;
+ beforeProductionVideos.value = beforeVids;
+ dialogVisitable.value = true;
+};
+// 鏄剧ず濯掍綋锛堝浘鐗� or 瑙嗛锛�
+function showMedia(mediaArray, index, type) {
+ mediaList.value = mediaArray;
+ currentMediaIndex.value = index;
+ mediaType.value = type;
+ isMediaViewerVisible.value = true;
+}
+// 鍏抽棴濯掍綋鏌ョ湅鍣�
+function closeMediaViewer() {
+ isMediaViewerVisible.value = false;
+ mediaList.value = [];
+ mediaType.value = 'image';
+}
+// 琛ㄥ崟鍏抽棴鏂规硶
+const cancel = () => {
+ dialogVisitable.value = false;
+};
+// 澶勭悊姣忎竴绫绘暟鎹細鍒嗙鍥剧墖鍜岃棰�
+function processItems(items) {
+ const images = [];
+ const videos = [];
+ items.forEach(item => {
+ if (item.contentType?.startsWith('image/')) {
+ images.push(item.url);
+ } else if (item.contentType?.startsWith('video/')) {
+ videos.push(item.url);
+ }
+ });
+ return { images, videos };
+}
+defineExpose({ openDialog });
+</script>
+
+<style scoped lang="scss">
+.upload-container {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ padding: 20px;
+ border: 1px solid #dcdfe6;
+ box-sizing: border-box;
+
+ .form-container {
+ flex: 1;
+ width: 100%;
+ margin-bottom: 20px;
+ }
+}
+
+.title {
+ font-size: 14px;
+ color: #165dff;
+ line-height: 20px;
+ font-weight: 600;
+ padding-left: 10px;
+ position: relative;
+ margin: 6px 0;
+
+ &::before {
+ content: "";
+ position: absolute;
+ left: 0;
+ top: 3px;
+ width: 4px;
+ height: 14px;
+ background-color: #165dff;
+ }
+}
+
+.media-viewer-overlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background-color: rgba(0, 0, 0, 0.8);
+ z-index: 9999;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.media-viewer-content {
+ position: relative;
+ max-width: 90vw;
+ max-height: 90vh;
+ overflow: hidden;
+}
+</style>
\ No newline at end of file
diff --git a/src/pages/inspectionManagement/index.vue b/src/pages/inspectionManagement/index.vue
new file mode 100644
index 0000000..349d1d0
--- /dev/null
+++ b/src/pages/inspectionManagement/index.vue
@@ -0,0 +1,545 @@
+<template>
+ <view class="inspection-management-page">
+ <PageHeader title="宸℃浠诲姟绠$悊"
+ @back="goBack" />
+ <view class="toolbar">
+ <view class="tab-wrap">
+ <view v-for="tab in tabs"
+ :key="tab.name"
+ class="tab-item"
+ :class="{ active: activeTab === tab.name }"
+ @click="switchTab(tab.name)">
+ {{ tab.label }}
+ </view>
+ </view>
+ <view class="search-section">
+ <view class="search-bar">
+ <view class="search-input">
+ <up-input class="search-text"
+ placeholder="璇疯緭鍏ュ贰妫�浠诲姟鍚嶇О"
+ v-model="queryParams.taskName"
+ clearable />
+ </view>
+ <view class="search-button"
+ @click="handleQuery">
+ <up-icon name="search"
+ size="24"
+ color="#999"></up-icon>
+ </view>
+ </view>
+ </view>
+ <view class="meta-bar">
+ <text class="meta-text">鍏� {{ total }} 鏉�</text>
+ </view>
+ </view>
+ <view class="list-section">
+ <uni-swipe-action>
+ <uni-swipe-action-item v-for="item in tableData"
+ :key="item.id"
+ :right-options="activeTab === 'taskManage' ? swipeOptions : []"
+ :disabled="activeTab !== 'taskManage'"
+ @click="onSwipeActionClick($event, item)">
+ <view class="ledger-item">
+ <view class="item-header">
+ <view class="item-left">
+ <view class="document-icon">
+ <up-icon name="file-text"
+ size="14"
+ color="#ffffff"></up-icon>
+ </view>
+ <text class="item-id">{{ item.taskName || "--" }}</text>
+ </view>
+ <view class="item-right">
+ <u-tag :type="getFrequencyTagType(item.frequencyType)"
+ :text="formatFrequency(item.frequencyType) || '鏈煡棰戞'" />
+ </view>
+ </view>
+ <up-divider></up-divider>
+ <view class="item-details">
+ <view class="detail-row">
+ <text class="detail-label">浠诲姟缂栧彿</text>
+ <text class="detail-value">{{ item.id || "--" }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">鎵ц宸℃浜�</text>
+ <view class="tag-wrap"
+ v-if="item.inspector?.length">
+ <uni-tag v-for="(person, index) in item.inspector"
+ :key="index"
+ :text="person"
+ type="primary"
+ size="small"
+ inverted />
+ </view>
+ <text class="detail-value"
+ v-else>--</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">寮�濮嬫棩鏈熶笌鏃堕棿</text>
+ <text class="detail-value highlight">{{ formatFrequencyDetail(item.frequencyDetail) }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">鐧昏浜�</text>
+ <text class="detail-value">{{ item.registrant || "--" }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">鐧昏鏃ユ湡</text>
+ <text class="detail-value">{{ item.createTime || "--" }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">澶囨敞</text>
+ <text class="detail-value">{{ item.remarks || "鏃�" }}</text>
+ </view>
+ <up-divider></up-divider>
+ <view class="card-actions">
+ <u-button v-if="activeTab === 'taskManage'"
+ type="primary"
+ size="small"
+ class="action-btn"
+ @click.stop="handleAdd(item)">缂栬緫</u-button>
+ <u-button v-else
+ type="success"
+ size="small"
+ class="action-btn"
+ @click.stop="viewFile(item)">鏌ョ湅闄勪欢</u-button>
+ </view>
+ </view>
+ </view>
+ </uni-swipe-action-item>
+ </uni-swipe-action>
+ <uni-load-more :status="loadMoreStatus"></uni-load-more>
+ <view v-if="!loading && tableData.length === 0"
+ class="no-data">
+ <text>鏆傛棤鏁版嵁</text>
+ </view>
+ </view>
+ <view v-if="activeTab === 'taskManage'"
+ class="fab-button"
+ @click="handleAdd()">
+ <up-icon name="plus"
+ size="24"
+ color="#ffffff"></up-icon>
+ </view>
+ <form-dia ref="formDia"
+ @closeDia="handleQuery" />
+ <view-files ref="viewFiles" />
+ </view>
+</template>
+
+<script setup>
+ import { ref, reactive, computed, nextTick } from "vue";
+ import { onShow, onReachBottom, onPullDownRefresh } from "@dcloudio/uni-app";
+ import PageHeader from "@/components/PageHeader.vue";
+ import FormDia from "@/pages/inspectionManagement/components/formDia.vue";
+ import ViewFiles from "@/pages/inspectionManagement/components/viewFiles.vue";
+ import {
+ delTimingTask,
+ inspectionTaskList,
+ timingTaskList,
+ } from "@/api/inspectionManagement/index.js";
+
+ const formDia = ref();
+ const viewFiles = ref();
+ const activeTab = ref("taskManage");
+ const tabs = [
+ { name: "taskManage", label: "瀹氭椂浠诲姟绠$悊" },
+ { name: "task", label: "瀹氭椂浠诲姟璁板綍" },
+ ];
+ const queryParams = reactive({
+ taskName: "",
+ });
+ const pageParams = reactive({
+ current: 1,
+ size: 10,
+ });
+ const total = ref(0);
+ const loading = ref(false);
+ const tableData = ref([]);
+ const swipeOptions = [
+ {
+ text: "鍒犻櫎",
+ style: {
+ backgroundColor: "#ee0a24",
+ },
+ },
+ ];
+
+ const noMore = computed(() => tableData.value.length >= total.value);
+ const loadMoreStatus = computed(() => {
+ if (loading.value) return "loading";
+ if (noMore.value) return "noMore";
+ return "more";
+ });
+
+ const goBack = () => {
+ uni.navigateBack();
+ };
+
+ const formatFrequency = value => {
+ if (value === "DAILY") return "姣忔棩";
+ if (value === "WEEKLY") return "姣忓懆";
+ if (value === "MONTHLY") return "姣忔湀";
+ if (value === "QUARTERLY") return "瀛e害";
+ return "";
+ };
+
+ const getFrequencyTagType = value => {
+ if (value === "DAILY") return "success";
+ if (value === "WEEKLY") return "primary";
+ if (value === "MONTHLY") return "warning";
+ return "info";
+ };
+
+ const formatFrequencyDetail = value => {
+ if (!value || typeof value !== "string") return "--";
+ return value.replace(
+ /MON|TUE|WED|THU|FRI|SAT|SUN/g,
+ item =>
+ ({
+ MON: "鍛ㄤ竴",
+ TUE: "鍛ㄤ簩",
+ WED: "鍛ㄤ笁",
+ THU: "鍛ㄥ洓",
+ FRI: "鍛ㄤ簲",
+ SAT: "鍛ㄥ叚",
+ SUN: "鍛ㄦ棩",
+ })[item] || item
+ );
+ };
+
+ const normalizeInspector = val => {
+ if (!val) return [];
+ if (Array.isArray(val)) return val.filter(Boolean);
+ if (typeof val === "string") {
+ return val
+ .split(",")
+ .map(item => item.trim())
+ .filter(Boolean);
+ }
+ return [String(val)];
+ };
+
+ const switchTab = tabName => {
+ if (activeTab.value === tabName) return;
+ activeTab.value = tabName;
+ handleQuery();
+ };
+
+ const getList = async () => {
+ if (loading.value) return;
+ loading.value = true;
+ try {
+ const params = {
+ ...queryParams,
+ current: pageParams.current,
+ size: pageParams.size,
+ };
+ const request = activeTab.value === "task" ? inspectionTaskList : timingTaskList;
+ const res = await request(params);
+ const records = res?.data?.records || [];
+ const normalized = records.map(item => ({
+ ...item,
+ inspector: normalizeInspector(item.inspector),
+ }));
+ if (pageParams.current === 1) {
+ tableData.value = normalized;
+ } else {
+ tableData.value = [...tableData.value, ...normalized];
+ }
+ total.value = Number(res?.data?.total || 0);
+ } catch (error) {
+ if (pageParams.current === 1) {
+ tableData.value = [];
+ }
+ uni.showToast({
+ title: "鑾峰彇鏁版嵁澶辫触",
+ icon: "none",
+ });
+ } finally {
+ loading.value = false;
+ }
+ };
+
+ const handleQuery = () => {
+ pageParams.current = 1;
+ total.value = 0;
+ getList();
+ };
+
+ const loadMore = () => {
+ if (loading.value || noMore.value) return;
+ pageParams.current += 1;
+ getList();
+ };
+
+ const handleAdd = row => {
+ nextTick(() => {
+ formDia.value?.openDialog(row ? "edit" : "add", row);
+ });
+ };
+
+ const viewFile = row => {
+ nextTick(() => {
+ viewFiles.value?.openDialog(row);
+ });
+ };
+
+ const deleteOne = async row => {
+ if (!row?.id) return;
+ const canDelete = await new Promise(resolve => {
+ uni.showModal({
+ title: "鎻愮ず",
+ content: "鏄惁纭鍒犻櫎璇ュ贰妫�浠诲姟锛�",
+ success: modalRes => resolve(Boolean(modalRes.confirm)),
+ fail: () => resolve(false),
+ });
+ });
+ if (!canDelete) return;
+ try {
+ await delTimingTask([row.id]);
+ uni.showToast({
+ title: "鍒犻櫎鎴愬姛",
+ icon: "success",
+ });
+ handleQuery();
+ } catch (error) {
+ uni.showToast({
+ title: "鍒犻櫎澶辫触",
+ icon: "none",
+ });
+ }
+ };
+
+ const onSwipeActionClick = (event, row) => {
+ if (activeTab.value !== "taskManage") return;
+ if (event?.position !== "right") return;
+ deleteOne(row);
+ };
+
+ onShow(() => {
+ handleQuery();
+ });
+
+ onReachBottom(() => {
+ loadMore();
+ });
+
+ onPullDownRefresh(() => {
+ handleQuery();
+ uni.stopPullDownRefresh();
+ });
+</script>
+
+<style scoped>
+ .inspection-management-page {
+ min-height: 100vh;
+ background: #f6f7fb;
+ }
+
+ .toolbar {
+ padding: 20rpx 24rpx;
+ background: #fff;
+ border-bottom: 1rpx solid #f0f0f0;
+ position: sticky;
+ top: 0;
+ z-index: 10;
+ }
+
+ .tab-wrap {
+ display: flex;
+ background: #f4f5f8;
+ border-radius: 16rpx;
+ padding: 6rpx;
+ }
+
+ .tab-item {
+ flex: 1;
+ text-align: center;
+ padding: 14rpx 0;
+ font-size: 26rpx;
+ color: #666;
+ border-radius: 12rpx;
+ }
+
+ .tab-item.active {
+ background: #1677ff;
+ color: #fff;
+ font-weight: 600;
+ }
+
+ .search-section {
+ margin-top: 20rpx;
+ }
+
+ .search-bar {
+ display: flex;
+ align-items: center;
+ background: #f7f8fa;
+ border-radius: 14rpx;
+ padding: 8rpx 12rpx 8rpx 16rpx;
+ border: 1rpx solid #eef1f5;
+ }
+
+ .search-input {
+ flex: 1;
+ min-width: 0;
+ }
+
+ .search-text {
+ background: transparent !important;
+ }
+
+ :deep(.search-text .u-input__content),
+ :deep(.search-text .up-input__content) {
+ background: transparent !important;
+ padding: 0 !important;
+ }
+
+ .search-button {
+ width: 64rpx;
+ height: 64rpx;
+ border-radius: 12rpx;
+ background: #ffffff;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ }
+
+ .meta-bar {
+ margin-top: 16rpx;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ background: #f7f9fc;
+ border-radius: 12rpx;
+ padding: 10rpx 16rpx;
+ }
+
+ .meta-text {
+ color: #5c6b8a;
+ font-size: 22rpx;
+ }
+
+ .list-section {
+ padding: 20rpx 24rpx;
+ padding-bottom: calc(132rpx + env(safe-area-inset-bottom));
+ }
+
+ .ledger-item {
+ background: #ffffff;
+ border-radius: 20rpx;
+ margin-bottom: 16rpx;
+ overflow: hidden;
+ box-shadow: 0 8rpx 20rpx rgba(0, 0, 0, 0.05);
+ padding: 0 20rpx;
+ }
+
+ .item-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 22rpx 0;
+ }
+
+ .item-left {
+ display: flex;
+ align-items: center;
+ gap: 10rpx;
+ flex: 1;
+ min-width: 0;
+ }
+
+ .item-right {
+ display: flex;
+ align-items: center;
+ }
+
+ .document-icon {
+ width: 38rpx;
+ height: 38rpx;
+ border-radius: 8rpx;
+ background: #2979ff;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ }
+
+ .item-id {
+ font-size: 28rpx;
+ color: #1f1f1f;
+ font-weight: 600;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+
+ .item-details {
+ padding: 18rpx 0 20rpx;
+ }
+
+ .detail-row {
+ display: flex;
+ align-items: flex-end;
+ justify-content: space-between;
+ margin-bottom: 12rpx;
+ }
+
+ .detail-label {
+ min-width: 160rpx;
+ color: #777;
+ font-size: 24rpx;
+ }
+
+ .detail-value {
+ flex: 1;
+ color: #333;
+ font-size: 24rpx;
+ text-align: right;
+ word-break: break-all;
+ margin-left: 12rpx;
+ }
+
+ .detail-value.highlight {
+ color: #2979ff;
+ font-weight: 500;
+ }
+
+ .tag-wrap {
+ flex: 1;
+ display: flex;
+ gap: 10rpx;
+ flex-wrap: wrap;
+ justify-content: flex-end;
+ }
+
+ .card-actions {
+ padding-top: 8rpx;
+ display: flex;
+ justify-content: flex-end;
+ }
+
+ .action-btn {
+ min-width: 140rpx;
+ }
+
+ .fab-button {
+ position: fixed;
+ bottom: calc(30px + env(safe-area-inset-bottom));
+ right: 30px;
+ width: 56px;
+ height: 56px;
+ background: #2979ff;
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ box-shadow: 0 4px 16px rgba(41, 121, 255, 0.3);
+ z-index: 1000;
+ }
+
+ .no-data {
+ padding: 40rpx 0;
+ text-align: center;
+ color: #999;
+ }
+</style>
--
Gitblit v1.9.3