From b88332e0a2686e68023e26438af97f79247b1ddd Mon Sep 17 00:00:00 2001
From: gongchunyi <deslre0381@gmail.com>
Date: 星期六, 16 五月 2026 14:14:53 +0800
Subject: [PATCH] feat: 设备保养修改,新增保养人、详情

---
 src/views/equipmentManagement/upkeep/index.vue                  |  111 ++++++---
 src/views/equipmentManagement/upkeep/Form/UpkeepDetailModal.vue |  202 ++++++++++++++++
 src/views/equipmentManagement/upkeep/Form/PlanModal.vue         |  245 ++++++++++++++++---
 src/views/equipmentManagement/upkeep/Form/formDia.vue           |   80 ++++-
 src/components/Dialog/FileListDialog.vue                        |   16 +
 src/views/equipmentManagement/upkeep/Form/MaintenanceModal.vue  |    9 
 src/api/equipmentManagement/maintenanceTaskFile.js              |   14 +
 7 files changed, 568 insertions(+), 109 deletions(-)

diff --git a/src/api/equipmentManagement/maintenanceTaskFile.js b/src/api/equipmentManagement/maintenanceTaskFile.js
index 8373ae3..2e4dd4e 100644
--- a/src/api/equipmentManagement/maintenanceTaskFile.js
+++ b/src/api/equipmentManagement/maintenanceTaskFile.js
@@ -18,6 +18,20 @@
   });
 }
 
