From d99337be75724c5add989f0775e1bd188f7516f7 Mon Sep 17 00:00:00 2001
From: huminmin <mac@MacBook-Pro.local>
Date: 星期五, 03 四月 2026 13:22:11 +0800
Subject: [PATCH] 设备保养增加备件领用

---
 src/views/equipmentManagement/spareParts/index.vue             |   32 ++++------
 src/views/equipmentManagement/upkeep/Form/MaintenanceModal.vue |  115 ++++++++++++++++++++++++++++++++++++++
 src/api/equipmentManagement/sparePartsUsage.js                 |    2 
 3 files changed, 129 insertions(+), 20 deletions(-)

diff --git a/src/api/equipmentManagement/sparePartsUsage.js b/src/api/equipmentManagement/sparePartsUsage.js
index 9fca9d3..e9384aa 100644
--- a/src/api/equipmentManagement/sparePartsUsage.js
+++ b/src/api/equipmentManagement/sparePartsUsage.js
@@ -6,7 +6,7 @@
  */
 export const getSparePartsUsagePage = (params) => {
   return request({
-    url: "/sparePartsUsage/listPage",
+    url: "/sparePartsRequisitionRecord/listPage",
     method: "get",
     params,
   });
diff --git a/src/views/equipmentManagement/spareParts/index.vue b/src/views/equipmentManagement/spareParts/index.vue
index 116ddf3..06ca37d 100644
--- a/src/views/equipmentManagement/spareParts/index.vue
+++ b/src/views/equipmentManagement/spareParts/index.vue
@@ -98,12 +98,12 @@
         <div class="search_form">
           <el-form :inline="true" :model="usageQuery" class="search-form">
             <el-form-item label="澶囦欢鍚嶇О">
-              <el-input v-model="usageQuery.sparePartName" placeholder="璇疯緭鍏ュ浠跺悕绉�" clearable style="width: 240px" />
+              <el-input v-model="usageQuery.sparePartsName" placeholder="璇疯緭鍏ュ浠跺悕绉�" clearable style="width: 240px" />
             </el-form-item>
             <el-form-item label="鏉ユ簮">
-              <el-select v-model="usageQuery.source" placeholder="璇烽�夋嫨" clearable style="width: 200px">
-                <el-option label="缁翠慨" value="缁翠慨" />
-                <el-option label="淇濆吇" value="淇濆吇" />
+              <el-select v-model="usageQuery.sourceType" placeholder="璇烽�夋嫨" clearable style="width: 200px">
+                <el-option label="缁翠慨" :value="0" />
+                <el-option label="淇濆吇" :value="1" />
               </el-select>
             </el-form-item>
             <el-form-item>
@@ -167,8 +167,8 @@
 // 澶囦欢棰嗙敤璁板綍
 const usageLoading = ref(false);
 const usageQuery = reactive({
-  sparePartName: "",
-  source: "",
+  sparePartsName: "",
+  sourceType: "",
 });
 const usagePagination = reactive({
   current: 1,
@@ -180,10 +180,10 @@
   { label: "鏉ユ簮", prop: "sourceText" },
   { label: "鍗曟嵁/璁板綍ID", prop: "sourceId" },
   { label: "璁惧鍚嶇О", prop: "deviceName" },
-  { label: "澶囦欢鍚嶇О", prop: "sparePartName" },
-  { label: "棰嗙敤鏁伴噺", prop: "qty" },
+  { label: "澶囦欢鍚嶇О", prop: "sparePartsName" },
+  { label: "棰嗙敤鏁伴噺", prop: "quantity" },
   { label: "鎿嶄綔浜�", prop: "operator" },
-  { label: "鏃堕棿", prop: "time" },
+  { label: "鏃堕棿", prop: "createTime" },
 ]);
 
 const handleTabChange = async (name) => {
@@ -340,8 +340,8 @@
     const res = await getSparePartsUsagePage({
       current: usagePagination.current,
       size: usagePagination.size,
-      sparePartName: usageQuery.sparePartName || undefined,
-      source: usageQuery.source || undefined,
+      sparePartsName: usageQuery.sparePartsName || undefined,
+      sourceType: usageQuery.sourceType || undefined,
     });
     if (res?.code === 200) {
       const records = res?.data?.records || [];
@@ -349,11 +349,7 @@
       usageTableData.value = records.map((r, idx) => ({
         rowKey: r.id ?? `${usagePagination.current}-${idx}`,
         ...r,
-        sourceText:
-          r.source === "缁翠慨" ? "缁翠慨" :
-          r.source === "淇濆吇" ? "淇濆吇" :
-          r.source === "manual" ? "鎵嬪伐" :
-          (r.source || "-"),
+        sourceText: r.sourceText === "" ? "-" : r.sourceText,
       }));
     } else {
       usagePagination.total = 0;
@@ -369,8 +365,8 @@
   fetchUsageData();
 };
 const resetUsageQuery = () => {
-  usageQuery.sparePartName = "";
-  usageQuery.source = "";
+  usageQuery.sparePartsName = "";
+  usageQuery.sourceType = "";
   usagePagination.current = 1;
   fetchUsageData();
 };
diff --git a/src/views/equipmentManagement/upkeep/Form/MaintenanceModal.vue b/src/views/equipmentManagement/upkeep/Form/MaintenanceModal.vue
index c660840..e86b64a 100644
--- a/src/views/equipmentManagement/upkeep/Form/MaintenanceModal.vue
+++ b/src/views/equipmentManagement/upkeep/Form/MaintenanceModal.vue
@@ -38,6 +38,41 @@
           placeholder="璇疯緭鍏ヤ繚鍏荤粨鏋�"
           type="text" />
       </el-form-item>
+      <el-form-item label="璁惧澶囦欢">
+        <el-select v-model="form.sparePartsIds" :loading="loadingSparePartOptions" placeholder="璇烽�夋嫨璁惧澶囦欢" multiple filterable>
+          <el-option
+              v-for="item in sparePartOptions"
+              :key="item.id"
+              :label="item.name"
+              :value="item.id"
+          />
+        </el-select>
+      </el-form-item>
+
+      <el-form-item v-if="selectedSpareParts.length" label="棰嗙敤鏁伴噺">
+        <div style="width: 100%">
+          <div
+              v-for="item in selectedSpareParts"
+              :key="item.id"
+              style="display: flex; align-items: center; gap: 10px; margin-bottom: 10px;"
+          >
+            <div style="flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">
+              {{ item.name }}
+              <span v-if="item.quantity !== null && item.quantity !== undefined" style="color: #909399;">
+                锛堝簱瀛橈細{{ item.quantity }}锛�
+              </span>
+            </div>
+            <el-input-number
+                v-model="sparePartQtyMap[item.id]"
+                :min="1"
+                :max="item.quantity !== null && item.quantity !== undefined ? Number(item.quantity) : undefined"
+                :step="1"
+                controls-position="right"
+                style="width: 180px"
+            />
+          </div>
+        </div>
+      </el-form-item>
     </el-form>
   </FormDialog>
 </template>
@@ -49,6 +84,8 @@
 import dayjs from "dayjs";
 import useUserStore from "@/store/modules/user";
 import { ElMessage } from "element-plus";
+import {computed, ref} from "vue";
+import {getSparePartsList} from "@/api/equipmentManagement/spareParts.js";
 
 defineOptions({
   name: "淇濆吇妯℃�佹",
@@ -67,6 +104,17 @@
   maintenanceActuallyTime: undefined, // 瀹為檯淇濆吇鏃ユ湡
   maintenanceResult: undefined, // 淇濆吇缁撴灉
   status: 0, // 淇濆吇鐘舵��
+  sparePartsIds: [],
+});
+
+const sparePartOptions = ref([])
+const loadingSparePartOptions = ref(true)
+const sparePartQtyMap = ref({})
+
+const selectedSpareParts = computed(() => {
+  const ids = Array.isArray(form.sparePartsIds) ? form.sparePartsIds : [];
+  const set = new Set(ids.map((i) => String(i)));
+  return (sparePartOptions.value || []).filter((p) => set.has(String(p.id)));
 });
 
 const setForm = (data) => {
@@ -78,6 +126,19 @@
       : dayjs().format("YYYY-MM-DD HH:mm:ss");
   form.maintenanceResult = data.maintenanceResult;
   form.status = 1; // 榛樿鐘舵�佷负瀹岀粨
+  // multiple 閫夋嫨鍣ㄨ姹傛暟缁勶紱鍚庣甯歌繑鍥� "1,2,3"
+  if (Array.isArray(data?.sparePartsIds)) {
+    form.sparePartsIds = data.sparePartsIds.map((v) => Number(v)).filter((v) => Number.isFinite(v));
+  } else if (typeof data?.sparePartsIds === "string") {
+    form.sparePartsIds = data.sparePartsIds
+        .split(",")
+        .map((s) => Number(String(s).trim()))
+        .filter((v) => Number.isFinite(v));
+  } else if (typeof data?.sparePartsIds === "number") {
+    form.sparePartsIds = [data.sparePartsIds];
+  } else {
+    form.sparePartsIds = [];
+  }
 };
 
 /**
@@ -86,11 +147,41 @@
 const sendForm = async () => {
   loading.value = true;
   try {
-    const { code } = await addMaintenance({ id: planId.value, ...form });
+    // 棰嗙敤鏁伴噺鏍¢獙
+    if (Array.isArray(form.sparePartsIds) && form.sparePartsIds.length > 0) {
+      for (const partId of form.sparePartsIds) {
+        const qty = Number(sparePartQtyMap.value?.[partId]);
+        if (!Number.isFinite(qty) || qty <= 0) {
+          proxy?.$modal?.msgError?.("璇峰~鍐欏浠堕鐢ㄦ暟閲�");
+          return;
+        }
+        const part = sparePartOptions.value.find((p) => String(p.id) === String(partId));
+        const stock = part?.quantity;
+        if (stock !== null && stock !== undefined && Number.isFinite(Number(stock))) {
+          if (qty > Number(stock)) {
+            proxy?.$modal?.msgError?.(`澶囦欢銆�${part?.name || ""}銆嶉鐢ㄦ暟閲忎笉鑳借秴杩囧簱瀛橈紙${stock}锛塦);
+            return;
+          }
+        }
+      }
+    }
+    const data = {
+      id: planId.value,
+      ...form,
+      sparePartsIds: form.sparePartsIds ? form.sparePartsIds.join(",") : "",
+      sparePartsQty: form.sparePartsIds
+          ? form.sparePartsIds.map((id) => sparePartQtyMap.value?.[id] ?? 1).join(",")
+          : "",
+      sparePartsUseList: form.sparePartsIds
+          ? form.sparePartsIds.map((id) => ({ id, quantity: sparePartQtyMap.value?.[id] ?? 1 }))
+          : [],
+    }
+    const { code } = await addMaintenance(data);
     if (code == 200) {
       ElMessage.success("淇濆吇鎴愬姛");
       emits("ok");
       resetForm();
+      sparePartQtyMap.value = {};
       visible.value = false;
     }
   } finally {
@@ -98,13 +189,34 @@
   }
 };
 
+const fetchSparePartOptions = () => {
+  loadingSparePartOptions.value = true;
+  // 鍜屽浠剁鐞嗛〉涓�鑷达細/spareParts/listPage 鈫� res.data.records
+  getSparePartsList({ current: 1, size: 1000 })
+      .then((res) => {
+        if (res.code === 200) {
+          sparePartOptions.value = res?.data?.records || [];
+        } else {
+          sparePartOptions.value = [];
+        }
+      })
+      .catch(() => {
+        sparePartOptions.value = [];
+      })
+      .finally(() => {
+        loadingSparePartOptions.value = false;
+      });
+}
+
 const handleCancel = () => {
   resetForm();
+  sparePartQtyMap.value = {};
   visible.value = false;
 };
 
 const handleClose = () => {
   resetForm();
+  sparePartQtyMap.value = {};
   visible.value = false;
 };
 
@@ -112,6 +224,7 @@
   planId.value = id; // 淇濆瓨璁″垝淇濆吇璁板綍鐨刬d
   visible.value = true;
   await nextTick();
+  fetchSparePartOptions()
   setForm(row);
 };
 

--
Gitblit v1.9.3