From fc62e83f73b4b8ff4714f995da4eafbb7f525d1a Mon Sep 17 00:00:00 2001
From: gongchunyi <deslre0381@gmail.com>
Date: 星期二, 18 八月 2026 23:30:10 +0800
Subject: [PATCH] feat(table): 统一表格日期字段展示格式为YYYY-MM-DD

---
 src/views/equipmentManagement/spareParts/index.vue |  512 +++++++++++++++++++++++++++++++++++++++++++++-----------
 1 files changed, 410 insertions(+), 102 deletions(-)

diff --git a/src/views/equipmentManagement/spareParts/index.vue b/src/views/equipmentManagement/spareParts/index.vue
index eb0bdd5..131e1d5 100644
--- a/src/views/equipmentManagement/spareParts/index.vue
+++ b/src/views/equipmentManagement/spareParts/index.vue
@@ -1,134 +1,204 @@
 <template>
   <div class="spare-part-category">
-		<div class="search_form">
-			<el-form :inline="true" :model="queryParams" class="search-form">
-				<el-form-item label="澶囦欢鍚嶇О">
-					<el-input
-						v-model="queryParams.name"
-						placeholder="璇疯緭鍏ュ浠跺悕绉�"
-						clearable
-						style="width: 240px"
+    <el-tabs v-model="activeTab" @tab-change="handleTabChange">
+      <el-tab-pane label="澶囦欢鍒楄〃" name="list">
+        <div class="search_form">
+          <el-form :inline="true" :model="queryParams" class="search-form">
+            <el-form-item label="澶囦欢鍚嶇О">
+              <el-input
+                v-model="queryParams.name"
+                placeholder="璇疯緭鍏ュ浠跺悕绉�"
+                clearable
+                style="width: 240px"
+              />
+            </el-form-item>
+            <el-form-item>
+              <el-button type="primary" @click="handleQuery">鏌ヨ</el-button>
+              <el-button @click="resetQuery">閲嶇疆</el-button>
+            </el-form-item>
+          </el-form>
+          <div>
+            <el-button type="primary" @click="addCategory">鏂板</el-button>
+          </div>
+        </div>
+				<div class="table_list">
+					<PIMTable
+						rowKey="id"
+						:column="columns"
+						:tableData="renderTableData"
+						:tableLoading="loading"
+						:page="pagination"
+						:isShowPagination="true"
+						@pagination="handleSizeChange"
+					>
+						<template #status="{ row }">
+							<el-tag type="success" size="small">{{ row.status }}</el-tag>
+						</template>
+					</PIMTable>
+				</div>
+
+        <el-dialog title="鍒嗙被绠$悊" v-model="dialogVisible" width="60%">
+          <el-form :model="form" :rules="rules" ref="formRef" label-width="100px">
+            <el-form-item label="璁惧" prop="deviceLedgerIds">
+              <el-select
+                v-model="form.deviceLedgerIds"
+                placeholder="璇烽�夋嫨璁惧"
+                filterable
+                default-first-option
+                :reserve-keyword="false"
+                multiple
+                style="width: 100%"
+              >
+                <el-option
+                  v-for="(item, index) in deviceOptions"
+                  :key="index"
+                  :label="item.deviceName"
+                  :value="item.id"
+                ></el-option>
+              </el-select>
+            </el-form-item>
+            <el-form-item label="澶囦欢鍚嶇О" prop="name">
+              <el-input v-model="form.name"></el-input>
+            </el-form-item>
+            <el-form-item label="澶囦欢缂栧彿" prop="sparePartsNo">
+              <el-input v-model="form.sparePartsNo"></el-input>
+            </el-form-item>
+            <el-form-item label="鏁伴噺" prop="quantity">
+              <el-input type="number" v-model="form.quantity"></el-input>
+            </el-form-item>
+            <el-form-item label="鐘舵��" prop="status">
+              <el-select v-model="form.status" placeholder="璇烽�夋嫨鐘舵��">
+                <el-option label="姝e父" value="姝e父"></el-option>
+                <el-option label="绂佺敤" value="绂佺敤"></el-option>
+              </el-select>
+            </el-form-item>
+            <el-form-item label="鎻忚堪" prop="description">
+              <el-input v-model="form.description"></el-input>
+            </el-form-item>
+            <el-form-item label="浠锋牸" prop="price">
+              <el-input-number
+                v-model="form.price"
+                placeholder="璇疯緭鍏ヤ环鏍�"
+                :min="0"
+                :step="0.01"
+                :precision="2"
+                style="width: 100%"
+              ></el-input-number>
+            </el-form-item>
+          </el-form>
+          <template #footer>
+            <span class="dialog-footer">
+              <el-button type="primary" @click="submitForm" :loading="formLoading">纭畾</el-button>
+              <el-button @click="dialogVisible = false" :disabled="formLoading">鍙栨秷</el-button>
+            </span>
+          </template>
+        </el-dialog>
+      </el-tab-pane>
+
+      <el-tab-pane label="澶囦欢棰嗙敤璁板綍" name="usage">
+        <div class="search_form">
+          <el-form :inline="true" :model="usageQuery" class="search-form">
+            <el-form-item label="澶囦欢鍚嶇О">
+              <el-input v-model="usageQuery.sparePartsName" placeholder="璇疯緭鍏ュ浠跺悕绉�" clearable style="width: 240px" />
+            </el-form-item>
+            <el-form-item label="鏉ユ簮">
+              <el-select v-model="usageQuery.sourceType" placeholder="璇烽�夋嫨" clearable style="width: 200px">
+                <el-option label="缁翠慨" :value="0" />
+                <el-option label="淇濆吇" :value="1" />
+                <el-option label="鐩存帴鍑哄簱" :value="2" />
+              </el-select>
+            </el-form-item>
+            <el-form-item>
+              <el-button type="primary" @click="handleUsageQuery">鏌ヨ</el-button>
+              <el-button @click="resetUsageQuery">閲嶇疆</el-button>
+            </el-form-item>
+          </el-form>
+          <div>
+            <el-button type="primary" @click="openRequisitionDialog">鐩存帴鍑哄簱</el-button>
+          </div>
+        </div>
+				<div class="table_list">
+					<PIMTable
+						rowKey="rowKey"
+						:column="usageColumns"
+						:tableData="usageTableData"
+						:tableLoading="usageLoading"
+						:page="usagePagination"
+						:isShowPagination="true"
+						@pagination="handleUsagePageChange"
 					/>