+export function bindMaintenanceTaskFile(data) {
+  return addMaintenanceTaskFile(data);
+}
+
+// 涓婁紶淇濆吇璁″垝闄勪欢
+export function uploadMaintenanceTaskFile(formData) {
+  return request({
+    url: "/maintenanceTaskFile/upload",
+    method: "post",
+    data: formData,
+    headers: { "Content-Type": "multipart/form-data" },
+  });
+}
+
 // 鍒犻櫎淇濆吇浠诲姟闄勪欢
 export function delMaintenanceTaskFile(id) {
   return request({
diff --git a/src/components/Dialog/FileListDialog.vue b/src/components/Dialog/FileListDialog.vue
index fc82411..ce1eced 100644
--- a/src/components/Dialog/FileListDialog.vue
+++ b/src/components/Dialog/FileListDialog.vue
@@ -10,6 +10,7 @@
                    class="upload-demo"
                    :action="uploadAction"
                    :headers="uploadHeaders"
+                   :data="uploadData"
                    :show-file-list="false"
                    :on-success="handleDefaultUploadSuccess"
                    :on-error="handleDefaultUploadError">
@@ -169,6 +170,14 @@
       type: String,
       default: `${import.meta.env.VITE_APP_BASE_API}/file/upload`,
     },
+    uploadData: {
+      type: Object,
+      default: () => ({}),
+    },
+    uploadDirectSave: {
+      type: Boolean,
+      default: false,
+    },
     isShowPagination: {
       type: Boolean,
       default: false,
@@ -278,8 +287,13 @@
       ElMessage.error(res?.msg || "鏂囦欢涓婁紶澶辫触");
       return;
     }
+    if (props.uploadDirectSave) {
+      ElMessage.success("涓婁紶鎴愬姛");
+      emit("upload", res?.data || {});
+      return;
+    }
     if (!props.rulesRegulationsManagementId) {
-      ElMessage.error("缂哄皯瑙勭珷鍒跺害ID锛屾棤娉曚繚瀛橀檮浠�");
+      ElMessage.error("缂哄皯涓氬姟ID锛屾棤娉曚繚瀛橀檮浠�");
       return;
     }
     const fileName = res?.data?.originalName || file?.name;
diff --git a/src/views/equipmentManagement/upkeep/Form/MaintenanceModal.vue b/src/views/equipmentManagement/upkeep/Form/MaintenanceModal.vue
index c660840..6b574a5 100644
--- a/src/views/equipmentManagement/upkeep/Form/MaintenanceModal.vue
+++ b/src/views/equipmentManagement/upkeep/Form/MaintenanceModal.vue
@@ -2,7 +2,7 @@
   <FormDialog
     v-model="visible"
     :title="'璁惧淇濆吇'"
-    width="500px"
+    width="640px"
     @confirm="sendForm"
     @cancel="handleCancel"
     @close="handleClose"
@@ -35,8 +35,13 @@
       <el-form-item label="淇濆吇缁撴灉">
         <el-input
           v-model="form.maintenanceResult"
+          type="textarea"
+          :rows="6"
           placeholder="璇疯緭鍏ヤ繚鍏荤粨鏋�"
-          type="text" />
+          maxlength="2000"
+          show-word-limit
+          resize="vertical"
+        />
       </el-form-item>
     </el-form>
   </FormDialog>
diff --git a/src/views/equipmentManagement/upkeep/Form/PlanModal.vue b/src/views/equipmentManagement/upkeep/Form/PlanModal.vue
index bb686dc..efa04a5 100644
--- a/src/views/equipmentManagement/upkeep/Form/PlanModal.vue
+++ b/src/views/equipmentManagement/upkeep/Form/PlanModal.vue
@@ -2,7 +2,7 @@
   <FormDialog
     v-model="visible"
     :title="id ? '缂栬緫璁惧淇濆吇璁″垝' : '鏂板璁惧淇濆吇璁″垝'"
-    width="500px"
+    width="640px"
     @confirm="sendForm"
     @cancel="handleCancel"
     @close="handleClose"
@@ -35,25 +35,17 @@
       <el-form-item label="淇濆吇椤圭洰">
         <el-input
           v-model="form.maintenanceLocation"
+          type="textarea"
+          :rows="3"
           placeholder="璇疯緭鍏ヤ繚鍏婚」鐩�"
         />
       </el-form-item>
       <el-form-item label="褰曞叆浜�">
-        <el-select
-          v-model="form.createUser"
-          placeholder="璇烽�夋嫨"
-          filterable
-          default-first-option
-          :reserve-keyword="false"
-          clearable
-        >
-          <el-option
-            v-for="item in userList"
-            :key="item.userId"
-            :label="item.nickName"
-            :value="item.userId"
-          />
-        </el-select>
+        <el-input
+          :model-value="registrantDisplayName"
+          disabled
+          placeholder="褰撳墠鐧诲綍鐢ㄦ埛"
+        />
       </el-form-item>
       <el-form-item v-if="id" label="淇濅慨鐘舵��">
         <el-select v-model="form.status">
@@ -73,6 +65,18 @@
           clearable
         />
       </el-form-item>
+      <el-form-item label="闄勪欢">
+        <el-upload
+          :http-request="handlePlanFileUpload"
+          :file-list="planFileList"
+          :on-remove="handlePlanFileRemove"
+          multiple
+          list-type="picture-card"
+        >
+          <el-icon><Plus /></el-icon>
+        </el-upload>
+        <span v-if="!id" class="upload-tip">鍙厛閫夋嫨闄勪欢锛屼繚瀛樿鍒掑悗鑷姩鍏宠仈鍒版湰璁″垝</span>
+      </el-form-item>
     </el-form>
   </FormDialog>
 </template>
@@ -84,18 +88,42 @@
   editUpkeep,
   getUpkeepById,
 } from "@/api/equipmentManagement/upkeep";
+import {
+  listMaintenanceTaskFiles,
+  bindMaintenanceTaskFile,
+  uploadMaintenanceTaskFile,
+  delMaintenanceTaskFile,
+} from "@/api/equipmentManagement/maintenanceTaskFile";
 import { ElMessage } from "element-plus";
+import { Plus } from "@element-plus/icons-vue";
 import useFormData from "@/hooks/useFormData";
 import { getDeviceLedger } from "@/api/equipmentManagement/ledger";
-import { onMounted } from "vue";
+import { computed, onMounted, ref } from "vue";
 import dayjs from "dayjs";
-import { userListNoPage } from "@/api/system/user.js";
+import useUserStore from "@/store/modules/user.js";
+import request from "@/utils/request";
 
 defineOptions({
   name: "璁惧淇濆吇鏂板璁″垝",
 });
 
 const emits = defineEmits(["ok"]);
+
+const userStore = useUserStore();
+const javaApi = import.meta.env.VITE_APP_BASE_API;
+
+const pendingTempFiles = ref([]);
+const planFileList = ref([]);
+
+const registrantDisplayName = computed(
+  () => userStore.nickName || userStore.name || "褰撳墠鐧诲綍鐢ㄦ埛"
+);
+
+const syncCreateUserFromLogin = () => {
+  if (userStore.id != null && userStore.id !== "") {
+    form.createUser = userStore.id;
+  }
+};
 
 const id = ref();
 const visible = ref(false);
@@ -108,64 +136,178 @@
 };
 
 const { form, resetForm } = useFormData({
-  deviceLedgerId: undefined, // 璁惧Id
-  deviceName: undefined, // 璁惧鍚嶇О
-  deviceModel: undefined, // 瑙勬牸鍨嬪彿
-  maintenanceLocation: undefined, // 淇濆吇椤圭洰
-  maintenancePlanTime: undefined, // 璁″垝淇濆吇鏃ユ湡
-  createUser: undefined, // 褰曞叆浜�
-  status: 0, //淇濅慨鐘舵��
+  deviceLedgerId: undefined,
+  deviceName: undefined,
+  deviceModel: undefined,
+  maintenanceLocation: undefined,
+  maintenancePlanTime: undefined,
+  createUser: undefined,
+  status: 0,
 });
 
 const setDeviceModel = (deviceId) => {
   const option = deviceOptions.value.find((item) => item.id === deviceId);
-  form.deviceModel = option.deviceModel;
+  if (option) {
+    form.deviceModel = option.deviceModel;
+  }
 };
 
