From 51081f3acbeef7e5358e20a653c38b0ed3afbe23 Mon Sep 17 00:00:00 2001
From: gongchunyi <deslre0381@gmail.com>
Date: 星期五, 15 五月 2026 19:39:19 +0800
Subject: [PATCH] feat: 设备维修新增报修人、验收人、维修人

---
 src/views/equipmentManagement/repair/index.vue |  309 ++++++++++++++++++++++++++++++++++++++-------------
 1 files changed, 228 insertions(+), 81 deletions(-)

diff --git a/src/views/equipmentManagement/repair/index.vue b/src/views/equipmentManagement/repair/index.vue
index e14ab37..3f48df5 100644
--- a/src/views/equipmentManagement/repair/index.vue
+++ b/src/views/equipmentManagement/repair/index.vue
@@ -98,74 +98,109 @@
           @pagination="changePage"
       >
         <template #statusRef="{ row }">
-          <el-tag v-if="row.status === 2" type="danger">澶辫触</el-tag>
-          <el-tag v-if="row.status === 1" type="success">瀹岀粨</el-tag>
           <el-tag v-if="row.status === 0" type="warning">寰呯淮淇�</el-tag>
+          <el-tag v-else-if="row.status === 3" type="info">寰呴獙鏀�</el-tag>
+          <el-tag v-else-if="row.status === 1" type="success">瀹屾垚</el-tag>
+          <el-tag v-else-if="row.status === 2" type="danger">缁翠慨澶辫触</el-tag>
         </template>
         <template #operation="{ row }">
-          <el-button
-            type="primary"
-            link
-            :disabled="row.status === 1"
-            @click="editRepair(row.id)"
-          >
+          <el-button type="info" link @click="openRepairDetail(row.id)">
+            璇︽儏
+          </el-button>
+          <el-button type="primary" link :disabled="!canEdit(row)" @click="editRepair(row.id)">
             缂栬緫
           </el-button>
-          <el-button
-            type="info"
-            link
-            :disabled="row.status === 1"
-            @click="openAttachment(row)"
-          >
-            闄勪欢
-          </el-button>
-          <el-button
-            type="success"
-            link
-            :disabled="row.status === 1"
-            @click="addMaintain(row)"
-          >
+          <el-button type="success" link :disabled="!canMaintain(row)" @click="addMaintain(row)">
             缁翠慨
           </el-button>
-          <el-button
-            type="danger"
-            link
-            :disabled="row.status === 1"
-            @click="delRepairByIds(row.id)"
-          >
+          <el-button type="warning" link :disabled="!canAccept(row)" @click="openAcceptance(row)">
+            楠屾敹
+          </el-button>
+          <el-button type="danger" link :disabled="!canDelete(row)" @click="delRepairByIds(row.id)">
             鍒犻櫎
+          </el-button>
+          <el-button type="primary" link @click="openAttachment(row)">
+            闄勪欢
           </el-button>
         </template>
       </PIMTable>
     </div>
     <RepairModal ref="repairModalRef" @ok="getTableData"/>
     <MaintainModal ref="maintainModalRef" @ok="getTableData"/>
+    <AcceptanceModal ref="acceptanceModalRef" @ok="getTableData"/>
+    <RepairDetailModal ref="repairDetailModalRef" :java-api="javaApi" />
 
-    <el-dialog v-model="attachment.visible" title="闄勪欢" width="860px" @closed="onAttachmentClosed">
+    <el-dialog v-model="attachment.visible" title="闄勪欢" width="900px" @closed="onAttachmentClosed">
       <div v-loading="attachment.loading" class="attachment-wrap">