-				</el-form-item>
-				<el-form-item>
-					<el-button type="primary" @click="handleQuery">鏌ヨ</el-button>
-					<el-button @click="resetQuery">閲嶇疆</el-button>
-				</el-form-item>
-			</el-form>
-			<div>
-				<el-button type="primary" @click="addCategory" >鏂板</el-button>
-			</div>
-		</div>
-    
-    <div class="table_list">
-      <el-table
-        v-loading="loading"
-        :data="renderTableData"
-        style="width: 100%; margin-top: 10px;"
-        border
-        row-key="id"
-      >
-      <el-table-column prop="deviceNameStr" label="璁惧鍚嶇О"  width="300"></el-table-column>
-        <el-table-column prop="name" label="澶囦欢鍚嶇О" width="200"></el-table-column>
-        <el-table-column prop="sparePartsNo" label="澶囦欢缂栧彿" width="200"></el-table-column>
-        <el-table-column prop="status" label="鐘舵��" width="100">
-          <template #default="{ row }">
-            <el-tag type="success" size="small">{{ row.status }}</el-tag>
-          </template>
-        </el-table-column>
-        <el-table-column prop="price" label="浠锋牸" width="140"></el-table-column>
-        <el-table-column prop="quantity" label="鏁伴噺" width="140"></el-table-column>
-        <el-table-column prop="description" label="鎻忚堪" width="150"></el-table-column>
-        <el-table-column label="鎿嶄綔" width="150" fixed="right" align="center">
-          <template #default="{ row }">
-            <el-button
-              link
-							type="primary"
-              @click="() => editCategory(row)"
-              :disabled="loading"
-            >
-              缂栬緫
-            </el-button>
-            <el-button
-							link
-              @click="() => deleteCategory(row.id)"
-              style="color: #f56c6c;"
-              :disabled="loading"
-            >
-              鍒犻櫎
-            </el-button>
-          </template>
-        </el-table-column>
-      </el-table>
-    </div>
-    <el-dialog title="鍒嗙被绠$悊" v-model="dialogVisible" width="60%">
-      <el-form :model="form" :rules="rules" ref="formRef" label-width="100px">
-        <el-form-item label="璁惧" prop="deviceLedgerIds">
+				</div>
+      </el-tab-pane>
+    </el-tabs>
+
+    <!-- 鐩存帴鍑哄簱寮圭獥 -->
+    <el-dialog title="鐩存帴鍑哄簱" v-model="requisitionDialogVisible" width="500px">
+      <el-form :model="requisitionForm" :rules="requisitionRules" ref="requisitionFormRef" label-width="100px">
+        <el-form-item label="璁惧閫夋嫨" prop="deviceLedgerId">
           <el-select
