From 4c8d18cc5ed8a7b0e220c91a858d16d0310896df Mon Sep 17 00:00:00 2001
From: zhangwencui <1064582902@qq.com>
Date: 星期一, 29 六月 2026 16:50:03 +0800
Subject: [PATCH] BOM新增修改删除功能开发、以及BOM结构的可编辑

---
 src/pages/procurementManagement/procurementLedger/detail.vue |  484 +++++++++++++++++++++++++++++++++++++++--------------
 1 files changed, 358 insertions(+), 126 deletions(-)

diff --git a/src/pages/procurementManagement/procurementLedger/detail.vue b/src/pages/procurementManagement/procurementLedger/detail.vue
index de6f687..656972b 100644
--- a/src/pages/procurementManagement/procurementLedger/detail.vue
+++ b/src/pages/procurementManagement/procurementLedger/detail.vue
@@ -15,29 +15,31 @@
                   placeholder="鑷姩鐢熸垚"
                   disabled />
       </up-form-item>
-      <up-form-item label="閿�鍞悎鍚屽彿"
-                    prop="salesContractNo"
-                    required
-                    @click="showPicker = true">
-        <up-input v-model="form.salesContractNo"
-                  readonly=""
-                  @click="showPicker = true"
-                  placeholder="鐐瑰嚮閫夋嫨閿�鍞悎鍚屽彿" />
+      <up-form-item v-if="showSalesContractBinding"
+                    label="閿�鍞悎鍚屽彿"
+                    prop="salesLedgerId">
+        <up-input v-model="salesContractNoText"
+                  placeholder="鐐瑰嚮閫夋嫨閿�鍞悎鍚屽彿"
+                  readonly
+                  :disabled="isReadOnly"
+                  @click="!isReadOnly && openSalesContractPicker()" />
         <template #right>
           <up-icon name="arrow-right"
-                   @click="showPicker = true"></up-icon>
+                   v-if="!isReadOnly"
+                   @click="openSalesContractPicker()"></up-icon>
         </template>
       </up-form-item>
       <up-form-item label="渚涘簲鍟嗗悕绉�"
                     prop="supplierName"
-                    required
-                    @click="showCustomerPicker = true">
+                    required>
         <up-input v-model="form.supplierName"
-                  readonly=""
-                  @click="showCustomerPicker = true"
+                  readonly
+                  :disabled="isReadOnly"
+                  @click="!isReadOnly && (showCustomerPicker = true)"
                   placeholder="鐐瑰嚮閫夋嫨渚涘簲鍟�" />
         <template #right>
           <up-icon name="arrow-right"
+                   v-if="!isReadOnly"
                    @click="showCustomerPicker = true"></up-icon>
         </template>
       </up-form-item>
@@ -45,11 +47,13 @@
                     prop="projectName"
                     required>
         <up-input v-model="form.projectName"
+                  :disabled="isReadOnly"
                   placeholder="璇疯緭鍏ラ」鐩悕绉�" />
       </up-form-item>
       <up-form-item label="浠樻鏂瑰紡"
                     prop="paymentMethod">
         <up-input v-model="form.paymentMethod"
+                  :disabled="isReadOnly"
                   placeholder="璇疯緭鍏ヤ粯娆炬柟寮�" />
       </up-form-item>
       <up-form-item label="绛捐鏃ユ湡"
@@ -57,9 +61,11 @@
                     prop="executionDate">
         <up-input v-model="form.executionDate"
                   placeholder="璇烽�夋嫨"
-                  readonly="" />
+                  readonly
+                  :disabled="isReadOnly" />
         <template #right>
           <up-icon name="arrow-right"
+                   v-if="!isReadOnly"
                    @click="showTimePicker = true"></up-icon>
         </template>
       </up-form-item>
@@ -75,54 +81,19 @@
                   placeholder="璇疯緭鍏�"
                   disabled />
       </up-form-item>