-        <div class="attachment-actions">
-          <el-upload
-            v-model:file-list="attachment.fileList"
-            :action="upload.url"
-            multiple
-            ref="attachmentFileUpload"
-            auto-upload
-            :headers="upload.headers"
-            :data="{ deviceRepairId: attachment.deviceRepairId }"
-            :before-upload="handleAttachmentBeforeUpload"
-            :on-error="handleAttachmentUploadError"
-            :on-success="handleAttachmentUploadSuccess"
-            :on-preview="handleAttachmentPreview"
-            :on-remove="handleAttachmentRemove"
-            list-type="picture-card"
-            :limit="9"
-            accept="image/png,image/jpeg,image/jpg"
-          >
-            +
-          </el-upload>
+        <p v-if="attachmentReadOnly" class="attachment-readonly-tip">
+          宸插紑濮嬬淮淇紝姝ゅ浠呭彲鏌ョ湅锛涜澶囬棶棰樺浘璇峰湪鎶ヤ慨銆屽緟缁翠慨銆嶆椂浜庛�岀紪杈戙�嶄腑缁存姢锛涚淮淇畬鎴愬浘璇峰湪銆岀淮淇�嶆椂涓婁紶銆�
+        </p>
+        <div class="attachment-section">
+          <div class="attachment-section-title">璁惧闂鍥剧墖</div>
+          <template v-if="attachmentReadOnly">
+            <div v-if="attachment.problemFileList.length" class="attachment-readonly-grid">
+              <div
+                v-for="f in attachment.problemFileList"
+                :key="f.id"
+                class="attachment-readonly-item"
+                @click="handleAttachmentPreview(f)"
+              >
+                <el-image :src="f.url" fit="cover" class="attachment-readonly-img" />
+              </div>
+            </div>
+            <el-empty
+              v-else
+              description="鏆傛棤璁惧闂鍥剧墖"
+              :image-size="60"
+            />
+          </template>
+          <template v-else>
+            <el-upload
+              v-model:file-list="attachment.problemFileList"
+              :action="upload.url"
+              multiple
+              auto-upload
+              :headers="upload.headers"
+              :data="uploadDataProblem"
+              :before-upload="handleAttachmentBeforeUpload"
+              :on-error="handleAttachmentUploadError"
+              :on-success="() => handleAttachmentUploadSuccess('problem')"
+              :on-preview="handleAttachmentPreview"
+              :on-remove="(file) => handleAttachmentRemove(file, 'problem')"
+              list-type="picture-card"
+              :limit="9"
+              accept="image/png,image/jpeg,image/jpg"
+            >
+              +
+            </el-upload>
+            <el-empty
+              v-if="!attachment.loading && attachment.problemFileList.length === 0"
+              description="鏆傛棤璁惧闂鍥剧墖"
+              :image-size="60"
+            />
+          </template>
         </div>
-
-        <el-empty v-if="!attachment.loading && attachment.files.length === 0" description="鏆傛棤闄勪欢" />
+        <div class="attachment-section">
+          <div class="attachment-section-title">缁翠慨瀹屾垚鍥剧墖</div>
+          <p v-if="!attachmentReadOnly" class="attachment-readonly-tip" style="margin-top: 0">
+            缁翠慨瀹屾垚鍥捐鍦ㄥ垪琛ㄣ�岀淮淇�嶆搷浣滀腑涓婁紶锛屾澶勪粎鏌ョ湅銆�
+          </p>
+          <div v-if="attachment.maintainFileList.length" class="attachment-readonly-grid">
+            <div
+              v-for="f in attachment.maintainFileList"
+              :key="f.id"
+              class="attachment-readonly-item"
+              @click="handleAttachmentPreview(f)"
+            >
+              <el-image :src="f.url" fit="cover" class="attachment-readonly-img" />
+            </div>
+          </div>
+          <el-empty
+            v-else
+            description="鏆傛棤缁翠慨瀹屾垚鍥剧墖"
+            :image-size="60"
+          />
+        </div>
       </div>
       <template #footer>
         <el-button @click="attachment.visible = false">鍏抽棴</el-button>
@@ -203,7 +238,15 @@
 import {ElMessageBox, ElMessage} from "element-plus";
 import dayjs from "dayjs";
 import MaintainModal from "./Modal/MaintainModal.vue";
+import AcceptanceModal from "./Modal/AcceptanceModal.vue";
+import RepairDetailModal from "./Modal/RepairDetailModal.vue";
 import { getToken } from "@/utils/auth";
