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/Modal/RepairModal.vue |  328 ++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 files changed, 313 insertions(+), 15 deletions(-)

diff --git a/src/views/equipmentManagement/repair/Modal/RepairModal.vue b/src/views/equipmentManagement/repair/Modal/RepairModal.vue
index 1aa82ec..ab64cbf 100644
--- a/src/views/equipmentManagement/repair/Modal/RepairModal.vue
+++ b/src/views/equipmentManagement/repair/Modal/RepairModal.vue
@@ -2,7 +2,7 @@
   <FormDialog
     v-model="visible"
     :title="id ? '缂栬緫璁惧鎶ヤ慨' : '鏂板璁惧鎶ヤ慨'"
-    width="800px"
+    width="880px"
     @confirm="sendForm"
     @cancel="handleCancel"
     @close="handleClose"
@@ -11,7 +11,7 @@
       <el-row>
         <el-col :span="12">
           <el-form-item label="璁惧鍚嶇О">
-            <el-select v-model="form.deviceLedgerId" @change="setDeviceModel" filterable>
+            <el-select v-model="form.deviceLedgerId" placeholder="璇烽�夋嫨" @change="setDeviceModel" filterable style="width: 100%">
               <el-option
                 v-for="(item, index) in deviceOptions"
                 :key="index"
@@ -30,6 +30,8 @@
             />
           </el-form-item>
         </el-col>
+      </el-row>
+      <el-row>
         <el-col :span="12">
           <el-form-item label="鎶ヤ慨鏃ユ湡">
             <el-date-picker
@@ -45,7 +47,45 @@
         </el-col>
         <el-col :span="12">
           <el-form-item label="鎶ヤ慨浜�">
-            <el-input v-model="form.repairName" placeholder="璇疯緭鍏ユ姤淇汉" />
+            <el-input v-model="form.repairName" disabled placeholder="褰撳墠鐧诲綍浜�" />
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row>
+        <el-col :span="12">
+          <el-form-item label="楠屾敹浜�">
+            <el-select
+              v-model="form.acceptanceName"
+              placeholder="璇烽�夋嫨楠屾敹浜�"
+              filterable
+              clearable
+              style="width: 100%"
+            >
+              <el-option
+                v-for="item in userList"
+                :key="item.userId ?? item.nickName"
+                :label="item.nickName"
+                :value="item.nickName"
+              />
+            </el-select>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="缁翠慨浜�" required>
+            <el-select
+              v-model="form.maintenanceName"
+              placeholder="璇烽�夋嫨缁翠慨浜�"
+              filterable
+              clearable
+              style="width: 100%"
+            >
+              <el-option
+                v-for="item in userList"
+                :key="'m-' + (item.userId ?? item.nickName)"
+                :label="item.nickName"
+                :value="item.nickName"
+              />
+            </el-select>
           </el-form-item>
         </el-col>
       </el-row>
@@ -72,22 +112,63 @@
           </el-form-item>
         </el-col>
       </el-row>
+      <el-row>
+        <el-col :span="24">
+          <el-form-item label="璁惧闂鍥剧墖">
+            <div v-if="id" class="problem-existing-wrap">
+              <div
+                v-for="img in displayedExistingProblems"
+                :key="img.id"
+                class="problem-thumb"
+              >
+                <el-image :src="img.url" fit="cover" class="problem-thumb-img" @click="previewProblem(img.url)" />
+                <el-icon class="problem-thumb-remove" @click.stop="markProblemRemove(img.id)"><Close /></el-icon>
+              </div>
+            </div>
+            <el-upload
+              class="repair-attachment-upload"
+              v-model:file-list="attachmentFileList"
+              drag
+              multiple
+              :auto-upload="false"
+              :limit="9"
+              accept="image/png,image/jpeg,image/jpg"
+              :before-upload="beforeAttachmentUpload"
+            >
+              <el-icon class="repair-upload-icon"><UploadFilled /></el-icon>
+              <div class="el-upload__text">灏嗘枃浠舵嫋鍒版澶勶紝鎴� <em>鐐瑰嚮閫夋嫨鏂囦欢</em></div>
+              <template #tip>
+                <div class="el-upload__tip">
+                  璁惧闂鍥撅細{{ id ? '缂栬緫鏃跺垹闄ら渶鐐瑰脊绐楀簳閮ㄣ�岀‘璁ゃ�嶅悗鎵嶇敓鏁堬紱' : '' }}鏀寔 png / jpg / jpeg锛屽崟寮犱笉瓒呰繃 50MB锛屾渶澶� 9 寮�
+                </div>
+              </template>
+            </el-upload>
+          </el-form-item>
+        </el-col>
+      </el-row>
     </el-form>
   </FormDialog>
 </template>
 
 <script setup>
