From de4ac959d99138074276563d6d4ca44d76b17705 Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期三, 20 五月 2026 16:44:25 +0800
Subject: [PATCH] Merge branch 'dev_NEW_pro' into dev_天津_宝东

---
 src/pages/sales/salesQuotation/edit.vue |  636 +++++++++++++++++++++++++++++++++++++++------------------
 1 files changed, 436 insertions(+), 200 deletions(-)

diff --git a/src/pages/sales/salesQuotation/edit.vue b/src/pages/sales/salesQuotation/edit.vue
index 80561e5..bfcb930 100644
--- a/src/pages/sales/salesQuotation/edit.vue
+++ b/src/pages/sales/salesQuotation/edit.vue
@@ -1,124 +1,301 @@
 <template>
   <view class="account-detail">
-    <PageHeader :title="pageTitle" @back="goBack" />
-
+    <PageHeader :title="pageTitle"
+                @back="goBack" />
     <view class="form-container">
-      <up-form ref="formRef" :model="form" label-width="110" input-align="right" error-message-align="right">
-        <u-cell-group title="浜у搧淇℃伅" class="form-section">
+      <up-form ref="formRef"
+               :model="form"
+               :rules="rules"
+               label-width="110"
+               input-align="right"
+               error-message-align="right">
+        <u-cell-group title="鍩虹淇℃伅"
+                      class="form-section">
+          <up-form-item label="瀹㈡埛鍚嶇О"
+                        prop="customer"
+                        required>
+            <up-input v-model="form.customer"
+                      placeholder="璇烽�夋嫨瀹㈡埛"
+                      readonly
+                      @click="showCustomerSheet = true" />
+            <template #right>
+              <up-icon name="arrow-right"
+                       @click="showCustomerSheet = true"></up-icon>
+            </template>
+          </up-form-item>
+          <up-form-item label="涓氬姟鍛�"
+                        prop="salesperson"
+                        required>
+            <up-input v-model="form.salesperson"
+                      placeholder="璇烽�夋嫨涓氬姟鍛�"
+                      readonly
+                      @click="showSalespersonSheet = true" />
+            <template #right>
+              <up-icon name="arrow-right"
+                       @click="showSalespersonSheet = true"></up-icon>
+            </template>
+          </up-form-item>
+          <up-form-item label="鎶ヤ环鏃ユ湡"
+                        prop="quotationDate"
+                        required>
+            <up-input v-model="form.quotationDate"
+                      placeholder="璇烽�夋嫨鎶ヤ环鏃ユ湡"
+                      readonly
+                      @click="showQuotationDatePicker = true" />
+            <template #right>
+              <up-icon name="arrow-right"
+                       @click="showQuotationDatePicker = true"></up-icon>
+            </template>
+          </up-form-item>
+          <up-form-item label="鏈夋晥鏈熻嚦"
+                        prop="validDate"
+                        required>
+            <up-input v-model="form.validDate"
+                      placeholder="璇烽�夋嫨鏈夋晥鏈�"
+                      readonly
+                      @click="showValidDatePicker = true" />
+            <template #right>
+              <up-icon name="arrow-right"
+                       @click="showValidDatePicker = true"></up-icon>
+            </template>
+          </up-form-item>
+          <up-form-item label="浠樻鏂瑰紡"
+                        prop="paymentMethod"
+                        required>
+            <up-input v-model="form.paymentMethod"
+                      placeholder="璇疯緭鍏ヤ粯娆炬柟寮�"
+                      clearable />
+          </up-form-item>
+          <up-form-item label="澶囨敞"
+                        prop="remark">
+            <up-textarea v-model="form.remark"
+                         placeholder="璇疯緭鍏ュ娉�"
+                         auto-height />
+          </up-form-item>
+        </u-cell-group>
+        <u-cell-group title="浜у搧淇℃伅"
+                      class="form-section">
           <view class="section-tools">
-            <up-button type="primary" size="small" text="鏂板浜у搧" :disabled="isEditMode" @click="addProduct" />
+            <up-button type="primary"
+                       size="small"
+                       text="鏂板浜у搧"
+                       @click="addProduct" />
           </view>