-      <view class="approval-process">
-        <view class="approval-header">
-          <text class="approval-title">瀹℃牳娴佺▼</text>
-          <text class="approval-desc">姣忎釜姝ラ鍙兘閫夋嫨涓�涓鎵逛汉</text>
-        </view>
-        <view class="approval-steps">
-          <view v-for="(step, stepIndex) in approverNodes"
-                :key="stepIndex"
-                class="approval-step">
-            <view class="step-dot"></view>
-            <view class="step-title">
-              <text>瀹℃壒浜�</text>
-            </view>
-            <view class="approver-container">
-              <view v-if="step.nickName"
-                    class="approver-item">
-                <view class="approver-avatar">
-                  <text class="avatar-text">{{ step.nickName.charAt(0) }}</text>
-                  <view class="status-dot"></view>
-                </view>
-                <view class="approver-info">
-                  <text class="approver-name">{{ step.nickName }}</text>
-                </view>
-                <view class="delete-approver-btn"
-                      @click="removeApprover(stepIndex)">脳</view>
-              </view>
-              <view v-else
-                    class="add-approver-btn"
-                    @click="addApprover(stepIndex)">
-                <view class="add-circle">+</view>
-                <text class="add-label">閫夋嫨瀹℃壒浜�</text>
-              </view>
-            </view>
-            <view class="step-line"
-                  v-if="stepIndex < approverNodes.length - 1"></view>
-            <view class="delete-step-btn"
-                  v-if="approverNodes.length > 1"
-                  @click="removeApprovalStep(stepIndex)">鍒犻櫎鑺傜偣</view>
-          </view>
-        </view>
-        <view class="add-step-btn">
-          <u-button icon="plus"
-                    plain
-                    type="primary"
-                    style="width: 100%"
-                    @click="addApprovalStep">鏂板鑺傜偣</u-button>
-        </view>
-      </view>
+      <up-form-item label="鍏ュ簱鐘舵��"
+                    prop="stockInStatus">
+        <up-input v-model="form.stockInStatus"
+                  placeholder="--"
+                  disabled />
+      </up-form-item>
+      <up-form-item label="澶囨敞"
+                    prop="remarks">
+        <up-textarea v-model="form.remarks"
+                     placeholder="璇疯緭鍏ュ娉�"
+                     auto-height
+                     :disabled="isReadOnly" />
+      </up-form-item>
       <up-popup :show="showTimePicker"
                 mode="bottom"
                 @close="showTimePicker = false">
@@ -132,18 +103,24 @@
                             @cancel="showTimePicker = false"
                             mode="date" />
       </up-popup>
-      <!-- 閿�鍞悎鍚屽彿閫夋嫨 -->
-      <up-action-sheet :show="showPicker"
-                       :actions="salesContractActionList"
-                       title="閫夋嫨閿�鍞悎鍚屽彿"
-                       @select="onSalesmanSelect"
-                       @close="showPicker = false" />
       <!-- 渚涘簲鍟嗛�夋嫨 -->
       <up-action-sheet :show="showCustomerPicker"
                        :actions="supplierActionList"
                        title="閫夋嫨渚涘簲鍟�"
                        @select="onCustomerSelect"
                        @close="showCustomerPicker = false" />
+      <!-- 妯℃澘閫夋嫨 -->
+      <up-action-sheet :show="showTemplatePicker"
+                       :actions="templateActionList"
+                       title="閫夋嫨妯℃澘"
+                       @select="onTemplateSelect"
+                       @close="showTemplatePicker = false" />
+      <!-- 閿�鍞悎鍚屽彿閫夋嫨 -->
+      <up-action-sheet :show="showSalesContractPicker"
+                       :actions="salesContractActionList"
+                       title="閫夋嫨閿�鍞悎鍚屽彿"
+                       @select="onSalesContractSelect"
+                       @close="showSalesContractPicker = false" />
       <!-- 浜у搧澶х被閫夋嫨鍣� -->
       <up-popup :show="showCategoryPicker"
                 mode="bottom">
@@ -190,7 +167,7 @@
                        size="small"
                        @click="addProduct"
                        class="add-btn"
-                       v-if="operationType !== 'view'">
+                       v-if="canEditProducts">
               鏂板
             </up-button>
           </view>
@@ -208,7 +185,7 @@
             </view>
             <!-- 鎿嶄綔鎸夐挳 -->
             <view class="product-actions"
