From 4a811fb2cd4ee4e1cbfe284bfd1fe3a7d16204ce Mon Sep 17 00:00:00 2001
From: yuan <123@>
Date: 星期四, 02 四月 2026 17:35:48 +0800
Subject: [PATCH] fix: 终检表单优化检验用粉剂/液情况字段

---
 src/views/salesManagement/salesLedger/index.vue |  627 +++++++++++++++++++++++++++++++++++++++++++-------------
 1 files changed, 474 insertions(+), 153 deletions(-)

diff --git a/src/views/salesManagement/salesLedger/index.vue b/src/views/salesManagement/salesLedger/index.vue
index d9a180f..671bb86 100644
--- a/src/views/salesManagement/salesLedger/index.vue
+++ b/src/views/salesManagement/salesLedger/index.vue
@@ -33,11 +33,11 @@
           <el-button type="primary" plain @click="handleImport">瀵煎叆</el-button>
           <el-button @click="handleOut">瀵煎嚭</el-button>
           <el-button type="danger" plain @click="handleDelete">鍒犻櫎</el-button>
-          <el-button type="primary" plain @click="handlePrint">鎵撳嵃</el-button>
+          <!-- <el-button type="primary" plain @click="handlePrint">鎵撳嵃</el-button> -->
         </div>
       </div>
       <el-table :data="tableData" border v-loading="tableLoading" @selection-change="handleSelectionChange"
-        :expand-row-keys="expandedRowKeys" :row-key="(row) => row.id" show-summary style="width: 100%"
+        :expand-row-keys="expandedRowKeys" :row-key="(row) => row.id" :row-class-name="tableRowClassName" show-summary style="width: 100%"
         :summary-method="summarizeMainTable" @expand-change="expandChange" height="calc(100vh - 18.5em)">
         <el-table-column align="center" type="selection" width="55" fixed="left"/>
         <el-table-column type="expand" width="60" fixed="left">
@@ -46,6 +46,8 @@
               <el-table-column align="center" label="搴忓彿" type="index"/>
               <el-table-column label="浜у搧澶х被" prop="productCategory" />
               <el-table-column label="瑙勬牸鍨嬪彿" prop="specificationModel" />
+              <el-table-column label="鎵瑰彿" prop="batchNo" />
+              <el-table-column label="UID鐮�" prop="uidNo" />
               <el-table-column label="鍗曚綅" prop="unit" />
 							<el-table-column label="浜у搧鐘舵��"
 															 width="100px"
@@ -110,18 +112,21 @@
         <el-table-column label="閿�鍞悎鍚屽彿" prop="salesContractNo" width="180" show-overflow-tooltip />
         <el-table-column label="瀹㈡埛鍚嶇О" prop="customerName" width="300" show-overflow-tooltip />
         <el-table-column label="涓氬姟鍛�" prop="salesman" width="100" show-overflow-tooltip />
-        <el-table-column label="椤圭洰鍚嶇О" prop="projectName" width="180" show-overflow-tooltip />
-        <el-table-column label="浠樻鏂瑰紡" prop="paymentMethod" show-overflow-tooltip />
+        <!-- <el-table-column label="椤圭洰鍚嶇О" prop="projectName" width="180" show-overflow-tooltip />
+        <el-table-column label="浠樻鏂瑰紡" prop="paymentMethod" show-overflow-tooltip /> -->
         <el-table-column label="鍚堝悓閲戦(鍏�)" prop="contractAmount" width="220" show-overflow-tooltip
           :formatter="formattedNumber" />
         <el-table-column label="褰曞叆浜�" prop="entryPersonName" width="100" show-overflow-tooltip />
         <el-table-column label="褰曞叆鏃ユ湡" prop="entryDate" width="120" show-overflow-tooltip />
         <el-table-column label="绛捐鏃ユ湡" prop="executionDate" width="120" show-overflow-tooltip />
+        <el-table-column label="浜や粯鏃ユ湡" prop="deliveryDate" width="120" show-overflow-tooltip />
+        <el-table-column label="澶囨敞" prop="remarks" width="200" show-overflow-tooltip />
         <el-table-column fixed="right" label="鎿嶄綔" min-width="100" align="center">
           <template #default="scope">
-            <el-button link type="primary" size="small" @click="openForm('edit', scope.row)">缂栬緫</el-button>
+            <el-button link type="primary" size="small" @click="openForm('edit', scope.row)" :disabled="!scope.row.isEdit">缂栬緫</el-button>
 <!--            <el-button link type="primary" size="small" @click="openForm('view', scope.row)">璇︽儏</el-button>-->
             <el-button link type="primary" size="small" @click="downLoadFile(scope.row)">闄勪欢</el-button>
+            <el-button link type="primary" size="small" @click="exportSaleOutbound(scope.row)">鎵撳嵃閿�鍞嚭搴撳崟</el-button>
 <!--            <el-button link type="primary" size="small" @click="openDeliveryForm(scope.row)">鍙戣揣</el-button>-->
           </template>
         </el-table-column>
@@ -132,6 +137,14 @@
     <FormDialog v-model="dialogFormVisible" :title="operationType === 'add' ? '鏂板閿�鍞彴璐﹂〉闈�' : '缂栬緫閿�鍞彴璐﹂〉闈�'" :width="'70%'"
       :operation-type="operationType" @close="closeDia" @confirm="submitForm" @cancel="closeDia">
       <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef">
+				<!-- 鎶ヤ环鍗曞鍏ュ叆鍙o細鏀惧湪琛ㄥ崟椤堕儴锛岄�夋嫨鍚庡弽鏄惧鎴�/涓氬姟鍛樼瓑 -->
+				<el-row v-if="operationType === 'add'" style="margin-bottom: 10px;">
+					<el-col :span="24" style="text-align: right;">
+						<el-button type="primary" plain @click="openQuotationDialog">
+							浠庨攢鍞姤浠峰鍏�
+						</el-button>
+					</el-col>
+				</el-row>
         <el-row :gutter="30">
           <el-col :span="12">
             <el-form-item label="閿�鍞悎鍚屽彿锛�" prop="salesContractNo">