+import { getCurrentInstance, computed } from "vue";
 import FormDialog from "@/components/Dialog/FormDialog.vue";
 import {
   addRepair,
   editRepair,
   getRepairById,
+  getRepairFileList,
+  uploadRepairFile,
+  deleteRepairFile,
 } from "@/api/equipmentManagement/repair";
+import { REPAIR_FILE_TYPE_PROBLEM, isProblemRepairFile } from "@/api/equipmentManagement/repairFileType.js";
 import { ElMessage } from "element-plus";
+import { UploadFilled, Close } from "@element-plus/icons-vue";
 import dayjs from "dayjs";
 import useFormData from "@/hooks/useFormData";
 import { getDeviceLedger } from "@/api/equipmentManagement/ledger";
 import useUserStore from "@/store/modules/user";
+import { userListNoPage } from "@/api/system/user.js";
 
 defineOptions({
   name: "璁惧鎶ヤ慨寮圭獥",
@@ -101,10 +182,133 @@
 
 const userStore = useUserStore();
 const deviceOptions = ref([]);
+const userList = ref([]);
+/** 寰呮彁浜ょ殑鏂板璁惧闂鍥撅紙纭淇濆瓨鍚庝笂浼狅級 */
+const attachmentFileList = ref([]);
+/** 缂栬緫鏃讹細宸插瓨鍦ㄧ殑璁惧闂鍥撅紙鏈嶅姟绔級 */
+const existingProblemImages = ref([]);
+/** 缂栬緫鏃讹細鏍囪寰呭垹闄ょ殑鍥剧墖 id锛屼粎鐐广�岀‘璁ゃ�嶅悗璋冩帴鍙e垹闄� */
+const pendingRemoveProblemIds = ref([]);
+
+const displayedExistingProblems = computed(() =>
+  existingProblemImages.value.filter((img) => !pendingRemoveProblemIds.value.includes(img.id))
+);
+
+const getFileAccessUrlForModal = (file = {}) => {
+  let raw = file?.link || file?.url || "";
+  if (!raw) return "";
+  if (String(raw).startsWith("http")) return raw;
+  const javaApi = getCurrentInstance()?.proxy?.javaApi || "";
+  let fileUrl = raw;
+  if (fileUrl.indexOf("\\") > -1) {
+    const lowerPath = fileUrl.toLowerCase();
+    const uploadPathIndex = lowerPath.indexOf("uploadpath");
+    if (uploadPathIndex > -1) {
+      fileUrl = fileUrl.substring(uploadPathIndex).replace(/\\/g, "/");
+    } else {
+      fileUrl = fileUrl.replace(/\\/g, "/");
+    }
+  }
+  fileUrl = fileUrl.replace(/^\/?uploadPath/, "/profile");
+  if (!fileUrl.startsWith("http")) {
+    if (!fileUrl.startsWith("/")) fileUrl = "/" + fileUrl;
+    fileUrl = javaApi + fileUrl;
+  }
+  return fileUrl;
+};
+
+const markProblemRemove = (fileId) => {
+  if (!pendingRemoveProblemIds.value.includes(fileId)) {
+    pendingRemoveProblemIds.value.push(fileId);
+  }
+};
+
+const previewProblem = (url) => {
+  window.open(url, "_blank");
+};
+
+const loadExistingProblemImages = async (repairId) => {
+  existingProblemImages.value = [];
+  pendingRemoveProblemIds.value = [];
+  if (!repairId) return;
+  try {
+    const res = await getRepairFileList(repairId);
+    const list = Array.isArray(res?.data) ? res.data : [];
+    existingProblemImages.value = list
+      .filter((item) => isProblemRepairFile(item.type))
+      .map((item) => ({
+        id: item.id,
+        name: item.name,
+        url: getFileAccessUrlForModal(item),
+      }));
+  } catch {
+    existingProblemImages.value = [];
+  }
+};
+
+const resetProblemAttachmentState = () => {
+  existingProblemImages.value = [];
+  pendingRemoveProblemIds.value = [];
+};
+const ATTACH_MAX_MB = 50;
 
 const loadDeviceName = async () => {
   const { data } = await getDeviceLedger();
   deviceOptions.value = data;
+};
+
+const loadUserList = async () => {
+  const res = await userListNoPage();
+  userList.value = Array.isArray(res?.data) ? res.data : [];
+};
+
+/** 鎶ヤ慨浜哄浐瀹氫负褰撳墠鐧诲綍鐢ㄦ埛锛堜笉鍙敼锛� */
+const syncRepairNameToLoginUser = () => {
+  form.repairName = userStore.nickName || "";
+};
+
+const beforeAttachmentUpload = (rawFile) => {
+  const okType = ["image/jpeg", "image/jpg", "image/png"].includes(rawFile.type);
+  if (!okType) {
+    ElMessage.error("鍙兘涓婁紶 png / jpg / jpeg 鍥剧墖");
+    return false;
+  }
+  const maxBytes = ATTACH_MAX_MB * 1024 * 1024;
+  if (rawFile.size > maxBytes) {
+    ElMessage.error(`鍗曚釜鏂囦欢涓嶈兘瓒呰繃 ${ATTACH_MAX_MB}MB`);
+    return false;
+  }
+  return true;
+};
+
+const clearAttachmentQueue = () => {
+  attachmentFileList.value = [];
+};
+
+const applyPendingProblemDeletes = async () => {
+  for (const fileId of pendingRemoveProblemIds.value) {
+    const { code } = await deleteRepairFile(fileId);
+    if (code !== 200) {
+      throw new Error("鍒犻櫎璁惧闂鍥剧墖澶辫触");
+    }
+  }
+  pendingRemoveProblemIds.value = [];
+};
+
+const uploadQueuedRepairImages = async (repairId) => {
+  const rows = attachmentFileList.value || [];
+  const files = rows.map((f) => f.raw).filter(Boolean);
+  if (!files.length || repairId == null) return;
+  for (const file of files) {
+    const fd = new FormData();
+    fd.append("file", file);
+    fd.append("deviceRepairId", String(repairId));
+    fd.append("fileType", String(REPAIR_FILE_TYPE_PROBLEM));
+    const res = await uploadRepairFile(fd);
+    if (res.code !== 200) {
+      throw new Error(res.msg || "闄勪欢涓婁紶澶辫触");
+    }
+  }
 };
 
 const { form, resetForm } = useFormData({
@@ -113,13 +317,15 @@
   deviceModel: undefined, // 瑙勬牸鍨嬪彿
   repairTime: dayjs().format("YYYY-MM-DD"), // 鎶ヤ慨鏃ユ湡锛岄粯璁ゅ綋澶�
   repairName: userStore.nickName, // 鎶ヤ慨浜�
+  acceptanceName: undefined, // 楠屾敹浜�
+  maintenanceName: undefined, // 缁翠慨浜猴紙鍙�夛紝涓庡垪琛ㄧ淮淇汉鍚屾簮锛�
   remark: undefined, // 鏁呴殰鐜拌薄
   status: 0, // 鎶ヤ慨鐘舵��
 });
 
 const setDeviceModel = (deviceId) => {
   const option = deviceOptions.value.find((item) => item.id === deviceId);
-  form.deviceModel = option.deviceModel;
+  form.deviceModel = option?.deviceModel;
 };
 
 const setForm = (data) => {
@@ -127,22 +333,53 @@
   form.deviceName = data.deviceName;
   form.deviceModel = data.deviceModel;
   form.repairTime = data.repairTime;
-  form.repairName = data.repairName;
+  form.acceptanceName = data.acceptanceName;
+  form.maintenanceName = data.maintenanceName;
   form.remark = data.remark;
   form.status = data.status;
+  syncRepairNameToLoginUser();
 };
 
 const sendForm = async () => {
+  syncRepairNameToLoginUser();
+  if (!form.deviceLedgerId) {
+    ElMessage.warning("璇烽�夋嫨璁惧鍚嶇О");
+    return;
+  }
+  if (!form.acceptanceName) {
+    ElMessage.warning("璇烽�夋嫨楠屾敹浜�");
+    return;
+  }
+  if (!form.maintenanceName) {
+    ElMessage.warning("璇烽�夋嫨缁翠慨浜�");
+    return;
+  }
   loading.value = true;
   try {
-    const { code } = id.value
-      ? await editRepair({ id: unref(id), ...form })
-      : await addRepair(form);
-    if (code == 200) {
-      ElMessage.success(`${id.value ? "缂栬緫" : "鏂板"}鎶ヤ慨鎴愬姛`);
-      visible.value = false;
-      emits("ok");
+    let repairId = id.value ? unref(id) : undefined;
+    if (repairId) {
+      const { code } = await editRepair({ id: repairId, ...form });
+      if (code !== 200) return;
+      await applyPendingProblemDeletes();
+    } else {
+      const res = await addRepair(form);
+      if (res.code !== 200) return;
+      repairId = res.data;
+      if (repairId == null && attachmentFileList.value.length) {
+        ElMessage.error("淇濆瓨鎴愬姛浣嗘湭杩斿洖鎶ヤ慨鍗曠紪鍙凤紝鏃犳硶涓婁紶闄勪欢");
+        return;
+      }
     }
+    if (attachmentFileList.value.length && repairId != null) {
+      await uploadQueuedRepairImages(repairId);
+    }
+    ElMessage.success(`${id.value ? "缂栬緫" : "鏂板"}鎶ヤ慨鎴愬姛`);
+    clearAttachmentQueue();
+    resetProblemAttachmentState();
+    visible.value = false;
+    emits("ok");
+  } catch (e) {
+    ElMessage.error(e?.message || "淇濆瓨鎴栦笂浼犻檮浠跺け璐�");
   } finally {
     loading.value = false;
   }
@@ -150,27 +387,38 @@
 
 const handleCancel = () => {
   resetForm();
+  syncRepairNameToLoginUser();
+  clearAttachmentQueue();
+  resetProblemAttachmentState();
   visible.value = false;
 };
 
 const handleClose = () => {
   resetForm();
+  syncRepairNameToLoginUser();
+  clearAttachmentQueue();
+  resetProblemAttachmentState();
   visible.value = false;
 };
 
 const openAdd = async () => {
   id.value = undefined;
+  clearAttachmentQueue();
+  resetProblemAttachmentState();
   visible.value = true;
   await nextTick();
-  await loadDeviceName();
+  await Promise.all([loadDeviceName(), loadUserList()]);
+  syncRepairNameToLoginUser();
 };
 
 const openEdit = async (editId) => {
+  clearAttachmentQueue();
+  resetProblemAttachmentState();
   const { data } = await getRepairById(editId);
   id.value = editId;
   visible.value = true;
   await nextTick();
-  await loadDeviceName();
+  await Promise.all([loadDeviceName(), loadUserList(), loadExistingProblemImages(editId)]);
   setForm(data);
 };
 
@@ -180,4 +428,54 @@
 });
 </script>
 
-<style lang="scss" scoped></style>
+<style lang="scss" scoped>
+.repair-attachment-upload {
+  width: 100%;
+  :deep(.el-upload) {
+    width: 100%;
+  }
+  :deep(.el-upload-dragger) {
+    width: 100%;
+    padding: 24px 16px;
+  }
+}
+.repair-upload-icon {
+  font-size: 48px;
+  color: var(--el-color-primary);
+  margin-bottom: 8px;
+}
+
+.problem-existing-wrap {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 10px;
+  margin-bottom: 12px;
+}
+
+.problem-thumb {
+  position: relative;
+  width: 88px;
+  height: 88px;
+  border-radius: 6px;
+  overflow: hidden;
+  border: 1px solid var(--el-border-color);
+}
+
+.problem-thumb-img {
+  width: 100%;
+  height: 100%;
+  cursor: pointer;
+}
+
+.problem-thumb-remove {
+  position: absolute;
+  top: 2px;
+  right: 2px;
+  font-size: 16px;
+  color: #fff;
+  background: rgba(0, 0, 0, 0.45);
+  border-radius: 50%;
+  padding: 2px;
+  cursor: pointer;
+}
+</style>

--
Gitblit v1.9.3