From 63afcf7bd6eff23e4feee8b6d12053bd8e78abb8 Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期六, 09 五月 2026 17:38:10 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/dev_NEW_pro' into dev_NEW_pro

---
 src/views/inventoryManagement/dispatchLog/Record.vue                  |   11 
 src/views/customerService/feedbackRegistration/components/formDia.vue |  895 +++++++++++----------
 src/views/equipmentManagement/operationManagement/index.vue           |  132 +++
 src/views/customerService/expiryAfterSales/index.vue                  |    6 
 src/views/inventoryManagement/stockReport/index.vue                   | 1251 +++++++++++++++---------------
 src/views/inventoryManagement/receiptManagement/Record.vue            |   11 
 src/views/customerService/expiryAfterSales/components/formDia.vue     |   92 +
 7 files changed, 1,272 insertions(+), 1,126 deletions(-)

diff --git a/src/views/customerService/expiryAfterSales/components/formDia.vue b/src/views/customerService/expiryAfterSales/components/formDia.vue
index 2fe603b..d899b8d 100644
--- a/src/views/customerService/expiryAfterSales/components/formDia.vue
+++ b/src/views/customerService/expiryAfterSales/components/formDia.vue
@@ -20,7 +20,7 @@
 								v-model="form.productName"
 								placeholder="璇疯緭鍏ヤ骇鍝佸悕绉�"
 								clearable
-								:disabled="operationType === 'view'"
+								:disabled="isFieldDisabled('productName')"
 							/>
 						</el-form-item>
 					</el-col>
@@ -30,7 +30,7 @@
 								v-model="form.batchNumber"
 								placeholder="璇疯緭鍏ヤ骇鍝佹壒鍙�"
 								clearable
-								:disabled="operationType === 'view'"
+								:disabled="isFieldDisabled('batchNumber')"
 							/>
 						</el-form-item>
 					</el-col>
@@ -46,7 +46,7 @@
 								type="date"
 								placeholder="璇烽�夋嫨涓存湡鏃ユ湡"
 								clearable
-								:disabled="operationType === 'view'"
+								:disabled="isFieldDisabled('expiryDate')"
 							/>
 						</el-form-item>
 					</el-col>
@@ -57,7 +57,7 @@
 								:min="0"
 								placeholder="璇疯緭鍏ュ簱瀛樻暟閲�"
 								style="width: 100%"
-								:disabled="operationType === 'view'"
+								:disabled="isFieldDisabled('stockQuantity')"
 							/>
 						</el-form-item>
 					</el-col>
@@ -69,7 +69,7 @@
 								v-model="form.customerName"
 								placeholder="璇疯緭鍏ュ鎴峰悕绉�"
 								clearable
-								:disabled="operationType === 'view'"
+								:disabled="isFieldDisabled('customerName')"
 							/>
 						</el-form-item>
 					</el-col>
@@ -79,7 +79,7 @@
 								v-model="form.contactPhone"
 								placeholder="璇疯緭鍏ヨ仈绯荤數璇�"
 								clearable
-								:disabled="operationType === 'view'"
+								:disabled="isFieldDisabled('contactPhone')"
 							/>
 						</el-form-item>
 					</el-col>
@@ -91,7 +91,7 @@
 								v-model="form.problemDesc"
 								placeholder="璇疯緭鍏ラ棶棰樻弿杩�"
 								clearable
-								:disabled="operationType === 'view'"
+								:disabled="isFieldDisabled('problemDesc')"
 								type="textarea"
 								:rows="3"
 							/>
@@ -105,7 +105,7 @@
 								v-model="form.handlerId"
 								placeholder="璇烽�夋嫨澶勭悊浜�"
 								clearable
-								:disabled="operationType === 'view'"
+								:disabled="isFieldDisabled('handlerId')"
 								style="width: 100%"
 							>
 								<el-option
@@ -127,7 +127,7 @@
 								type="date"
 								placeholder="璇烽�夋嫨澶勭悊鏃ユ湡"
 								clearable
-								:disabled="operationType === 'view'"
+								:disabled="isFieldDisabled('handleDate')"
 							/>
 						</el-form-item>
 					</el-col>
@@ -139,7 +139,7 @@
 								v-model="form.handleResult"
 								placeholder="璇疯緭鍏ュ鐞嗙粨鏋�"
 								clearable
-								:disabled="operationType === 'view'"
+								:disabled="isFieldDisabled('handleResult')"
 								type="textarea"
 								:rows="3"
 							/>
@@ -175,6 +175,8 @@
 			return '鏂板涓存湡鍞悗';
 		case 'edit':
 			return '缂栬緫涓存湡鍞悗';
+		case 'handle':
+			return '澶勭悊涓存湡鍞悗';
 		case 'view':
 			return '鏌ョ湅涓存湡鍞悗';
 		default:
@@ -212,6 +214,13 @@
 })
 const { form, rules } = toRefs(data);
 const userList = ref([])
