From c2ded572eff614cf2a259e263e60a5e8feb3e676 Mon Sep 17 00:00:00 2001
From: gongchunyi <deslre0381@gmail.com>
Date: 星期四, 23 四月 2026 16:52:30 +0800
Subject: [PATCH] fix: 销售订单发货状态修改

---
 src/views/equipmentManagement/repair/index.vue |  434 +++++++++++++++++++++++++++++++++++++++++++++++++----
 1 files changed, 399 insertions(+), 35 deletions(-)

diff --git a/src/views/equipmentManagement/repair/index.vue b/src/views/equipmentManagement/repair/index.vue
index 96fb91b..e14ab37 100644
--- a/src/views/equipmentManagement/repair/index.vue
+++ b/src/views/equipmentManagement/repair/index.vue
@@ -1,24 +1,83 @@
 <template>
   <div class="app-container">
+    <el-form :model="filters" :inline="true">
+      <el-form-item label="璁惧鍚嶇О">
+        <el-input
+            v-model="filters.deviceName"
+            style="width: 240px"
+            placeholder="璇疯緭鍏ヨ澶囧悕绉�"
+            clearable
+            :prefix-icon="Search"
+            @change="getTableData"
+        />
+      </el-form-item>
+      <el-form-item label="瑙勬牸鍨嬪彿">
+        <el-input
+            v-model="filters.deviceModel"
+            style="width: 240px"
+            placeholder="璇烽�夋嫨瑙勬牸鍨嬪彿"
+            clearable
+            :prefix-icon="Search"
+            @change="getTableData"
+        />
+      </el-form-item>
+      <el-form-item label="鏁呴殰鐜拌薄">
+        <el-input
+            v-model="filters.remark"
+            style="width: 240px"
+            placeholder="璇疯緭鍏ユ晠闅滅幇璞�"
+            clearable
+            :prefix-icon="Search"
+            @change="getTableData"
+        />
+      </el-form-item>
+      <el-form-item label="缁翠慨浜�">
+        <el-input
+            v-model="filters.maintenanceName"
+            style="width: 240px"
+            placeholder="璇疯緭鍏ョ淮淇汉"
+            clearable
+            :prefix-icon="Search"
+            @change="getTableData"
+        />
+      </el-form-item>
+      <el-form-item label="鎶ヤ慨鏃ユ湡">
+        <el-date-picker
+            v-model="filters.repairTimeStr"
+            type="date"
+            placeholder="璇烽�夋嫨鎶ヤ慨鏃ユ湡"
+            size="default"
+            @change="(date) => handleDateChange(date,2)"
+        />
+      </el-form-item>
+      <el-form-item label="缁翠慨鏃ユ湡">
+        <el-date-picker
+            v-model="filters.maintenanceTimeStr"
+            type="date"
+            placeholder="璇烽�夋嫨缁翠慨鏃ユ湡"
+            size="default"
+            @change="(date) => handleDateChange(date,1)"
+        />
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" @click="getTableData">鎼滅储</el-button>
+        <el-button @click="resetFilters">閲嶇疆</el-button>
+      </el-form-item>
+    </el-form>
     <div class="table_list">
       <div class="actions">
         <el-text class="mx-1" size="large">璁惧鎶ヤ慨</el-text>
         <div>
-          <el-button
-            type="primary"
-            icon="Plus"
-            :disabled="multipleList.length !== 1"
-            @click="addMaintain"
-          >
-            鏂板缁翠慨
-          </el-button>
           <el-button type="success" icon="Van" @click="addRepair">
             鏂板鎶ヤ慨
+          </el-button>
+          <el-button @click="handleOut">
+            瀵煎嚭
           </el-button>
           <el-button
             type="danger"
             icon="Delete"
-            :disabled="multipleList.length <= 0"
+            :disabled="multipleList.length <= 0 || hasFinishedStatus"
             @click="delRepairByIds(multipleList.map((item) => item.id))"
           >
             鎵归噺鍒犻櫎