@@ -160,24 +173,12 @@
             </el-form-item>
           </el-col>
 					<el-col :span="12">
-						<el-form-item label="椤圭洰鍚嶇О锛�" prop="projectName">
-							<el-input v-model="form.projectName" placeholder="璇疯緭鍏�" clearable :disabled="operationType === 'view'" />
-						</el-form-item>
-					</el-col>
-        </el-row>
-        <el-row :gutter="30">
-					<el-col :span="12">
 						<el-form-item label="绛捐鏃ユ湡锛�" prop="executionDate">
 							<el-date-picker style="width: 100%" v-model="form.executionDate" value-format="YYYY-MM-DD"
 															format="YYYY-MM-DD" type="date" placeholder="璇烽�夋嫨" clearable :disabled="operationType === 'view'" />
 						</el-form-item>
 					</el-col>
-					<el-col :span="12">
-						<el-form-item label="浠樻鏂瑰紡">
-							<el-input v-model="form.paymentMethod" placeholder="璇疯緭鍏�" clearable :disabled="operationType === 'view'" />
-						</el-form-item>
-					</el-col>
-				</el-row>
+        </el-row>
 				<el-row :gutter="30">
 					<el-col :span="12">
 						<el-form-item label="褰曞叆浜猴細" prop="entryPerson">
@@ -196,6 +197,14 @@
 						</el-form-item>
 					</el-col>
 				</el-row>
+        <el-row :gutter="30">
+          <el-col :span="12">
+            <el-form-item label="浜よ揣鏃ユ湡锛�" prop="entryDate">
+              <el-date-picker style="width: 100%" v-model="form.deliveryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD"
+                              type="date" placeholder="璇烽�夋嫨" clearable />
+            </el-form-item>
+          </el-col>
+        </el-row>
 				<el-row>
 					<el-form-item label="浜у搧淇℃伅锛�" prop="entryDate">
 						<el-button v-if="operationType !== 'view'" type="primary" @click="openProductForm('add')">娣诲姞</el-button>
@@ -204,10 +213,13 @@
 				</el-row>
 				<el-table :data="productData" border @selection-change="productSelected" show-summary
 									:summary-method="summarizeMainTable">
-					<el-table-column align="center" type="selection" width="55" v-if="operationType !== 'view'" />
+					<el-table-column align="center" type="selection" width="55" v-if="operationType !== 'view'"
+						:selectable="(row) => !isProductShipped(row)" />
 					<el-table-column align="center" label="搴忓彿" type="index" width="60" />
 					<el-table-column label="浜у搧澶х被" prop="productCategory" />
 					<el-table-column label="瑙勬牸鍨嬪彿" prop="specificationModel" />
+					<el-table-column label="UID鐮�" prop="uidNo" />
+					<el-table-column label="鎵瑰彿" prop="batchNo" />
 					<el-table-column label="鍗曚綅" prop="unit" />
 					<el-table-column label="鏁伴噺" prop="quantity" />
 					<el-table-column label="绋庣巼(%)" prop="taxRate" />
@@ -216,20 +228,22 @@
 					<el-table-column label="涓嶅惈绋庢�讳环(鍏�)" prop="taxExclusiveTotalPrice" :formatter="formattedNumber" />
 					<el-table-column fixed="right" label="鎿嶄綔" min-width="60" align="center" v-if="operationType !== 'view'">
 						<template #default="scope">
-							<el-button link type="primary" size="small" @click="openProductForm('edit', scope.row,scope.$index)">缂栬緫</el-button>
+							<el-button link type="primary" size="small" 
+								:disabled="isProductShipped(scope.row)"
+								@click="openProductForm('edit', scope.row,scope.$index)">缂栬緫</el-button>
 						</template>
 					</el-table-column>
 				</el-table>
 				<el-row :gutter="30">
 					<el-col :span="24">
-						<el-form-item label="澶囨敞路锛�" prop="remark">
-							<el-input v-model="form.remark" placeholder="璇疯緭鍏�" clearable type="textarea" :rows="2" :disabled="operationType === 'view'" />
+						<el-form-item label="澶囨敞锛�" prop="remarks">
+							<el-input v-model="form.remarks" placeholder="璇疯緭鍏�" clearable type="textarea" :rows="2" :disabled="operationType === 'view'" />
 						</el-form-item>
 					</el-col>
 				</el-row>
 				<el-row :gutter="30">
 					<el-col :span="24">
-						<el-form-item label="闄勪欢鏉愭枡锛�" prop="remark">
+						<el-form-item label="闄勪欢鏉愭枡锛�" prop="salesLedgerFiles">
 							<el-upload v-model:file-list="fileList" :action="upload.url" multiple ref="fileUpload" auto-upload
 												 :headers="upload.headers" :before-upload="handleBeforeUpload" :on-error="handleUploadError"
 												 :on-success="handleUploadSuccess" :on-remove="handleRemove">
@@ -285,7 +299,7 @@
 				<el-table-column prop="customer" label="瀹㈡埛鍚嶇О" min-width="220" show-overflow-tooltip />
 				<el-table-column prop="salesperson" label="涓氬姟鍛�" width="120" show-overflow-tooltip />
 				<el-table-column prop="quotationDate" label="鎶ヤ环鏃ユ湡" width="140" />
-				<el-table-column prop="status" label="瀹℃壒鐘舵��" width="120" align="center" />
+				<!-- <el-table-column prop="status" label="瀹℃壒鐘舵��" width="120" align="center" /> -->
 				<el-table-column prop="totalAmount" label="鎶ヤ环閲戦(鍏�)" width="160" align="right">
 					<template #default="scope">
 						{{ Number(scope.row.totalAmount ?? 0).toFixed(2) }}
@@ -297,6 +311,15 @@
 					</template>
 				</el-table-column>
 			</el-table>