-            v-model="form.deviceLedgerIds"
+            v-model="requisitionForm.deviceLedgerId"
             placeholder="璇烽�夋嫨璁惧"
             filterable
-            default-first-option
-            :reserve-keyword="false"
-            multiple
+            clearable
             style="width: 100%"
           >
             <el-option
-              v-for="(item, index) in deviceOptions"
-              :key="index"
+              v-for="item in requisitionDeviceOptions"
+              :key="item.id"
               :label="item.deviceName"
               :value="item.id"
-            ></el-option>
+            />
           </el-select>
         </el-form-item>
-        <el-form-item label="澶囦欢鍚嶇О" prop="name">
-          <el-input v-model="form.name"></el-input>
-        </el-form-item>
-        <el-form-item label="澶囦欢缂栧彿" prop="sparePartsNo">
-          <el-input v-model="form.sparePartsNo"></el-input>
-        </el-form-item>
-        <el-form-item label="鏁伴噺" prop="quantity">
-          <el-input type="number" v-model="form.quantity"></el-input>
-        </el-form-item>
-        <el-form-item label="鐘舵��" prop="status">
-          <el-select v-model="form.status" placeholder="璇烽�夋嫨鐘舵��">
-            <el-option label="姝e父" value="姝e父"></el-option>
-            <el-option label="绂佺敤" value="绂佺敤"></el-option>
-          </el-select>
-        </el-form-item>
-        <el-form-item label="鎻忚堪" prop="description">
-          <el-input v-model="form.description"></el-input>
-        </el-form-item>
-        <el-form-item label="浠锋牸" prop="price">
-          <el-input-number
-            v-model="form.price"
-            placeholder="璇疯緭鍏ヤ环鏍�"
-            :min="0"
-            :step="0.01"
-            :precision="2"
+        <el-form-item label="澶囦欢閫夋嫨" prop="sparePartsId">
+          <el-select
+            v-model="requisitionForm.sparePartsId"
+            placeholder="璇烽�夋嫨澶囦欢"
+            filterable
+            clearable
             style="width: 100%"
-          ></el-input-number>
+            @change="onRequisitionSparePartChange"
+          >
+            <el-option
+              v-for="item in requisitionSparePartsOptions"
+              :key="item.id"
+              :label="`${item.name}锛堝簱瀛�: ${item.quantity}锛塦"
+              :value="item.id"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="棰嗙敤鏁伴噺" prop="quantity">
+          <el-input-number
+            v-model="requisitionForm.quantity"
+            :min="1"
+            :max="selectedSparePartMaxQty"
+            style="width: 100%"
+          />
+        </el-form-item>
+        <el-form-item label="澶囨敞" prop="remark">
+          <el-input v-model="requisitionForm.remark" placeholder="璇疯緭鍏ュ娉�" />
         </el-form-item>
       </el-form>
       <template #footer>
-        <span class="dialog-footer">
-          <el-button @click="dialogVisible = false" :disabled="formLoading">鍙栨秷</el-button>
-          <el-button type="primary" @click="submitForm" :loading="formLoading">纭畾</el-button>
-        </span>
+        <el-button type="primary" @click="submitRequisition" :loading="requisitionLoading">纭畾</el-button>
+        <el-button @click="requisitionDialogVisible = false">鍙栨秷</el-button>
       </template>
     </el-dialog>
   </div>
 </template>
 
 <script setup>
-import { ref, computed, onMounted, reactive, watch } from 'vue';
+import { ref, computed, onMounted, reactive } from 'vue';
 import { ElMessage, ElMessageBox } from 'element-plus';
 import { getSparePartsList, addSparePart, editSparePart, delSparePart } from "@/api/equipmentManagement/spareParts";
 import { getDeviceLedger } from "@/api/equipmentManagement/ledger";
+import PIMTable from "@/components/PIMTable/PIMTable.vue";
+import { getSparePartsUsagePage, outboundSpareParts } from "@/api/equipmentManagement/sparePartsUsage";
 
 // 鍔犺浇鐘舵��
 const loading = ref(false);
 const formLoading = ref(false);
+const activeTab = ref("list");
 // 瀵硅瘽妗嗘樉绀虹姸鎬�
 const dialogVisible = ref(false);
 // 缂栬緫 ID
@@ -147,6 +217,95 @@
 const queryParams = reactive({
   name: ''
 });