-          <view v-if="form.products.length === 0" class="empty-text"><text>鏆傛棤浜у搧</text></view>
-          <view v-else class="product-list">
-            <view v-for="(product, index) in form.products" :key="product.uid || index" class="product-card">
+          <view v-if="form.products.length === 0"
+                class="empty-text">
+            <text>鏆傛棤浜у搧锛岃鍏堟坊鍔犱骇鍝�</text>
+          </view>
+          <view v-else
+                class="product-list">
+            <view v-for="(product, index) in form.products"
+                  :key="product.uid"
+                  class="product-card">
               <view class="product-header">
                 <text class="product-title">浜у搧 {{ index + 1 }}</text>
-                <up-icon name="trash" color="#ee0a24" size="18" @click="removeProduct(index)"></up-icon>
+                <up-icon name="trash"
+                         color="#ee0a24"
+                         size="18"
+                         @click="removeProduct(index)"></up-icon>
               </view>
               <up-divider></up-divider>
               <view class="product-body">
-                <up-form-item label="浜у搧">
-                  <up-input v-model="product.product" placeholder="璇烽�夋嫨浜у搧" readonly @click="openProductPicker(index)" />
-                  <template #right><up-icon name="arrow-right" @click="openProductPicker(index)"></up-icon></template>
+                <up-form-item label="浜у搧鍚嶇О">
+                  <up-input v-model="product.product"
+                            placeholder="璇烽�夋嫨浜у搧"
+                            readonly
+                            @click="openProductPicker(index)" />
+                  <template #right>
+                    <up-icon name="arrow-right"
+                             @click="openProductPicker(index)"></up-icon>
+                  </template>
                 </up-form-item>
-                <up-form-item label="瑙勬牸">
-                  <up-input v-model="product.specification" placeholder="璇烽�夋嫨瑙勬牸" readonly @click="openModelPicker(index)" />
-                  <template #right><up-icon name="arrow-right" @click="openModelPicker(index)"></up-icon></template>
+                <up-form-item label="瑙勬牸鍨嬪彿">
+                  <up-input v-model="product.ProductModel"
+                            placeholder="璇烽�夋嫨瑙勬牸鍨嬪彿"
+                            readonly
+                            @click="openModelPicker(index)" />
+                  <template #right>
+                    <up-icon name="arrow-right"
+                             @click="openModelPicker(index)"></up-icon>
+                  </template>
                 </up-form-item>
-                <up-form-item label="鍗曚綅"><up-input v-model="product.unit" placeholder="璇疯緭鍏ュ崟浣�" clearable /></up-form-item>
-                <up-form-item label="绾稿紶"><up-input v-model="product.paper" placeholder="璇疯緭鍏ョ焊寮�" clearable /></up-form-item>
-                <up-form-item label="瀹氶噺"><up-input v-model="product.paperWeight" placeholder="璇疯緭鍏ュ畾閲�" clearable /></up-form-item>
+                <up-form-item label="鍗曚綅">
+                  <up-input v-model="product.unit"
+                            placeholder="璇疯緭鍏ュ崟浣�"
+                            clearable />
+                </up-form-item>
                 <up-form-item label="鏁伴噺">
-                  <up-input v-model="product.quantity" type="number" placeholder="璇疯緭鍏ユ暟閲�" clearable @blur="calculateAmount(product)" />
+                  <up-input v-model="product.quantity"
+                            type="number"
+                            placeholder="璇疯緭鍏ユ暟閲�"
+                            clearable
+                            @blur="calculateAmount(product)" />
                 </up-form-item>
                 <up-form-item label="鍗曚环">
-                  <up-input v-model="product.unitPrice" type="number" placeholder="璇疯緭鍏ュ崟浠�" clearable @blur="calculateAmount(product)" />
-                </up-form-item>
-                <up-form-item label="鍗扮増璐�">
-                  <up-input v-model="product.printingFee" type="number" placeholder="璇疯緭鍏ュ嵃鐗堣垂" clearable @blur="syncTotalAmount" />
-                </up-form-item>
-                <up-form-item label="鍒�鐗堣垂">
-                  <up-input v-model="product.dieCuttingFee" type="number" placeholder="璇疯緭鍏ュ垁鐗堣垂" clearable @blur="syncTotalAmount" />
-                </up-form-item>
-                <up-form-item label="纾ㄥ叿璐�">
-                  <up-input v-model="product.grindingFee" type="number" placeholder="璇疯緭鍏ョ(鍏疯垂" clearable @blur="syncTotalAmount" />
+                  <up-input v-model="product.unitPrice"
+                            type="number"
+                            placeholder="璇疯緭鍏ュ崟浠�"
+                            clearable
+                            @blur="calculateAmount(product)" />
                 </up-form-item>
                 <up-form-item label="閲戦">