+			
+			<pagination
+				v-show="quotationPage.total > 0"
+				:total="quotationPage.total"
+				layout="total, sizes, prev, pager, next, jumper"
+				:page="quotationPage.current"
+				:limit="quotationPage.size"
+				@pagination="quotationPaginationChange"
+			/>
 			
 			<template #footer>
 				<el-button @click="quotationDialogVisible = false">鍏抽棴</el-button>
@@ -331,6 +354,39 @@
 						</el-form-item>
 					</el-col>
 				</el-row>
+        <el-row :gutter="30">
+          <el-col :span="24">
+            <el-form-item label="UID鐮侊細" prop="uidNo">
+              <el-input v-model="productForm.uidNo" placeholder="璇疯緭鍏�" disabled />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="30">
+          <el-col :span="24">
+            <el-form-item label="鎵瑰彿锛�" prop="batchNo">
+              <el-select v-model="productForm.batchNo"
+                          placeholder="璇烽�夋嫨"
+                          clearable
+                          filterable
+                          @change="handleBatchNoChange">
+                <el-option v-for="item in batchNoOptions" :key="item.value" :label="item.label" :value="item.value" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="30">
+          <el-col :span="24">
+            <el-form-item label="渚涘簲鍟嗭細" prop="customer">
+              <el-select v-model="productForm.customer"
+                          placeholder="璇烽�夋嫨"
+                          clearable
+                          filterable
+                          :disabled="!supplierOptions.length">
+                <el-option v-for="item in supplierOptions" :key="item.value" :label="item.label" :value="item.value" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
 				<el-row :gutter="30">
 					<el-col :span="12">
 						<el-form-item label="鍗曚綅锛�" prop="unit">
@@ -483,6 +539,7 @@
 										<th>浜у搧鍚嶇О</th>
 										<th>瑙勬牸鍨嬪彿</th>
 										<th>鍗曚綅</th>
+										<th>UID鐮�</th>
 										<th>鍗曚环</th>
 										<th>闆跺敭鏁伴噺</th>
 										<th>闆跺敭閲戦</th>
@@ -493,6 +550,7 @@
 										<td>{{ product.productCategory || '' }}</td>
 										<td>{{ product.specificationModel || '' }}</td>
 										<td>{{ product.unit || '' }}</td>
+										<td>{{ product.uidNo || '' }}</td>
 										<td>{{ product.taxInclusiveUnitPrice || '0' }}</td>
 										<td>{{ product.quantity || '0' }}</td>
 										<td>{{ product.taxInclusiveTotalPrice || '0' }}</td>
@@ -567,51 +625,6 @@
 						</el-form-item>
 					</el-col>
 				</el-row>
-
-        <!-- 瀹℃壒浜洪�夋嫨锛堜豢鍗忓悓瀹℃壒閲岀殑瀹℃壒浜鸿妭鐐归�夋嫨锛� -->
-        <el-row>
-          <el-col :span="24">
-            <el-form-item>
-              <template #label>
-                <span>瀹℃壒浜洪�夋嫨锛�</span>
-                <el-button type="primary" @click="addApproverNode" style="margin-left: 8px;">鏂板鑺傜偣</el-button>
-              </template>
-              <div style="display: flex; align-items: flex-end; flex-wrap: wrap;">
-                <div
-                  v-for="(node, index) in approverNodes"
-                  :key="node.id"
-                  style="margin-right: 20px; text-align: center; margin-bottom: 10px;"
-                >
-                  <div>
-                    <span>瀹℃壒浜�</span>
-                    鈫�
-                  </div>
-                  <el-select
-                    v-model="node.userId"
-                    placeholder="閫夋嫨浜哄憳"
-                    filterable
-                    style="width: 140px; margin-bottom: 8px;"
-                  >
-                    <el-option
-                      v-for="user in userList"
-                      :key="user.userId"
-                      :label="user.nickName"
-                      :value="user.userId"
-                    />
-                  </el-select>
-                  <div>
-                    <el-button
-                      type="danger"
-                      size="small"
-                      @click="removeApproverNode(index)"
-                      v-if="approverNodes.length > 1"
-                    >鍒犻櫎</el-button>
-                  </div>
-                </div>
-              </div>
-            </el-form-item>
-          </el-col>
-        </el-row>
 			</el-form>
 			<template #footer>
 				<div class="dialog-footer">
@@ -636,20 +649,22 @@
 import FormDialog from '@/components/Dialog/FormDialog.vue';
 import { getQuotationList } from "@/api/salesManagement/salesQuotation.js";
 import {
-	ledgerListPage,
-	productList,
-	customerList,
-	addOrUpdateSalesLedger,
-	getSalesLedgerWithProducts,
-	delLedger,
-	addOrUpdateSalesLedgerProduct,
-	delProduct,
-	delLedgerFile, getProductInventory,
+  ledgerListPage,
+  productList,
+  customerList,
+  addOrUpdateSalesLedger,
+  getSalesLedgerWithProducts,
+  delLedger,
+  addOrUpdateSalesLedgerProduct,
+  delProduct,
+  delLedgerFile, getProductInventory, saleOutboundExport,
 } from "@/api/salesManagement/salesLedger.js";
-import { modelList, productTreeList } from "@/api/basicData/product.js";
+import { getStockInventoryAll } from "@/api/inventoryManagement/stockInventory.js";
 import useFormData from "@/hooks/useFormData.js";
 import dayjs from "dayjs";
 import { getCurrentDate } from "@/utils/index.js";
+// 鐢� /stockInventory/getStockInventoryAll 椹卞姩鈥滄壒鍙�/渚涘簲鍟嗏�濊仈鍔�
+import {safeTrainingExport} from "@/api/safeProduction/safetyTrainingAssessment.js";
 
 const userStore = useUserStore();
 const { proxy } = getCurrentInstance();
@@ -661,6 +676,7 @@
 const customerOption = ref([]);
 const productOptions = ref([]);
 const modelOptions = ref([]);
