Преглед на файлове

feat(订单表单): 添加草稿状态下物料明细编辑与持久化功能

yz преди 1 седмица
родител
ревизия
35a779f6f7
променени са 3 файла, в които са добавени 123 реда и са изтрити 4 реда
  1. 11 0
      src/components/order-form/material-detail-mixin.js
  2. 111 4
      src/components/order-form/order-form-mixin.js
  3. 1 0
      src/components/order-form/order-form.vue

+ 11 - 0
src/components/order-form/material-detail-mixin.js

@@ -100,6 +100,15 @@ export default {
         typeof item.id === 'string' &&
         typeof item.itemCode === 'string'
       )
+    },
+
+    /**
+     * 是否为草稿状态 - 草稿状态下允许编辑所有物料明细
+     * @type {import('vue').PropOptions<boolean>}
+     */
+    isDraft: {
+      type: Boolean,
+      default: false
     }
   },
 
@@ -335,6 +344,8 @@ export default {
      * @returns {boolean} 是否可编辑,true表示可编辑,false表示不可编辑
      */
     isRowEditable(row) {
+      // 草稿状态下允许编辑所有行
+      if (this.isDraft === true) return true
       // 如果没有数据来源信息,默认可编辑
       if (!row || !row.dataSource) {
         return true

+ 111 - 4
src/components/order-form/order-form-mixin.js

@@ -6,7 +6,7 @@
 // API接口导入
 import { add, update as updateOrderHeader, getDetail } from '@/api/order/order'
 import { getList as getOrderItemList } from '@/api/order/order-item'
-import { createSalesOrder, updateOrder, addOrderItem } from '@/api/order/sales-order'
+import { createSalesOrder, updateOrder, addOrderItem, updateOrderItem } from '@/api/order/sales-order'
 import { getCustomerInfo } from '@/api/common/index'
 import { getList as getAddressList } from '@/api/order/address'
 import { submitOrderToU9 } from '@/api/order/sales-order'
@@ -158,6 +158,13 @@ export default {
       materialDetails: [],
 
       /**
+       * 远程明细原始快照映射
+       * @description 记录从服务器加载的远程物料明细的原始值,用于草稿状态下的变更对比
+       * @type {Record<string, {orderQuantity:number, confirmQuantity:number, unitPrice:number, taxRate:number}>}
+       */
+      originalRemoteDetailsById: {},
+
+      /**
        * 订单类型选项列表
        * @description 订单类型下拉选择器的选项数据
        * @type {typeof ORDER_TYPE_OPTIONS}
@@ -440,6 +447,16 @@ export default {
           }
         ]
       }
+    },
+
+    /**
+     * 是否为草稿状态
+     * @description 根据订单状态判断是否为草稿
+     * @returns {boolean}
+     */
+    isDraft() {
+      const status = Number(this.formData && this.formData.status)
+      return status === ORDER_STATUS.DRAFT
     }
   },
 
@@ -681,6 +698,9 @@ export default {
         // 设置物料明细数据(确保是数组类型)
         this.materialDetails = Array.isArray(materialResponse) ? materialResponse : []
 
+        // 新增:建立远程明细原始快照,用于后续对比并持久化更新
+        this.originalRemoteDetailsById = this.buildOriginalRemoteSnapshot(this.materialDetails)
+
         console.log(`成功加载订单详情,订单编码: ${orderData.orderCode || orderId}`)
 
       } catch (error) {
@@ -803,6 +823,52 @@ export default {
       }
     },
 
+    // 构建远程明细原始快照
+    buildOriginalRemoteSnapshot(materials) {
+      const map = {}
+      try {
+        (materials || [])
+          .filter(m => m && m.dataSource === MaterialDetailDataSource.REMOTE)
+          .forEach(m => {
+            const key = String(m.id || m.itemId || m.itemCode || '')
+            if (!key) return
+            map[key] = {
+              orderQuantity: Math.round(Number(m.orderQuantity) || 0),
+              confirmQuantity: Math.round(Number(m.confirmQuantity) || 0),
+              unitPrice: preciseRound(Number(m.unitPrice) || 0, 2),
+              taxRate: preciseRound(Number(m.taxRate) || 0, 4)
+            }
+          })
+      } catch (e) {
+        // eslint-disable-next-line no-console
+        console.warn('构建原始快照失败:', e)
+      }
+      return map
+    },
+
+    // 判断远程明细是否发生变化(与原始快照对比)
+    hasRemoteMaterialChanged(currentRow) {
+      if (!currentRow) return false
+      const key = String(currentRow.id || currentRow.itemId || currentRow.itemCode || '')
+      if (!key) return false
+      const original = this.originalRemoteDetailsById && this.originalRemoteDetailsById[key]
+      if (!original) return false
+
+      const curr = {
+        orderQuantity: Math.round(Number(currentRow.orderQuantity) || 0),
+        confirmQuantity: Math.round(Number(currentRow.confirmQuantity) || 0),
+        unitPrice: preciseRound(Number(currentRow.unitPrice) || 0, 2),
+        taxRate: preciseRound(Number(currentRow.taxRate) || 0, 4)
+      }
+
+      return (
+        curr.orderQuantity !== original.orderQuantity ||
+        curr.confirmQuantity !== original.confirmQuantity ||
+        curr.unitPrice !== original.unitPrice ||
+        curr.taxRate !== original.taxRate
+      )
+    },
+
     /**
      * 映射订单数据到表单格式
      * @description 将API返回的订单数据安全地映射为表单数据格式,并格式化数字字段
@@ -1001,7 +1067,7 @@ export default {
      */
     async submitOrderData(submitData) {
       if (this.isEdit) {
-        // 编辑状态下:先仅更新订单基础信息(不包含物料明细),再单独添加“导入”的物料
+        // 编辑状态下:先仅更新订单基础信息(不包含物料明细),再单独处理导入新增与远程变更
         // 第一步:更新订单头
         const headerData = this.prepareSubmitData()
         const headerResponse = await updateOrderHeader(headerData)
@@ -1018,7 +1084,7 @@ export default {
           const orderCode = (this.formData && this.formData.orderCode) || ''
 
           const payloads = importedMaterials.map(material => ({
-            orderId: String(orderId), // 以字符串传输,避免大整数精度问题
+            orderId: String(orderId),
             orderCode,
             itemId: String(material.itemId || ''),
             itemCode: material.itemCode || '',
@@ -1045,7 +1111,48 @@ export default {
           }
         }
 
-        // 两步均成功,返回头部更新的响应
+        // 第三步:草稿状态下,找出远程明细的变更并调用 updateOrderItem 持久化
+        if (this.isDraft) {
+          const orderId = (this.formData && this.formData.id) || this.orderId
+          const remoteChanged = (this.materialDetails || [])
+            .filter(m => m.dataSource === MaterialDetailDataSource.REMOTE)
+            .filter(m => this.hasRemoteMaterialChanged(m))
+
+          if (remoteChanged.length > 0) {
+            const updatePayloads = remoteChanged.map(material => ({
+              id: String(material.id || ''),
+              orderId: String(orderId || ''),
+              orderCode: (this.formData && this.formData.orderCode) || '',
+              itemId: String(material.itemId || ''),
+              itemCode: material.itemCode || '',
+              itemName: material.itemName || '',
+              specs: material.specs || '',
+              mainItemCategoryId: String(material.mainItemCategoryId || ''),
+              mainItemCategoryName: material.mainItemCategoryName || '',
+              warehouseId: String(material.warehouseId || ''),
+              warehouseName: material.warehouseName || '',
+              availableQuantity: Number(material.availableQuantity) || 0,
+              orderQuantity: Number(material.orderQuantity) || 0,
+              confirmQuantity: Number(material.confirmQuantity) || Number(material.orderQuantity) || 0,
+              unitPrice: Number(material.unitPrice) || 0,
+              taxRate: Number(material.taxRate) || 0,
+              taxAmount: Number(material.taxAmount) || 0,
+              totalAmount: Number(material.totalAmount) || 0,
+              itemStatus: material.itemStatus || ORDER_ITEM_STATUS.UNCONFIRMED
+            }))
+
+            const updateResults = await Promise.all(updatePayloads.map(p => updateOrderItem(p)))
+            const updatesOk = updateResults.every(r => r && r.data && r.data.success)
+            if (!updatesOk) {
+              throw new Error('部分物料更新失败')
+            }
+
+            // 更新成功后,刷新原始快照,避免后续重复提交
+            this.originalRemoteDetailsById = this.buildOriginalRemoteSnapshot(this.materialDetails)
+          }
+        }
+
+        // 返回头部更新的响应
         return headerResponse
       } else {
         // 新建状态下使用createSalesOrder接口,包含物料明细数据

+ 1 - 0
src/components/order-form/order-form.vue

@@ -58,6 +58,7 @@
           :order-id="orderId"
           :edit-mode="true"
           :material-details="materialDetails"
+          :is-draft="isDraft"
           @refresh="handleMaterialChange"
           @material-import="handleMaterialImport"
           @material-delete="handleMaterialDelete"