-                  <up-input :model-value="formatAmount(product.amount)" disabled placeholder="鑷姩璁$畻锛堟暟閲�*鍗曚环锛�" />
+                  <up-input :model-value="formatAmount(product.amount)"
+                            disabled
+                            placeholder="鑷姩璁$畻" />
                 </up-form-item>
               </view>
             </view>
           </view>
         </u-cell-group>
-
-        <u-cell-group title="澶囨敞淇℃伅" class="form-section">
-          <up-form-item label="澶囨敞">
-            <up-textarea v-model="form.remark" placeholder="璇疯緭鍏ュ娉紙閫夊~锛�" auto-height />
-          </up-form-item>
-        </u-cell-group>
-
-        <u-cell-group title="姹囨��" class="form-section">
+        <u-cell-group title="姹囨�讳俊鎭�"
+                      class="form-section">
           <up-form-item label="鎶ヤ环鎬婚">
-            <up-input :model-value="formatAmount(totalAmount)" disabled placeholder="鑷姩姹囨��" />
+            <up-input :model-value="formatAmount(totalAmount)"
+                      disabled
+                      placeholder="鑷姩姹囨��" />
           </up-form-item>
-          <view class="summary-tip">鎬婚瑙勫垯锛氬崟浠� + 鍗扮増璐� + 鍒�鐗堣垂 + 纾ㄥ叿璐癸紙鎸変骇鍝侀�愯姹傚拰锛�</view>
         </u-cell-group>
       </up-form>
     </view>
-
-    <FooterButtons :loading="loading" confirmText="淇濆瓨" @cancel="goBack" @confirm="handleSubmit" />
-
-    <up-action-sheet :show="showProductSheet" title="閫夋嫨浜у搧" :actions="productActions" @select="onSelectProduct" @close="showProductSheet = false" />
-    <up-action-sheet :show="showModelSheet" title="閫夋嫨瑙勬牸" :actions="modelActions" @select="onSelectModel" @close="showModelSheet = false" />
+    <FooterButtons :loading="loading"
+                   confirmText="淇濆瓨"
+                   @cancel="goBack"
+                   @confirm="handleSubmit" />
+    <up-action-sheet :show="showCustomerSheet"
+                     title="閫夋嫨瀹㈡埛"
+                     :actions="customerActions"
+                     @select="onSelectCustomer"
+                     @close="showCustomerSheet = false" />
+    <up-action-sheet :show="showSalespersonSheet"
+                     title="閫夋嫨涓氬姟鍛�"
+                     :actions="salespersonActions"
+                     @select="onSelectSalesperson"
+                     @close="showSalespersonSheet = false" />
+    <up-action-sheet :show="showProductSheet"
+                     title="閫夋嫨浜у搧"
+                     :actions="productActions"
+                     @select="onSelectProduct"
+                     @close="showProductSheet = false" />
+    <up-action-sheet :show="showModelSheet"
+                     title="閫夋嫨瑙勬牸鍨嬪彿"
+                     :actions="modelActions"
+                     @select="onSelectModel"
+                     @close="showModelSheet = false" />
+    <up-datetime-picker :show="showQuotationDatePicker"
+                        v-model="quotationDateValue"
+                        mode="date"
+                        @confirm="onQuotationDateConfirm"
+                        @cancel="showQuotationDatePicker = false" />
+    <up-datetime-picker :show="showValidDatePicker"
+                        v-model="validDateValue"
+                        mode="date"
+                        @confirm="onValidDateConfirm"
+                        @cancel="showValidDatePicker = false" />
   </view>
 </template>
 
 <script setup>