+const supplierOptions = ref([]);
 const tableLoading = ref(false);
 const page = reactive({
 	current: 1,
@@ -686,6 +702,7 @@
 		customerId: "",
 		entryPerson: "",
 		entryDate: "",
+    deliveryDate: "",
 		maintenanceTime: "",
 		productData: [],
 		executionDate: "",
@@ -695,6 +712,7 @@
 		customerId: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
 		entryPerson: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
 		entryDate: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
+    deliveryDate: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
 		executionDate: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
 	},
 });
@@ -707,7 +725,9 @@
 const productFormData = reactive({
 	productForm: {
 		productCategory: "",
+		customer: "",
 		specificationModel: "",
+    uidNo: "",
 		unit: "",
 		quantity: "",
 		taxInclusiveUnitPrice: "",
@@ -715,10 +735,13 @@
 		taxInclusiveTotalPrice: "",
 		taxExclusiveTotalPrice: "",
 		invoiceType: "",
+    batchNo: "",
 	},
 	productRules: {
 		productCategory: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
 		productModelId: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
+    batchNo: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
+		customer: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
 		specificationModel: [
 			{ required: true, message: "璇烽�夋嫨", trigger: "change" },
 		],
@@ -757,6 +780,12 @@
 const quotationSearchForm = reactive({
 	quotationNo: "",
 	customer: "",
+});
+// 鎶ヤ环鍗曞脊妗嗗垎椤�
+const quotationPage = reactive({
+	current: 1,
+	size: 10,
+	total: 0,
 });
 const selectedQuotation = ref(null);
 
@@ -881,62 +910,203 @@
 			tableLoading.value = false;
 		});
 };