-/**
- * @desc 璁剧疆琛ㄥ崟鍐呭
- * @param data 璁惧淇℃伅
- */
+const resetAttachmentState = () => {
+  pendingTempFiles.value = [];
+  planFileList.value = [];
+};
+
+const normalizeFilePreviewUrl = (url = "") => {
+  if (!url) return "";
+  if (url.startsWith("http")) return url;
+  if (url.startsWith("/profile")) return javaApi + url;
+  return url;
+};
+
+const loadPlanFiles = async (planId) => {
+  if (!planId) return;
+  const res = await listMaintenanceTaskFiles({
+    current: 1,
+    size: 100,
+    deviceMaintenanceId: planId,
+  });
+  const records = res?.data?.records || [];
+  planFileList.value = records.map((item) => ({
+    name: item.name,
+    url: normalizeFilePreviewUrl(item.url),
+    status: "success",
+    uid: `saved-${item.id}`,
+    fileId: item.id,
+  }));
+};
+
+const uploadTempFile = (file) => {
+  const fd = new FormData();
+  fd.append("file", file);
+  fd.append("type", "16");
+  return request({
+    url: "/file/upload",
+    method: "post",
+    data: fd,
+    headers: { "Content-Type": "multipart/form-data" },
+  });
+};
+
+const handlePlanFileUpload = async (options) => {
+  const { file, onSuccess, onError } = options;
+  try {
+    if (id.value) {
+      const fd = new FormData();
+      fd.append("file", file);
+      fd.append("deviceMaintenanceId", String(id.value));
+      const res = await uploadMaintenanceTaskFile(fd);
+      if (res.code === 200) {
+        await loadPlanFiles(id.value);
+        onSuccess(res);
+        ElMessage.success("闄勪欢涓婁紶鎴愬姛");
+      } else {
+        onError(new Error(res.msg || "涓婁紶澶辫触"));
+      }
+      return;
+    }
+    const res = await uploadTempFile(file);
+    if (res.code !== 200) {
+      onError(new Error(res.msg || "涓婁紶澶辫触"));
+      return;
+    }
+    const data = res.data || {};
+    pendingTempFiles.value.push({
+      tempId: data.tempId,
+      name: data.originalName || file.name,
+    });
+    onSuccess(res);
+    planFileList.value.push({
+      name: data.originalName || file.name,
+      url: "",
+      status: "success",
+      uid: data.tempId,
+      tempId: data.tempId,
+    });
+  } catch (e) {
+    onError(e);
+    ElMessage.error("闄勪欢涓婁紶澶辫触");
+  }
+};
+
+const handlePlanFileRemove = async (file) => {
+  if (file.fileId) {
+    try {
+      await delMaintenanceTaskFile(file.fileId);
+      await loadPlanFiles(id.value);
+    } catch (e) {
+      ElMessage.error("鍒犻櫎闄勪欢澶辫触");
+    }
+    return;
+  }
+  const tempId = file.tempId || file.uid;
+  pendingTempFiles.value = pendingTempFiles.value.filter((f) => f.tempId !== tempId);
+  planFileList.value = planFileList.value.filter((f) => (f.tempId || f.uid) !== tempId);
+};
+
+const bindPendingFiles = async (planId) => {
+  if (!pendingTempFiles.value.length) return;
+  for (const item of pendingTempFiles.value) {
+    await bindMaintenanceTaskFile({
+      tempId: item.tempId,
+      name: item.name,
+      deviceMaintenanceId: planId,
+    });
+  }
+};
+
 const setForm = (data) => {
   form.deviceLedgerId = data.deviceLedgerId;
   form.deviceName = data.deviceName;
   form.deviceModel = data.deviceModel;
   form.maintenanceLocation = data.maintenanceLocation;
-  form.createUser = Number(data.createUser);
   form.status = data.status;
+  syncCreateUserFromLogin();
   form.maintenancePlanTime = dayjs(data.maintenancePlanTime).format(
     "YYYY-MM-DD HH:mm:ss"
   );
 };
 