-  import { computed, onMounted, ref } from "vue";
+  import { computed, onMounted, onUnmounted, ref } from "vue";
   import { onLoad } from "@dcloudio/uni-app";
   import FooterButtons from "@/components/FooterButtons.vue";
   import PageHeader from "@/components/PageHeader.vue";
+  import { formatDateToYMD } from "@/utils/ruoyi";
   import { modelList, productTreeList } from "@/api/basicData/product";
-  import { addOrUpdateQuotationProduct, editQuotationProduct } from "@/api/salesManagement/salesQuotationProduct";
+  import { userListNoPageByTenantId } from "@/api/system/user";
+  import {
+    addQuotation,
+    getCustomerList,
+    updateQuotation,
+  } from "@/api/salesManagement/salesQuotation";
 
   const formRef = ref();
   const loading = ref(false);
   const quotationId = ref("");
+  const showCustomerSheet = ref(false);
+  const showSalespersonSheet = ref(false);
   const showProductSheet = ref(false);
   const showModelSheet = ref(false);
+  const showQuotationDatePicker = ref(false);
+  const showValidDatePicker = ref(false);
+  const quotationDateValue = ref(Date.now());
+  const validDateValue = ref(Date.now());
   const currentProductIndex = ref(-1);
+  const customerList = ref([]);
+  const salespersonList = ref([]);
   const productList = ref([]);
   const modelActions = ref([]);
 
   let uidSeed = 1;
+
   const form = ref({
     id: undefined,
+    quotationNo: "",
+    customerId: undefined,
+    customer: "",
+    salesperson: "",
+    quotationDate: "",
+    validDate: "",
+    paymentMethod: "",
+    status: "鑽夌",
     remark: "",
     products: [],
+    subtotal: 0,
+    freight: 0,
+    otherFee: 0,
+    discountRate: 0,
+    discountAmount: 0,
     totalAmount: 0,
   });
 
+  const rules = {
+    customer: [{ required: true, message: "璇烽�夋嫨瀹㈡埛", trigger: "change" }],
+    salesperson: [{ required: true, message: "璇烽�夋嫨涓氬姟鍛�", trigger: "change" }],
+    quotationDate: [
+      { required: true, message: "璇烽�夋嫨鎶ヤ环鏃ユ湡", trigger: "change" },
+    ],
+    validDate: [{ required: true, message: "璇烽�夋嫨鏈夋晥鏈�", trigger: "change" }],
+    paymentMethod: [
+      { required: true, message: "璇疯緭鍏ヤ粯娆炬柟寮�", trigger: "blur" },
+    ],
+  };
+
   const pageTitle = computed(() => (quotationId.value ? "缂栬緫鎶ヤ环" : "鏂板鎶ヤ环"));
-  const isEditMode = computed(() => Boolean(quotationId.value));
-  const productActions = computed(() => productList.value.map(item => ({ name: item.label, value: item.value, label: item.label })));
-  const totalAmount = computed(() => calcTotalAmountFromProducts(form.value.products));
+  const totalAmount = computed(() =>
+    Number(
+      (form.value.products || [])
+        .reduce((sum, item) => sum + Number(item.amount || 0), 0)
+        .toFixed(2)
+    )
+  );
+  const customerActions = computed(() =>
+    customerList.value.map(item => ({
+      name: item.customerName,
+      value: item.id,
+    }))
+  );
+  const salespersonActions = computed(() =>
+    salespersonList.value.map(item => ({
+      name: item.nickName,
+      value: item.nickName,
+    }))
+  );
+  const productActions = computed(() =>
+    productList.value.map(item => ({
+      name: item.label,
+      value: item.value,
+      label: item.label,
+    }))
+  );
 
   const createEmptyProduct = () => ({
     uid: `p_${uidSeed++}`,
-    id: "",
-    salesQuotationId: "",
     productId: "",
     product: "",
-    specificationId: "",
-    specification: "",
+    productModelId: "",
+    ProductModel: "",
     unit: "",
-    paper: "",
-    paperWeight: "",
     quantity: 1,
     unitPrice: 0,
-    printingFee: 0,
-    dieCuttingFee: 0,
-    grindingFee: 0,
     amount: 0,
     modelOptions: [],
   });