+const handleEditableFields = ["handlerId", "handleDate", "handleResult"];
+
+const isFieldDisabled = (field) => {
+	if (operationType.value === "view") return true;
+	if (operationType.value === "handle") return !handleEditableFields.includes(field);
+	return false;
+};
 
 // 鎵撳紑寮规
 const openDialog = (type, row) => {
@@ -242,7 +251,7 @@
 	} else {
 		// 缂栬緫鎴栨煡鐪嬫椂濉厖鏁版嵁
 		form.value = { ...row };
-		if (type === 'edit' && !form.value.handlerId) {
+		if (type === 'handle' && !form.value.handlerId) {
 			form.value.handlerId = userStore.id;
 			form.value.handleDate = getCurrentDate();
 		}
@@ -250,36 +259,49 @@
 }
 
 const submitForm = () => {
+	if (operationType.value === "handle") {
+		if (!form.value.handlerId || !form.value.handleDate || !form.value.handleResult) {
+			proxy.$modal.msgWarning("璇峰~鍐欏鐞嗕汉銆佸鐞嗘棩鏈熷拰澶勭悊缁撴灉");
+			return;
+		}
+		handleSubmit();
+		return;
+	}
 	proxy.$refs["formRef"].validate(valid => {
 		if (valid) {
-			const submitData = {
-				id: form.value.id,
-				productName: form.value.productName,
-				batchNumber: form.value.batchNumber,
-				expireDate: form.value.expiryDate,
-				stockQuantity: form.value.stockQuantity,
-				customerName: form.value.customerName,
-				contactPhone: form.value.contactPhone,
-				disRes: form.value.problemDesc,
-				status: form.value.status,
-				disposeUserId: form.value.handlerId,
-				disposeNickName: userList.value.find(item => item.userId === form.value.handlerId)?.nickName,
-				disposeResult: form.value.handleResult,
-				disDate: form.value.handleDate
-			};
-			
-			const apiCall = operationType.value === 'add' ? expiryAfterSalesAdd : expiryAfterSalesUpdate;
-			apiCall(submitData).then(() => {
-				proxy.$modal.msgSuccess(operationType.value === 'add' ? "鏂板鎴愬姛" : "鏇存柊鎴愬姛");
-				closeDia();
-			}).catch(error => {
-				console.error('鎻愪氦鏁版嵁澶辫触:', error);
-				proxy.$modal.msgError('鎻愪氦鏁版嵁澶辫触锛岃绋嶅悗閲嶈瘯');
-			});
+			handleSubmit();
 		}
 	});
 }
 
+const handleSubmit = () => {
+	const submitData = {
+		id: form.value.id,
+		productName: form.value.productName,
+		batchNumber: form.value.batchNumber,
+		expireDate: form.value.expiryDate,
+		stockQuantity: form.value.stockQuantity,
+		customerName: form.value.customerName,
+		contactPhone: form.value.contactPhone,
+		disRes: form.value.problemDesc,
+		status: operationType.value === "handle" ? 2 : form.value.status,
+		disposeUserId: form.value.handlerId,
+		disposeNickName: userList.value.find(item => item.userId === form.value.handlerId)?.nickName,
+		disposeResult: form.value.handleResult,
+		disDate: form.value.handleDate
+	};
+
+	const apiCall = operationType.value === 'add' ? expiryAfterSalesAdd : expiryAfterSalesUpdate;
+	apiCall(submitData).then(() => {
+		const successText = operationType.value === "add" ? "鏂板鎴愬姛" : operationType.value === "handle" ? "澶勭悊鎴愬姛" : "鏇存柊鎴愬姛";
+		proxy.$modal.msgSuccess(successText);
+		closeDia();
+	}).catch(error => {
+		console.error('鎻愪氦鏁版嵁澶辫触:', error);
+		proxy.$modal.msgError('鎻愪氦鏁版嵁澶辫触锛岃绋嶅悗閲嶈瘯');
+	});
+}
+
 // 鍏抽棴寮规
 const closeDia = () => {
 	proxy.resetForm("formRef");
diff --git a/src/views/customerService/expiryAfterSales/index.vue b/src/views/customerService/expiryAfterSales/index.vue
index c94e3dd..9966785 100644
--- a/src/views/customerService/expiryAfterSales/index.vue
+++ b/src/views/customerService/expiryAfterSales/index.vue
@@ -39,7 +39,7 @@
 				<el-button type="danger" @click="handleDelete">鍒犻櫎</el-button>
 			</div>
 		</div>
-		
+
 		<div class="table_list">
 			<PIMTable
 				rowKey="id"
@@ -60,7 +60,7 @@
 
 				<template #operation="{ row }">
 					<el-button type="primary" link @click="openForm('view', row)">鏌ョ湅</el-button>
-					<el-button type="primary" link @click="openForm('edit', row)" v-if="row.status === 1">缂栬緫</el-button>
+					<el-button type="primary" link @click="openForm('handle', row)" v-if="row.status === 1">澶勭悊</el-button>
 				</template>
 			</PIMTable>
 		</div>
@@ -201,7 +201,7 @@
 		current: page.value.current,
 		size: page.value.size
 	};
-	
+
 	expiryAfterSalesListPage(queryParams).then(res => {
 		// 鏄犲皠鍚庣杩斿洖鏁版嵁鍒板墠绔〃鏍�
 		tableData.value = res.data.records.map(item => ({
diff --git a/src/views/customerService/feedbackRegistration/components/formDia.vue b/src/views/customerService/feedbackRegistration/components/formDia.vue
index 5c9e565..790ddbe 100644
--- a/src/views/customerService/feedbackRegistration/components/formDia.vue
+++ b/src/views/customerService/feedbackRegistration/components/formDia.vue
@@ -1,490 +1,507 @@
 <template>
   <div>
-    <el-dialog
-        v-model="dialogFormVisible"
-        title="鏂板鍞悗鍗�"
-        width="90%"
-        @close="closeDia"
-    >
+    <el-dialog v-model="dialogFormVisible"
+               title="鏂板鍞悗鍗�"
+               width="90%"
+               @close="closeDia">
       <div>
         <span class="descriptions">鍩虹璧勬枡</span>
-        <el-form
-            :model="form"
-            label-width="140px"
-            label-position="top"
-            :rules="rules"
-            ref="formRef"
-        >
+        <el-form :model="form"
+                 label-width="140px"
+                 label-position="top"
+                 :rules="rules"
+                 ref="formRef">
           <el-row :gutter="30">
             <el-col :span="4">
-              <el-form-item label="瀹㈡埛鍚嶇О锛�" prop="customerName">
-                <el-select
-                    v-model="form.customerName"
-                    filterable
-                    @change="customerNameChange"
-                >
-                  <el-option
-                      v-for="item in customerNameOptions"
-                      :key="item.value"
-                      :label="item.label"
-                      :value="item.value"
-                  />
+              <el-form-item label="瀹㈡埛鍚嶇О锛�"
+                            prop="customerName">
+                <el-select v-model="form.customerName"
+                           filterable
+                           @change="customerNameChange">
+                  <el-option v-for="item in customerNameOptions"
+                             :key="item.value"
+                             :label="item.label"
+                             :value="item.value" />
                 </el-select>
               </el-form-item>
             </el-col>
             <el-col :span="4">
-              <el-form-item label="鍞悗绫诲瀷锛�" prop="serviceType">
-                <el-select
-                    v-model="form.serviceType"
-                    filterable
-                >
-                  <el-option
-                      v-for="dict in serviceTypeOptions"
-                      :key="dict.value"
-                      :label="dict.label"
-                      :value="dict.value"
-                  />
+              <el-form-item label="鍞悗绫诲瀷锛�"
+                            prop="serviceType">
+                <el-select v-model="form.serviceType"
+                           filterable>
+                  <el-option v-for="dict in serviceTypeOptions"
+                             :key="dict.value"
+                             :label="dict.label"
+                             :value="dict.value" />
                 </el-select>
               </el-form-item>
             </el-col>
             <el-col :span="4">
-              <el-form-item label="鍏宠仈閿�鍞崟鍙凤細" prop="salesContractNo">
-                <el-select
-                    v-model="form.salesContractNo"
-                    @change="associatedSalesOrderNumberChange"
-                    filterable
-                >
-                  <el-option
-                      v-for="item in associatedSalesOrderNumberOptions"
-                      :key="item.value"
-                      :label="item.label"
-                      :value="item.value"
-                  />
+              <el-form-item label="鍏宠仈閿�鍞崟鍙凤細"
+                            prop="salesContractNo">
+                <el-select v-model="form.salesContractNo"
+                           @change="associatedSalesOrderNumberChange"
+                           filterable>
+                  <el-option v-for="item in associatedSalesOrderNumberOptions"
+                             :key="item.value"
+                             :label="item.label"
+                             :value="item.value" />
                 </el-select>
               </el-form-item>
             </el-col>
             <el-col :span="4">
-              <el-form-item label="绱ф�ョ▼搴︼細" prop="urgency">
-                <el-select
-                    v-model="form.urgency"
-                    filterable
-                >
-                  <el-option
-                      v-for="dict in urgencyOptions"
-                      :key="dict.value"
-                      :label="dict.label"
-                      :value="dict.value"
-                  />
+              <el-form-item label="绱ф�ョ▼搴︼細"
+                            prop="urgency">
+                <el-select v-model="form.urgency"
+                           filterable>
+                  <el-option v-for="dict in urgencyOptions"
+                             :key="dict.value"
+                             :label="dict.label"
+                             :value="dict.value" />
                 </el-select>
               </el-form-item>
             </el-col>
             <el-col :span="4">
-              <el-form-item label="闂鎻忚堪锛�" prop="proDesc">
-                <el-input
-                    v-model="form.proDesc"
-                    placeholder="璇疯緭鍏ラ棶棰樻弿杩�"
-                />
+              <el-form-item label="闂鎻忚堪锛�"
+                            prop="proDesc">
+                <el-input v-model="form.proDesc"
+                          placeholder="璇疯緭鍏ラ棶棰樻弿杩�" />
               </el-form-item>
             </el-col>
           </el-row>
         </el-form>
         <hr>
-          <div style="padding-top: 20px">
-            <div style="display: flex; justify-content: space-between">
-              <span class="descriptions">鍏宠仈浜у搧</span>
-            <el-button
-              type="primary"
-              style="margin-right: 12px; margin-bottom: 10px"
-              @click="isShowProductSelectDialog = true"
-            >
+        <div style="padding-top: 20px">
+          <div style="display: flex; justify-content: space-between">
+            <span class="descriptions">鍏宠仈浜у搧</span>
+            <el-button type="primary"
+                       style="margin-right: 12px; margin-bottom: 10px"
+                       @click="isShowProductSelectDialog = true">
               閫夋嫨浜у搧
             </el-button>
-            </div>
-            <PIMTable
-                :isShowPagination="false"
-                rowKey="id"
-                :column="tableColumn"
-                :tableData="tableData"
-            >
-              <template #approveStatus="{ row }">
-                <el-tag :type="getApproveStatusType(row)" size="small">
-                  {{ getApproveStatusText(row) }}
-                </el-tag>
-              </template>
-              <template #shippingStatus="{ row }">
-                <el-tag :type="getShippingStatusType(row)" size="small">
-                  {{ getShippingStatusText(row) }}
-                </el-tag>
-              </template>
-            </PIMTable>
           </div>
+          <PIMTable :isShowPagination="false"
+                    rowKey="id"
+                    :column="tableColumn"
+                    :tableData="tableData">
+            <template #approveStatus="{ row }">
+              <el-tag :type="getApproveStatusType(row)"
+                      size="small">
+                {{ getApproveStatusText(row) }}
+              </el-tag>
+            </template>
+            <template #shippingStatus="{ row }">
+              <el-tag :type="getShippingStatusType(row)"
+                      size="small">
+                {{ getShippingStatusText(row) }}
+              </el-tag>
+            </template>
+          </PIMTable>
+        </div>
       </div>
-			<template #footer>
-				<div class="dialog-footer">
-					<el-button type="primary" @click="submitForm">纭</el-button>
-					<el-button @click="closeDia">鍙栨秷</el-button>
-				</div>
-			</template>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button type="primary"
+                     @click="submitForm">纭</el-button>
+          <el-button @click="closeDia">鍙栨秷</el-button>
+        </div>
+      </template>
     </el-dialog>
     <!-- 閫夋嫨浜у搧寮圭獥 -->
-    <ProductSelectDialog
-      v-model="isShowProductSelectDialog"
-      :products="currentSalesOrderProducts"
-      :selected-ids="currentSelectedProductIds"
-      @confirm="handleSelectProducts"
-    />
+    <ProductSelectDialog v-model="isShowProductSelectDialog"
+                         :products="currentSalesOrderProducts"
+                         :selected-ids="currentSelectedProductIds"
+                         @confirm="handleSelectProducts" />
   </div>
 </template>
 
 <script setup>
-import { ref, reactive, toRefs, getCurrentInstance, computed } from "vue";
-import ProductSelectDialog from "./ProductSelectDialog.vue";
-import useUserStore from "@/store/modules/user.js";
-import {userListNoPageByTenantId} from "@/api/system/user.js";
-import {afterSalesServiceAdd, afterSalesServiceUpdate, getAllCustomerList, getSalesLedger } from "@/api/customerService/index.js";
-import { getCurrentDate } from "@/utils/index.js";
-const { proxy } = getCurrentInstance()
-const emit = defineEmits(['close'])
-const dialogFormVisible = ref(false);
-const operationType = ref('')
-const formRef = ref(null)
-const customerNameOptions = ref([])
-const userStore = useUserStore();
+  import { ref, reactive, toRefs, getCurrentInstance, computed } from "vue";
+  import ProductSelectDialog from "./ProductSelectDialog.vue";
+  import useUserStore from "@/store/modules/user.js";
+  import { userListNoPageByTenantId } from "@/api/system/user.js";
+  import {
+    afterSalesServiceAdd,
+    afterSalesServiceUpdate,
+    getAllCustomerList,
+    getSalesLedger,
+  } from "@/api/customerService/index.js";
+  import { getCurrentDate } from "@/utils/index.js";
+  const { proxy } = getCurrentInstance();
+  const emit = defineEmits(["close"]);
+  const dialogFormVisible = ref(false);
+  const operationType = ref("");
+  const formRef = ref(null);
+  const customerNameOptions = ref([]);
+  const userStore = useUserStore();
 
-const data = reactive({
-	form: {
-    topic: "",
-    serviceType: "",
-    urgency: "",
-    salesLedgerId: null,
-    productModelIds: "",
-    customerId: null,
-    salesContractNo: "",
-    proDesc: "",
-    customerName: ""
-	},
-	rules: {
-    customerName: [{required: true, message: "璇烽�夋嫨瀹㈡埛鍚嶇О", trigger: "change"}],
-    serviceType: [{required: true, message: "璇烽�夋嫨鍞悗绫诲瀷", trigger: "change"}],
-    urgency: [{required: true, message: "璇烽�夋嫨绱ф�ョ▼搴�", trigger: "change"}],
-		feedbackDate: [{required: true, message: "璇烽�夋嫨", trigger: "change"}],
-	}
-})
-
-// 鑷畾涔夋牎楠屽嚱鏁帮細鍒ゆ柇鏄惁闇�瑕佹牎楠屽敭鍚庣紪鍙�
-
-const { form, rules } = toRefs(data);
-const userList = ref([])
-
-const formatCurrency = (val) => {
-  if (val === null || val === undefined || val === '') return '-'
-  const num = Number(val)
-  return Number.isFinite(num) ? num.toFixed(2) : '-'
-}
-
-const { post_sale_waiting_list, degree_of_urgency } = proxy.useDict(
-  "post_sale_waiting_list",
-  "degree_of_urgency"
-);
-
-const serviceTypeOptions = computed(() => post_sale_waiting_list?.value || []);
-const urgencyOptions = computed(() => degree_of_urgency?.value || []);
-
-const getProductRowId = (row) => {
-  return row?.id ?? row?.productModelId ?? row?.modelId ?? `${row?.productCategory || row?.productName || ""}-${row?.specificationModel || row?.model || ""}-${row?.unit || ""}`
-}
-
-const normalizeProductRow = (row) => {
-  return {
-    ...row,
-    id: getProductRowId(row),
-    productCategory: row?.productCategory ?? row?.productName ?? '',
-    specificationModel: row?.specificationModel ?? row?.model ?? '',
-    unit: row?.unit ?? '',
-    approveStatus: row?.approveStatus ?? null,
-    shippingStatus: row?.shippingStatus ?? '',
-    expressCompany: row?.expressCompany ?? '',
-    expressNumber: row?.expressNumber ?? '',
-    shippingCarNumber: row?.shippingCarNumber ?? '',
-    shippingDate: row?.shippingDate ?? '',
-    quantity: row?.quantity ?? 0,
-    taxRate: row?.taxRate ?? 0,
-    taxInclusiveUnitPrice: row?.taxInclusiveUnitPrice ?? 0,
-    taxInclusiveTotalPrice: row?.taxInclusiveTotalPrice ?? 0,
-    taxExclusiveTotalPrice: row?.taxExclusiveTotalPrice ?? 0,
-    noQuantity: row?.noQuantity ?? 0,
-  }
-}
-
-const tableColumn = ref([
-  { label: "浜у搧澶х被", prop: "productCategory" },
-  { label: "瑙勬牸鍨嬪彿", prop: "specificationModel" },
-  { label: "鍗曚綅", prop: "unit" },
-  {
-    label: "浜у搧鐘舵��",
-    prop: "approveStatus",
-    width: 100,
-    align: "center",
-    dataType: "slot",
-    slot: "approveStatus",
-  },
-  {
-    label: "鍙戣揣鐘舵��",
-    align: "center",
-    width: 140,
-    dataType: "slot",
-    slot: "shippingStatus",
-  },
-  { label: "蹇�掑叕鍙�", prop: "expressCompany", width: 140 },
-  { label: "蹇�掑崟鍙�", prop: "expressNumber", width: 160 },
-  { label: "鍙戣揣杞︾墝", prop: "shippingCarNumber", minWidth: 100, align: "center" },
-  { label: "鍙戣揣鏃ユ湡", prop: "shippingDate", minWidth: 100, align: "center" },
-  { label: "鏁伴噺", prop: "quantity", width: 100 },
-  { label: "绋庣巼(%)", prop: "taxRate", width: 100 },
-  {
-    label: "鍚◣鍗曚环(鍏�)",
-    prop: "taxInclusiveUnitPrice",
-    width: 160,
-    formatData: formatCurrency,
-  },
-  {
-    label: "鍚◣鎬讳环(鍏�)",
-    prop: "taxInclusiveTotalPrice",
-    width: 160,
-    formatData: formatCurrency,
-  },
-  {
-    label: "涓嶅惈绋庢�讳环(鍏�)",
-    prop: "taxExclusiveTotalPrice",
-    width: 160,
-    formatData: formatCurrency,
-  },
-  {
-    dataType: "action",
-    label: "鎿嶄綔",
-    align: "center",
-    fixed: 'right',
-    operation: [
-      {
-        name: "鍒犻櫎",
-        type: "text",
-        clickFun: (row) => {
-          tableData.value = tableData.value.filter(i => getProductRowId(i) !== getProductRowId(row))
-        },
-
-      },
-    ],
-  },
-])
-const tableData = ref([])
-// 閫夋嫨浜у搧寮圭獥
-const isShowProductSelectDialog = ref(false)
-const handleSelectProducts = (rows) => {
-  if (!Array.isArray(rows)) return
-  const existingIds = new Set(tableData.value.map(i => String(getProductRowId(i))))
-  const mapped = rows
-    .map(normalizeProductRow)
-    .filter(r => !existingIds.has(String(getProductRowId(r))))
-  tableData.value = tableData.value.concat(mapped)
-}
-const currentSelectedProductIds = computed(() => {
-  return tableData.value.map(item => getProductRowId(item)).filter(item => item !== undefined && item !== null && item !== '')
-})
-
-const associatedSalesOrderNumberChange = () => {
-  const opt = associatedSalesOrderNumberOptions.value.find(
-    (item) => item.value === form.value.salesContractNo
-  )
-  tableData.value = (opt?.productData || []).map(normalizeProductRow)
-  form.value.salesLedgerId = opt?.id || null
-}
-
-const associatedSalesOrderNumberOptions = ref([])
-
-const currentSalesOrderProducts = computed(() => {
-  const opt = associatedSalesOrderNumberOptions.value.find(
-    (item) => item.value === form.value.salesContractNo
-  )
-  return (opt?.productData || []).map(normalizeProductRow)
-})
-
-const customerNameChange = (val) => {
-  form.value.salesContractNo = "";
-  form.value.salesLedgerId = null;
-  tableData.value = [];
-  associatedSalesOrderNumberOptions.value = [];
-  const opt = customerNameOptions.value.find(item => item.value === val);
-  if (opt) {
-    form.value.customerId = opt.id;
-  } else {
-    form.value.customerId = null;
-  }
-  getSalesLedger({
-    customerName: form.value.customerName
-  }).then(res => {
-    if(res.code === 200){
-      associatedSalesOrderNumberOptions.value = res.data.records.map(item => ({
-        label: item.salesContractNo,
-        value: item.salesContractNo,
-        productData:item.productData,
-        id: item.id
-      }))
-    }
-  })
-}
-
-const getApproveStatusText = (row) => {
-  if (!row) return '涓嶈冻'
-  if (row.approveStatus === 1 && (!row.shippingDate || !row.shippingCarNumber)) {
-    return '鍏呰冻'
-  }
-  if (row.approveStatus === 0 && (row.shippingDate || row.shippingCarNumber)) {
-    return '宸插嚭搴�'
-  }
-  return '涓嶈冻'
-}
-
-const getApproveStatusType = (row) => {
-  const statusText = getApproveStatusText(row)
-  return statusText === '涓嶈冻' ? 'danger' : 'success'
-}
-
-const getShippingStatusText = (row) => {
-  if (!row) return '寰呭彂璐�'
-  if (row.shippingDate || row.shippingCarNumber) {
-    return '宸插彂璐�'
-  }
-  const status = row.shippingStatus
-  if (status === null || status === undefined || status === '') {
-    return '寰呭彂璐�'
-  }
-  const map = {
-    '寰呭彂璐�': '寰呭彂璐�',
-    '寰呭鏍�': '寰呭鏍�',
-    '瀹℃牳涓�': '瀹℃牳涓�',
-    '瀹℃牳鎷掔粷': '瀹℃牳鎷掔粷',
-    '瀹℃牳閫氳繃': '瀹℃牳閫氳繃',
-    '宸插彂璐�': '宸插彂璐�'
-  }
-  return map[String(status).trim()] || '寰呭彂璐�'
-}
-
-const getShippingStatusType = (row) => {
-  if (!row) return 'info'
-  if (row.shippingDate || row.shippingCarNumber) {
-    return 'success'
-  }
-  const status = row.shippingStatus
-  if (status === null || status === undefined || status === '') {
-    return 'info'
-  }
-  const map = {
-    '寰呭彂璐�': 'info',
-    '寰呭鏍�': 'warning',
-    '瀹℃牳涓�': 'warning',
-    '瀹℃牳鎷掔粷': 'danger',
-    '瀹℃牳閫氳繃': 'success',
-    '宸插彂璐�': 'success'
-  }
-  return map[String(status).trim()] || 'info'
-}
-
-// 鎵撳紑寮规
-const openDialog =async (type, row) => {
-  // 璇锋眰澶氫釜鎺ュ彛锛岃幏鍙栨暟鎹�
-  let res = await getAllCustomerList({
-    current: 1,
-  size: 1000,
-  total: 0,
+  const data = reactive({
+    form: {
+      topic: "",
+      serviceType: "",
+      urgency: "",
+      salesLedgerId: null,
+      productModelIds: "",
+      customerId: null,
+      salesContractNo: "",
+      proDesc: "",
+      customerName: "",
+    },
+    rules: {
+      customerName: [
+        { required: true, message: "璇烽�夋嫨瀹㈡埛鍚嶇О", trigger: "change" },
+      ],
+      serviceType: [
+        { required: true, message: "璇烽�夋嫨鍞悗绫诲瀷", trigger: "change" },
+      ],
+      urgency: [{ required: true, message: "璇烽�夋嫨绱ф�ョ▼搴�", trigger: "change" }],
+      feedbackDate: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
+    },
   });
-  if(res.records){
-    customerNameOptions.value = res.records.map(item => ({
-      label: item.customerName,
-      value: item.customerName,
-      id: item.id
-    }));
-  }
 
+  // 鑷畾涔夋牎楠屽嚱鏁帮細鍒ゆ柇鏄惁闇�瑕佹牎楠屽敭鍚庣紪鍙�
 
-  operationType.value = type;
-  dialogFormVisible.value = true;
-	form.value = {}
-	proxy.resetForm("formRef");
-	form.value.checkUserId = userStore.id;
-	form.value.feedbackDate = getCurrentDate();
-  // 鏂板鏃舵竻绌哄凡閫夊叧鑱斾骇鍝�
-  if (type === "add") {
-    tableData.value = []
-  }
-	userListNoPageByTenantId().then((res) => {
-		userList.value = res.data;
-	});
-	if (type === "edit") {
-		form.value = {...row}
-    if (form.value.customerName) {
-      const res = await getSalesLedger({ customerName: form.value.customerName })
-      if (res?.code === 200) {
-        console.log(res)
-        associatedSalesOrderNumberOptions.value = (res.data?.records || []).map(item => ({
+  const { form, rules } = toRefs(data);
+  const userList = ref([]);
+
+  const formatCurrency = val => {
+    if (val === null || val === undefined || val === "") return "-";
+    const num = Number(val);
+    return Number.isFinite(num) ? num.toFixed(2) : "-";
+  };
+
+  const { post_sale_waiting_list, degree_of_urgency } = proxy.useDict(
+    "post_sale_waiting_list",
+    "degree_of_urgency"
+  );
+
+  const serviceTypeOptions = computed(() => post_sale_waiting_list?.value || []);
+  const urgencyOptions = computed(() => degree_of_urgency?.value || []);
+
+  const getProductRowId = row => {
+    return (
+      row?.id ??
+      row?.productModelId ??
+      row?.modelId ??
+      `${row?.productCategory || row?.productName || ""}-${
+        row?.specificationModel || row?.model || ""
+      }-${row?.unit || ""}`
+    );
+  };
+
+  const normalizeProductRow = row => {
+    return {
+      ...row,
+      id: getProductRowId(row),
+      productCategory: row?.productCategory ?? row?.productName ?? "",
+      specificationModel: row?.specificationModel ?? row?.model ?? "",
+      unit: row?.unit ?? "",
+      approveStatus: row?.approveStatus ?? null,
+      shippingStatus: row?.shippingStatus ?? "",
+      expressCompany: row?.expressCompany ?? "",
+      expressNumber: row?.expressNumber ?? "",
+      shippingCarNumber: row?.shippingCarNumber ?? "",
+      shippingDate: row?.shippingDate ?? "",
+      quantity: row?.quantity ?? 0,
+      taxRate: row?.taxRate ?? 0,
+      taxInclusiveUnitPrice: row?.taxInclusiveUnitPrice ?? 0,
+      taxInclusiveTotalPrice: row?.taxInclusiveTotalPrice ?? 0,
+      taxExclusiveTotalPrice: row?.taxExclusiveTotalPrice ?? 0,
+      noQuantity: row?.noQuantity ?? 0,
+    };
+  };
+
+  const tableColumn = ref([
+    { label: "浜у搧澶х被", prop: "productCategory" },
+    { label: "瑙勬牸鍨嬪彿", prop: "specificationModel" },
+    { label: "鍗曚綅", prop: "unit" },
+    {
+      label: "浜у搧鐘舵��",
+      prop: "approveStatus",
+      width: 100,
+      align: "center",
+      dataType: "slot",
+      slot: "approveStatus",
+    },
+    {
+      label: "鍙戣揣鐘舵��",
+      align: "center",
+      width: 140,
+      dataType: "slot",
+      slot: "shippingStatus",
+    },
+    { label: "蹇�掑叕鍙�", prop: "expressCompany", width: 140 },
+    { label: "蹇�掑崟鍙�", prop: "expressNumber", width: 160 },
+    {
+      label: "鍙戣揣杞︾墝",
+      prop: "shippingCarNumber",
+      minWidth: 100,
+      align: "center",
+    },
+    { label: "鍙戣揣鏃ユ湡", prop: "shippingDate", minWidth: 100, align: "center" },
+    { label: "鏁伴噺", prop: "quantity", width: 100 },
+    { label: "绋庣巼(%)", prop: "taxRate", width: 100 },
+    {
+      label: "鍚◣鍗曚环(鍏�)",
+      prop: "taxInclusiveUnitPrice",
+      width: 160,
+      formatData: formatCurrency,
+    },
+    {
+      label: "鍚◣鎬讳环(鍏�)",
+      prop: "taxInclusiveTotalPrice",
+      width: 160,
+      formatData: formatCurrency,
+    },
+    {
+      label: "涓嶅惈绋庢�讳环(鍏�)",
+      prop: "taxExclusiveTotalPrice",
+      width: 160,
+      formatData: formatCurrency,
+    },
+    {
+      dataType: "action",
+      label: "鎿嶄綔",
+      align: "center",
+      fixed: "right",
+      operation: [
+        {
+          name: "鍒犻櫎",
+          type: "text",
+          clickFun: row => {
+            tableData.value = tableData.value.filter(
+              i => getProductRowId(i) !== getProductRowId(row)
+            );
+          },
+        },
+      ],
+    },
+  ]);
+  const tableData = ref([]);
+  // 閫夋嫨浜у搧寮圭獥
+  const isShowProductSelectDialog = ref(false);
+  const handleSelectProducts = rows => {
+    if (!Array.isArray(rows)) return;
+    const existingIds = new Set(
+      tableData.value.map(i => String(getProductRowId(i)))
+    );
+    const mapped = rows
+      .map(normalizeProductRow)
+      .filter(r => !existingIds.has(String(getProductRowId(r))));
+    tableData.value = tableData.value.concat(mapped);
+  };
+  const currentSelectedProductIds = computed(() => {
+    return tableData.value
+      .map(item => getProductRowId(item))
+      .filter(item => item !== undefined && item !== null && item !== "");
+  });
+
+  const associatedSalesOrderNumberChange = () => {
+    const opt = associatedSalesOrderNumberOptions.value.find(
+      item => item.value === form.value.salesContractNo
+    );
+    tableData.value = (opt?.productData || []).map(normalizeProductRow);
+    form.value.salesLedgerId = opt?.id || null;
+  };
+
+  const associatedSalesOrderNumberOptions = ref([]);
+
+  const currentSalesOrderProducts = computed(() => {
+    const opt = associatedSalesOrderNumberOptions.value.find(
+      item => item.value === form.value.salesContractNo
+    );
+    return (opt?.productData || []).map(normalizeProductRow);
+  });
+
+  const customerNameChange = val => {
+    form.value.salesContractNo = "";
+    form.value.salesLedgerId = null;
+    tableData.value = [];
+    associatedSalesOrderNumberOptions.value = [];
+    const opt = customerNameOptions.value.find(item => item.value === val);
+    if (opt) {
+      form.value.customerId = opt.id;
+    } else {
+      form.value.customerId = null;
+    }
+    getSalesLedger({
+      customerName: form.value.customerName,
+    }).then(res => {
+      if (res.code === 200) {
+        associatedSalesOrderNumberOptions.value = res.data.records.map(item => ({
           label: item.salesContractNo,
           value: item.salesContractNo,
           productData: item.productData,
-          id: item.id
-        }))
+          id: item.id,
+        }));
       }
+    });
+  };
+
+  const getApproveStatusText = row => {
+    if (!row) return "涓嶈冻";
+    if (
+      row.approveStatus === 1 &&
+      (!row.shippingDate || !row.shippingCarNumber)
+    ) {
+      return "鍏呰冻";
     }
-    console.log(form.value)
-	}
-}
-const submitForm = () => {
-	proxy.$refs["formRef"].validate(valid => {
-		if (valid) {
-      // 鍖归厤浜у搧鍨嬪彿IDs
-      form.value.productModelIds = tableData.value.map(item => item.id).join(",")
-			if (operationType.value === "add") {
-				afterSalesServiceAdd(form.value).then(response => {
-					proxy.$modal.msgSuccess("鏂板鎴愬姛")
-					closeDia()
-				})
-			} else {
-				afterSalesServiceUpdate(form.value).then(response => {
-					proxy.$modal.msgSuccess("淇敼鎴愬姛")
-					closeDia()
-				})
-			}
-		}
-	})
-}
-// 鍏抽棴寮规
-const closeDia = () => {
-	proxy.resetForm("formRef");
-  dialogFormVisible.value = false;
-  emit('close')
-};
-defineExpose({
-  openDialog,
-});
+    if (row.approveStatus === 0 && (row.shippingDate || row.shippingCarNumber)) {
+      return "宸插嚭搴�";
+    }
+    return "涓嶈冻";
+  };
+
+  const getApproveStatusType = row => {
+    const statusText = getApproveStatusText(row);
+    return statusText === "涓嶈冻" ? "danger" : "success";
+  };
+
+  const getShippingStatusText = row => {
+    if (!row) return "寰呭彂璐�";
+    if (row.shippingDate || row.shippingCarNumber) {
+      return "宸插彂璐�";
+    }
+    const status = row.shippingStatus;
+    if (status === null || status === undefined || status === "") {
+      return "寰呭彂璐�";
+    }
+    const map = {
+      寰呭彂璐�: "寰呭彂璐�",
+      寰呭鏍�: "寰呭鏍�",
+      瀹℃牳涓�: "瀹℃牳涓�",
+      瀹℃牳鎷掔粷: "瀹℃牳鎷掔粷",
+      瀹℃牳閫氳繃: "瀹℃牳閫氳繃",
+      宸插彂璐�: "宸插彂璐�",
+    };
+    return map[String(status).trim()] || "寰呭彂璐�";
+  };
+
+  const getShippingStatusType = row => {
+    if (!row) return "info";
+    if (row.shippingDate || row.shippingCarNumber) {
+      return "success";
+    }
+    const status = row.shippingStatus;
+    if (status === null || status === undefined || status === "") {
+      return "info";
+    }
+    const map = {
+      寰呭彂璐�: "info",
+      寰呭鏍�: "warning",
+      瀹℃牳涓�: "warning",
+      瀹℃牳鎷掔粷: "danger",
+      瀹℃牳閫氳繃: "success",
+      宸插彂璐�: "success",
+    };
+    return map[String(status).trim()] || "info";
+  };
+
+  // 鎵撳紑寮规
+  const openDialog = async (type, row) => {
+    // 璇锋眰澶氫釜鎺ュ彛锛岃幏鍙栨暟鎹�
+    let res = await getAllCustomerList({
+      current: 1,
+      size: 1000,
+      total: 0,
+    });
+    console.log(res, "res");
+
+    if (res.data.records) {
+      customerNameOptions.value = res.data.records.map(item => ({
+        label: item.customerName,
+        value: item.customerName,
+        id: item.id,
+      }));
+    } else {
+    }
+
+    operationType.value = type;
+    dialogFormVisible.value = true;
+    form.value = {};
+    proxy.resetForm("formRef");
+    form.value.checkUserId = userStore.id;
+    form.value.feedbackDate = getCurrentDate();
+    // 鏂板鏃舵竻绌哄凡閫夊叧鑱斾骇鍝�
+    if (type === "add") {
+      tableData.value = [];
+    }
+    userListNoPageByTenantId().then(res => {
+      userList.value = res.data;
+    });
+    if (type === "edit") {
+      form.value = { ...row };
+      if (form.value.customerName) {
+        const res = await getSalesLedger({
+          customerName: form.value.customerName,
+        });
+        if (res?.code === 200) {
+          console.log(res);
+          associatedSalesOrderNumberOptions.value = (res.data?.records || []).map(
+            item => ({
+              label: item.salesContractNo,
+              value: item.salesContractNo,
+              productData: item.productData,
+              id: item.id,
+            })
+          );
+        }
+      }
+      console.log(form.value);
+    }
+  };
+  const submitForm = () => {
+    proxy.$refs["formRef"].validate(valid => {
+      if (valid) {
+        // 鍖归厤浜у搧鍨嬪彿IDs
+        form.value.productModelIds = tableData.value
+          .map(item => item.id)
+          .join(",");
+        if (operationType.value === "add") {
+          afterSalesServiceAdd(form.value).then(response => {
+            proxy.$modal.msgSuccess("鏂板鎴愬姛");
+            closeDia();
+          });
+        } else {
+          afterSalesServiceUpdate(form.value).then(response => {
+            proxy.$modal.msgSuccess("淇敼鎴愬姛");
+            closeDia();
+          });
+        }
+      }
+    });
+  };
+  // 鍏抽棴寮规
+  const closeDia = () => {
+    proxy.resetForm("formRef");
+    dialogFormVisible.value = false;
+    emit("close");
+  };
+  defineExpose({
+    openDialog,
+  });
 </script>
 
 <style scoped lang="scss">
-.descriptions {
-  margin-bottom: 20px;
-  display: inline-block;
-  font-size: 1rem;
-  font-weight: 600;
-  padding-left: 12px;
-  position: relative;
-}
+  .descriptions {
+    margin-bottom: 20px;
+    display: inline-block;
+    font-size: 1rem;
+    font-weight: 600;
+    padding-left: 12px;
+    position: relative;
+  }
 
-.descriptions::before {
-  content: "";
-  position: absolute;
-  left: 0;
-  top: 50%;
-  transform: translateY(-50%);
-  width: 4px;
-  height: 1rem;
-  background-color: #002FA7; /* Element 榛樿绾㈣壊 */
-  border-radius: 2px;
-}
+  .descriptions::before {
+    content: "";
+    position: absolute;
+    left: 0;
+    top: 50%;
+    transform: translateY(-50%);
+    width: 4px;
+    height: 1rem;
+    background-color: #002fa7; /* Element 榛樿绾㈣壊 */
+    border-radius: 2px;
+  }
 </style>
diff --git a/src/views/equipmentManagement/operationManagement/index.vue b/src/views/equipmentManagement/operationManagement/index.vue
index 008cc2c..99c3bd1 100644
--- a/src/views/equipmentManagement/operationManagement/index.vue
+++ b/src/views/equipmentManagement/operationManagement/index.vue
@@ -104,7 +104,7 @@
           align="center"
         >
           <template #default="scope">
-            {{ scope.row.runtimeDuration || '-' }}
+            {{ getRuntimeDurationDisplay(scope.row) }}
           </template>
         </el-table-column>
         <el-table-column
@@ -154,7 +154,8 @@
 </template>
 
 <script setup>
-import { ref, onMounted, computed } from 'vue'
+import { ref, onMounted, onUnmounted, computed } from 'vue'
+import dayjs from 'dayjs'
 import { ElMessage } from 'element-plus'
 import {
   VideoPlay,
@@ -193,6 +194,98 @@
 
   return filtered
 })
+
+// 杩愯涓棤缁撴潫鏃堕棿鏃讹紝杩愯鏃堕暱闇�闅忓綋鍓嶆椂闂村彉鍖栵紝鐢� tick 瑙﹀彂妯℃澘閲嶇畻
+const runtimeDisplayTick = ref(0)
+
+/** 鍙栧悗绔彲鑳戒娇鐢ㄧ殑寮�濮�/缁撴潫鏃堕棿瀛楁 */
+const pickStartTime = (row) => row?.startRuntimeTime ?? row?.startTime ?? row?.start_time
+const pickEndTime = (row) => row?.endRuntimeTime ?? row?.endTime ?? row?.end_time
+
+/**
+ * 瑙f瀽鎺ュ彛/鍓嶇鍐欏叆鐨勫悇绫绘椂闂达細鏃堕棿鎴炽�両SO 瀛楃涓层�亂yyy-MM-dd HH:mm:ss銆丣ackson 鏁扮粍 [y,M,d,h,m,s]銆佸惈涓枃鐨� toLocaleString 绛�
+ */
+const parseDeviceTime = (input) => {
+  if (input === null || input === undefined || input === '') return null
+  if (typeof input === 'number' && !Number.isNaN(input)) {
+    const d = dayjs(input)
+    return d.isValid() ? d.toDate() : null
+  }
+  if (Array.isArray(input)) {
+    const [y, mo, day, h = 0, mi = 0, se = 0] = input
+    if (y == null || y === '') return null
+    const d = dayjs()
+        .year(Number(y))
+        .month(Number(mo || 1) - 1)
+        .date(Number(day || 1))
+        .hour(Number(h) || 0)
+        .minute(Number(mi) || 0)
+        .second(Number(se) || 0)
+    return d.isValid() ? d.toDate() : null
+  }
+  const s = String(input).trim()
+  if (!s || s === '-') return null
+  let d = dayjs(s)
+  if (d.isValid()) return d.toDate()
+  d = dayjs(s.replace(/-/g, '/'))
+  if (d.isValid()) return d.toDate()
+  d = dayjs(s.replace(/\//g, '-'))
+  if (d.isValid()) return d.toDate()
+  return null
+}
+
+const formatDurationMs = (durationMs) => {
+  if (durationMs == null || Number.isNaN(durationMs) || durationMs < 0) return '-'
+  const hours = Math.floor(durationMs / (1000 * 60 * 60))
+  const minutes = Math.floor((durationMs % (1000 * 60 * 60)) / (1000 * 60))
+  if (hours === 0 && minutes === 0) return '涓嶈冻1鍒嗛挓'
+  return `${hours}灏忔椂${minutes}鍒嗛挓`
+}
+
+const hasMeaningfulEnd = (endRaw) =>
+    endRaw !== null &&
+    endRaw !== undefined &&
+    String(endRaw).trim() !== '' &&
+    String(endRaw).trim() !== '-'
+
+const formatStoredDuration = (row) => {
+  const rd = row?.runtimeDuration
+  if (rd === null || rd === undefined) return ''
+  const t = String(rd).trim()
+  return t === '' || t === '-' ? '' : String(rd)
+}
+
+/** 杩愯涓細濮嬬粓鐢ㄣ�屽綋鍓嶆椂闂� - 寮�濮嬫椂闂淬�嶏紱宸插仠姝細浼樺厛鎺ュ彛 runtimeDuration锛屽惁鍒欑敤缁撴潫-寮�濮嬶紱鏃犵粨鏉熷彲鐪嬪凡瀛樻椂闀挎垨鍔ㄦ�佹帹绠� */
+const getRuntimeDurationDisplay = (row) => {
+  void runtimeDisplayTick.value
+  const start = parseDeviceTime(pickStartTime(row))
+  if (!start) {
+    return formatStoredDuration(row) || '-'
+  }
+
+  const statusStr = String(row?.status ?? '').trim()
+  const isRunning = statusStr === '杩愯涓�' || statusStr === '1'
+  const endRaw = pickEndTime(row)
+  const hasEnd = hasMeaningfulEnd(endRaw)
+
+  // 鏃犵粨鏉熸椂闂达細杩愯涓竴瀹氬姩鎬佺畻锛涘凡鍋滄鍒欎紭鍏堝睍绀哄悗绔凡瀛樻椂闀匡紝娌℃湁鍐嶆寜褰撳墠鏃堕棿鎺ㄧ畻
+  if (!hasEnd) {
+    if (isRunning) return formatDurationMs(Date.now() - start.getTime())
+    const stored = formatStoredDuration(row)
+    if (stored) return stored
+    return formatDurationMs(Date.now() - start.getTime())
+  }
+
+  if (isRunning) {
+    return formatDurationMs(Date.now() - start.getTime())
+  }
+
+  const end = parseDeviceTime(endRaw)
+  const stored = formatStoredDuration(row)
+  if (stored) return stored
+  if (end) return formatDurationMs(end.getTime() - start.getTime())
+  return '-'
+}
 
 // 妫�鏌ヨ澶囨槸鍚﹁秴鏃舵湭鍚姩
 const isOverdue = (device) => {
@@ -246,12 +339,11 @@
       device.endRuntimeTime = currentTime
       // 璁$畻杩愯鏃堕暱
       if (device.startRuntimeTime) {
-        const startTime = new Date(device.startRuntimeTime)
-        const endTime = new Date(currentTime)
-        const duration = endTime - startTime
-        const hours = Math.floor(duration / (1000 * 60 * 60))
-        const minutes = Math.floor((duration % (1000 * 60 * 60)) / (1000 * 60))
-        device.runtimeDuration = `${hours}灏忔椂${minutes}鍒嗛挓`
+        const startTime = parseDeviceTime(device.startRuntimeTime)
+        const endTime = parseDeviceTime(currentTime)
+        if (startTime && endTime) {
+          device.runtimeDuration = formatDurationMs(endTime.getTime() - startTime.getTime())
+        }
       }
     }
     const params = {
@@ -297,9 +389,31 @@
 
 
 
-// 缁勪欢鎸傝浇鏃跺垵濮嬪寲鏁版嵁
+const POLL_MS = 60 * 1000
+const RUNTIME_TICK_MS = 30 * 1000
+let listPollTimer = null
+let runtimeTickTimer = null
+
+// 缁勪欢鎸傝浇鏃舵媺鍙栨暟鎹紝骞舵瘡鍒嗛挓鍒锋柊涓�娆″垪琛紱杩愯涓椂闀挎瘡 30 绉掑埛鏂版樉绀�
 onMounted(() => {
   getList()
+  listPollTimer = setInterval(() => {
+    getList()
+  }, POLL_MS)
+  runtimeTickTimer = setInterval(() => {
+    runtimeDisplayTick.value++
+  }, RUNTIME_TICK_MS)
+})
+
+onUnmounted(() => {
+  if (listPollTimer != null) {
+    clearInterval(listPollTimer)
+    listPollTimer = null
+  }
+  if (runtimeTickTimer != null) {
+    clearInterval(runtimeTickTimer)
+    runtimeTickTimer = null
+  }
 })
 </script>
 
diff --git a/src/views/inventoryManagement/dispatchLog/Record.vue b/src/views/inventoryManagement/dispatchLog/Record.vue
index 88fc8d2..f41eaaa 100644
--- a/src/views/inventoryManagement/dispatchLog/Record.vue
+++ b/src/views/inventoryManagement/dispatchLog/Record.vue
@@ -96,7 +96,9 @@
         </el-table-column>
 				<el-table-column label="瀹℃壒鐘舵��" prop="approvalStatus" show-overflow-tooltip>
 					<template #default="scope">
-						{{ getApprovalStatusLabel(scope.row.approvalStatus) }}
+						<el-tag :type="getApprovalStatusTagType(scope.row.approvalStatus)" size="small">
+							{{ getApprovalStatusLabel(scope.row.approvalStatus) }}
+						</el-tag>
 					</template>
 				</el-table-column>
 			</el-table>
@@ -216,6 +218,13 @@
 	return approvalStatusLabelMap[status] || "寰呭鎵�";
 };
 
+// 閫氳繃/椹冲洖鍥哄畾鑹诧紱鍏朵綑锛堝惈寰呭鎵广�佺┖鍊笺�佹湭鏄犲皠浣嗘枃妗堜负寰呭鎵癸級缁熶竴鐢� warning 棰勮鑹�
+const getApprovalStatusTagType = (status) => {
+	if (status === 1 || status === "1" || status === "approved" || status === "APPROVED") return "success";
+	if (status === 2 || status === "2" || status === "rejected" || status === "REJECTED") return "danger";
+	return "warning";
+};
+
 // 鑾峰彇鏉ユ簮绫诲瀷閫夐」
 const fetchStockRecordTypeOptions = () => {
   if (props.type === '0') {
diff --git a/src/views/inventoryManagement/receiptManagement/Record.vue b/src/views/inventoryManagement/receiptManagement/Record.vue
index 75d884a..c9cb6ee 100644
--- a/src/views/inventoryManagement/receiptManagement/Record.vue
+++ b/src/views/inventoryManagement/receiptManagement/Record.vue
@@ -91,7 +91,9 @@
                          prop="approvalStatus"
                          show-overflow-tooltip>
           <template #default="scope">
-            {{ getApprovalStatusLabel(scope.row.approvalStatus) }}
+            <el-tag :type="getApprovalStatusTagType(scope.row.approvalStatus)" size="small">
+              {{ getApprovalStatusLabel(scope.row.approvalStatus) }}
+            </el-tag>
           </template>
         </el-table-column>
       </el-table>
@@ -187,6 +189,13 @@
   return approvalStatusLabelMap[status] || "寰呭鎵�";
 };
 
+// 閫氳繃/椹冲洖鍥哄畾鑹诧紱鍏朵綑锛堝惈寰呭鎵广�佺┖鍊笺�佹湭鏄犲皠浣嗘枃妗堜负寰呭鎵癸級缁熶竴鐢� warning 棰勮鑹�
+const getApprovalStatusTagType = (status) => {
+  if (status === 1 || status === "1" || status === "approved" || status === "APPROVED") return "success";
+  if (status === 2 || status === "2" || status === "rejected" || status === "REJECTED") return "danger";
+  return "warning";
+};
+
 const pageProductChange = obj => {
   page.current = obj.page;
   page.size = obj.limit;
diff --git a/src/views/inventoryManagement/stockReport/index.vue b/src/views/inventoryManagement/stockReport/index.vue
index 89773cf..8139508 100644
--- a/src/views/inventoryManagement/stockReport/index.vue
+++ b/src/views/inventoryManagement/stockReport/index.vue
@@ -4,708 +4,683 @@
     <div class="search_form">
       <div class="search_left">
         <span class="search_title">鎶ヨ〃绫诲瀷锛�</span>
-        <el-select
-          v-model="searchForm.reportType"
-          style="width: 150px;"
-          placeholder="璇烽�夋嫨"
-          @change="handleReportTypeChange"
-        >
-          <el-option label="鏃ユ姤" value="daily" />
-          <el-option label="鏈堟姤" value="monthly" />
-          <el-option label="杩涘嚭瀛樻姤琛�" value="inout" />
+        <el-select v-model="searchForm.reportType"
+                   style="width: 150px;"
+                   placeholder="璇烽�夋嫨"
+                   @change="handleReportTypeChange">
+          <el-option label="鏃ユ姤"
+                     value="daily" />
+          <el-option label="鏈堟姤"
+                     value="monthly" />
+          <el-option label="杩涘嚭瀛樻姤琛�"
+                     value="inout" />
         </el-select>
-        
         <span class="search_title ml10">鏃堕棿鑼冨洿锛�</span>
-         <el-date-picker
-           v-if="searchForm.reportType === 'daily'"
-           v-model="searchForm.singleDate"
-           type="date"
-           placeholder="璇烽�夋嫨鏃ユ湡"
-           format="YYYY-MM-DD"
-           value-format="YYYY-MM-DD"
-           style="width: 200px;"
-         />
-        <el-date-picker
-          v-else-if="searchForm.reportType === 'monthly'"
-          v-model="searchForm.monthRange"
-          type="monthrange"
-          range-separator="鑷�"
-          start-placeholder="寮�濮嬫湀浠�"
-          end-placeholder="缁撴潫鏈堜唤"
-          format="YYYY-MM-DD"
-          value-format="YYYY-MM-DD"
-          style="width: 240px;"
-        />
-        <el-date-picker
-          v-else
-          v-model="searchForm.dateRange"
-          type="daterange"
-          range-separator="鑷�"
-          start-placeholder="寮�濮嬫棩鏈�"
-          end-placeholder="缁撴潫鏃ユ湡"
-          format="YYYY-MM-DD"
-          value-format="YYYY-MM-DD"
-          style="width: 240px;"
-        />
-        
-        <el-button type="primary" @click="onSearch" style="margin-left: 10px">
+        <el-date-picker v-if="searchForm.reportType === 'daily'"
+                        v-model="searchForm.singleDate"
+                        type="date"
+                        placeholder="璇烽�夋嫨鏃ユ湡"
+                        format="YYYY-MM-DD"
+                        value-format="YYYY-MM-DD"
+                        style="width: 200px;" />
+        <el-date-picker v-else-if="searchForm.reportType === 'monthly'"
+                        v-model="searchForm.monthRange"
+                        type="monthrange"
+                        range-separator="鑷�"
+                        start-placeholder="寮�濮嬫湀浠�"
+                        end-placeholder="缁撴潫鏈堜唤"
+                        format="YYYY-MM-DD"
+                        value-format="YYYY-MM-DD"
+                        style="width: 240px;" />
+        <el-date-picker v-else
+                        v-model="searchForm.dateRange"
+                        type="daterange"
+                        range-separator="鑷�"
+                        start-placeholder="寮�濮嬫棩鏈�"
+                        end-placeholder="缁撴潫鏃ユ湡"
+                        format="YYYY-MM-DD"
+                        value-format="YYYY-MM-DD"
+                        style="width: 240px;" />
+        <el-button type="primary"
+                   @click="onSearch"
+                   style="margin-left: 10px">
           鏌ヨ
         </el-button>
         <el-button @click="handleReset">閲嶇疆</el-button>
       </div>
-
       <div class="search_right">
-<!--        <el-button type="success" @click="handleExport" icon="Download">-->
-<!--          瀵煎嚭鎶ヨ〃-->
-<!--        </el-button>-->
+        <!--        <el-button type="success" @click="handleExport" icon="Download">-->
+        <!--          瀵煎嚭鎶ヨ〃-->
+        <!--        </el-button>-->
       </div>
     </div>
-
-<!--    &lt;!&ndash; 缁熻鍗$墖 &ndash;&gt;-->
-<!--    <div class="stats_cards" v-if="reportData.summary">-->
-<!--      <el-row :gutter="20">-->
-<!--        <el-col :span="6">-->
-<!--          <el-card class="stats_card">-->
-<!--            <div class="stats_content">-->
-<!--              <div class="stats_icon in">-->
-<!--                <el-icon><TrendCharts /></el-icon>-->
-<!--              </div>-->
-<!--              <div class="stats_info">-->
-<!--                <div class="stats_value">{{ reportData.summary.totalIn || 0 }}</div>-->
-<!--                <div class="stats_label">鎬诲叆搴撻噺</div>-->
-<!--              </div>-->
-<!--            </div>-->
-<!--          </el-card>-->
-<!--        </el-col>-->
-<!--        <el-col :span="6">-->
-<!--          <el-card class="stats_card">-->
-<!--            <div class="stats_content">-->
-<!--              <div class="stats_icon out">-->
-<!--                <el-icon><TrendCharts /></el-icon>-->
-<!--              </div>-->
-<!--              <div class="stats_info">-->
-<!--                <div class="stats_value">{{ reportData.summary.totalOut || 0 }}</div>-->
-<!--                <div class="stats_label">鎬诲嚭搴撻噺</div>-->
-<!--              </div>-->
-<!--            </div>-->
-<!--          </el-card>-->
-<!--        </el-col>-->
-<!--        <el-col :span="6">-->
-<!--          <el-card class="stats_card">-->
-<!--            <div class="stats_content">-->
-<!--              <div class="stats_icon stock">-->
-<!--                <el-icon><Box /></el-icon>-->
-<!--              </div>-->
-<!--              <div class="stats_info">-->
-<!--                <div class="stats_value">{{ reportData.summary.currentStock || 0 }}</div>-->
-<!--                <div class="stats_label">褰撳墠搴撳瓨</div>-->
-<!--              </div>-->
-<!--            </div>-->
-<!--          </el-card>-->
-<!--        </el-col>-->
-<!--        <el-col :span="6">-->
-<!--          <el-card class="stats_card">-->
-<!--            <div class="stats_content">-->
-<!--              <div class="stats_icon turnover">-->
-<!--                <el-icon><Refresh /></el-icon>-->
-<!--              </div>-->
-<!--              <div class="stats_info">-->
-<!--                <div class="stats_value">{{ reportData.summary.turnoverRate || 0 }}%</div>-->
-<!--                <div class="stats_label">鍛ㄨ浆鐜�</div>-->
-<!--              </div>-->
-<!--            </div>-->
-<!--          </el-card>-->
-<!--        </el-col>-->
-<!--      </el-row>-->
-<!--    </div>-->
-
-<!--    &lt;!&ndash; 鍥捐〃鍖哄煙 &ndash;&gt;-->
-<!--    <div class="chart_section" v-if="reportData.chartData">-->
-<!--      <el-row :gutter="20">-->
-<!--        <el-col :span="12">-->
-<!--          <el-card>-->
-<!--            <template #header>-->
-<!--              <span>搴撳瓨瓒嬪娍鍥�</span>-->
-<!--            </template>-->
-<!--            <div ref="trendChart" style="height: 300px;"></div>-->
-<!--          </el-card>-->
-<!--        </el-col>-->
-<!--        <el-col :span="12">-->
-<!--          <el-card>-->
-<!--            <template #header>-->
-<!--              <span>杩涘嚭搴撳姣�</span>-->
-<!--            </template>-->
-<!--            <div ref="comparisonChart" style="height: 300px;"></div>-->
-<!--          </el-card>-->
-<!--        </el-col>-->
-<!--      </el-row>-->
-<!--    </div>-->
-
+    <!--    &lt;!&ndash; 缁熻鍗$墖 &ndash;&gt;-->
+    <!--    <div class="stats_cards" v-if="reportData.summary">-->
+    <!--      <el-row :gutter="20">-->
+    <!--        <el-col :span="6">-->
+    <!--          <el-card class="stats_card">-->
+    <!--            <div class="stats_content">-->
+    <!--              <div class="stats_icon in">-->
+    <!--                <el-icon><TrendCharts /></el-icon>-->
+    <!--              </div>-->
+    <!--              <div class="stats_info">-->
+    <!--                <div class="stats_value">{{ reportData.summary.totalIn || 0 }}</div>-->
+    <!--                <div class="stats_label">鎬诲叆搴撻噺</div>-->
+    <!--              </div>-->
+    <!--            </div>-->
+    <!--          </el-card>-->
+    <!--        </el-col>-->
+    <!--        <el-col :span="6">-->
+    <!--          <el-card class="stats_card">-->
+    <!--            <div class="stats_content">-->
+    <!--              <div class="stats_icon out">-->
+    <!--                <el-icon><TrendCharts /></el-icon>-->
+    <!--              </div>-->
+    <!--              <div class="stats_info">-->
+    <!--                <div class="stats_value">{{ reportData.summary.totalOut || 0 }}</div>-->
+    <!--                <div class="stats_label">鎬诲嚭搴撻噺</div>-->
+    <!--              </div>-->
+    <!--            </div>-->
+    <!--          </el-card>-->
+    <!--        </el-col>-->
+    <!--        <el-col :span="6">-->
+    <!--          <el-card class="stats_card">-->
+    <!--            <div class="stats_content">-->
+    <!--              <div class="stats_icon stock">-->
+    <!--                <el-icon><Box /></el-icon>-->
+    <!--              </div>-->
+    <!--              <div class="stats_info">-->
+    <!--                <div class="stats_value">{{ reportData.summary.currentStock || 0 }}</div>-->
+    <!--                <div class="stats_label">褰撳墠搴撳瓨</div>-->
+    <!--              </div>-->
+    <!--            </div>-->
+    <!--          </el-card>-->
+    <!--        </el-col>-->
+    <!--        <el-col :span="6">-->
+    <!--          <el-card class="stats_card">-->
+    <!--            <div class="stats_content">-->
+    <!--              <div class="stats_icon turnover">-->
+    <!--                <el-icon><Refresh /></el-icon>-->
+    <!--              </div>-->
+    <!--              <div class="stats_info">-->
+    <!--                <div class="stats_value">{{ reportData.summary.turnoverRate || 0 }}%</div>-->
+    <!--                <div class="stats_label">鍛ㄨ浆鐜�</div>-->
+    <!--              </div>-->
+    <!--            </div>-->
+    <!--          </el-card>-->
+    <!--        </el-col>-->
+    <!--      </el-row>-->
+    <!--    </div>-->
+    <!--    &lt;!&ndash; 鍥捐〃鍖哄煙 &ndash;&gt;-->
+    <!--    <div class="chart_section" v-if="reportData.chartData">-->
+    <!--      <el-row :gutter="20">-->
+    <!--        <el-col :span="12">-->
+    <!--          <el-card>-->
+    <!--            <template #header>-->
+    <!--              <span>搴撳瓨瓒嬪娍鍥�</span>-->
+    <!--            </template>-->
+    <!--            <div ref="trendChart" style="height: 300px;"></div>-->
+    <!--          </el-card>-->
+    <!--        </el-col>-->
+    <!--        <el-col :span="12">-->
+    <!--          <el-card>-->
+    <!--            <template #header>-->
+    <!--              <span>杩涘嚭搴撳姣�</span>-->
+    <!--            </template>-->
+    <!--            <div ref="comparisonChart" style="height: 300px;"></div>-->
+    <!--          </el-card>-->
+    <!--        </el-col>-->
+    <!--      </el-row>-->
+    <!--    </div>-->
     <!-- 璇︾粏鏁版嵁琛ㄦ牸 -->
     <div class="table_section">
       <el-card>
         <template #header>
           <span>{{ getTableTitle() }}</span>
         </template>
-         <el-table
-           v-loading="tableLoading"
-           :data="reportData.tableData"
-           border
-           height="400"
-           style="width: 100%"
-           :header-cell-style="{ background: '#F0F1F5', color: '#333333' }"
-         >
-          <el-table-column
-            align="center"
-            label="搴忓彿"
-            type="index"
-            width="60"
-          />
-           <el-table-column
-             label="鍏ュ簱鏃堕棿"
-             prop="createTime"
-             width="200"
-             show-overflow-tooltip
-             v-if="searchForm.reportType !== 'inout'"
-           />
-           <el-table-column
-             label="鍏ュ簱鎵规"
-             prop="inboundBatches"
-             width="240"
-             show-overflow-tooltip
-             v-if="searchForm.reportType !== 'inout'"
-           />
-           <el-table-column
-             label="浜у搧澶х被"
-             prop="productName"
-             show-overflow-tooltip
-           />
-           <el-table-column
-             label="瑙勬牸鍨嬪彿"
-             prop="model"
-             show-overflow-tooltip
-           />
-           <el-table-column
-             label="鍗曚綅"
-             prop="unit"
-             show-overflow-tooltip
-           />
-           <el-table-column
-             label="鍏ュ簱鏁伴噺"
-             prop="totalStockIn"
-             align="center"
-             v-if="searchForm.reportType === 'inout'"
-           />
-           <el-table-column
-               label="鍏ュ簱鏁伴噺"
-               prop="stockInNum"
-               align="center"
-               v-else
-           />
-           <el-table-column
-             label="鍑哄簱鏁伴噺"
-             prop="totalStockOut"
-             width="100"
-             align="center"
-             v-if="searchForm.reportType === 'inout'"
-           />
-           <el-table-column
-             label="鐜板湪搴撳瓨"
-             prop="currentStock"
-             align="center"
-           />
-           <el-table-column label="鏉ユ簮"
-                            prop="recordType"
-                            v-if="searchForm.reportType !== 'inout'"
-                            show-overflow-tooltip>
-             <template #default="scope">
-               {{ getRecordType(scope.row.recordType) }}
-             </template>
-           </el-table-column>
-           <el-table-column
-             label="鍏ュ簱浜�"
-             prop="createBy"
-             width="80"
-             v-if="searchForm.reportType !== 'inout'"
-             show-overflow-tooltip
-           />
+        <el-table v-loading="tableLoading"
+                  :data="reportData.tableData"
+                  border
+                  height="400"
+                  style="width: 100%"
+                  :header-cell-style="{ background: '#F0F1F5', color: '#333333' }">
+          <el-table-column align="center"
+                           label="搴忓彿"
+                           type="index"
+                           width="60" />
+          <el-table-column label="鍏ュ簱鏃堕棿"
+                           prop="createTime"
+                           width="200"
+                           show-overflow-tooltip
+                           v-if="searchForm.reportType !== 'inout'" />
+          <el-table-column label="鍏ュ簱鎵规"
+                           prop="inboundBatches"
+                           width="180"
+                           show-overflow-tooltip
+                           v-if="searchForm.reportType !== 'inout'" />
+          <el-table-column label="鎵瑰彿"
+                           prop="batchNo"
+                           width="180"
+                           show-overflow-tooltip
+                           v-if="searchForm.reportType !== 'inout'" />
+          <el-table-column label="浜у搧澶х被"
+                           prop="productName"
+                           show-overflow-tooltip />
+          <el-table-column label="瑙勬牸鍨嬪彿"
+                           prop="model"
+                           show-overflow-tooltip />
+          <el-table-column label="鍗曚綅"
+                           prop="unit"
+                           show-overflow-tooltip />
+          <el-table-column label="鍏ュ簱鏁伴噺"
+                           prop="totalStockIn"
+                           align="center"
+                           v-if="searchForm.reportType === 'inout'" />
+          <el-table-column label="鍏ュ簱鏁伴噺"
+                           prop="stockInNum"
+                           align="center"
+                           v-else />
+          <el-table-column label="鍑哄簱鏁伴噺"
+                           prop="totalStockOut"
+                           width="100"
+                           align="center"
+                           v-if="searchForm.reportType === 'inout'" />
+          <el-table-column label="鐜板湪搴撳瓨"
+                           prop="currentStock"
+                           align="center" />
+          <el-table-column label="鏉ユ簮"
+                           prop="recordType"
+                           v-if="searchForm.reportType !== 'inout'"
+                           show-overflow-tooltip>
+            <template #default="scope">
+              {{ getRecordType(scope.row.recordType) }}
+            </template>
+          </el-table-column>
+          <el-table-column label="鍏ュ簱浜�"
+                           prop="createBy"
+                           width="80"
+                           v-if="searchForm.reportType !== 'inout'"
+                           show-overflow-tooltip />
         </el-table>
-        <pagination
-          :total="total"
-          layout="total, sizes, prev, pager, next, jumper"
-          :page="page.current"
-          :limit="page.size"
-          @pagination="paginationChange"
-        />
+        <pagination :total="total"
+                    layout="total, sizes, prev, pager, next, jumper"
+                    :page="page.current"
+                    :limit="page.size"
+                    @pagination="paginationChange" />
       </el-card>
     </div>
   </div>
 </template>
 
 <script setup>
-import { ref, reactive, onMounted, nextTick, getCurrentInstance } from 'vue'
-import { ElMessage } from 'element-plus'
-import * as echarts from 'echarts'
-import pagination from '@/components/PIMTable/Pagination.vue'
-import {
-  getStockInventoryInAndOutReportList,
-  getStockInventoryReportList
-} from "@/api/inventoryManagement/stockInventory.js";
-import {
-  findAllQualifiedStockInRecordTypeOptions,findAllUnQualifiedStockInRecordTypeOptions,
-} from "@/api/basicData/enum.js";
+  import { ref, reactive, onMounted, nextTick, getCurrentInstance } from "vue";
+  import { ElMessage } from "element-plus";
+  import * as echarts from "echarts";
+  import pagination from "@/components/PIMTable/Pagination.vue";
+  import {
+    getStockInventoryInAndOutReportList,
+    getStockInventoryReportList,
+  } from "@/api/inventoryManagement/stockInventory.js";
+  import {
+    findAllQualifiedStockInRecordTypeOptions,
+    findAllUnQualifiedStockInRecordTypeOptions,
+  } from "@/api/basicData/enum.js";
 
+  const { proxy } = getCurrentInstance();
+  // 鍝嶅簲寮忔暟鎹�
+  const tableLoading = ref(false);
+  const trendChart = ref(null);
+  const comparisonChart = ref(null);
 
-const { proxy } = getCurrentInstance()
-// 鍝嶅簲寮忔暟鎹�
-const tableLoading = ref(false)
-const trendChart = ref(null)
-const comparisonChart = ref(null)
+  const searchForm = reactive({
+    reportType: "daily",
+    singleDate: "",
+    dateRange: [],
+    monthRange: [],
+  });
 
-const searchForm = reactive({
-  reportType: 'daily',
-  singleDate: '',
-  dateRange: [],
-  monthRange: []
-})
-
-const reportData = ref({
-  summary: null,
-  chartData: null,
-  tableData: []
-})
-
-const page = reactive({
-  current: 1,
-  size: 10,
-})
-
-const total = ref(0)
-
-const stockRecordTypeOptions = ref([])
-
-const getRecordType = (recordType) => {
-  return stockRecordTypeOptions.value.find(item => item.value === recordType)?.label || ''
-}
-
-// 鑾峰彇鏉ユ簮绫诲瀷閫夐」
-const fetchStockRecordTypeOptions = () => {
-  findAllQualifiedStockInRecordTypeOptions()
-      .then(res => {
-        stockRecordTypeOptions.value = res.data;
-        findAllUnQualifiedStockInRecordTypeOptions()
-          .then(res => {
-          stockRecordTypeOptions.value = [...stockRecordTypeOptions.value,...res.data];
-      })
-      })
-}
-
-// 鑾峰彇琛ㄦ牸鏍囬
-const getTableTitle = () => {
-  const typeMap = {
-    daily: '鏃ユ姤璇︾粏鏁版嵁',
-    monthly: '鏈堟姤璇︾粏鏁版嵁',
-    inout: '杩涘嚭瀛樻姤琛ㄨ缁嗘暟鎹�'
-  }
-  return typeMap[searchForm.reportType] || '鎶ヨ〃璇︾粏鏁版嵁'
-}
-
-// 鎶ヨ〃绫诲瀷鏀瑰彉
-const handleReportTypeChange = () => {
-  page.current = 1
-  reportData.value = {
+  const reportData = ref({
     summary: null,
     chartData: null,
-    tableData: []
-  }
-}
+    tableData: [],
+  });
 
-// 鏌ヨ鏁版嵁
-const handleQuery = async () => {
-  if (!validateSearchForm()) {
-    return
-  }
-  
-  tableLoading.value = true
-  try {
-    const baseParams = getQueryParams()
+  const page = reactive({
+    current: 1,
+    size: 10,
+  });
+
+  const total = ref(0);
+
+  const stockRecordTypeOptions = ref([]);
+
+  const getRecordType = recordType => {
+    return (
+      stockRecordTypeOptions.value.find(item => item.value === recordType)
+        ?.label || ""
+    );
+  };
+
+  // 鑾峰彇鏉ユ簮绫诲瀷閫夐」
+  const fetchStockRecordTypeOptions = () => {
+    findAllQualifiedStockInRecordTypeOptions().then(res => {
+      stockRecordTypeOptions.value = res.data;
+      findAllUnQualifiedStockInRecordTypeOptions().then(res => {
+        stockRecordTypeOptions.value = [
+          ...stockRecordTypeOptions.value,
+          ...res.data,
+        ];
+      });
+    });
+  };
+
+  // 鑾峰彇琛ㄦ牸鏍囬
+  const getTableTitle = () => {
+    const typeMap = {
+      daily: "鏃ユ姤璇︾粏鏁版嵁",
+      monthly: "鏈堟姤璇︾粏鏁版嵁",
+      inout: "杩涘嚭瀛樻姤琛ㄨ缁嗘暟鎹�",
+    };
+    return typeMap[searchForm.reportType] || "鎶ヨ〃璇︾粏鏁版嵁";
+  };
+
+  // 鎶ヨ〃绫诲瀷鏀瑰彉
+  const handleReportTypeChange = () => {
+    page.current = 1;
+    reportData.value = {
+      summary: null,
+      chartData: null,
+      tableData: [],
+    };
+  };
+
+  // 鏌ヨ鏁版嵁
+  const handleQuery = async () => {
+    if (!validateSearchForm()) {
+      return;
+    }
+
+    tableLoading.value = true;
+    try {
+      const baseParams = getQueryParams();
+      const params = {
+        ...baseParams,
+        current: page.current,
+        size: page.size,
+      };
+      let response;
+
+      if (searchForm.reportType === "inout") {
+        response = await getStockInventoryInAndOutReportList(params);
+      } else {
+        response = await getStockInventoryReportList(params);
+      }
+      if (response.code === 200) {
+        reportData.value.tableData = response.data.records || [];
+        total.value = response.data.total || 0;
+        // reportData.value.summary = response.data.summary
+        // reportData.value.chartData = response.data.chartData
+        // nextTick(() => {
+        //   initCharts()
+        // })
+      }
+    } catch (error) {
+      ElMessage.error("鏌ヨ澶辫触锛�" + error.message);
+    } finally {
+      tableLoading.value = false;
+    }
+  };
+
+  // 鏌ヨ鎸夐挳锛氶噸缃埌绗竴椤靛苟鏌ヨ
+  const onSearch = () => {
+    page.current = 1;
+    handleQuery();
+  };
+
+  // 鍒嗛〉鍙樺寲
+  const paginationChange = obj => {
+    page.current = obj.page;
+    page.size = obj.limit;
+    handleQuery();
+  };
+  // // 鐢熸垚鍋囨暟鎹�
+  // const generateMockData = () => {
+  //   // 鐢熸垚缁熻鍗$墖鍋囨暟鎹�
+  //   const summary = {
+  //     totalIn: 1000,
+  //     totalOut: 600,
+  //     currentStock: 400,
+  //     turnoverRate: 30
+  //   }
+
+  //   // 鐢熸垚鍥捐〃鍋囨暟鎹�
+  //   const trendDates = ['2025-09-15', '2025-09-16', '2025-09-17', '2025-09-18', '2025-09-19']
+  //   const trendValues = [300, 350, 400, 380, 420]
+  //   const comparisonDates = ['2025-09-15', '2025-09-16', '2025-09-17']
+  //   const inValues = [100, 150, 200]
+  //   const outValues = [80, 120, 100]
+
+  //   const chartData = {
+  //     trendDates,
+  //     trendValues,
+  //     comparisonDates,
+  //     inValues,
+  //     outValues
+  //   }
+
+  //   reportData.value = {
+  //     summary,
+  //     chartData,
+  //     tableData: []
+  //   }
+  // }
+  // 楠岃瘉鎼滅储琛ㄥ崟
+  const validateSearchForm = () => {
+    if (searchForm.reportType === "daily") {
+      if (!searchForm.singleDate) {
+        ElMessage.warning("璇烽�夋嫨鏃ユ湡");
+        return false;
+      }
+    } else if (searchForm.reportType === "inout") {
+      if (!searchForm.dateRange || searchForm.dateRange.length !== 2) {
+        ElMessage.warning("璇烽�夋嫨鏃ユ湡鑼冨洿");
+        return false;
+      }
+    } else if (searchForm.reportType === "monthly") {
+      if (!searchForm.monthRange || searchForm.monthRange.length !== 2) {
+        ElMessage.warning("璇烽�夋嫨鏈堜唤鑼冨洿");
+        return false;
+      }
+    }
+    return true;
+  };
+
+  // 鑾峰彇鏌ヨ鍙傛暟
+  const getQueryParams = () => {
     const params = {
-      ...baseParams,
-      current: page.current,
-      size: page.size,
-    }
-    let response
+      reportType: searchForm.reportType,
+      reportDate: "",
+      startMonth: "",
+      endMonth: "",
+      startDate: "",
+      endDate: "",
+    };
 
-    if (searchForm.reportType === 'inout') {
-      response = await getStockInventoryInAndOutReportList(params)
+    if (searchForm.reportType === "daily") {
+      params.reportDate = searchForm.singleDate;
+    } else if (searchForm.reportType === "monthly") {
+      params.startMonth = searchForm.monthRange[0];
+      params.endMonth = searchForm.monthRange[1];
     } else {
-      response = await getStockInventoryReportList(params)
+      params.startDate = searchForm.dateRange[0];
+      params.endDate = searchForm.dateRange[1];
     }
-    if (response.code === 200) {
-      reportData.value.tableData = response.data.records || []
-      total.value = response.data.total || 0
-      // reportData.value.summary = response.data.summary
-      // reportData.value.chartData = response.data.chartData
-      // nextTick(() => {
-      //   initCharts()
-      // })
-      
+
+    return params;
+  };
+
+  // 閲嶇疆鎼滅储
+  const handleReset = () => {
+    searchForm.reportType = "daily";
+    searchForm.singleDate = "";
+    searchForm.dateRange = [];
+    searchForm.monthRange = [];
+    reportData.value = {
+      summary: null,
+      chartData: null,
+      tableData: [],
+    };
+  };
+
+  // 瀵煎嚭鎶ヨ〃
+  const handleExport = async () => {
+    if (!validateSearchForm()) {
+      return;
     }
-  } catch (error) {
-    ElMessage.error('鏌ヨ澶辫触锛�' + error.message)
-  } finally {
-    tableLoading.value = false
-  }
-}
 
-// 鏌ヨ鎸夐挳锛氶噸缃埌绗竴椤靛苟鏌ヨ
-const onSearch = () => {
-  page.current = 1
-  handleQuery()
-}
+    try {
+      const params = getQueryParams();
+      // const response = await exportStockReport(params)
+      proxy.download("/stockin/exportCopy", params, "搴撳瓨鎶ヨ〃.xlsx");
+      // 鍒涘缓涓嬭浇閾炬帴
+      // const blob = new Blob([response], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' })
+      // const url = window.URL.createObjectURL(blob)
+      // const link = document.createElement('a')
+      // link.href = url
+      // link.download = `${getTableTitle()}_${new Date().getTime()}.xlsx`
+      // document.body.appendChild(link)
+      // link.click()
+      // document.body.removeChild(link)
+      // window.URL.revokeObjectURL(url)
 
-// 鍒嗛〉鍙樺寲
-const paginationChange = (obj) => {
-  page.current = obj.page
-  page.size = obj.limit
-  handleQuery()
-}
-// // 鐢熸垚鍋囨暟鎹�
-// const generateMockData = () => {
-//   // 鐢熸垚缁熻鍗$墖鍋囨暟鎹�
-//   const summary = {
-//     totalIn: 1000,
-//     totalOut: 600,
-//     currentStock: 400,
-//     turnoverRate: 30
-//   }
-
-//   // 鐢熸垚鍥捐〃鍋囨暟鎹�
-//   const trendDates = ['2025-09-15', '2025-09-16', '2025-09-17', '2025-09-18', '2025-09-19']
-//   const trendValues = [300, 350, 400, 380, 420]
-//   const comparisonDates = ['2025-09-15', '2025-09-16', '2025-09-17']
-//   const inValues = [100, 150, 200]
-//   const outValues = [80, 120, 100]
-
-//   const chartData = {
-//     trendDates,
-//     trendValues,
-//     comparisonDates,
-//     inValues,
-//     outValues
-//   }
-
-//   reportData.value = {
-//     summary,
-//     chartData,
-//     tableData: []
-//   }
-// }
-// 楠岃瘉鎼滅储琛ㄥ崟
-const validateSearchForm = () => {
-  if (searchForm.reportType === 'daily') {
-    if (!searchForm.singleDate) {
-      ElMessage.warning('璇烽�夋嫨鏃ユ湡')
-      return false
+      // ElMessage.success('瀵煎嚭鎴愬姛')
+    } catch (error) {
+      ElMessage.error("瀵煎嚭澶辫触锛�" + error.message);
     }
-  } else if (searchForm.reportType === 'inout') {
-    if (!searchForm.dateRange || searchForm.dateRange.length !== 2) {
-      ElMessage.warning('璇烽�夋嫨鏃ユ湡鑼冨洿')
-      return false
-    }
-  } else if (searchForm.reportType === 'monthly') {
-    if (!searchForm.monthRange || searchForm.monthRange.length !== 2) {
-      ElMessage.warning('璇烽�夋嫨鏈堜唤鑼冨洿')
-      return false
-    }
-  }
-  return true
-}
+  };
 
-// 鑾峰彇鏌ヨ鍙傛暟
-const getQueryParams = () => {
-  const params = {
-    reportType: searchForm.reportType,
-    reportDate: "",
-    startMonth: "",
-    endMonth: "",
-    startDate: "",
-    endDate: ""
-  }
-  
-  if (searchForm.reportType === 'daily') {
-    params.reportDate = searchForm.singleDate
-  } else if (searchForm.reportType === 'monthly') {
-    params.startMonth = searchForm.monthRange[0]
-    params.endMonth = searchForm.monthRange[1]
-  } else {
-    params.startDate = searchForm.dateRange[0]
-    params.endDate = searchForm.dateRange[1]
-  }
-  
-  return params
-}
+  // 鍒濆鍖栧浘琛�
+  const initCharts = () => {
+    if (!reportData.value.chartData) return;
 
-// 閲嶇疆鎼滅储
-const handleReset = () => {
-  searchForm.reportType = 'daily'
-  searchForm.singleDate = ''
-  searchForm.dateRange = []
-  searchForm.monthRange = []
-  reportData.value = {
-    summary: null,
-    chartData: null,
-    tableData: []
-  }
-}
+    initTrendChart();
+    initComparisonChart();
+  };
 
-// 瀵煎嚭鎶ヨ〃
-const handleExport = async () => {
-  if (!validateSearchForm()) {
-    return
-  }
-  
-  try {
-    const params = getQueryParams()
-    // const response = await exportStockReport(params)
-    proxy.download("/stockin/exportCopy", params, '搴撳瓨鎶ヨ〃.xlsx')
-    // 鍒涘缓涓嬭浇閾炬帴
-    // const blob = new Blob([response], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' })
-    // const url = window.URL.createObjectURL(blob)
-    // const link = document.createElement('a')
-    // link.href = url
-    // link.download = `${getTableTitle()}_${new Date().getTime()}.xlsx`
-    // document.body.appendChild(link)
-    // link.click()
-    // document.body.removeChild(link)
-    // window.URL.revokeObjectURL(url)
-    
-    // ElMessage.success('瀵煎嚭鎴愬姛')
-  } catch (error) {
-    ElMessage.error('瀵煎嚭澶辫触锛�' + error.message)
-  }
-}
+  // 鍒濆鍖栬秼鍔垮浘
+  const initTrendChart = () => {
+    if (!trendChart.value) return;
 
-// 鍒濆鍖栧浘琛�
-const initCharts = () => {
-  if (!reportData.value.chartData) return
-  
-  initTrendChart()
-  initComparisonChart()
-}
-
-// 鍒濆鍖栬秼鍔垮浘
-const initTrendChart = () => {
-  if (!trendChart.value) return
-  
-  const chart = echarts.init(trendChart.value)
-  const option = {
-    title: {
-      text: '搴撳瓨鍙樺寲瓒嬪娍',
-      left: 'center'
-    },
-    tooltip: {
-      trigger: 'axis'
-    },
-    legend: {
-      data: ['搴撳瓨閲�'],
-      top: 30
-    },
-    xAxis: {
-      type: 'category',
-      data: reportData.value.chartData.trendDates || []
-    },
-    yAxis: {
-      type: 'value'
-    },
-    series: [{
-      name: '搴撳瓨閲�',
-      type: 'line',
-      data: reportData.value.chartData.trendValues || [],
-      smooth: true,
-      itemStyle: {
-        color: '#409EFF'
-      }
-    }]
-  }
-  chart.setOption(option)
-}
-
-// 鍒濆鍖栧姣斿浘
-const initComparisonChart = () => {
-  if (!comparisonChart.value) return
-  
-  const chart = echarts.init(comparisonChart.value)
-  const option = {
-    title: {
-      text: '杩涘嚭搴撳姣�',
-      left: 'center'
-    },
-    tooltip: {
-      trigger: 'axis'
-    },
-    legend: {
-      data: ['鍏ュ簱', '鍑哄簱'],
-      top: 30
-    },
-    xAxis: {
-      type: 'category',
-      data: reportData.value.chartData.comparisonDates || []
-    },
-    yAxis: {
-      type: 'value'
-    },
-    series: [
-      {
-        name: '鍏ュ簱',
-        type: 'bar',
-        data: reportData.value.chartData.inValues || [],
-        itemStyle: {
-          color: '#67C23A'
-        }
+    const chart = echarts.init(trendChart.value);
+    const option = {
+      title: {
+        text: "搴撳瓨鍙樺寲瓒嬪娍",
+        left: "center",
       },
-      {
-        name: '鍑哄簱',
-        type: 'bar',
-        data: reportData.value.chartData.outValues || [],
-        itemStyle: {
-          color: '#F56C6C'
-        }
-      }
-    ]
-  }
-  chart.setOption(option)
-}
+      tooltip: {
+        trigger: "axis",
+      },
+      legend: {
+        data: ["搴撳瓨閲�"],
+        top: 30,
+      },
+      xAxis: {
+        type: "category",
+        data: reportData.value.chartData.trendDates || [],
+      },
+      yAxis: {
+        type: "value",
+      },
+      series: [
+        {
+          name: "搴撳瓨閲�",
+          type: "line",
+          data: reportData.value.chartData.trendValues || [],
+          smooth: true,
+          itemStyle: {
+            color: "#409EFF",
+          },
+        },
+      ],
+    };
+    chart.setOption(option);
+  };
 
-// 缁勪欢鎸傝浇鏃惰缃粯璁ゆ椂闂�
-onMounted(() => {
-  const today = new Date()
-  searchForm.singleDate = today.toISOString().split('T')[0]
-  
-  const yesterday = new Date(today.getTime() - 24 * 60 * 60 * 1000)
-  searchForm.dateRange = [
-    yesterday.toISOString().split('T')[0],
-    today.toISOString().split('T')[0]
-  ]
+  // 鍒濆鍖栧姣斿浘
+  const initComparisonChart = () => {
+    if (!comparisonChart.value) return;
 
-  fetchStockRecordTypeOptions()
-  // 鍒濆鍖栧姞杞戒竴娆℃暟鎹�
-  handleQuery()
-})
+    const chart = echarts.init(comparisonChart.value);
+    const option = {
+      title: {
+        text: "杩涘嚭搴撳姣�",
+        left: "center",
+      },
+      tooltip: {
+        trigger: "axis",
+      },
+      legend: {
+        data: ["鍏ュ簱", "鍑哄簱"],
+        top: 30,
+      },
+      xAxis: {
+        type: "category",
+        data: reportData.value.chartData.comparisonDates || [],
+      },
+      yAxis: {
+        type: "value",
+      },
+      series: [
+        {
+          name: "鍏ュ簱",
+          type: "bar",
+          data: reportData.value.chartData.inValues || [],
+          itemStyle: {
+            color: "#67C23A",
+          },
+        },
+        {
+          name: "鍑哄簱",
+          type: "bar",
+          data: reportData.value.chartData.outValues || [],
+          itemStyle: {
+            color: "#F56C6C",
+          },
+        },
+      ],
+    };
+    chart.setOption(option);
+  };
+
+  // 缁勪欢鎸傝浇鏃惰缃粯璁ゆ椂闂�
+  onMounted(() => {
+    const today = new Date();
+    searchForm.singleDate = today.toISOString().split("T")[0];
+
+    const yesterday = new Date(today.getTime() - 24 * 60 * 60 * 1000);
+    searchForm.dateRange = [
+      yesterday.toISOString().split("T")[0],
+      today.toISOString().split("T")[0],
+    ];
+
+    fetchStockRecordTypeOptions();
+    // 鍒濆鍖栧姞杞戒竴娆℃暟鎹�
+    handleQuery();
+  });
 </script>
 
 <style scoped>
-.app-container {
-  padding: 20px;
-}
+  .app-container {
+    padding: 20px;
+  }
 
-.search_form {
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-  margin-bottom: 20px;
-  padding: 20px;
-  background: #fff;
-  border-radius: 4px;
-  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
-}
+  .search_form {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    margin-bottom: 20px;
+    padding: 20px;
+    background: #fff;
+    border-radius: 4px;
+    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+  }
 
-.search_left {
-  display: flex;
-  align-items: center;
-}
+  .search_left {
+    display: flex;
+    align-items: center;
+  }
 
-.search_title {
-  font-weight: 500;
-  color: #333;
-  margin-right: 8px;
-}
+  .search_title {
+    font-weight: 500;
+    color: #333;
+    margin-right: 8px;
+  }
 
-.ml10 {
-  margin-left: 10px;
-}
+  .ml10 {
+    margin-left: 10px;
+  }
 
-.stats_cards {
-  margin-bottom: 20px;
-}
+  .stats_cards {
+    margin-bottom: 20px;
+  }
 
-.stats_card {
-  border-radius: 8px;
-  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
-}
+  .stats_card {
+    border-radius: 8px;
+    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+  }
 
-.stats_content {
-  display: flex;
-  align-items: center;
-  padding: 10px 0;
-}
+  .stats_content {
+    display: flex;
+    align-items: center;
+    padding: 10px 0;
+  }
 
-.stats_icon {
-  width: 50px;
-  height: 50px;
-  border-radius: 50%;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  margin-right: 15px;
-  font-size: 24px;
-  color: #fff;
-}
+  .stats_icon {
+    width: 50px;
+    height: 50px;
+    border-radius: 50%;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    margin-right: 15px;
+    font-size: 24px;
+    color: #fff;
+  }
 
-.stats_icon.in {
-  background: linear-gradient(135deg, #67C23A, #85CE61);
-}
+  .stats_icon.in {
+    background: linear-gradient(135deg, #67c23a, #85ce61);
+  }
 
-.stats_icon.out {
-  background: linear-gradient(135deg, #F56C6C, #F78989);
-}
+  .stats_icon.out {
+    background: linear-gradient(135deg, #f56c6c, #f78989);
+  }
 
-.stats_icon.stock {
-  background: linear-gradient(135deg, #409EFF, #66B1FF);
-}
+  .stats_icon.stock {
+    background: linear-gradient(135deg, #409eff, #66b1ff);
+  }
 
-.stats_icon.turnover {
-  background: linear-gradient(135deg, #E6A23C, #EEBE77);
-}
+  .stats_icon.turnover {
+    background: linear-gradient(135deg, #e6a23c, #eebe77);
+  }
 
-.stats_info {
-  flex: 1;
-}
+  .stats_info {
+    flex: 1;
+  }
 
-.stats_value {
-  font-size: 24px;
-  font-weight: bold;
-  color: #333;
-  line-height: 1;
-  margin-bottom: 5px;
-}
+  .stats_value {
+    font-size: 24px;
+    font-weight: bold;
+    color: #333;
+    line-height: 1;
+    margin-bottom: 5px;
+  }
 
-.stats_label {
-  font-size: 14px;
-  color: #666;
-}
+  .stats_label {
+    font-size: 14px;
+    color: #666;
+  }
 
-.chart_section {
-  margin-bottom: 20px;
-}
+  .chart_section {
+    margin-bottom: 20px;
+  }
 
-.table_section {
-  margin-bottom: 20px;
-}
+  .table_section {
+    margin-bottom: 20px;
+  }
 
-:deep(.el-card__header) {
-  background: #f8f9fa;
-  border-bottom: 1px solid #e9ecef;
-  font-weight: 500;
-}
+  :deep(.el-card__header) {
+    background: #f8f9fa;
+    border-bottom: 1px solid #e9ecef;
+    font-weight: 500;
+  }
 
-:deep(.el-table .el-table__header-wrapper th) {
-  background-color: #F0F1F5 !important;
-  color: #333333;
-  font-weight: 600;
-}
+  :deep(.el-table .el-table__header-wrapper th) {
+    background-color: #f0f1f5 !important;
+    color: #333333;
+    font-weight: 600;
+  }
 
-:deep(.el-table .el-table__body-wrapper td) {
-  padding: 8px 0;
-}
+  :deep(.el-table .el-table__body-wrapper td) {
+    padding: 8px 0;
+  }
 </style>

--
Gitblit v1.9.3