+// 鍒嗛〉鍙傛暟
+const pagination = reactive({
+  current: 1,
+  size: 10,
+  total: 0
+});
+
+// 澶囦欢棰嗙敤璁板綍
+const usageLoading = ref(false);
+const usageQuery = reactive({
+  sparePartsName: "",
+  sourceType: "",
+});
+const usagePagination = reactive({
+  current: 1,
+  size: 10,
+  total: 0,
+});
+const usageTableData = ref([]);
+const usageColumns = ref([
+  { label: "鏉ユ簮", prop: "sourceText" },
+  { label: "鍗曟嵁/璁板綍ID", prop: "sourceId" },
+  { label: "璁惧鍚嶇О", prop: "deviceName" },
+  { label: "澶囦欢鍚嶇О", prop: "sparePartsName" },
+  { label: "棰嗙敤鏁伴噺", prop: "quantity" },
+  { label: "鎿嶄綔浜�", prop: "operator" },
+  { label: "鏃堕棿", prop: "createTime" },
+]);
+
+const handleTabChange = async (name) => {
+  if (name === "usage") {
+    usagePagination.current = 1;
+    await fetchUsageData();
+  }
+};
+const columns = ref([
+  {
+    label: "璁惧鍚嶇О",
+    prop: "deviceNameStr",
+  },
+  {
+    label: "澶囦欢鍚嶇О",
+    prop: "name",
+  },
+  {
+    label: "澶囦欢缂栧彿",
+    prop: "sparePartsNo",
+  },
+  {
+    label: "鐘舵��",
+    prop: "status",
+    slot: "status",
+    dataType: "slot",
+  },
+  {
+    label: "浠锋牸",
+    prop: "price",
+  },
+  {
+    label: "鏁伴噺",
+    prop: "quantity",
+  },
+  {
+    label: "鎻忚堪",
+    prop: "description",
+  },
+  {
+    label: "鎿嶄綔",
+    prop: "operation",
+    width: 150,
+    fixed: 'right',
+    align: "center",
+    dataType: "action",
+    operation: [
+      {
+        name: "缂栬緫",
+        clickFun: (row) => {
+          editCategory(row)
+        },
+      },
+      {
+        name: "鍒犻櫎",
+        clickFun: (row) => {
+          deleteCategory(row.id)
+        },
+      },
+    ],
+  },
+]);
 // 琛ㄥ崟鏁版嵁
 const form = reactive({
   id:'',
@@ -215,7 +374,10 @@
 const fetchListData = async () => {
   loading.value = true;
   try {
-    const params = {};
+    const params = {
+      current: pagination.current,
+      size: pagination.size
+    };
     if (queryParams.name) {
       params.name = queryParams.name;
     }
@@ -223,6 +385,7 @@
     if (res.code === 200) {
       renderTableData.value = res.data.records || [];
       categories.value = res.data.records || [];
+      pagination.total = res.data.total || 0;
     }
   } catch (error) {
 		loading.value = false;
@@ -231,14 +394,65 @@
   }
 }
 
+const fetchUsageData = async () => {
+  usageLoading.value = true;
+  try {
+    const res = await getSparePartsUsagePage({
+      current: usagePagination.current,
+      size: usagePagination.size,
+      sparePartsName: usageQuery.sparePartsName || undefined,
+      sourceType: usageQuery.sourceType || undefined,
+    });
+    if (res?.code === 200) {
+      const records = res?.data?.records || [];
+      usagePagination.total = res?.data?.total || 0;
+      usageTableData.value = records.map((r, idx) => ({
+        rowKey: r.id ?? `${usagePagination.current}-${idx}`,
+        ...r,
+        sourceText: r.sourceText === "" ? "-" : r.sourceText,
+      }));
+    } else {
+      usagePagination.total = 0;
+      usageTableData.value = [];
+    }
+  } finally {
+    usageLoading.value = false;
+  }
+};
+
+const handleUsageQuery = () => {
+  usagePagination.current = 1;
+  fetchUsageData();
+};
+const resetUsageQuery = () => {
+  usageQuery.sparePartsName = "";
+  usageQuery.sourceType = "";
+  usagePagination.current = 1;
+  fetchUsageData();
+};
+const handleUsagePageChange = (obj) => {
+  usagePagination.current = obj.page;
+  usagePagination.size = obj.limit;
+  fetchUsageData();
+};
+
 // 鏌ヨ
 const handleQuery = () => {
+  pagination.current = 1;
   fetchListData();
 }
 
 // 閲嶇疆鏌ヨ
 const resetQuery = () => {
   queryParams.name = '';
+  pagination.current = 1;
+  fetchListData();
+}
+
+// 鍒嗛〉鍙樺寲
+const handleSizeChange = ({ page, limit }) => {
+  pagination.current = page;
+  pagination.size = limit;
   fetchListData();
 }
 
@@ -261,6 +475,7 @@
   form.status = '';
   form.description = '';
   form.deviceLedgerIds = [];
+  form.quantity = undefined;
   form.price = null;
   operationType.value = 'add'
   dialogVisible.value = true;
@@ -354,6 +569,93 @@
   }
 };
 