+import useUserStore from "@/store/modules/user";
+import {
+  REPAIR_FILE_TYPE_PROBLEM,
+  isProblemRepairFile,
+  isMaintainRepairFile,
+} from "@/api/equipmentManagement/repairFileType.js";
 
 defineOptions({
   name: "璁惧鎶ヤ慨",
@@ -215,6 +258,9 @@
 // 妯℃�佹瀹炰緥
 const repairModalRef = ref();
 const maintainModalRef = ref();
+const acceptanceModalRef = ref();
+const repairDetailModalRef = ref();
+const userStore = useUserStore();
 
 // 琛ㄦ牸澶氶�夋閫変腑椤�
 const multipleList = ref([]);
@@ -223,10 +269,30 @@
   visible: false,
   loading: false,
   deviceRepairId: undefined,
+  /** 鎶ヤ慨鍗曠姸鎬侊細闈炲緟缁翠慨(0)鏃堕檮浠跺脊绐楀彧璇� */
+  repairStatus: undefined,
   files: [],
-  fileList: [],
+  problemFileList: [],
+  maintainFileList: [],
   previewVisible: false,
   previewUrl: "",
+});
+
+/** 宸插紑濮嬬淮淇悗锛氬垪琛ㄣ�岄檮浠躲�嶄粎鏌ョ湅锛屼笉鍙鍒� */
+const attachmentReadOnly = computed(
+  () => attachment.repairStatus != null && attachment.repairStatus !== 0
+);
+
+const uploadDataProblem = computed(() => ({
+  deviceRepairId: attachment.deviceRepairId,
+  fileType: REPAIR_FILE_TYPE_PROBLEM,
+}));
+
+const toUploadFileItem = (item) => ({
+  id: item.id,
+  name: item.name,
+  url: getFileAccessUrl(item),
+  fileType: item.type,
 });
 
 const getFileAccessUrl = (file = {}) => {
@@ -312,6 +378,11 @@
         prop: "repairName",
       },
       {
+        label: "楠屾敹浜�",
+        align: "center",
+        prop: "acceptanceName",
+      },
+      {
         label: "鏁呴殰鐜拌薄",
         align: "center",
         prop: "remark",
@@ -345,10 +416,27 @@
         dataType: "slot",
         slot: "operation",
         align: "center",
-        width: "300px",
+        width: "420px",
       },
     ]
 );
+
+const isCurrentUser = (name) => !!name && name === userStore.nickName;
+
+const canEdit = (row) => row.status === 0;
+/** 浠呮姤淇椂鎸囧畾鐨勭淮淇汉鍙淮淇� */
+const canMaintain = (row) => {
+  if (row.status !== 0) return false;
+  if (!row.maintenanceName) return false;
+  return isCurrentUser(row.maintenanceName);
+};
+const canDelete = (row) => row.status === 0;
+/** 浠呮姤淇椂鎸囧畾鐨勯獙鏀朵汉鍙獙鏀� */
+const canAccept = (row) => {
+  if (row.status !== 3) return false;
+  if (!row.acceptanceName) return false;
+  return isCurrentUser(row.acceptanceName);
+};
 
 // type === 1 缁翠慨 2鎶ヤ慨闂�
 const handleDateChange = (value, type) => {
@@ -371,10 +459,14 @@
   multipleList.value = selectionList;
 };
 
-// 妫�鏌ラ�変腑鐨勮褰曚腑鏄惁鏈夊畬缁撶姸鎬佺殑
+// 鎵归噺鍒犻櫎锛氫粎鍏佽寰呯淮淇�
 const hasFinishedStatus = computed(() => {
-  return multipleList.value.some(item => item.status === 1)
-})
+  return multipleList.value.some((item) => item.status !== 0);
+});
+
+const openRepairDetail = (id) => {
+  repairDetailModalRef.value?.open(id);
+};
 
 // 鏂板鎶ヤ慨
 const addRepair = () => {
@@ -386,9 +478,22 @@
   repairModalRef.value.openEdit(id);
 };
 