-// 鐢ㄦ埛鍒楄〃
-const userList = ref([]);
-
 onMounted(() => {
   loadDeviceName();
-  userListNoPage().then((res) => {
-    userList.value = res.data;
-  });
 });
 
 const openEdit = async (editId) => {
+  resetAttachmentState();
   const { data } = await getUpkeepById(editId);
   id.value = editId;
   visible.value = true;
   await nextTick();
   setForm(data);
+  await loadPlanFiles(editId);
 };
 
 const sendForm = async () => {
+  syncCreateUserFromLogin();
   loading.value = true;
   try {
-    const { code } = id.value
-      ? await editUpkeep({ id: unref(id), ...form })
-      : await addUpkeep(form);
-    if (code == 200) {
-      ElMessage.success(`${id.value ? "缂栬緫" : "鏂板"}璁″垝鎴愬姛`);
-      visible.value = false;
-      emits("ok");
+    if (id.value) {
+      const { code } = await editUpkeep({ id: unref(id), ...form });
+      if (code == 200) {
+        ElMessage.success("缂栬緫璁″垝鎴愬姛");
+        visible.value = false;
+        emits("ok");
+      }
+    } else {
+      const res = await addUpkeep(form);
+      if (res.code == 200) {
+        const planId = res.data?.id;
+        if (planId) {
+          await bindPendingFiles(planId);
+        }
+        ElMessage.success("鏂板璁″垝鎴愬姛");
+        visible.value = false;
+        emits("ok");
+      }
     }
   } finally {
     loading.value = false;
@@ -174,16 +316,21 @@
 
 const handleCancel = () => {
   resetForm();
+  resetAttachmentState();
   visible.value = false;
 };
 
 const handleClose = () => {
   resetForm();
+  resetAttachmentState();
   visible.value = false;
 };
 
 const openModal = () => {
   id.value = undefined;
+  resetForm();
+  resetAttachmentState();
+  syncCreateUserFromLogin();
   visible.value = true;
 };
 
@@ -193,4 +340,12 @@
 });
 </script>
 
-<style lang="scss" scoped></style>
+<style lang="scss" scoped>
+.upload-tip {
+  display: block;
+  font-size: 12px;
+  color: #999;
+  margin-top: 8px;
+  line-height: 1.4;
+}
+</style>
diff --git a/src/views/equipmentManagement/upkeep/Form/UpkeepDetailModal.vue b/src/views/equipmentManagement/upkeep/Form/UpkeepDetailModal.vue
new file mode 100644
index 0000000..972cb34
--- /dev/null
+++ b/src/views/equipmentManagement/upkeep/Form/UpkeepDetailModal.vue
@@ -0,0 +1,202 @@
+<template>
+  <el-dialog
+    v-model="visible"
+    title="淇濆吇璁″垝璇︽儏"
+    width="820px"
+    destroy-on-close
+    @closed="onClosed"
+  >
+    <div v-loading="loading" class="upkeep-detail">
+      <template v-if="detail">
+        <el-descriptions :column="2" border size="small">
+          <el-descriptions-item label="璁惧鍚嶇О">{{ detail.deviceName || "鈥�" }}</el-descriptions-item>
+          <el-descriptions-item label="瑙勬牸鍨嬪彿">{{ detail.deviceModel || "鈥�" }}</el-descriptions-item>
+          <el-descriptions-item label="淇濆吇椤圭洰" :span="2">
+            <div class="multiline">{{ detail.maintenanceLocation || "鈥�" }}</div>
+          </el-descriptions-item>
+          <el-descriptions-item label="淇濆吇浜�">{{ detail.maintenancePerson || "鈥�" }}</el-descriptions-item>
+          <el-descriptions-item label="璁″垝淇濆吇鏃ユ湡">{{ fmtDate(detail.maintenancePlanTime) }}</el-descriptions-item>
+          <el-descriptions-item label="褰曞叆浜�">{{ detail.createUserName || "鈥�" }}</el-descriptions-item>
+          <el-descriptions-item label="瀹為檯淇濆吇浜�">{{ detail.maintenanceActuallyName || "鈥�" }}</el-descriptions-item>
+          <el-descriptions-item label="瀹為檯淇濆吇鏃ユ湡">{{ fmtDateTime(detail.maintenanceActuallyTime) }}</el-descriptions-item>
+          <el-descriptions-item label="鐘舵��">
+            <el-tag v-if="detail.status === 2" type="danger" size="small">澶辫触</el-tag>
+            <el-tag v-else-if="detail.status === 1" type="success" size="small">瀹岀粨</el-tag>
+            <el-tag v-else-if="detail.status === 0" type="warning" size="small">寰呬繚鍏�</el-tag>
+            <span v-else>鈥�</span>
+          </el-descriptions-item>
+          <el-descriptions-item label="淇濆吇缁撴灉" :span="2">
+            <div class="multiline">{{ detail.maintenanceResult || "鈥�" }}</div>
+          </el-descriptions-item>
+          <el-descriptions-item label="褰曞叆鏃堕棿">{{ fmtDateTime(detail.createTime) }}</el-descriptions-item>
+          <el-descriptions-item label="鏇存柊鏃堕棿">{{ fmtDateTime(detail.updateTime) }}</el-descriptions-item>
+        </el-descriptions>
+
+        <div class="attach-block">
+          <div class="attach-title">闄勪欢锛坽{ fileList.length }}锛�</div>
+          <div v-if="fileList.length" class="img-grid">
+            <div v-for="file in fileList" :key="file.id" class="attach-item">
+              <el-image
+                v-if="isImageFile(file.name)"
+                :src="file.url"
+                fit="cover"
+                class="thumb"
+                :preview-src-list="imagePreviewList"
+                preview-teleported
+              />
+              <div v-else class="file-chip">
+                <el-link type="primary" :href="file.url" target="_blank">{{ file.name }}</el-link>
+              </div>
+              <div class="file-name" :title="file.name">{{ file.name }}</div>
+            </div>
+          </div>
+          <el-empty v-else description="鏆傛棤闄勪欢" :image-size="56" />
+        </div>
+      </template>
+    </div>
+    <template #footer>
+      <el-button type="primary" @click="visible = false">鍏抽棴</el-button>
+    </template>
+  </el-dialog>
+</template>
+
+<script setup>
+import { computed, ref } from "vue";
+import dayjs from "dayjs";
+import { getUpkeepById } from "@/api/equipmentManagement/upkeep";
+import { listMaintenanceTaskFiles } from "@/api/equipmentManagement/maintenanceTaskFile";
+
+defineOptions({ name: "UpkeepDetailModal" });
+
+const props = defineProps({
+  javaApi: {
+    type: String,
+    default: "",
+  },
+});
+
+const visible = ref(false);
+const loading = ref(false);
+const detail = ref(null);
+const fileList = ref([]);
+
+const apiBase = computed(() => props.javaApi || import.meta.env.VITE_APP_BASE_API || "");
+
+const imagePreviewList = computed(() =>
+  fileList.value.filter((f) => isImageFile(f.name)).map((f) => f.url)
+);
+
+const fmtDate = (v) => (v ? dayjs(v).format("YYYY-MM-DD") : "鈥�");
+const fmtDateTime = (v) => (v ? dayjs(v).format("YYYY-MM-DD HH:mm:ss") : "鈥�");
+
+const isImageFile = (name = "") => /\.(jpg|jpeg|png|gif|webp|bmp)$/i.test(name);
+
+const normalizeFileUrl = (rawUrl = "") => {
+  let fileUrl = rawUrl || "";
+  if (!fileUrl) return "";
+  if (fileUrl.startsWith("http://") || fileUrl.startsWith("https://")) return fileUrl;
+  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/i, "/profile");
+  if (!fileUrl.startsWith("/")) fileUrl = "/" + fileUrl;
+  if (!fileUrl.startsWith(apiBase.value)) fileUrl = apiBase.value + fileUrl;
+  return fileUrl;
+};
+
+const loadFiles = async (id) => {
+  const res = await listMaintenanceTaskFiles({
+    current: 1,
+    size: 100,
+    deviceMaintenanceId: id,
+  });
+  const records = res?.data?.records || [];
+  fileList.value = records.map((item) => ({
+    id: item.id,
+    name: item.name,
+    url: normalizeFileUrl(item.url),
+  }));
+};
+
+const open = async (row) => {
+  if (!row?.id) return;
+  visible.value = true;
+  loading.value = true;
+  detail.value = null;
+  fileList.value = [];
+  try {
+    const { data } = await getUpkeepById(row.id);
+    detail.value = { ...row, ...data };
+    await loadFiles(row.id);
+  } finally {
+    loading.value = false;
+  }
+};
+
+const onClosed = () => {
+  detail.value = null;
+  fileList.value = [];
+};
+
+defineExpose({ open });
+</script>
+
+<style lang="scss" scoped>
+.upkeep-detail {
+  min-height: 120px;
+}
+.multiline {
+  white-space: pre-wrap;
+  word-break: break-word;
+  line-height: 1.6;
+}
+.attach-block {
+  margin-top: 20px;
+}
+.attach-title {
+  font-size: 14px;
+  font-weight: 600;
+  margin-bottom: 12px;
+  color: #303133;
+}
+.img-grid {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 12px;
+}
+.attach-item {
+  width: 100px;
+}
+.thumb {
+  width: 100px;
+  height: 100px;
+  border-radius: 4px;
+  border: 1px solid #ebeef5;
+}
+.file-chip {
+  width: 100px;
+  height: 100px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  padding: 8px;
+  border: 1px dashed #dcdfe6;
+  border-radius: 4px;
+  box-sizing: border-box;
+}
+.file-name {
+  display: block;
+  margin-top: 6px;
+  font-size: 12px;
+  color: #606266;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+</style>
diff --git a/src/views/equipmentManagement/upkeep/Form/formDia.vue b/src/views/equipmentManagement/upkeep/Form/formDia.vue
index 3ce522b..4c07007 100644
--- a/src/views/equipmentManagement/upkeep/Form/formDia.vue
+++ b/src/views/equipmentManagement/upkeep/Form/formDia.vue
@@ -35,8 +35,17 @@
 			<el-row>
 				<el-col :span="12">
 					<el-form-item label="褰曞叆浜�" prop="inspector">
+						<el-input
+							:model-value="registrantDisplayName"
+							disabled
+							placeholder="褰撳墠鐧诲綍鐢ㄦ埛"
+						/>
+					</el-form-item>
+				</el-col>
+				<el-col :span="12">
+					<el-form-item label="淇濆吇浜�" prop="maintenancePerson">
 						<el-select
-							v-model="form.inspector"
+							v-model="form.maintenancePerson"
 							filterable
 							default-first-option
 							:reserve-keyword="false"
@@ -47,11 +56,13 @@
 								v-for="item in userList"
 								:label="item.nickName"
 								:value="item.userId"
-								:key="item.userId"
+								:key="'mp-' + item.userId"
 							/>
 						</el-select>
 					</el-form-item>
 				</el-col>
+			</el-row>
+			<el-row>
 				<el-col :span="12">
 					<el-form-item label="鐧昏鏃堕棿" prop="registrationDate">
 						<el-date-picker
@@ -123,7 +134,19 @@
 				</el-col>
 			</el-row>
 			<el-row>
-				<el-col :span="12">
+				<el-col :span="24">
+					<el-form-item label="淇濆吇椤圭洰" prop="maintenanceItems">
+						<el-input
+							v-model="form.maintenanceItems"
+							type="textarea"
+							:rows="3"
+							placeholder="璇疯緭鍏ヤ繚鍏婚」鐩�"
+						/>
+					</el-form-item>
+				</el-col>
+			</el-row>
+			<el-row>
+				<el-col :span="24">
 					<el-form-item label="澶囨敞" prop="remarks">
 						<el-input v-model="form.remarks" placeholder="璇疯緭鍏ュ娉�" type="textarea" />
 					</el-form-item>
@@ -135,7 +158,7 @@
 
 <script setup>
 import FormDialog from "@/components/Dialog/FormDialog.vue";
-import { reactive, ref, getCurrentInstance, toRefs } from "vue";
+import { reactive, ref, computed, getCurrentInstance, toRefs } from "vue";
 import {userListNoPageByTenantId} from "@/api/system/user.js";
 import { getDeviceLedger } from "@/api/equipmentManagement/ledger";
 import { deviceMaintenanceTaskAdd, deviceMaintenanceTaskEdit } from "@/api/equipmentManagement/upkeep";
@@ -152,8 +175,10 @@
 	form: {
 		taskId: undefined,
 		taskName: undefined,
-		// 褰曞叆浜猴細鍗曢�変竴涓敤鎴� id
+		// 褰曞叆浜恒�佷繚鍏讳汉锛氱敤鎴� id
 		inspector: undefined,
+		maintenancePerson: undefined,
+		maintenanceItems: '',
 		remarks: '',
 		frequencyType: '',
 		frequencyDetail: '',
@@ -165,6 +190,8 @@
 	rules: {
 		taskId: [{ required: true, message: "璇烽�夋嫨璁惧", trigger: "change" },],
 		inspector: [{ required: true, message: "璇烽�夋嫨褰曞叆浜�", trigger: "blur" },],
+		maintenancePerson: [{ required: true, message: "璇烽�夋嫨淇濆吇浜�", trigger: "change" }],
+		maintenanceItems: [{ required: true, message: "璇疯緭鍏ヤ繚鍏婚」鐩�", trigger: "blur" }],
 		registrationDate: [{ required: true, message: "璇烽�夋嫨鐧昏鏃堕棿", trigger: "change" }],
 		frequencyDetail: [{
 			validator: (rule, value, callback) => {
@@ -192,6 +219,16 @@
 })
 const { form, rules } = toRefs(data)
 const userList = ref([])
+
+const registrantDisplayName = computed(
+	() => userStore.nickName || userStore.name || "褰撳墠鐧诲綍鐢ㄦ埛"
+)
+
+const syncRegistrantFromLogin = () => {
+	if (userStore.id != null && userStore.id !== "") {
+		form.value.inspector = userStore.id
+	}
+}
 
 const loadDeviceName = async () => {
 	const { data } = await getDeviceLedger();
@@ -226,21 +263,17 @@
 	
 	if (type === 'edit' && row) {
 		form.value = { ...row }
-		// 缂栬緫鏃剁敤鎺ュ彛杩斿洖鐨� registrantId 鍥炴樉褰曞叆浜�
-		if (row.registrantId) {
-			form.value.inspector = row.registrantId
+		if (row.maintenancePersonId) {
+			form.value.maintenancePerson = row.maintenancePersonId
 		}
-
-		// 濡傛灉鏈夎澶嘔D锛岃嚜鍔ㄨ缃澶囦俊鎭�
 		if (form.value.taskId) {
 			setDeviceModel(form.value.taskId);
 		}
 	} else if (type === 'add') {
-		// 鏂板鏃惰缃櫥璁版棩鏈熶负褰撳ぉ
 		form.value.registrationDate = getCurrentDate();
-		// 鏂板鏃惰缃綍鍏ヤ汉涓哄綋鍓嶇櫥褰曡处鎴�
-		form.value.inspector = userStore.id;
+		form.value.maintenancePerson = userStore.id;
 	}
+	syncRegistrantFromLogin()
 }
 
 // 鍏抽棴瀵硅瘽妗�
@@ -260,7 +293,8 @@
 		taskId: undefined,
 		taskName: undefined,
 		inspector: undefined,
-		inspector: undefined,
+		maintenancePerson: undefined,
+		maintenanceItems: '',
 		remarks: '',
 		frequencyType: '',
 		frequencyDetail: '',
@@ -273,19 +307,21 @@
 
 // 鎻愪氦琛ㄥ崟
 const submitForm = () => {
+	syncRegistrantFromLogin()
 	proxy.$refs["formRef"].validate(async valid => {
 		if (valid) {
 			try {
 				const payload = { ...form.value }
-				// 涓嶅啀鍚戝悗绔紶淇濆吇浜哄瓧娈碉紝浠呬娇鐢ㄦ帴鍙h姹傜殑 registrant / registrantId
-				// 鏍规嵁閫夋嫨鐨�"褰曞叆浜�"璁剧疆 registrant / registrantId
-				if (payload.inspector) {
-					const selectedUser = userList.value.find(
-						(u) => String(u.userId) === String(payload.inspector)
+				payload.registrantId = userStore.id
+				payload.registrant = userStore.nickName || userStore.name
+				const maintenancePersonUserId = form.value.maintenancePerson
+				if (maintenancePersonUserId) {
+					const maintainer = userList.value.find(
+						(u) => String(u.userId) === String(maintenancePersonUserId)
 					)
-					if (selectedUser) {
-						payload.registrantId = selectedUser.userId
-						payload.registrant = selectedUser.nickName
+					if (maintainer) {
+						payload.maintenancePersonId = maintainer.userId
+						payload.maintenancePerson = maintainer.nickName
 					}
 				}
 				delete payload.inspector
diff --git a/src/views/equipmentManagement/upkeep/index.vue b/src/views/equipmentManagement/upkeep/index.vue
index 4cf6fc0..9820910 100644
--- a/src/views/equipmentManagement/upkeep/index.vue
+++ b/src/views/equipmentManagement/upkeep/index.vue
@@ -162,23 +162,19 @@
         @selection-change="handleSelectionChange"
         @pagination="changePage"
       >
-        <template #maintenanceResultRef="{ row }">
-          <div>{{ row.maintenanceResult || '-' }}</div>
-        </template>
         <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>
         </template>
         <template #operation="{ row }">
-          <!-- 杩欎釜鍔熻兘璺熸柊澧炰繚鍏诲姛鑳戒竴妯′竴鏍凤紝鏈夊暐鎰忎箟锛� -->
-          <!-- <el-button
-              type="primary"
-              text
-              @click="addMaintain(row)"
+          <el-button
+            type="primary"
+            link
+            @click="openDetail(row)"
           >
-            鏂板淇濆吇
-          </el-button> -->
+            璇︽儏
+          </el-button>
           <el-button
             type="primary"
             link
@@ -218,15 +214,20 @@
     <PlanModal ref="planModalRef" @ok="getTableData" />
         <MaintenanceModal ref="maintainModalRef" @ok="getTableData" />
         <FormDia ref="formDiaRef" @closeDia="getScheduledTableData" />
-    <FileListDialog 
+    <UpkeepDetailModal ref="upkeepDetailModalRef" :java-api="javaApi" />
+    <FileListDialog
       ref="fileListDialogRef"
       v-model="fileDialogVisible"
-      :show-upload-button="true"
-      :show-delete-button="true"
+      :title="currentRecordFinished ? '闄勪欢锛堝凡瀹岀粨锛屼粎鍙煡鐪嬶級' : '闄勪欢'"
+      :show-upload-button="!currentRecordFinished"
+      :show-delete-button="!currentRecordFinished"
       :delete-method="handleAttachmentDelete"
       :name-column-label="'闄勪欢鍚嶇О'"
-      :rulesRegulationsManagementId="currentMaintenanceTaskId"
-      @upload="handleAttachmentUpload" />
+      :upload-url="maintenanceFileUploadUrl"
+      :upload-data="maintenanceFileUploadData"
+      :upload-direct-save="true"
+      @upload="refreshFileList"
+    />
   </div>
 </template>
 
@@ -236,6 +237,7 @@
 import { ElMessage, ElMessageBox } from 'element-plus'
 import PlanModal from './Form/PlanModal.vue'
 import MaintenanceModal from './Form/MaintenanceModal.vue'
+import UpkeepDetailModal from './Form/UpkeepDetailModal.vue'
 import FormDia from './Form/formDia.vue'
 import FileListDialog from '@/components/Dialog/FileListDialog.vue'
 import {
@@ -246,7 +248,6 @@
 } from '@/api/equipmentManagement/upkeep'
 import {
   listMaintenanceTaskFiles,
-  addMaintenanceTaskFile,
   delMaintenanceTaskFile,
 } from '@/api/equipmentManagement/maintenanceTaskFile'
 import dayjs from 'dayjs'
@@ -260,12 +261,40 @@
 const planModalRef = ref()
 // 淇濆吇寮圭獥鎺у埗鍣�
 const maintainModalRef = ref()
+const upkeepDetailModalRef = ref()
 // 瀹氭椂浠诲姟寮圭獥鎺у埗鍣�
 const formDiaRef = ref()
 // 闄勪欢寮圭獥
 const fileListDialogRef = ref(null)
 const fileDialogVisible = ref(false)
 const currentMaintenanceTaskId = ref(null)
+/** 褰撳墠闄勪欢鎵�灞炶褰曟槸鍚﹀凡瀹岀粨锛坰tatus=1锛� */
+const currentRecordFinished = ref(false)
+
+const javaApi = import.meta.env.VITE_APP_BASE_API
+const maintenanceFileUploadUrl = `${javaApi}/maintenanceTaskFile/upload`
+const maintenanceFileUploadData = computed(() => ({
+  deviceMaintenanceId: currentMaintenanceTaskId.value,
+}))
+
+const normalizeMaintenanceFileUrl = (rawUrl = "") => {
+  let fileUrl = rawUrl || ""
+  if (!fileUrl) return ""
+  if (fileUrl.startsWith("http://") || fileUrl.startsWith("https://")) return fileUrl
+  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/i, "/profile")
+  if (!fileUrl.startsWith("/")) fileUrl = "/" + fileUrl
+  if (!fileUrl.startsWith(javaApi)) fileUrl = javaApi + fileUrl
+  return fileUrl
+}
 
 // 浠诲姟璁板綍tab锛堝師璁惧淇濆吇椤甸潰锛夌浉鍏冲彉閲�
 const filters = reactive({
@@ -338,6 +367,13 @@
 		}
 	},
 	{ prop: "registrant", label: "鐧昏浜�", minWidth: 100 },
+	{ prop: "maintenancePerson", label: "淇濆吇浜�", minWidth: 100 },
+	{
+		prop: "maintenanceItems",
+		label: "淇濆吇椤圭洰",
+		minWidth: 180,
+		showOverflowTooltip: true,
+	},
 	{ prop: "registrationDate", label: "鐧昏鏃ユ湡", minWidth: 100 },
 	{
 		fixed: "right",
@@ -365,6 +401,12 @@
 		label: "淇濆吇椤圭洰",
 		align: "center",
 		prop: "maintenanceLocation",
+		showOverflowTooltip: true,
+	},
+	{
+		label: "淇濆吇浜�",
+		align: "center",
+		prop: "maintenancePerson",
 	},
 	{
 		label: "璁″垝淇濆吇鏃ユ湡",
@@ -400,8 +442,8 @@
 		label: "淇濆吇缁撴灉",
 		align: "center",
 		prop: "maintenanceResult",
-		dataType: "slot",
-		slot: "maintenanceResultRef",
+		minWidth: 200,
+		showOverflowTooltip: true,
 	},
 	{
 		label: "鐘舵��",
@@ -416,7 +458,7 @@
 		dataType: "slot",
 		slot: "operation",
 		align: "center",
-		width: "350px",
+    width: "400px",
 	},
 ])
 
@@ -610,14 +652,13 @@
       current: 1,
       size: 100,
       deviceMaintenanceId,
-      rulesRegulationsManagementId:deviceMaintenanceId
     }
     const res = await listMaintenanceTaskFiles(params)
     const records = res?.data?.records || []
     const mapped = records.map(item => ({
       id: item.id,
       name: item.fileName || item.name,
-      url: item.fileUrl || item.url,
+      url: normalizeMaintenanceFileUrl(item.fileUrl || item.url),
       raw: item,
     }))
     fileListDialogRef.value?.setList(mapped)
@@ -626,9 +667,14 @@
   }
 }
 
+const openDetail = (row) => {
+  upkeepDetailModalRef.value?.open(row)
+}
+
 // 鎵撳紑闄勪欢寮圭獥
 const openFileDialog = async (row) => {
   currentMaintenanceTaskId.value = row.id
+  currentRecordFinished.value = row.status === 1
   fileDialogVisible.value = true
   await fetchMaintenanceTaskFiles(row.id)
 }
@@ -639,25 +685,12 @@
   await fetchMaintenanceTaskFiles(currentMaintenanceTaskId.value)
 }
 
-// 涓婁紶闄勪欢
-const handleAttachmentUpload = async (filePayload) => {
-  if (!currentMaintenanceTaskId.value) return
-  try {
-    const payload = {
-      name: filePayload?.fileName || filePayload?.name,
-      url: filePayload?.fileUrl || filePayload?.url,
-      deviceMaintenanceId: currentMaintenanceTaskId.value,
-    }
-    await addMaintenanceTaskFile(payload)
-    ElMessage.success('鏂囦欢涓婁紶鎴愬姛')
-    await refreshFileList()
-  } catch (error) {
-    ElMessage.error('鏂囦欢涓婁紶澶辫触')
-  }
-}
-
-// 鍒犻櫎闄勪欢
+// 鍒犻櫎闄勪欢锛堝凡瀹岀粨涓嶅彲鍒狅級
 const handleAttachmentDelete = async (row) => {
+  if (currentRecordFinished.value) {
+    ElMessage.warning('璇ヤ繚鍏诲凡瀹岀粨锛屼笉鍙垹闄ら檮浠�')
+    return false
+  }
   if (!row?.id) return false
   try {
     await ElMessageBox.confirm('纭鍒犻櫎璇ラ檮浠讹紵', '鎻愮ず', { type: 'warning' })

--
Gitblit v1.9.3