+// 鐩存帴鍑哄簱
+const requisitionDialogVisible = ref(false);
+const requisitionLoading = ref(false);
+const requisitionFormRef = ref(null);
+const requisitionDeviceOptions = ref([]);
+const requisitionSparePartsOptions = ref([]);
+const selectedSparePartMaxQty = ref(1);
+const requisitionForm = reactive({
+  deviceLedgerId: null,
+  sparePartsId: null,
+  quantity: 1,
+  remark: '',
+});
+const requisitionRules = {
+  sparePartsId: [{ required: true, message: '璇烽�夋嫨澶囦欢', trigger: 'change' }],
+  quantity: [{ required: true, message: '璇疯緭鍏ラ鐢ㄦ暟閲�', trigger: 'blur' }],
+};
+
+const loadRequisitionOptions = async () => {
+  try {
+    const [deviceRes, spareRes] = await Promise.all([
+      getDeviceLedger(),
+      getSparePartsList({ current: 1, size: 9999 }),
+    ]);
+    requisitionDeviceOptions.value = deviceRes.data || [];
+    requisitionSparePartsOptions.value = spareRes.data?.records || [];
+  } catch (e) {
+    ElMessage.error('鍔犺浇閫夐」澶辫触');
+  }
+};
+
+const openRequisitionDialog = async () => {
+  requisitionForm.deviceLedgerId = null;
+  requisitionForm.sparePartsId = null;
+  requisitionForm.quantity = 1;
+  requisitionForm.remark = '';
+  selectedSparePartMaxQty.value = 1;
+  await loadRequisitionOptions();
+  requisitionDialogVisible.value = true;
+};
+
+const onRequisitionSparePartChange = (val) => {
+  const selected = requisitionSparePartsOptions.value.find(item => item.id === val);
+  selectedSparePartMaxQty.value = selected ? selected.quantity : 1;
+  if (requisitionForm.quantity > selectedSparePartMaxQty.value) {
+    requisitionForm.quantity = selectedSparePartMaxQty.value;
+  }
+};
+
+const submitRequisition = async () => {
+  if (!requisitionFormRef.value) return;
+  try {
+    await requisitionFormRef.value.validate();
+  } catch {
+    return;
+  }
+  if (requisitionForm.quantity <= 0) {
+    ElMessage.warning('棰嗙敤鏁伴噺蹇呴』澶т簬0');
+    return;
+  }
+  const selected = requisitionSparePartsOptions.value.find(item => item.id === requisitionForm.sparePartsId);
+  if (selected && requisitionForm.quantity > selected.quantity) {
+    ElMessage.warning(`搴撳瓨涓嶈冻锛屽綋鍓嶅簱瀛樹负 ${selected.quantity}`);
+    return;
+  }
+  requisitionLoading.value = true;
+  try {
+    const res = await outboundSpareParts({
+      deviceLedgerId: requisitionForm.deviceLedgerId || undefined,
+      sparePartsId: requisitionForm.sparePartsId,
+      quantity: requisitionForm.quantity,
+      remark: requisitionForm.remark || undefined,
+    });
+    if (res.code === 200) {
+      ElMessage.success('棰嗙敤鎴愬姛');
+      requisitionDialogVisible.value = false;
+      fetchUsageData();
+    } else {
+      ElMessage.error(res.msg || '棰嗙敤澶辫触');
+    }
+  } catch {
+    ElMessage.error('棰嗙敤澶辫触');
+  } finally {
+    requisitionLoading.value = false;
+  }
+};
+
 // 缁勪欢鎸傝浇鏃惰幏鍙栧垪琛ㄦ暟鎹�
 onMounted(() => {
   fetchListData();
@@ -373,6 +675,12 @@
   margin-top: unset;
 }
 
+.pagination-container {
+  margin-top: 20px;
+  display: flex;
+  justify-content: flex-end;
+}
+
 .el-table__header-wrapper th {
   background-color: #f5f7fa;
   font-weight: 600;

--
Gitblit v1.9.3