-// 鏂板缁翠慨
+// 缁翠慨锛堜粎鎸囧畾缁翠慨浜猴級
 const addMaintain = (row) => {
+  if (!canMaintain(row)) {
+    ElMessage.warning("浠呮寚瀹氱殑缁翠慨浜哄彲杩涜缁翠慨");
+    return;
+  }
   maintainModalRef.value.open(row.id, row);
+};
+
+// 楠屾敹锛堜粎鎶ヤ慨鏃舵寚瀹氱殑楠屾敹浜恒�佷笖寰呴獙鏀剁姸鎬佸彲鐐癸級
+const openAcceptance = (row) => {
+  if (!canAccept(row)) {
+    ElMessage.warning("浠呮寚瀹氱殑楠屾敹浜哄彲杩涜楠屾敹");
+    return;
+  }
+  acceptanceModalRef.value.open(row.id, row);
 };
 
 const changePage = ({page, limit}) => {
@@ -401,13 +506,13 @@
 const delRepairByIds = async (ids) => {
   // 妫�鏌ユ槸鍚︽湁瀹岀粨鐘舵�佺殑璁板綍
   const idsArray = Array.isArray(ids) ? ids : [ids];
-  const hasFinished = idsArray.some(id => {
-    const record = dataList.value.find(item => item.id === id);
-    return record && record.status === 1;
+  const cannotDelete = idsArray.some((id) => {
+    const record = dataList.value.find((item) => item.id === id);
+    return record && record.status !== 0;
   });
 
-  if (hasFinished) {
-    ElMessage.warning('涓嶈兘鍒犻櫎鐘舵�佷负瀹岀粨鐨勮褰�');
+  if (cannotDelete) {
+    ElMessage.warning("浠呭緟缁翠慨鐘舵�佸彲鍒犻櫎");
     return;
   }
 
@@ -440,7 +545,9 @@
 };
 
 const openAttachment = async (row) => {
-  attachment.fileList = [];
+  attachment.problemFileList = [];
+  attachment.maintainFileList = [];
+  attachment.repairStatus = row?.status;
   attachment.visible = true;
   attachment.deviceRepairId = row?.id;
   await refreshAttachmentList();
@@ -452,11 +559,12 @@
   try {
     const res = await getRepairFileList(attachment.deviceRepairId);
     attachment.files = Array.isArray(res?.data) ? res.data : [];
-    attachment.fileList = attachment.files.map((item) => ({
-      id: item.id,
-      name: item.name,
-      url: getFileAccessUrl(item),
-    }));
+    attachment.problemFileList = attachment.files
+      .filter((item) => isProblemRepairFile(item.type))
+      .map(toUploadFileItem);
+    attachment.maintainFileList = attachment.files
+      .filter((item) => isMaintainRepairFile(item.type))
+      .map(toUploadFileItem);
   } finally {
     attachment.loading = false;
   }
@@ -465,13 +573,18 @@
 const onAttachmentClosed = () => {
   attachment.loading = false;
   attachment.deviceRepairId = undefined;
+  attachment.repairStatus = undefined;
   attachment.files = [];
-  attachment.fileList = [];
+  attachment.problemFileList = [];
+  attachment.maintainFileList = [];
   attachment.previewVisible = false;
   attachment.previewUrl = "";
 };
 
 const handleAttachmentBeforeUpload = (file) => {
+  if (attachmentReadOnly.value) {
+    return false;
+  }
   const isImage = ["image/png", "image/jpeg", "image/jpg"].includes(file.type);
   if (!isImage) {
     ElMessage.error("鍙兘涓婁紶 png/jpg/jpeg 鍥剧墖");
@@ -480,11 +593,9 @@
   return true;
 };
 
-const handleAttachmentUploadSuccess = async (res) => {
-  if (res?.code === 200) {
-    ElMessage.success("涓婁紶鎴愬姛");
-    await refreshAttachmentList();
-  }
+const handleAttachmentUploadSuccess = async () => {
+  ElMessage.success("涓婁紶鎴愬姛");
+  await refreshAttachmentList();
 };
 
 const handleAttachmentUploadError = () => {
@@ -501,15 +612,19 @@
   attachment.previewVisible = true;
 };
 
-const handleAttachmentRemove = async (file) => {
-  // 浠呯Щ闄ゅ墠绔湭鍏ュ簱鏂囦欢鏃讹紝涓嶈皟鐢ㄥ垹闄ゆ帴鍙�
+const handleAttachmentRemove = async (file, category) => {
+  if (attachmentReadOnly.value) {
+    return false;
+  }
   const matched = attachment.files.find((item) => item.id === file?.id)
     || attachment.files.find((item) => item.name === file?.name);
   if (!matched) return;
+  const expectProblem = category === "problem";
+  if (expectProblem && !isProblemRepairFile(matched.type)) return;
+  if (!expectProblem && !isMaintainRepairFile(matched.type)) return;
   try {
     await confirmDeleteAttachment(matched);
   } finally {
-    // 鍙栨秷鍒犻櫎鏃讹紝el-upload 宸插厛绉婚櫎锛屽埛鏂颁竴娆′繚鎸佷笌鍚庣涓�鑷�
     await refreshAttachmentList();
   }
 };
@@ -548,11 +663,43 @@
   min-height: 240px;
 }
 
-.attachment-actions {
-  display: flex;
-  align-items: center;
-  justify-content: space-between;
+.attachment-section {
+  margin-bottom: 20px;
+}
+.attachment-section-title {
+  font-size: 14px;
+  font-weight: 600;
+  color: #303133;
   margin-bottom: 12px;
+  padding-left: 8px;
+  border-left: 3px solid var(--el-color-primary);
+}
+
+.attachment-readonly-tip {
+  font-size: 13px;
+  color: var(--el-text-color-secondary);
+  margin-bottom: 16px;
+  line-height: 1.5;
+}
+
+.attachment-readonly-grid {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 10px;
+}
+
+.attachment-readonly-item {
+  width: 100px;
+  height: 100px;
+  border-radius: 6px;
+  overflow: hidden;
+  cursor: pointer;
+  border: 1px solid var(--el-border-color);
+}
+
+.attachment-readonly-img {
+  width: 100%;
+  height: 100%;
 }
 
 .attachment-preview-wrap {

--
Gitblit v1.9.3