-// 鑾峰彇浜у搧澶х被tree鏁版嵁
-const getProductOptions = () => {
+let stockInventoryAllTree = [];
+let batchNodeByBatchNo = new Map();
+
+const normalizeStockInventoryTree = (nodes = []) => {
+	const normalizeNodeValue = (node) => {
+		// 鍚庣鏈夋椂浼氬嚭鐜� id=null 鐨勫眰绾э紝杩欓噷缁欎竴涓彲鐢ㄧ殑 key
+		if (node?.id !== null && node?.id !== undefined) return String(node.id);
+		if (node?.nodeType === "batch") return String(node.batchNo ?? node.label ?? "");
+		if (node?.nodeType === "customer") return String(node.customer ?? node.label ?? "");
+		if (node?.nodeType === "model") return String(node.productModelId ?? node.model ?? node.label ?? "");
+		return String(node.productName ?? node.label ?? "");
+	};
+
+	const normalized = (list) =>
+		(list || []).map((n) => {
+			const value = normalizeNodeValue(n);
+			const label = n.label ?? n.productName ?? n.model ?? n.batchNo ?? n.customer ?? "";
+			return {
+				...n,
+				value,
+				label,
+				children: normalized(n.children),
+			};
+		});
+
+	return normalized(nodes);
+};
+
+// 浠呭睍绀烘渶澶� 3 涓眰绾э細绗� 1 灞�(product) -> 绗� 2 灞�(model) -> 绗� 3 灞�(batch)锛屾洿娣辩殑鑺傜偣涓嶅睍绀�
+const filterStockInventoryFirst3Levels = (nodes = []) => {
+	const MAX_LEVEL = 3;
+
+	const cloneAndFilterByLevel = (list = [], level = 1) => {
+		return (list || [])
+			.map((n) => {
+				// 鍚庣画灞傜骇閲屽鏋滆繕鏈� customer锛岀洿鎺ュ墧闄�
+				if (n.nodeType === "customer") return null;
+
+				// 鍒拌揪灞曠ず娣卞害鍚庯紝涓嶅啀鍚戜笅鎸傚瓙鑺傜偣
+				if (level >= MAX_LEVEL) {
+					return { ...n, children: [] };
+				}
+
+				// 鐗逛緥锛歜atch 鑺傜偣鏈韩涔熶笉鍐嶅睍绀� children锛堜繚鎸佷笌鎺ュ彛鑺傜偣璇箟涓�鑷达級
+				if (n.nodeType === "batch") {
+					return { ...n, children: [] };
+				}
+
+				return { ...n, children: cloneAndFilterByLevel(n.children, level + 1) };
+			})
+			.filter(Boolean);
+	};
+
+	return cloneAndFilterByLevel(nodes, 1);
+};
+
+const findNodeObjByValue = (nodes = [], value) => {
+	for (let i = 0; i < (nodes || []).length; i++) {
+		const node = nodes[i];
+		if (String(node?.value) === String(value)) return node;
+		const children = node?.children || [];
+		if (children.length) {
+			const found = findNodeObjByValue(children, value);
+			if (found) return found;
+		}
+	}
+	return null;
+};
+
+// 鑾峰彇搴撳瓨鏍戯紙鐢ㄤ簬浜у搧澶х被/瑙勬牸鍨嬪彿鑱斿姩锛�
+const getProductOptions = async () => {
 	// 杩斿洖 Promise锛屼究浜庡湪缂栬緫浜у搧鏃剁瓑寰呭姞杞藉畬鎴�
-	return productTreeList().then((res) => {
-		productOptions.value = convertIdToValue(res);
-		return productOptions.value;
-	});
+	const res = await getStockInventoryAll();
+	const data = res?.data || [];
+	stockInventoryAllTree = normalizeStockInventoryTree(data);
+	productOptions.value = filterStockInventoryFirst3Levels(stockInventoryAllTree);
+	return productOptions.value;
 };
 const formattedNumber = (row, column, cellValue) => {
 	return parseFloat(cellValue).toFixed(2);
 };
-// 鑾峰彇tree瀛愭暟鎹�
+// 鑾峰彇tree瀛愭暟鎹紙鍏堥�変骇鍝侊紝鍐嶉�夎鏍煎瀷鍙凤級
 const getModels = (value) => {
-	productForm.value.productCategory = findNodeById(productOptions.value, value);
-	modelList({ id: value }).then((res) => {
-		modelOptions.value = res;
-	});
+	const node = findNodeObjByValue(stockInventoryAllTree, value);
+	if (!node) return;
+	if (node.nodeType !== "product") return;
+
+	// 閫夋嫨浜у搧鍚庯紝閲嶇疆涓嬫父瀛楁
+	productForm.value.productCategory = node.label;
+	modelOptions.value = (node.children || [])
+		.filter((c) => c.nodeType === "model")
+		.map((m) => ({
+			id: m.value,
+			model: m.model ?? m.label ?? "",
+			unit: m.unit ?? "",
+			uidNo: m.uidNo ?? m.identifierCode ?? "",
+		}));
+
+	productForm.value.productModelId = null;
+	productForm.value.specificationModel = "";
+	productForm.value.uidNo = "";
+	productForm.value.unit = "";
+	productForm.value.batchNo = "";
+	productForm.value.customer = "";
+	productForm.value.taxInclusiveUnitPrice = "";
+	productForm.value.taxInclusiveTotalPrice = "";
+	productForm.value.taxExclusiveTotalPrice = "";
+
+	modelOptions.value = modelOptions.value || [];
+	batchNoOptions.value = [];
+	supplierOptions.value = [];
+	batchNodeByBatchNo = new Map();
 };
+
+// 瑙勬牸鍨嬪彿閫夋嫨鍚庯細鍥炴樉 UID锛屽苟鐢熸垚鈥滄壒鍙蜂笅鎷夆��
 const getProductModel = (value) => {
-	const index = modelOptions.value.findIndex((item) => item.id === value);
-	if (index !== -1) {
-		productForm.value.specificationModel = modelOptions.value[index].model;
-		productForm.value.unit = modelOptions.value[index].unit;
+	const modelNode = findNodeObjByValue(stockInventoryAllTree, value);
+	if (!modelNode || modelNode.nodeType !== "model") return;
+
+	const prevBatchNo = productForm.value.batchNo;
+	const prevCustomer = productForm.value.customer;
+
+	productForm.value.productModelId = modelNode.value;
+	productForm.value.specificationModel = modelNode.model ?? modelNode.label ?? "";
+	// 鏈変簺鎺ュ彛/鏍戞暟鎹噷鍙兘涓嶅寘鍚� unit锛岃繖绉嶆儏鍐典笅涓嶈瑕嗙洊缂栬緫鏃跺凡鍥炴樉鐨勫��
+	const nextUnit = modelNode.unit ?? "";
+	if (nextUnit !== null && nextUnit !== undefined && String(nextUnit).trim() !== "") {
+		productForm.value.unit = nextUnit;
+	}
+	// 鏈変簺鎺ュ彛/鏍戞暟鎹噷鍙兘涓嶅寘鍚� uidNo锛岃繖绉嶆儏鍐典笅涓嶈瑕嗙洊缂栬緫鏃跺凡鍥炴樉鐨勫��
+	const nextUidNo = modelNode.uidNo ?? modelNode.identifierCode ?? "";
+	if (nextUidNo !== null && nextUidNo !== undefined && String(nextUidNo).trim() !== "") {
+		productForm.value.uidNo = nextUidNo;
+	}
+
+	const batchNodes = (modelNode.children || []).filter((b) => b.nodeType === "batch");
+	batchNodeByBatchNo = new Map(
+		batchNodes.map((b) => {
+			const key = String(b.batchNo ?? b.value ?? b.label ?? "").trim();
+			return [key, b];
+		})
+	);
+	batchNoOptions.value = batchNodes.map((b) => ({
+		label: String(b.batchNo ?? b.label ?? "").trim(),
+		value: String(b.batchNo ?? b.value ?? b.label ?? "").trim(),
+	}));
+
+	// 鎵瑰彿涓嶅啀灞炰簬鏂拌鏍兼椂锛屾竻绌�
+	const batchValues = new Set(batchNoOptions.value.map((x) => x.value));
+	if (!prevBatchNo || !batchValues.has(prevBatchNo)) {
+		productForm.value.batchNo = "";
+	}
+
+	// 闇�瑕佷緵搴斿晢锛氭壒鍙峰洖鏄惧悗鍐嶇敓鎴�
+	productForm.value.customer = "";
+	supplierOptions.value = [];
+	if (productForm.value.batchNo) {
+		handleBatchNoChange(productForm.value.batchNo, prevCustomer);
+	}
+};
+
+const handleBatchNoChange = (batchNo, prevCustomer) => {
+	const safeBatchNo = String(batchNo ?? "").trim();
+	if (!safeBatchNo || !batchNodeByBatchNo.size) {
+		productForm.value.customer = "";
+		supplierOptions.value = [];
+		return;
+	}
+
+	const batchNode = batchNodeByBatchNo.get(String(safeBatchNo));
+	if (!batchNode) {
+		productForm.value.customer = "";
+		supplierOptions.value = [];
+		return;
+	}
+
+	// UID鐮佸彲鑳芥潵婧愪簬 batch 鑺傜偣锛堜笉鍚屾帴鍙e瓧娈靛悕涓嶄竴鑷存椂灏介噺鍏滃簳锛�
+	const nextUidNo = batchNode.uidNo ?? batchNode.identifierCode ?? batchNode.uid ?? "";
+	if (nextUidNo !== null && nextUidNo !== undefined && String(nextUidNo).trim() !== "") {
+		productForm.value.uidNo = nextUidNo;
+	}
+
+	const customers = (batchNode.children || [])
+		.filter((c) => c.nodeType === "customer")
+		.map((c) => c.customer ?? c.label ?? "")
+		.filter(Boolean);
+
+	const uniq = Array.from(new Set(customers));
+	supplierOptions.value = uniq.map((s) => ({ label: s, value: s }));
+
+	// 缂栬緫鍦烘櫙灏介噺鍥炴樉锛涙柊澧炲満鏅笉鍥炴樉
+	if (prevCustomer && uniq.includes(prevCustomer)) {
+		productForm.value.customer = prevCustomer;
 	} else {
-		productForm.value.specificationModel = null;
-		productForm.value.unit = null;
+		productForm.value.customer = "";
 	}
 };
-const findNodeById = (nodes, productId) => {
-	for (let i = 0; i < nodes.length; i++) {
-		if (nodes[i].value === productId) {
-			return nodes[i].label; // 鎵惧埌鑺傜偣锛岃繑鍥炶鑺傜偣
-		}
-		if (nodes[i].children && nodes[i].children.length > 0) {
-			const foundNode = findNodeById(nodes[i].children, productId);
-			if (foundNode) {
-				return foundNode; // 鍦ㄥ瓙鑺傜偣涓壘鍒帮紝杩斿洖璇ヨ妭鐐�
-			}
-		}
-	}
-	return null; // 娌℃湁鎵惧埌鑺傜偣锛岃繑鍥瀗ull
-};
-function convertIdToValue(data) {
-	return data.map((item) => {
-		const { id, children, ...rest } = item;
-		const newItem = {
-			...rest,
-			value: id, // 灏� id 鏀逛负 value
-		};
-		if (children && children.length > 0) {
-			newItem.children = convertIdToValue(children);
-		}
-		
-		return newItem;
-	});
-}
 // 鏍规嵁鍚嶇О鍙嶆煡浜у搧澶х被 id锛屼究浜庝粎瀛樺悕绉版椂鐨勫弽鏄�
 function findNodeIdByLabel(nodes, label) {
 	if (!label) return null;
@@ -978,6 +1148,23 @@
 	} else {
 		expandedRowKeys.value = [];
 	}
+};
+
+// 娣诲姞琛ㄨ绫诲悕鏂规硶
+const tableRowClassName = ({ row }) => {
+  if (!row.deliveryDate) return '';
+  if (row.isFh) return '';
+
+  const diff = row.deliveryDaysDiff;
+  if (diff === 15) {
+    return 'yellow';
+  } else if (diff === 10) {
+    return 'pink';
+  } else if (diff === 2) {
+    return 'purple';
+  } else if (diff < 2) {
+    return 'red';
+  }
 };
 // 涓昏〃鍚堣鏂规硶
 const summarizeMainTable = (param) => {
@@ -1035,6 +1222,8 @@
 const openQuotationDialog = async () => {
 	if (operationType.value === "view") return;
 	quotationDialogVisible.value = true;
+	// 鎵撳紑寮圭獥鏃堕噸缃垎椤靛埌绗竴椤�
+	quotationPage.current = 1;
 	// 鍏堢‘淇濆鎴峰垪琛ㄥ凡鍔犺浇锛屼究浜庡悗缁洖濉� customerId
 	if (!customerOption.value || customerOption.value.length === 0) {
 		try {
@@ -1051,14 +1240,15 @@
 	quotationLoading.value = true;
 	try {
 		const params = {
-			// 鍏煎鍚庣鍒嗛〉瀛楁锛氳繖閲屾部鐢ㄦ姤浠烽〉闈㈠凡鏈夊彲鐢ㄧ殑瀛楁鍛藉悕
-			currentPage: 1,
-			pageSize: 100,
+			// 鍚庣鍒嗛〉瀛楁锛歝urrent / size
+			current: quotationPage.current,
+			size: quotationPage.size,
 			...quotationSearchForm,
 			status: "閫氳繃",
 		};
 		const res = await getQuotationList(params);
 		quotationList.value = res?.data?.records || [];
+		quotationPage.total = res?.data?.total || 0;
 	} finally {
 		quotationLoading.value = false;
 	}
@@ -1067,7 +1257,15 @@
 const resetQuotationSearch = async () => {
 	quotationSearchForm.quotationNo = "";
 	quotationSearchForm.customer = "";
+	quotationPage.current = 1;
 	await fetchQuotationList();
+};
+
+// 鎶ヤ环鍗曞脊妗嗗垎椤靛垏鎹�
+const quotationPaginationChange = (obj) => {
+	quotationPage.current = obj.page;
+	quotationPage.size = obj.limit;
+	fetchQuotationList();
 };
 
 // 閫変腑鎶ヤ环鍗曞悗鍥炲~鍒板彴璐﹁〃鍗�
@@ -1076,10 +1274,14 @@
 	selectedQuotation.value = row;
 	
 	// 涓氬姟鍛�
-	form.value.salesman = row.salesperson || "";
+	form.value.salesman = (row.salesperson || "").trim();
 	
 	// 瀹㈡埛鍚嶇О -> customerId
-	const customer = (customerOption.value || []).find((c) => c.customerName === row.customer);
+	const qCustomerName = String(row.customer || "").trim();
+	const customer = (customerOption.value || []).find((c) => {
+		const name = String(c.customerName || "").trim();
+		return name === qCustomerName || name.includes(qCustomerName) || qCustomerName.includes(name);
+	});
 	if (customer?.id) {
 		form.value.customerId = customer.id;
 	} else {
@@ -1098,7 +1300,9 @@
 		return {
 			// 鍙拌处瀛楁
 			productCategory: p.product || p.productName || "",
+			productModelId: p.productModelId || "",
 			specificationModel: p.specification || "",
+			uidNo: p.uidNo || "",
 			unit: p.unit || "",
 			quantity: quantity,
 			taxRate: taxRate,
@@ -1175,6 +1379,8 @@
 		}
 	});
 };
+
+const batchNoOptions = ref([]);
 // 鍏抽棴寮规
 const closeDia = () => {
 	proxy.resetForm("formRef");
@@ -1184,6 +1390,12 @@
 const productIndex = ref(0);
 // 鎵撳紑浜у搧寮规
 const openProductForm = async (type, row, index) => {
+	// 缂栬緫鏃舵鏌ヤ骇鍝佹槸鍚﹀凡鍙戣揣鎴栧鏍搁�氳繃
+	if (type === "edit" && isProductShipped(row)) {
+		proxy.$modal.msgWarning("宸插彂璐ф垨瀹℃牳閫氳繃鐨勪骇鍝佷笉鑳界紪杈�");
+		return;
+	}
+	
 	productOperationType.value = type;
 	productForm.value = {};
 	proxy.resetForm("productFormRef");
@@ -1192,25 +1404,44 @@
 		productIndex.value = index;
 		// 缂栬緫鏃舵牴鎹骇鍝佸ぇ绫诲悕绉板弽鏌� tree 鑺傜偣 id锛屽苟鍔犺浇瑙勬牸鍨嬪彿鍒楄〃
 		try {
-			const options = productOptions.value && productOptions.value.length > 0
-				? productOptions.value
-				: await getProductOptions();
-			const categoryId = findNodeIdByLabel(options, productForm.value.productCategory);
-			if (categoryId) {
-				const models = await modelList({ id: categoryId });
-				modelOptions.value = models || [];
-				// 鏍规嵁褰撳墠瑙勬牸鍨嬪彿鍚嶇О鍙嶆煡骞惰缃� productModelId锛屼究浜庝笅鎷夋鏄剧ず宸查�夊��
-				const currentModel = (modelOptions.value || []).find(
-					(m) => m.model === productForm.value.specificationModel
-				);
+			if (!productOptions.value || productOptions.value.length === 0) {
+				await getProductOptions();
+			}
+
+			// 鍥炴樉锛氭牴鎹�滀骇鍝佸ぇ绫烩�濆弽鏌ヤ骇鍝佽妭鐐�
+			const categoryKey = findNodeIdByLabel(productOptions.value, productForm.value.productCategory);
+			if (categoryKey) {
+				const categoryNode = findNodeObjByValue(stockInventoryAllTree, categoryKey);
+				const models = (categoryNode?.children || [])
+					.filter((n) => n.nodeType === "model")
+					.map((m) => ({
+						id: m.value,
+						model: m.model ?? m.label ?? "",
+						unit: m.unit ?? "",
+						uidNo: m.uidNo ?? m.identifierCode ?? "",
+					}));
+				modelOptions.value = models;
+
+				// 鏍规嵁褰撳墠瑙勬牸鍨嬪彿鍥炴樉
+				const targetSpec = String(productForm.value.specificationModel ?? "").trim();
+				const currentModel =
+					(models || []).find((m) => String(m.model ?? "").trim() === targetSpec) ||
+					(models || []).find((m) => String(m.model ?? "").trim().includes(targetSpec)) ||
+					(models || []).find((m) => targetSpec.includes(String(m.model ?? "").trim()));
 				if (currentModel) {
+					productForm.value.customer = productForm.value.customer || row.customer || row.supplierName || "";
 					productForm.value.productModelId = currentModel.id;
+					getProductModel(currentModel.id);
 				}
 			}
 		} catch (e) {
 			// 鍔犺浇澶辫触鏃朵繚鎸佸彲缂栬緫锛屼笉涓柇寮圭獥
 			console.error("鍔犺浇浜у搧瑙勬牸鍨嬪彿澶辫触", e);
 		}
+		// 鏈�缁堝厹搴曪細濡傛灉涓�旇閲嶇疆娓呯┖锛岃嚦灏戝洖鏄捐鏁版嵁閲岀殑 UID
+		productForm.value.uidNo = row.uidNo ?? productForm.value.uidNo ?? "";
+		// 鏈�缁堝厹搴曪細鍚屾牱淇濊瘉鍗曚綅涓嶄細鍥犳爲鏁版嵁缂哄け鑰岃瑕嗙洊涓虹┖
+		productForm.value.unit = row.unit ?? productForm.value.unit ?? "";
 	} else {
 		getProductOptions()
 	}
@@ -1250,6 +1481,14 @@
 		proxy.$modal.msgWarning("璇烽�夋嫨鏁版嵁");
 		return;
 	}
+	
+	// 妫�鏌ユ槸鍚︽湁宸插彂璐ф垨瀹℃牳閫氳繃鐨勪骇鍝�
+	const shippedProducts = productSelectedRows.value.filter(row => isProductShipped(row));
+	if (shippedProducts.length > 0) {
+		proxy.$modal.msgWarning("宸插彂璐ф垨瀹℃牳閫氳繃鐨勪骇鍝佷笉鑳藉垹闄�");
+		return;
+	}
+	
 	if (operationType.value === "add") {
 		productSelectedRows.value.forEach((selectedRow) => {
 			const index = productData.value.findIndex(
@@ -1324,15 +1563,55 @@
 			proxy.$modal.msg("宸插彇娑�");
 		});
 };
+/** 鍒ゆ柇鍗曚釜浜у搧鏄惁宸插彂璐э紙鏍规嵁shippingStatus鍒ゆ柇锛屽凡鍙戣揣鎴栧鏍搁�氳繃涓嶅彲缂栬緫鍜屽垹闄わ級 */
+const isProductShipped = (product) => {
+	if (!product) return false;
+	const status = String(product.shippingStatus || "").trim();
+	// 濡傛灉鍙戣揣鐘舵�佹槸"宸插彂璐�"鎴�"瀹℃牳閫氳繃"锛屽垯涓嶅彲缂栬緫鍜屽垹闄�
+	return status === "宸插彂璐�" || status === "瀹℃牳閫氳繃";
+};
+
+/** 鍒ゆ柇閿�鍞鍗曚笅鏄惁瀛樺湪宸插彂璐�/鍙戣揣瀹屾垚鐨勪骇鍝侊紙涓嶅彲鍒犻櫎锛� */
+const hasShippedProducts = (products) => {
+	if (!products || !products.length) return false;
+	return products.some((p) => {
+		const status = String(p.shippingStatus || "").trim();
+		// 鏈夊彂璐ф棩鏈熸垨杞︾墝鍙疯涓哄凡鍙戣揣
+		if (p.shippingDate || p.shippingCarNumber) return true;
+		// 宸茶繘琛屽彂璐с�佸彂璐у畬鎴愩�佸凡鍙戣揣 鍧囦笉鍙垹闄�
+		return status === "宸茶繘琛屽彂璐�" || status === "鍙戣揣瀹屾垚" || status === "宸插彂璐�";
+	});
+};
+
 // 鍒犻櫎
-const handleDelete = () => {
-	let ids = [];
-	if (selectedRows.value.length > 0) {
-		ids = selectedRows.value.map((item) => item.id);
-	} else {
+const handleDelete = async () => {
+	if (selectedRows.value.length === 0) {
 		proxy.$modal.msgWarning("璇烽�夋嫨鏁版嵁");
 		return;
 	}
+	const ids = selectedRows.value.map((item) => item.id);
+
+	// 妫�鏌ユ槸鍚︽湁宸茶繘琛屽彂璐ф垨鍙戣揣瀹屾垚鐨勯攢鍞鍗曪紝鑻ユ湁鍒欎笉鍏佽鍒犻櫎
+	const cannotDeleteNames = [];
+	for (const row of selectedRows.value) {
+		let products = row.children && row.children.length > 0 ? row.children : null;
+		if (!products) {
+			try {
+				const res = await productList({ salesLedgerId: row.id, type: 1 });
+				products = res.data || [];
+			} catch {
+				products = [];
+			}
+		}
+		if (hasShippedProducts(products)) {
+			cannotDeleteNames.push(row.salesContractNo || `ID:${row.id}`);
+		}
+	}
+	if (cannotDeleteNames.length > 0) {
+		proxy.$modal.msgWarning("宸茶繘琛屽彂璐ф垨鍙戣揣瀹屾垚鐨勯攢鍞鍗曚笉鑳藉垹闄わ細" + cannotDeleteNames.join("銆�"));
+		return;
+	}
+
 	ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�", "瀵煎嚭", {
 		confirmButtonText: "纭",
 		cancelButtonText: "鍙栨秷",
@@ -1574,6 +1853,7 @@
                   <th>浜у搧鍚嶇О</th>
                   <th>瑙勬牸鍨嬪彿</th>
                   <th>鍗曚綅</th>
+                  <th>UID鐮�</th>
                   <th>鍗曚环</th>
                   <th>闆跺敭鏁伴噺</th>
                   <th>闆跺敭閲戦</th>
@@ -1586,6 +1866,7 @@
                       <td>${product.productCategory || ''}</td>
                       <td>${product.specificationModel || ''}</td>
                       <td>${product.unit || ''}</td>
+                      <td>${product.uidNo || ''}</td>
                       <td>${product.taxInclusiveUnitPrice || '0'}</td>
                       <td>${product.quantity || '0'}</td>
                       <td>${product.taxInclusiveTotalPrice || '0'}</td>
@@ -2004,13 +2285,6 @@
 const submitDelivery = () => {
   proxy.$refs["deliveryFormRef"].validate((valid) => {
     if (valid) {
-      // 瀹℃壒浜哄繀濉牎楠岋紙鎵�鏈夎妭鐐归兘瑕侀�変汉锛�
-      const hasEmptyApprover = approverNodes.value.some(node => !node.userId);
-      if (hasEmptyApprover) {
-        proxy.$modal.msgError("璇蜂负鎵�鏈夊鎵硅妭鐐归�夋嫨瀹℃壒浜猴紒");
-        return;
-      }
-      const approveUserIds = approverNodes.value.map(node => node.userId).join(",");
       // 淇濆瓨褰撳墠灞曞紑鐨勮ID锛屼互渚垮彂璐у悗閲嶆柊鍔犺浇瀛愯〃鏍兼暟鎹�
       const currentExpandedKeys = [...expandedRowKeys.value];
       const salesLedgerId = currentDeliveryRow.value.salesLedgerId;
@@ -2018,7 +2292,6 @@
         salesLedgerId: salesLedgerId,
         salesLedgerProductId: currentDeliveryRow.value.id,
         type: deliveryForm.value.type,
-				approveUserIds,
       })
         .then(() => {
           proxy.$modal.msgSuccess("鍙戣揣鎴愬姛");
@@ -2058,6 +2331,38 @@
 	let res = await userStore.getInfo();
 	currentFactoryName.value = res.user.currentFactoryName;
 };
+
+const exportSaleOutbound = row => {
+  saleOutboundExport({id: row.id})
+      .then(res => {
+        // 鍒涘缓Blob瀵硅薄
+        const blob = new Blob([res], {
+          type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
+        });
+        // 鍒涘缓涓嬭浇閾炬帴
+        const url = window.URL.createObjectURL(blob);
+        const link = document.createElement("a");
+        link.href = url;
+        link.download = `閿�鍞嚭搴撳崟.docx`;
+
+        // 妯℃嫙鐐瑰嚮涓嬭浇
+        document.body.appendChild(link);
+        link.click();
+
+        // 娓呯悊涓存椂瀵硅薄
+        setTimeout(() => {
+          document.body.removeChild(link);
+          window.URL.revokeObjectURL(url);
+        }, 100);
+
+        ElMessage.success("瀵煎嚭鎴愬姛");
+      })
+      .catch(err => {
+        console.error("瀵煎嚭澶辫触:", err);
+        ElMessage.error("瀵煎嚭澶辫触锛岃閲嶈瘯");
+      });
+};
+
 onMounted(() => {
 	getList();
 	userListNoPage().then(res => {
@@ -2072,6 +2377,22 @@
 	margin-left: 10px;
 }
 
+::v-deep .yellow {
+  background-color: #FAF0DE;
+}
+
+::v-deep .pink {
+  background-color: #FAE1DE;
+}
+
+::v-deep .red {
+  background-color: #FAE1DE;
+}
+
+::v-deep .purple{
+  background-color: #F4DEFA;
+}
+
 .table_list {
 	margin-top: unset;
 }

--
Gitblit v1.9.3