@@ -127,68 +304,51 @@
     const result = [];
     const walk = list => {
       (list || []).forEach(item => {
-        if (item.children && item.children.length) walk(item.children);
-        else result.push({ label: item.label || item.productName || "", value: item.id || item.value });
+        if (item.children && item.children.length) {
+          walk(item.children);
+        } else {
+          result.push({
+            label: item.label || item.productName || "",
+            value: item.id || item.value,
+          });
+        }
       });
     };
     walk(nodes);
     return result;
   };
 
-  const findProductIdByLabel = label => {
-    if (!label) return "";
-    const hit = (productList.value || []).find(item => item.label === label);
-    return hit?.value || "";
-  };
-
   const formatAmount = amount => `楼${Number(amount || 0).toFixed(2)}`;
   const goBack = () => uni.navigateBack();
 
-  const calcTotalAmountFromProducts = products =>
-    Number(
-      (products || [])
-        .reduce((sum, item) => {
-          const unitPrice = Number(item?.unitPrice || 0);
-          const printingFee = Number(item?.printingFee || 0);
-          const dieCuttingFee = Number(item?.dieCuttingFee || 0);
-          const grindingFee = Number(item?.grindingFee || 0);
-          return sum + unitPrice + printingFee + dieCuttingFee + grindingFee;
-        }, 0)
-        .toFixed(2)
+  const calculateAmount = product => {
+    product.amount = Number(
+      (Number(product.quantity || 0) * Number(product.unitPrice || 0)).toFixed(2)
     );
-
-  const syncTotalAmount = () => {
     form.value.totalAmount = totalAmount.value;
   };
 
-  const calculateAmount = product => {
-    product.amount = Number((Number(product.quantity || 0) * Number(product.unitPrice || 0)).toFixed(2));
-    syncTotalAmount();
-  };
-
-  const addProduct = () => {
-    if (isEditMode.value) {
-      uni.showToast({ title: "缂栬緫妯″紡涓嬩笉鍏佽鏂板浜у搧", icon: "none" });
-      return;
-    }
-    form.value.products.push(createEmptyProduct());
-  };
-
+  const addProduct = () => form.value.products.push(createEmptyProduct());
   const removeProduct = index => {
     form.value.products.splice(index, 1);
-    syncTotalAmount();
+    form.value.totalAmount = totalAmount.value;
   };
 
   const fetchModelOptions = async (productId, product) => {
-    const rows = await modelList({ id: productId }).catch(() => []);
-    product.modelOptions = Array.isArray(rows) ? rows : [];
+    try {
+      const res = await modelList({ id: productId });
+      const rows = res?.data?.records || res?.data || res?.records || res || [];
+      product.modelOptions = Array.isArray(rows) ? rows : [];
+    } catch (error) {
+      console.error("鑾峰彇瑙勬牸鍨嬪彿澶辫触:", error);
+      product.modelOptions = [];
+    }
   };
 
   const openProductPicker = index => {
     currentProductIndex.value = index;
     showProductSheet.value = true;
   };
-
   const openModelPicker = index => {
     currentProductIndex.value = index;
     const current = form.value.products[index];
@@ -196,70 +356,122 @@
       uni.showToast({ title: "璇峰厛閫夋嫨浜у搧", icon: "none" });
       return;
     }
-    modelActions.value = (current.modelOptions || []).map(item => ({ name: item.model, value: item.id, unit: item.unit }));
+    modelActions.value = (current.modelOptions || []).map(item => ({
+      name: item.model || item.specification,
+      value: item.id,
+      unit: item.unit,
+    }));
     if (!modelActions.value.length) {
-      uni.showToast({ title: "鏆傛棤瑙勬牸鏁版嵁", icon: "none" });
+      uni.showToast({ title: "鏆傛棤瑙勬牸鍨嬪彿", icon: "none" });
       return;
     }
     showModelSheet.value = true;
   };
 
+  const onSelectCustomer = action => {
+    form.value.customerId = action.value;
+    form.value.customer = action.name;
+    showCustomerSheet.value = false;
+  };
+  const onSelectSalesperson = action => {
+    form.value.salesperson = action.value;
+    showSalespersonSheet.value = false;
+  };
   const onSelectProduct = action => {
     const current = form.value.products[currentProductIndex.value];
     if (!current) return;
     current.productId = action.value;
     current.product = action.label;
-    current.specificationId = "";
-    current.specification = "";
+    current.productModelId = "";
+    current.ProductModel = "";
     current.unit = "";
     current.modelOptions = [];
     showProductSheet.value = false;
     fetchModelOptions(action.value, current);
   };
-
   const onSelectModel = action => {
     const current = form.value.products[currentProductIndex.value];
     if (!current) return;
-    current.specificationId = action.value;
-    current.specification = action.name;
+    current.productModelId = action.value;
+    current.ProductModel = action.name;
     current.unit = action.unit || current.unit;
     showModelSheet.value = false;
   };
+  const onQuotationDateConfirm = e => {
+    form.value.quotationDate = formatDateToYMD(e.value);
+    showQuotationDatePicker.value = false;
+  };
+  const onValidDateConfirm = e => {
+    form.value.validDate = formatDateToYMD(e.value);
+    showValidDatePicker.value = false;
+  };
 
-  const fetchProductOptions = async () => {
-    const productTree = await productTreeList().catch(() => []);
-    productList.value = flattenProductTree(Array.isArray(productTree) ? productTree : productTree?.data || []);
+  const fetchBaseOptions = async () => {
+    const [customers, users, productTree] = await Promise.all([
+      getCustomerList({ current: -1, size: -1 }).catch(() => ({})),
+      userListNoPageByTenantId().catch(() => ({})),
+      productTreeList().catch(() => []),
+    ]);
+    customerList.value = customers?.data?.records || customers?.records || [];
+    const userRows = users?.data || [];
+    salespersonList.value = Array.isArray(userRows) ? userRows : [];
+    productList.value = flattenProductTree(
+      Array.isArray(productTree) ? productTree : productTree?.data || []
+    );
+  };
+
+  // 鏍规嵁鍚嶇О鍙嶆煡鑺傜偣 id锛屼究浜庝粎瀛樺悕绉版椂鐨勫弽鏄�
+  const findNodeIdByLabel = (nodes, label) => {
+    if (!label) return null;
+    for (let i = 0; i < nodes.length; i++) {
+      const node = nodes[i];
+      if (node.label === label) return node.value;
+      if (node.children && node.children.length > 0) {
+        const found = findNodeIdByLabel(node.children, label);
+        if (found !== null && found !== undefined) return found;
+      }
+    }
+    return null;
   };
 
   const normalizeProductRows = async rows => {
     const normalized = await Promise.all(
       (Array.isArray(rows) ? rows : []).map(async item => {
+        const productName = item.product || item.productName || "";
+        // 浼樺厛鐢� productId锛涘鏋滃彧鏈夊悕绉帮紝灏濊瘯鍙嶆煡 id 浠ヤ究閫夋嫨鍣ㄥ弽鏄�
+        let resolvedProductId =
+          item.productId ||
+          findNodeIdByLabel(productList.value, productName) ||
+          "";
+
         const row = {
           uid: `p_${uidSeed++}`,
-          id: item.id || "",
-          salesQuotationId: item.salesQuotationId || "",
-          productId: item.productId || "",
-          product: item.product || item.productName || "",
-          specificationId: item.specificationId || "",
-          specification: item.specification || "",
+          productId: resolvedProductId,
+          product: productName,
+          productModelId: item.productModelId || "",
+          ProductModel: item.ProductModel || item.specification || "",
           unit: item.unit || "",
-          paper: item.paper || "",
-          paperWeight: item.paperWeight || "",
           quantity: Number(item.quantity || 1),
           unitPrice: Number(item.unitPrice || 0),
-          printingFee: Number(item.printingFee || 0),
-          dieCuttingFee: Number(item.dieCuttingFee || 0),
-          grindingFee: Number(item.grindingFee || 0),
-          amount: Number(item.amount || Number(item.quantity || 0) * Number(item.unitPrice || 0)),
+          amount: Number(item.amount || 0),
           modelOptions: [],
         };
+
         if (row.productId) {
           await fetchModelOptions(row.productId, row);
-          if (!row.specificationId && row.specification) {
-            const matchedModel = (row.modelOptions || []).find(model => model.model === row.specification);
-            if (matchedModel) {
-              row.specificationId = matchedModel.id;
-              if (!row.unit) row.unit = matchedModel.unit || "";
+          // 濡傛灉娌℃湁 productModelId 浣嗘湁 ProductModel 鍚嶇О锛屽皾璇曚粠 modelOptions 涓尮閰� ID
+          if (!row.productModelId && row.ProductModel) {
+            const foundModel = row.modelOptions.find(
+              m =>
+                m.model === row.ProductModel ||
+                m.specification === row.ProductModel
+            );
+            if (foundModel) {
+              row.productModelId = foundModel.id;
+              // 缁熶竴浣跨敤 modelOptions 涓殑瀛楁
+              row.ProductModel =
+                foundModel.model || foundModel.specification || row.ProductModel;
+              row.unit = foundModel.unit || row.unit;
             }
           }
         }
@@ -269,26 +481,41 @@
     form.value.products = normalized;
   };
 
-  const loadEditFromStorage = async () => {
+  const loadDetail = async () => {
     if (!quotationId.value) return;
-    const cached = uni.getStorageSync("salesQuotationEdit");
-    if (!cached || typeof cached !== "object") return;
-    if (cached.id && String(cached.id) !== String(quotationId.value)) return;
 
-    const data = cached;
-    form.value = {
-      ...form.value,
-      id: data.id || form.value.id,
-      remark: data.remark || "",
-    };
-
-    const rows = Array.isArray(data.products) && data.products.length ? data.products : [data];
-    const normalizedRows = rows.map(item => ({
-      ...item,
-      productId: item.productId || findProductIdByLabel(item.product || item.productName || ""),
-    }));
-    await normalizeProductRows(normalizedRows);
-    syncTotalAmount();
+    // 鐩存帴浠庢湰鍦板瓨鍌ㄨ幏鍙栨暟鎹紝涓嶅啀璋冪敤璇︽儏鎺ュ彛
+    const cachedData = uni.getStorageSync("salesQuotationDetail");
+    if (
+      cachedData &&
+      (cachedData.id === quotationId.value ||
+        cachedData.id === Number(quotationId.value))
+    ) {
+      const data = cachedData;
+      form.value = {
+        ...form.value,
+        id: data.id,
+        quotationNo: data.quotationNo || "",
+        customerId: data.customerId,
+        customer: data.customer || "",
+        salesperson: data.salesperson || "",
+        quotationDate: data.quotationDate || "",
+        validDate: data.validDate || "",
+        paymentMethod: data.paymentMethod || "",
+        status: data.status || "鑽夌",
+        remark: data.remark || "",
+        subtotal: data.subtotal || 0,
+        freight: data.freight || 0,
+        otherFee: data.otherFee || 0,
+        discountRate: data.discountRate || 0,
+        discountAmount: data.discountAmount || 0,
+        totalAmount: data.totalAmount || 0,
+      };
+      await normalizeProductRows(data.products || []);
+      form.value.totalAmount = totalAmount.value;
+    } else {
+      console.warn("鏈壘鍒扮紦瀛樼殑鎶ヤ环鍗曡鎯呮暟鎹�");
+    }
   };
 
   const validateProducts = () => {
@@ -296,7 +523,14 @@
       uni.showToast({ title: "璇疯嚦灏戞坊鍔犱竴涓骇鍝�", icon: "none" });
       return false;
     }
-    const invalid = form.value.products.some(item => !item.productId || !item.specificationId || !item.unit || !Number(item.unitPrice || 0));
+    const invalid = form.value.products.some(
+      item =>
+        !item.productId ||
+        !item.productModelId ||
+        !item.unit ||
+        !Number(item.quantity) ||
+        !Number(item.unitPrice)
+    );
     if (invalid) {
       uni.showToast({ title: "璇峰畬鍠勪骇鍝佷俊鎭�", icon: "none" });
       return false;
@@ -304,56 +538,30 @@
     return true;
   };
 
-  const buildProductPayload = item => {
-    const quantity = Number(item?.quantity || 0);
-    const unitPrice = Number(item?.unitPrice || 0);
-    const printingFee = Number(item?.printingFee || 0);
-    const dieCuttingFee = Number(item?.dieCuttingFee || 0);
-    const grindingFee = Number(item?.grindingFee || 0);
-    return {
-      id: item?.id || undefined,
-      salesQuotationId: item?.salesQuotationId || null,
-      product: item?.product || "",
-      specification: item?.specification || "",
-      unit: item?.unit || "",
-      paper: item?.paper || "",
-      paperWeight: item?.paperWeight || "",
-      unitPrice,
-      printingFee,
-      dieCuttingFee,
-      grindingFee,
-      quantity,
-      amount: Number(item?.amount ?? quantity * unitPrice),
-      remark: form.value.remark || "",
-    };
-  };
-
   const handleSubmit = async () => {
-    if (!validateProducts()) return;
-
+    const valid = await formRef.value.validate().catch(() => false);
+    if (!valid || !validateProducts()) return;
     loading.value = true;
-    if (quotationId.value) {
-      const editingItem = form.value.products[0] || {};
-      const payload = buildProductPayload({
-        ...editingItem,
-        id: editingItem.id || quotationId.value,
-      });
-      editQuotationProduct(payload)
-        .then(() => {
-          uni.showToast({ title: "淇濆瓨鎴愬姛", icon: "success" });
-          setTimeout(() => uni.navigateBack(), 300);
-        })
-        .catch(() => {
-          uni.showToast({ title: "淇濆瓨澶辫触", icon: "error" });
-        })
-        .finally(() => {
-          loading.value = false;
-        });
-      return;
-    }
 
-    const payloadList = form.value.products.map(item => buildProductPayload(item));
-    addOrUpdateQuotationProduct(payloadList)
+    // 鍚屾鏈�鏂扮殑鎬婚
+    form.value.totalAmount = totalAmount.value;
+    form.value.subtotal = totalAmount.value;
+
+    const payload = {
+      ...form.value,
+      products: form.value.products.map(item => ({
+        productId: item.productId,
+        product: item.product,
+        productModelId: item.productModelId,
+        ProductModel: item.ProductModel,
+        quantity: Number(item.quantity || 0),
+        unit: item.unit,
+        unitPrice: Number(item.unitPrice || 0),
+        amount: Number(item.amount || 0),
+      })),
+    };
+    const action = quotationId.value ? updateQuotation : addQuotation;
+    action(payload)
       .then(() => {
         uni.showToast({ title: "淇濆瓨鎴愬姛", icon: "success" });
         setTimeout(() => uni.navigateBack(), 300);
@@ -371,21 +579,56 @@
       quotationId.value = options.id;
       form.value.id = options.id;
     } else {
-      form.value.products = [];
+      const today = formatDateToYMD(Date.now());
+      form.value.quotationDate = today;
+      form.value.validDate = today;
     }
   });
 
   onMounted(async () => {
-    await fetchProductOptions();
-    if (quotationId.value) await loadEditFromStorage();
+    await fetchBaseOptions();
+    if (quotationId.value) {
+      await loadDetail();
+    }
   });
+
+  onUnmounted(() => {});
 </script>
 
 <style scoped lang="scss">
   @import "@/static/scss/form-common.scss";
 
+  .account-detail {
+    min-height: 100vh;
+    background: #f8f9fa;
+    padding-bottom: 100px;
+  }
+
   .form-container {
     padding: 12px 12px 0;
+  }
+
+  .hero-card {
+    margin-bottom: 12px;
+    padding: 18px 18px 16px;
+    border-radius: 16px;
+    background: linear-gradient(135deg, #eef6ff 0%, #ffffff 100%);
+    box-shadow: 0 6px 18px rgba(41, 121, 255, 0.08);
+  }
+
+  .hero-title {
+    display: block;
+    font-size: 18px;
+    font-weight: 600;
+    color: #1f2d3d;
+    margin-bottom: 6px;
+  }
+
+  .hero-desc {
+    display: block;
+    font-size: 13px;
+    line-height: 1.6;
+    color: #7a8599;
   }
 
   .form-section {
@@ -435,13 +678,6 @@
     padding: 16px 12px;
     color: #999;
     font-size: 14px;
-  }
-
-  .summary-tip {
-    padding: 0 24rpx 24rpx;
-    color: #909399;
-    font-size: 12px;
-    line-height: 1.6;
   }
 
   :deep(.u-cell-group__title) {

--
Gitblit v1.9.3