-                  v-if="operationType !== 'view'">
+                  v-if="canEditProducts && canEditProductRow(product)">
               <up-button type="error"
                          size="mini"
                          @click="removeProduct(idx)"
@@ -227,10 +204,12 @@
               <up-input v-model="product.productCategory"
                         readonly
                         placeholder="璇烽�夋嫨"
+                        :disabled="!canEditProductRow(product)"
                         @click="openCategoryPicker(idx)" />
               <template #right>
                 <up-icon name="arrow-right"
-                         @click="showCategoryPicker = true"></up-icon>
+                         v-if="canEditProducts && canEditProductRow(product)"
+                         @click="openCategoryPicker(idx)"></up-icon>
               </template>
             </up-form-item>
             <!-- 瑙勬牸鍨嬪彿 -->
@@ -241,10 +220,12 @@
               <up-input v-model="product.specificationModel"
                         readonly
                         placeholder="璇烽�夋嫨"
+                        :disabled="!canEditProductRow(product)"
                         @click="openSpecificationPicker(idx)" />
               <template #right>
                 <up-icon name="arrow-right"
-                         @click="showSpecificationPicker = true"></up-icon>
+                         v-if="canEditProducts && canEditProductRow(product)"
+                         @click="openSpecificationPicker(idx)"></up-icon>
               </template>
             </up-form-item>
             <!-- 鍗曚綅 -->
@@ -253,6 +234,7 @@
                           required
                           :rules="productRules">
               <up-input v-model="product.unit"
+                        :disabled="!canEditProductRow(product)"
                         placeholder="璇疯緭鍏�" />
             </up-form-item>
             <!-- 绋庣巼 -->
@@ -263,10 +245,12 @@
               <up-input v-model="product.taxRate"
                         readonly
                         placeholder="璇烽�夋嫨"
+                        :disabled="!canEditProductRow(product)"
                         @click="openTaxRatePicker(idx)" />
               <template #right>
                 <up-icon name="arrow-right"
-                         @click="showTaxRatePicker = true"></up-icon>
+                         v-if="canEditProducts && canEditProductRow(product)"
+                         @click="openTaxRatePicker(idx)"></up-icon>
               </template>
             </up-form-item>
             <!-- 鍚◣鍗曚环 -->
@@ -276,6 +260,7 @@
                           :rules="productRules">
               <up-input v-model="product.taxInclusiveUnitPrice"
                         type="number"
+                        :disabled="!canEditProductRow(product)"
                         placeholder="璇疯緭鍏�"
                         @blur="formatTaxPrice(idx)" />
             </up-form-item>
@@ -286,6 +271,7 @@
                           :rules="productRules">
               <up-input v-model="product.quantity"
                         type="number"
+                        :disabled="!canEditProductRow(product)"
                         placeholder="璇疯緭鍏�"
                         @blur="formatAmount(idx)" />
             </up-form-item>
@@ -296,6 +282,7 @@
                           :rules="productRules">
               <up-input v-model="product.taxInclusiveTotalPrice"
                         type="number"
+                        :disabled="!canEditProductRow(product)"
                         placeholder="璇疯緭鍏�"
                         @blur="formatTaxTotal(idx)" />
             </up-form-item>
@@ -306,8 +293,15 @@
                           :rules="productRules">
               <up-input v-model="product.taxExclusiveTotalPrice"
                         type="number"
+                        :disabled="!canEditProductRow(product)"
                         placeholder="璇疯緭鍏�"
                         @blur="formatNoTaxTotal(idx)" />
+            </up-form-item>
+            <up-form-item label="鍏ュ簱瀹℃牳鐘舵��"
+                          prop="stockInApprovalStatus">
+              <up-input v-model="product.stockInApprovalStatus"
+                        placeholder="--"
+                        disabled />
             </up-form-item>
             <!-- 鍙戠エ绫诲瀷 -->
             <up-form-item label="鍙戠エ绫诲瀷"
@@ -317,10 +311,12 @@
               <up-input v-model="product.invoiceType"
                         readonly
                         placeholder="璇烽�夋嫨"