@@ -26,34 +85,52 @@
         </div>
       </div>
       <PIMTable
-        rowKey="id"
-        isSelection
-        :column="columns"
-        :tableData="dataList"
-        :page="{
+          rowKey="id"
+          isSelection
+          :column="columns"
+          :tableData="dataList"
+          :page="{
           current: pagination.currentPage,
           size: pagination.pageSize,
           total: pagination.total,
         }"
-        @selection-change="handleSelectionChange"
+          @selection-change="handleSelectionChange"
+          @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="error">寰呯淮淇�</el-tag>
+          <el-tag v-if="row.status === 0" type="warning">寰呯淮淇�</el-tag>
         </template>
         <template #operation="{ row }">
           <el-button
             type="primary"
-            text
-            icon="editPen"
+            link
+            :disabled="row.status === 1"
             @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>
+          <el-button
             type="danger"
-            text
-            icon="delete"
+            link
+            :disabled="row.status === 1"
             @click="delRepairByIds(row.id)"
           >
             鍒犻櫎
@@ -61,23 +138,79 @@
         </template>
       </PIMTable>
     </div>
-    <RepairModal ref="repairModalRef" @ok="getTableData" />
-    <MaintainModal ref="maintainModalRef" @ok="getTableData" />
+    <RepairModal ref="repairModalRef" @ok="getTableData"/>
+    <MaintainModal ref="maintainModalRef" @ok="getTableData"/>
+
+    <el-dialog v-model="attachment.visible" title="闄勪欢" width="860px" @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>
+        </div>
+
+        <el-empty v-if="!attachment.loading && attachment.files.length === 0" description="鏆傛棤闄勪欢" />
+      </div>
+      <template #footer>
+        <el-button @click="attachment.visible = false">鍏抽棴</el-button>
+      </template>
+    </el-dialog>
+
+    <el-dialog
+      v-model="attachment.previewVisible"
+      title="鍥剧墖棰勮"
+      width="70%"
+      append-to-body
+    >
+      <div class="attachment-preview-wrap">
+        <img
+          v-if="attachment.previewUrl"
+          :src="attachment.previewUrl"
+          alt="preview"
+          class="attachment-preview-img"
+        />
+      </div>
+    </el-dialog>
   </div>
 </template>
 
 <script setup>
-import { usePaginationApi } from "@/hooks/usePaginationApi";
-import { getRepairPage, delRepair } from "@/api/equipmentManagement/repair";
-import { onMounted } from "vue";
+import { onMounted, getCurrentInstance, computed } from "vue";
+import {usePaginationApi} from "@/hooks/usePaginationApi";
+import {
+  getRepairPage,
+  delRepair,
+  getRepairFileList,
+  deleteRepairFile,
+} from "@/api/equipmentManagement/repair";
 import RepairModal from "./Modal/RepairModal.vue";
-import { ElMessageBox, ElMessage } from "element-plus";
+import {ElMessageBox, ElMessage} from "element-plus";
 import dayjs from "dayjs";
 import MaintainModal from "./Modal/MaintainModal.vue";
+import { getToken } from "@/utils/auth";
 
 defineOptions({
   name: "璁惧鎶ヤ慨",
 });
+
+const {proxy} = getCurrentInstance();
+const javaApi = proxy?.javaApi || "";
 
 // 妯℃�佹瀹炰緥
 const repairModalRef = ref();
@@ -86,12 +219,75 @@
 // 琛ㄦ牸澶氶�夋閫変腑椤�
 const multipleList = ref([]);
 