+                        :disabled="!canEditProductRow(product)"
                         @click="openInvoiceTypePicker(idx)" />
               <template #right>
                 <up-icon name="arrow-right"
-                         @click="showInvoiceTypePicker = true"></up-icon>
+                         v-if="canEditProducts && canEditProductRow(product)"
+                         @click="openInvoiceTypePicker(idx)"></up-icon>
               </template>
             </up-form-item>
             <!-- 搴撳瓨棰勮鏁伴噺 -->
@@ -330,6 +326,7 @@
                           :rules="productRules">
               <up-input v-model="product.warnNum"
                         type="number"
+                        :disabled="!canEditProductRow(product)"
                         placeholder="璇疯緭鍏�" />
             </up-form-item>
             <up-form-item label="鏄惁璐ㄦ"
@@ -337,13 +334,16 @@
                           required
                           :rules="productRules">
               <u-radio-group v-model="product.isChecked"
+                             :disabled="!canEditProductRow(product)"
                              placement="row"
                              @change="groupChange">
                 <u-radio :customStyle="{marginRight: '40rpx'}"
                          label="鏄�"
+                         :disabled="!canEditProductRow(product)"
                          :name="true">
                 </u-radio>
                 <u-radio label="鍚�"
+                         :disabled="!canEditProductRow(product)"
                          :name="false">
                 </u-radio>
               </u-radio-group>
@@ -352,7 +352,13 @@
         </view>
       </view>
       <!-- 浣跨敤鍏叡搴曢儴鎸夐挳缁勪欢 -->
-      <FooterButtons :show="operationType !== 'view'"
+      <view v-if="operationType === 'add' && !isReadOnly"
+            class="footer-template-tool"
+            @click="openTemplatePicker()">
+        <text class="footer-template-tool__label">妯℃澘</text>
+        <text class="footer-template-tool__value">{{ templateName || '閫夋嫨' }}</text>
+      </view>
+      <FooterButtons :show="operationType !== 'view' && !isApprovalPassed"
                      cancelText="鍙栨秷"
                      confirmText="淇濆瓨"
                      @cancel="goBack"
@@ -372,9 +378,12 @@
     createPurchaseNo,
     getOptions,
     getPurchaseById,
-    getSalesNo,
     approveProcessGetInfo,
+    getPurchaseTemplateList,
+    getProductInfoByContractNo,
+    getSalesNo,
   } from "@/api/procurementManagement/procurementLedger";
+  import { delProduct } from "@/api/salesManagement/salesLedger";
   import PageHeader from "@/components/PageHeader.vue";
   import FooterButtons from "@/components/FooterButtons.vue";
   import { userListNoPageByTenantId } from "@/api/system/user";
@@ -382,12 +391,38 @@
   const operationType = ref("");
   const editData = ref(null);
   const formRef = ref(null);
+  const showSalesContractBinding = false;
+
+  // 瀹℃壒閫氳繃锛坅pprovalStatus === 3锛夊悗锛屾暣鍗曠姝㈢紪杈戯紙鍚骇鍝併�佸熀鏈俊鎭�佸鎵规祦绋嬶級
+  const isApprovalPassed = computed(() => {
+    const status = editData.value?.approvalStatus ?? form.value?.approvalStatus;
+    return Number(status) === 3;
+  });
+  const isStockInFully = computed(() => {
+    const status = editData.value?.stockInStatus ?? form.value?.stockInStatus;
+    return status === "瀹屽叏鍏ュ簱";
+  });
+  const canEditProducts = computed(() => {
+    return (
+      operationType.value !== "view" &&
+      !isApprovalPassed.value &&
+      !isStockInFully.value
+    );
+  });
+  // 鏄惁鏁翠綋鍙锛氭煡鐪嬫ā寮� 鎴� 宸插鎵归�氳繃
+  const isReadOnly = computed(() => {
+    return (
+      operationType.value === "view" ||
+      isApprovalPassed.value ||
+      isStockInFully.value
+    );
+  });
 
   const userStore = useUserStore();
   const form = ref({
     id: "",
-    salesContractNo: "",
     purchaseContractNumber: "",
+    salesLedgerId: "",
     supplierId: "",
     supplierName: "",
     projectName: "",
@@ -397,27 +432,42 @@
     entryDate: "",
     approveUserIds: "",
     executionDate: "",
+    stockInStatus: "",
+    remarks: "",
   });
   const showTimePicker = ref(false);
-  const showPicker = ref(false);
   const showCustomerPicker = ref(false);
-  const salesContractList = ref([]);
   const supplierList = ref([]);
   const productData = ref([]);
   const currentDate = ref(Date.now());
-  // 璁$畻閿�鍞悎鍚屽彿閫夋嫨鍒楄〃
-  const salesContractActionList = computed(() => {
-    return salesContractList.value.map(item => ({
-      name: item.text,
-      value: item.value,
+  const templateList = ref([]);
+  const templateName = ref("");
+  const showTemplatePicker = ref(false);
+  const salesContractList = ref([]);
+  const showSalesContractPicker = ref(false);
+  const salesContractNoText = ref("");
+
+  // 璁$畻渚涘簲鍟嗛�夋嫨鍒楄〃锛堝彧淇濈暀 isWhite === 0 鐨勪緵搴斿晢锛�
+  const supplierActionList = computed(() => {
+    return supplierList.value
+      .filter(item => item.isWhite === 0)
+      .map(item => ({
+        name: item.text,
+        value: item.value,
+      }));
+  });
+
+  const templateActionList = computed(() => {
+    return templateList.value.map(item => ({
+      name: item.templateName,
+      value: item.templateName,
     }));
   });
 
-  // 璁$畻渚涘簲鍟嗛�夋嫨鍒楄〃
-  const supplierActionList = computed(() => {
-    return supplierList.value.map(item => ({
-      name: item.text,
-      value: item.value,
+  const salesContractActionList = computed(() => {
+    return salesContractList.value.map(item => ({
+      name: item.salesContractNo,
+      value: item.id,
     }));
   });
 
@@ -476,9 +526,6 @@
 
   // 琛ㄥ崟鏍¢獙瑙勫垯
   const rules = {
-    salesContractNo: [
-      { required: true, message: "璇烽�夋嫨閿�鍞悎鍚屽彿", trigger: "blur" },
-    ],
     supplierName: [
       { required: true, message: "璇烽�夋嫨渚涘簲鍟嗗悕绉�", trigger: "blur" },
     ],
@@ -528,6 +575,7 @@
   };
 
   const addProduct = () => {
+    if (!canEditProducts.value) return;
     if (productData.value === null) {
       productData.value = [];
     }
@@ -542,22 +590,17 @@
       taxInclusiveTotalPrice: "",
       taxExclusiveTotalPrice: "",
       invoiceType: "",
+      stockInApprovalStatus: "",
+      availableQuality: "",
+      returnQuality: "",
       isChecked: false,
       warnNum: "",
     });
   };
 
-  // 閿�鍞悎鍚屽彿閫夋嫨浜嬩欢
-  const onSalesmanSelect = item => {
-    form.value.salesContractNo = item.name;
-    // 鏌ユ壘瀵瑰簲鐨刬d
-    const selectedItem = salesContractList.value.find(
-      contract => contract.text === item.name
-    );
-    if (selectedItem) {
-      form.value.salesLedgerId = selectedItem.value;
-    }
-    showPicker.value = false;
+  const canEditProductRow = product => {
+    if (!canEditProducts.value) return false;
+    return product?.stockInApprovalStatus !== "瀹屽叏鍏ュ簱";
   };
 
   // 渚涘簲鍟嗛�夋嫨浜嬩欢
@@ -574,28 +617,159 @@
   };
 
   const removeProduct = idx => {
-    productData.value.splice(idx, 1);
+    if (!canEditProducts.value) return;
+    const row = productData.value[idx];
+    if (!canEditProductRow(row)) return;
+
+    // 鏂板妯″紡鎴栬繕鏈惤搴撶殑浜у搧锛岀洿鎺ュ墠绔垹闄�
+    if (operationType.value === "add" || !row?.id) {
+      productData.value.splice(idx, 1);
+      return;
+    }
+
+    uni.showModal({
+      title: "鎻愮ず",
+      content: "閫変腑鐨勪骇鍝佸皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�",
+      confirmText: "纭",
+      cancelText: "鍙栨秷",
+      success: res => {
+        if (!res.confirm) {
+          uni.showToast({
+            title: "宸插彇娑�",
+            icon: "none",
+          });
+          return;
+        }
+        const ids = [row.id];
+        delProduct(ids).then(() => {
+          uni.showToast({
+            title: "鍒犻櫎鎴愬姛",
+            icon: "success",
+          });
+          const currentId = form.value.id || editData.value?.id;
+          if (currentId) {
+            getPurchaseById({ id: currentId, type: 2 }).then(res2 => {
+              productData.value = res2.productData || [];
+            });
+          } else {
+            // 鍥為��澶勭悊锛氬鏋滄病鏈夊綋鍓岻D锛屽垯鏈湴鍒犻櫎
+            productData.value.splice(idx, 1);
+          }
+        });
+      },
+    });
   };
 
   // 鏄剧ず閫夋嫨鍣�
   const openCategoryPicker = idx => {
+    if (!canEditProducts.value) return;
+    if (!canEditProductRow(productData.value[idx])) return;
     currentProductIndex.value = idx;
     showCategoryPicker.value = true;
   };
 
   const openSpecificationPicker = idx => {
+    if (!canEditProducts.value) return;
+    if (!canEditProductRow(productData.value[idx])) return;
     currentProductIndex.value = idx;
     showSpecificationPicker.value = true;
   };
 
   const openTaxRatePicker = idx => {
+    if (!canEditProducts.value) return;
+    if (!canEditProductRow(productData.value[idx])) return;
     currentProductIndex.value = idx;
     showTaxRatePicker.value = true;
   };
 
   const openInvoiceTypePicker = idx => {
+    if (!canEditProducts.value) return;
+    if (!canEditProductRow(productData.value[idx])) return;
     currentProductIndex.value = idx;
     showInvoiceTypePicker.value = true;
+  };
+
+  const normalizeProductRow = row => {
+    const source = row || {};
+    return {
+      id: source.id,
+      productCategory: source.productCategory || "",
+      specificationModel: source.specificationModel || "",
+      productModelId: source.productModelId || source.materialModelId || "",
+      unit: source.unit || "",
+      taxRate: source.taxRate ?? "",
+      taxInclusiveUnitPrice: source.taxInclusiveUnitPrice ?? "",
+      quantity: source.quantity ?? "",
+      taxInclusiveTotalPrice: source.taxInclusiveTotalPrice ?? "",
+      taxExclusiveTotalPrice: source.taxExclusiveTotalPrice ?? "",
+      invoiceType: source.invoiceType || "",
+      stockInApprovalStatus: source.stockInApprovalStatus || "",
+      availableQuality: source.availableQuality ?? "",
+      returnQuality: source.returnQuality ?? "",
+      isChecked:
+        typeof source.isChecked === "boolean"
+          ? source.isChecked
+          : Boolean(source.isChecked),
+      warnNum: source.warnNum ?? "",
+    };
+  };
+
+  const openTemplatePicker = async () => {
+    if (isReadOnly.value) return;
+    if (!templateList.value.length) {
+      const res = await getPurchaseTemplateList();
+      if (res && res.code === 200 && Array.isArray(res.data)) {
+        templateList.value = res.data;
+      }
+    }
+    showTemplatePicker.value = true;
+  };
+
+  const onTemplateSelect = item => {
+    const selected = templateList.value.find(t => t.templateName === item.name);
+    if (!selected) {
+      showTemplatePicker.value = false;
+      return;
+    }
+    templateName.value = selected.templateName || "";
+    if (selected.supplierId) form.value.supplierId = selected.supplierId;
+    if (selected.supplierName) form.value.supplierName = selected.supplierName;
+    if (selected.projectName) form.value.projectName = selected.projectName;
+    if (selected.paymentMethod) form.value.paymentMethod = selected.paymentMethod;
+    const list =
+      selected.productList ||
+      selected.productData ||
+      selected.purchaseLedgerProductList ||
+      [];
+    productData.value = Array.isArray(list) ? list.map(normalizeProductRow) : [];
+    showTemplatePicker.value = false;
+  };
+
+  const openSalesContractPicker = async () => {
+    if (isReadOnly.value) return;
+    if (!salesContractList.value.length) {
+      const res = await getSalesNo();
+      if (res && res.code === 200 && Array.isArray(res.data)) {
+        salesContractList.value = res.data;
+      } else if (Array.isArray(res)) {
+        salesContractList.value = res;
+      }
+    }
+    showSalesContractPicker.value = true;
+  };
+
+  const onSalesContractSelect = async item => {
+    form.value.salesLedgerId = item.value;
+    salesContractNoText.value = item.name || "";
+    showSalesContractPicker.value = false;
+    const res = await getProductInfoByContractNo({
+      contractNo: form.value.salesLedgerId,
+    });
+    if (res && res.code === 200 && Array.isArray(res.data)) {
+      productData.value = res.data.map(normalizeProductRow);
+    } else if (Array.isArray(res)) {
+      productData.value = res.map(normalizeProductRow);
+    }
   };
 
   // 閫夋嫨鍣ㄧ‘璁や簨浠�
@@ -819,6 +993,21 @@
   };
 
   const onSubmit = () => {
+    // 瀹℃壒閫氳繃鐨勫彴璐︾姝㈠啀娆′慨鏀�
+    if (isApprovalPassed.value) {
+      uni.showToast({
+        title: "宸插鎵归�氳繃鐨勫彴璐︿笉鍏佽淇敼",
+        icon: "none",
+      });
+      return;
+    }
+    if (isStockInFully.value) {
+      uni.showToast({
+        title: "瀹屽叏鍏ュ簱鐨勫彴璐︿笉鍏佽淇敼",
+        icon: "none",
+      });
+      return;
+    }
     const hasEmptyApprover = approverNodes.value.some(node => !node.userId);
     if (hasEmptyApprover) {
       uni.showToast({
@@ -837,10 +1026,6 @@
         icon: "none",
       });
       return;
-    }
-    // 濡傛灉salesLedgerId涓虹┖锛屽垯涓嶄紶閫抯alesContractNo
-    if (!form.value.salesLedgerId) {
-      form.value.salesContractNo = "";
     }
     if (operationType.value == "add") {
       delete form.value.id;
@@ -878,9 +1063,17 @@
   const fillFormData = () => {
     if (!editData.value) return;
     getPurchaseById({ id: editData.value.id, type: 2 }).then(res => {
-      productData.value = res.productData;
-      if (res && res.approveUserIds) {
-        const userIds = res.approveUserIds.split(",");
+      const data = res?.data ? res.data : res || {};
+      const products =
+        data.purchaseLedgerProductList ||
+        res?.productData ||
+        data.productData ||
+        [];
+      productData.value = Array.isArray(products)
+        ? products.map(normalizeProductRow)
+        : [];
+      if (data && data.approveUserIds) {
+        const userIds = data.approveUserIds.split(",");
         approverNodes.value = userIds.map((userId, idx) => {
           const userIdNum = parseInt(userId.trim());
           // 浠巙serList涓壘鍒板搴旂殑鐢ㄦ埛淇℃伅
@@ -903,27 +1096,24 @@
     // 濉厖鍩烘湰淇℃伅
     form.value.purchaseContractNumber =
       editData.value.purchaseContractNumber || "";
-    form.value.salesContractNo = editData.value.salesContractNo || "";
     form.value.supplierName = editData.value.supplierName || "";
     form.value.projectName = editData.value.projectName || "";
     form.value.paymentMethod = editData.value.paymentMethod || "";
-    form.value.salesLedgerId = editData.value.salesLedgerId || "";
     form.value.recorderId = editData.value.recorderId || "";
     form.value.recorderName = editData.value.recorderName || "";
     form.value.entryDate = editData.value.entryDate || "";
     form.value.id = editData.value.id || "";
     form.value.supplierId = editData.value.supplierId || "";
     form.value.executionDate = editData.value.executionDate || "";
-  };
-
-  const getSalesNoList = () => {
-    getSalesNo().then(res => {
-      // 灏嗙敤鎴锋暟鎹粍瑁呮垚 picker 闇�瑕佺殑鏍煎紡
-      salesContractList.value = res.map(user => ({
-        text: user.salesContractNo,
-        value: user.id,
-      }));
-    });
+    form.value.stockInStatus = editData.value.stockInStatus || "";
+    form.value.remarks = editData.value.remarks || "";
+    form.value.salesLedgerId = editData.value.salesLedgerId || "";
+    if (showSalesContractBinding && form.value.salesLedgerId) {
+      const match = salesContractList.value.find(
+        i => i.id === form.value.salesLedgerId
+      );
+      if (match) salesContractNoText.value = match.salesContractNo || "";
+    }
   };
 
   const getOptionsLIst = () => {
@@ -932,6 +1122,7 @@
       supplierList.value = res.data.map(item => ({
         text: item.supplierName,
         value: item.id,
+        isWhite: item.isWhite,
       }));
     });
   };
@@ -975,8 +1166,6 @@
     userListNoPageByTenantId().then(res => {
       userList.value = res.data;
     });
-    // 鑾峰彇閿�鍞悎鍚屽彿鍒楄〃
-    getSalesNoList();
     // 鑾峰彇渚涘簲鍟嗗垪琛�
     getOptionsLIst();
     // 鑾峰彇浜у搧澶х被tree鏁版嵁
@@ -986,6 +1175,15 @@
       setUserInfo();
       createPurchaseNo().then(res => {
         form.value.purchaseContractNumber = res.data;
+      });
+    }
+    if (showSalesContractBinding) {
+      getSalesNo().then(res => {
+        if (res && res.code === 200 && Array.isArray(res.data)) {
+          salesContractList.value = res.data;
+        } else if (Array.isArray(res)) {
+          salesContractList.value = res;
+        }
       });
     }
 
@@ -1023,6 +1221,7 @@
   };
 
   const addApprover = stepIndex => {
+    if (isReadOnly.value) return;
     // 璺宠浆鍒拌仈绯讳汉閫夋嫨椤甸潰
     uni.setStorageSync("stepIndex", stepIndex);
     uni.navigateTo({
@@ -1031,18 +1230,21 @@
   };
 
   const addApprovalStep = () => {
+    if (isReadOnly.value) return;
     // 娣诲姞鏂扮殑瀹℃壒姝ラ
     approverNodes.value.push({ userId: null, nickName: null });
     console.log(approverNodes.value, "approverNodes.value");
   };
 
   const removeApprover = stepIndex => {
+    if (isReadOnly.value) return;
     // 绉婚櫎瀹℃壒浜�
     approverNodes.value[stepIndex].userId = null;
     approverNodes.value[stepIndex].nickName = null;
   };
 
   const removeApprovalStep = stepIndex => {
+    if (isReadOnly.value) return;
     // 纭繚鑷冲皯淇濈暀涓�涓鎵规楠�
     if (approverNodes.value.length > 1) {
       approverNodes.value.splice(stepIndex, 1);
@@ -1439,4 +1641,34 @@
     color: #3b82f6;
     font-size: 14px;
   }
-</style>
\ No newline at end of file
+
+  .footer-template-tool {
+    position: fixed;
+    right: 12px;
+    bottom: 78px;
+    z-index: 1001;
+    display: flex;
+    align-items: center;
+    gap: 6px;
+    padding: 6px 10px;
+    background: rgba(255, 255, 255, 0.96);
+    border: 1px solid #e5e7eb;
+    border-radius: 999px;
+    box-shadow: 0 6px 18px rgba(0, 0, 0, 0.08);
+  }
+
+  .footer-template-tool__label {
+    font-size: 12px;
+    color: #6b7280;
+  }
+
+  .footer-template-tool__value {
+    font-size: 12px;
+    color: #006cfb;
+    font-weight: 600;
+    max-width: 180px;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+  }
+</style>

--
Gitblit v1.9.3