+const attachment = reactive({
+  visible: false,
+  loading: false,
+  deviceRepairId: undefined,
+  files: [],
+  fileList: [],
+  previewVisible: false,
+  previewUrl: "",
+});
+
+const getFileAccessUrl = (file = {}) => {
+  if (file?.link) {
+    if (String(file.link).startsWith('http')) return file.link;
+    return normalizeFileUrl(file.link);
+  }
+  return normalizeFileUrl(file?.url || '');
+};
+
+const normalizeFileUrl = (rawUrl = '') => {
+  let fileUrl = rawUrl || '';
+
+  if (fileUrl && 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 attachmentUploadAction = "/device/repair/uploadFile";
+const upload = reactive({
+  url: import.meta.env.VITE_APP_BASE_API + attachmentUploadAction,
+  headers: {
+    Authorization: "Bearer " + getToken(),
+  },
+});
+
 // 琛ㄦ牸閽╁瓙
-const { filters, columns, dataList, pagination, getTableData, resetFilters } =
-  usePaginationApi(
+const {
+  filters,
+  columns,
+  dataList,
+  pagination,
+  getTableData,
+  resetFilters,
+  onCurrentChange,
+} = usePaginationApi(
     getRepairPage,
     {
-      searchText: undefined,
+      deviceName: undefined,
+      deviceModel: undefined,
+      remark: undefined,
+      maintenanceName: undefined,
+      repairTimeStr: undefined,
+      maintenanceTimeStr: undefined,
     },
     [
       {
@@ -149,19 +345,40 @@
         dataType: "slot",
         slot: "operation",
         align: "center",
-        width: "200px",
+        width: "300px",
       },
     ]
-  );
+);
+
+// type === 1 缁翠慨 2鎶ヤ慨闂�
+const handleDateChange = (value, type) => {
+  filters.maintenanceTimeStr = null
+  filters.c = null
+  if (type === 1) {
+    if (value) {
+      filters.maintenanceTimeStr = dayjs(value).format("YYYY-MM-DD");
+    }
+  } else {
+    if (value) {
+      filters.repairTimeStr = dayjs(value).format("YYYY-MM-DD");
+    }
+  }
+  getTableData();
+};
 
 // 澶氶�夊悗鍋氫粈涔�
 const handleSelectionChange = (selectionList) => {
   multipleList.value = selectionList;
 };
 
+// 妫�鏌ラ�変腑鐨勮褰曚腑鏄惁鏈夊畬缁撶姸鎬佺殑
+const hasFinishedStatus = computed(() => {
+  return multipleList.value.some(item => item.status === 1)
+})
+
 // 鏂板鎶ヤ慨
 const addRepair = () => {
-  repairModalRef.value.openModal();
+  repairModalRef.value.openAdd();
 };
 
 // 缂栬緫鎶ヤ慨
@@ -170,22 +387,143 @@
 };
 
 // 鏂板缁翠慨
-const addMaintain = () => {
-  const row = multipleList.value[0];
+const addMaintain = (row) => {
   maintainModalRef.value.open(row.id, row);
+};
+
+const changePage = ({page, limit}) => {
+  pagination.currentPage = page;
+  pagination.pageSize = limit;
+  onCurrentChange(page);
 };
 
 // 鍗曡鍒犻櫎
 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;
+  });
+
+  if (hasFinished) {
+    ElMessage.warning('涓嶈兘鍒犻櫎鐘舵�佷负瀹岀粨鐨勮褰�');
+    return;
+  }
+
   ElMessageBox.confirm("纭鍒犻櫎鎶ヤ慨鏁版嵁, 姝ゆ搷浣滀笉鍙��?", "璀﹀憡", {
     confirmButtonText: "纭畾",
     cancelButtonText: "鍙栨秷",
     type: "warning",
   }).then(async () => {
-    const { code } = await delRepair(ids);
+    const {code} = await delRepair(ids);
     if (code === 200) {
       ElMessage.success("鍒犻櫎鎴愬姛");
       getTableData();
+    }
+  });
+};
+
+// 瀵煎嚭
+const handleOut = () => {
+  ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚鍑猴紝鏄惁纭瀵煎嚭锛�", "瀵煎嚭", {
+    confirmButtonText: "纭",
+    cancelButtonText: "鍙栨秷",
+    type: "warning",
+  })
+      .then(() => {
+        proxy.download("/device/repair/export", {}, "璁惧鎶ヤ慨.xlsx");
+      })
+      .catch(() => {
+        ElMessage.info("宸插彇娑�");
+      });
+};
+
+const openAttachment = async (row) => {
+  attachment.fileList = [];
+  attachment.visible = true;
+  attachment.deviceRepairId = row?.id;
+  await refreshAttachmentList();
+};
+
+const refreshAttachmentList = async () => {
+  if (!attachment.deviceRepairId) return;
+  attachment.loading = true;
+  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),
+    }));
+  } finally {
+    attachment.loading = false;
+  }
+};
+
+const onAttachmentClosed = () => {
+  attachment.loading = false;
+  attachment.deviceRepairId = undefined;
+  attachment.files = [];
+  attachment.fileList = [];
+  attachment.previewVisible = false;
+  attachment.previewUrl = "";
+};
+
+const handleAttachmentBeforeUpload = (file) => {
+  const isImage = ["image/png", "image/jpeg", "image/jpg"].includes(file.type);
+  if (!isImage) {
+    ElMessage.error("鍙兘涓婁紶 png/jpg/jpeg 鍥剧墖");
+    return false;
+  }
+  return true;
+};
+
+const handleAttachmentUploadSuccess = async (res) => {
+  if (res?.code === 200) {
+    ElMessage.success("涓婁紶鎴愬姛");
+    await refreshAttachmentList();
+  }
+};
+
+const handleAttachmentUploadError = () => {
+  ElMessage.error("涓婁紶澶辫触");
+};
+
+const handleAttachmentPreview = (file) => {
+  const rawUrl = file?.url || file?.response?.data?.link || file?.response?.data?.url || "";
+  if (!rawUrl) {
+    ElMessage.warning("鍥剧墖鍦板潃鏃犳晥锛屾棤娉曢瑙�");
+    return;
+  }
+  attachment.previewUrl = normalizeFileUrl(rawUrl);
+  attachment.previewVisible = true;
+};
+
+const handleAttachmentRemove = async (file) => {
+  // 浠呯Щ闄ゅ墠绔湭鍏ュ簱鏂囦欢鏃讹紝涓嶈皟鐢ㄥ垹闄ゆ帴鍙�
+  const matched = attachment.files.find((item) => item.id === file?.id)
+    || attachment.files.find((item) => item.name === file?.name);
+  if (!matched) return;
+  try {
+    await confirmDeleteAttachment(matched);
+  } finally {
+    // 鍙栨秷鍒犻櫎鏃讹紝el-upload 宸插厛绉婚櫎锛屽埛鏂颁竴娆′繚鎸佷笌鍚庣涓�鑷�
+    await refreshAttachmentList();
+  }
+};
+
+const confirmDeleteAttachment = (fileRow) => {
+  return ElMessageBox.confirm("纭鍒犻櫎璇ラ檮浠讹紵", "璀﹀憡", {
+    confirmButtonText: "纭畾",
+    cancelButtonText: "鍙栨秷",
+    type: "warning",
+  }).then(async () => {
+    const { code } = await deleteRepairFile(fileRow.id);
+    if (code === 200) {
+      ElMessage.success("鍒犻櫎鎴愬姛");
+      await refreshAttachmentList();
     }
   });
 };
@@ -199,9 +537,35 @@
 .table_list {
   margin-top: unset;
 }
+
 .actions {
   display: flex;
   justify-content: space-between;
   margin-bottom: 10px;
 }
+
+.attachment-wrap {
+  min-height: 240px;
+}
+
+.attachment-actions {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  margin-bottom: 12px;
+}
+
+.attachment-preview-wrap {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  min-height: 360px;
+}
+
+.attachment-preview-img {
+  max-width: 100%;
+  max-height: 70vh;
+  object-fit: contain;
+}
+
 </style>

--
Gitblit v1.9.3