yz hace 3 semanas
padre
commit
dedf32a2a5
Se han modificado 1 ficheros con 184 adiciones y 44 borrados
  1. 184 44
      src/components/forecast-form/forecast-form-mixin.js

+ 184 - 44
src/components/forecast-form/forecast-form-mixin.js

@@ -364,29 +364,40 @@ export default {
        * @param {boolean} val - 新的可见性值
        */
       handler(/** @type {boolean} */ val) {
+        console.log('👁️ [DEBUG] visible watcher 触发,值:', val);
         if (val) {
+          console.log('✅ [DEBUG] 表单显示,开始初始化');
           this.$nextTick(() => {
+            console.log('🔄 [DEBUG] $nextTick 回调执行');
+
             // 表单显示时,初始化表单数据
             if (this.initialFormData) {
+              console.log('📋 [DEBUG] 使用初始表单数据');
               this.formData = this.cleanAndFormatFormData(this.initialFormData)
             } else {
-              // 使用 initFormData,确保新增模式默认填入“下个月”而不是当前月
+              console.log('🆕 [DEBUG] 使用默认表单数据');
+              // 使用 initFormData,确保新增模式默认填入"下个月"而不是当前月
               this.initFormData()
             }
 
             // 如果是编辑模式且有ID,则加载详情数据
             if (this.isEdit && this.formData.id) {
+              console.log('✏️ [DEBUG] 编辑模式,加载详情数据');
               this.loadForecastDetail(this.formData.id)
             }
 
             // 如果不是编辑模式,则生成预测编码
             if (!this.isEdit && !this.formData.forecastCode) {
             //   this.generateForecastCode()
+              console.log('🔢 [DEBUG] 新增模式,生成预测编码(已注释)');
             }
 
             // 新增模式下,自动获取并填充客户信息
             if (!this.isEdit) {
+              console.log('👤 [DEBUG] 新增模式,加载客户信息');
               this.loadCurrentCustomerInfo()
+            } else {
+              console.log('✏️ [DEBUG] 编辑模式,跳过客户信息加载');
             }
 
             // 新增模式下进行年月预测存在性检查(默认年月)
@@ -395,6 +406,7 @@ export default {
             }
           })
         } else {
+          console.log('❌ [DEBUG] 表单隐藏');
           // 弹窗关闭:编辑态下清空选择,防止跨会话污染
           if (this.isEdit) {
             this.selectedRowKeys = []
@@ -414,6 +426,7 @@ export default {
      * @this {ForecastFormMixinComponent & Vue}
      */
     initialFormData(/** @type {ForecastFormModel} */ val) {
+      console.log('📋 [DEBUG] initialFormData 变化:', { val, hasValue: !!val });
       if (val) {
         this.formData = this.cleanAndFormatFormData(val)
       }
@@ -429,6 +442,13 @@ export default {
        * @param {ForecastFormModel} newData
        */
       handler(/** @type {ForecastFormModel} */ newData) {
+        console.log('📝 [DEBUG] editData 变化:', {
+          newData,
+          hasData: !!newData,
+          isEdit: this.isEdit,
+          newDataKeys: newData ? Object.keys(newData) : []
+        });
+
         if (newData && this.isEdit) {
           this.formData = {
             ...newData,
@@ -524,6 +544,21 @@ export default {
         }
       },
       immediate: true
+    },
+
+    /**
+     * 监听物料选项变化
+     * @param {Array} val - 新的物料选项数组
+     * @this {ForecastFormMixinComponent & Vue}
+     */
+    stockSelectOptions: {
+      handler(/** @type {Array} */ val) {
+        console.log('📋 [DEBUG] stockSelectOptions 变化:', {
+          length: val?.length,
+          options: val?.slice(0, 3) // 只显示前3个,避免console过长
+        });
+      },
+      immediate: true
     }
   },
 
@@ -532,6 +567,12 @@ export default {
    * @this {ForecastFormMixinComponent & Vue}
    */
   created() {
+    console.log('🏗️ [DEBUG] 组件 created 生命周期:', {
+      isEdit: this.isEdit,
+      hasEditData: !!this.editData,
+      props: this.$props
+    });
+
     this.initFormOption()
     this.initFormData()
   },
@@ -542,6 +583,71 @@ export default {
    */
   methods: {
     /**
+     * 创建物料匹配器函数
+     * @description 提取通用的物料匹配逻辑,避免代码重复
+     * @param {Object} material - 要匹配的物料对象
+     * @returns {Function} 匹配器函数
+     * @private
+     */
+    createMaterialMatcher(/** @type {Object} */ material) {
+      const id = material?.id
+      const goodsId = material?.goodsId
+      const code = material?.code
+
+      return (/** @type {Object} */ target) => {
+        if (id != null && target?.id != null && String(id) === String(target.id)) return true
+        if (goodsId != null && target?.goodsId != null && String(goodsId) === String(target.goodsId)) return true
+        if (code && target?.code && String(code) === String(target.code)) return true
+        return false
+      }
+    },
+
+    /**
+     * 优化的物料过滤算法
+     * @description 使用Map提高查找性能,避免循环嵌套
+     * @param {Array} stockTableData - 表格中的物料数据
+     * @param {Array} stockDescList - 可选的物料数据源
+     * @returns {Array} 过滤后的可选物料列表
+     * @private
+     */
+    optimizeStockSelectOptions(/** @type {Array} */ stockTableData, /** @type {Array} */ stockDescList) {
+      try {
+        const table = Array.isArray(stockTableData) ? stockTableData : []
+        const source = Array.isArray(stockDescList) ? stockDescList : []
+
+        // 使用Map构建已存在物料的快速查找索引
+        const materialMap = new Map()
+        table.forEach(item => {
+          if (item?.id != null) {
+            materialMap.set(`id:${String(item.id)}`, true)
+          }
+          if (item?.goodsId != null) {
+            materialMap.set(`goods:${String(item.goodsId)}`, true)
+          }
+          if (item?.code) {
+            materialMap.set(`code:${String(item.code)}`, true)
+          }
+        })
+
+        console.log('materialMap', materialMap)
+        // 过滤掉已在表格中的物料
+        return source.filter(item => {
+          console.log('item', item)
+          console.log('materialMap', materialMap)
+          if (item?.id != null && materialMap.has(`id:${String(item.id)}`)) return false
+          if (item?.goodsId != null && materialMap.has(`goods:${String(item.goodsId)}`)) return false
+          if (item?.code && materialMap.has(`code:${String(item.code)}`)) return false
+          return true
+        }).map(item => ({
+          label: item?.cname || item?.code || '',
+          value: String(item?.id || '')
+        }))
+      } catch (e) {
+        console.warn('优化物料过滤算法失败:', e)
+        return []
+      }
+    },
+    /**
      * 创建初始表单数据
      * @description 创建销售预测表单的初始数据结构
      * @returns {ForecastFormModel} 初始化的表单数据对象
@@ -759,27 +865,45 @@ export default {
      * @returns {Promise<void>}
      */
     async loadCurrentCustomerInfo() {
+      console.log('👤 [DEBUG] loadCurrentCustomerInfo 开始执行')
+      console.log('🔧 [DEBUG] 当前模式:', this.isEdit ? '编辑模式' : '新增模式');
+
       try {
+        console.log('📡 [DEBUG] 正在调用 getCustomerInfo API');
         const response = await getCustomerInfo()
         const ok = response && response.data && response.data.success
         const data = ok ? response.data.data : null
+
+        console.log('📦 [DEBUG] 客户信息API返回:', { ok, data });
+
         if (ok && data) {
           // 根据接口common.d.ts中的CustomerInfoData结构进行赋值
           this.formData.customerId = data.Customer_ID ? data.Customer_ID.toString() : null
           this.formData.customerCode = data.Customer_CODE || ''
           this.formData.customerName = data.Customer_NAME || ''
+          console.log('✅ [DEBUG] 客户信息设置完成:', this.formData);
         }
       } catch (e) {
         console.error('获取客户信息失败:', e)
       } finally {
         // 新增模式下,无论客户信息是否获取成功,都应确保物料明细加载一次。
         // 使用表格是否为空作为幂等保护,避免重复加载。
+        console.log('🔍 [DEBUG] 检查是否需要加载物料:', {
+          isEdit: this.isEdit,
+          stockTableDataLength: this.stockTableData?.length,
+          isArray: Array.isArray(this.stockTableData)
+        });
+
         if (!this.isEdit && Array.isArray(this.stockTableData) && this.stockTableData.length === 0) {
+          console.log('📋 [DEBUG] 条件满足,调用 loadUserLinkGoods');
           try {
             await this.loadUserLinkGoods()
           } catch (err) {
             // loadUserLinkGoods 内部已做错误提示,这里静默即可
+            console.error('❌ [DEBUG] loadUserLinkGoods 调用失败:', err)
           }
+        } else {
+          console.log('⏭️ [DEBUG] 跳过 loadUserLinkGoods,条件不满足');
         }
       }
     },
@@ -904,8 +1028,16 @@ export default {
      * @this {ForecastFormMixinComponent & Vue}
      */
     initFormData() {
+      console.log('🔧 [DEBUG] initFormData 执行:', {
+        isEdit: this.isEdit,
+        hasEditData: !!this.editData,
+        editDataId: this.editData?.id,
+        formDataId: this.formData?.id
+      });
+
       if (this.isEdit && this.editData) {
         // 编辑模式:使用传入的数据,确保year字段为字符串格式
+        console.log('✏️ [DEBUG] 使用编辑模式初始化数据');
         this.formData = {
           ...this.editData,
           year: this.editData.year ? this.editData.year.toString() : ''
@@ -1223,6 +1355,7 @@ export default {
      * @this {ForecastFormMixinComponent & Vue}
      */
     async loadUserLinkGoods() {
+      console.log('🚀 [DEBUG] loadUserLinkGoods 开始执行')
       try {
         this.tableLoading = true
         // 初始化容器
@@ -1233,15 +1366,27 @@ export default {
         this.selectedStockId = null
         // 重置选择状态
         this.selectedRowKeys = []
+
+        console.log('📡 [DEBUG] 正在调用 getUserLinkGoods API')
         const res = await getUserLinkGoods()
         const payload = res && res.data && res.data.data ? res.data.data : null
         const brandList = (payload && payload.pjpfBrandDescList) || []
         const stockList = (payload && payload.pjpfStockDescList) || []
+
+        console.log('📦 [DEBUG] API返回数据:', {
+          brandCount: brandList.length,
+          stockCount: stockList.length,
+          payload: payload
+        })
+
         this.brandDescList = brandList
         // 存储库存列表供选择用,不直接展示到表格
         this.stockDescList = stockList
         // 默认显示全部物料至下方表格,预测数量默认 1,用户可手动删除不需要的物料
         this.stockTableData = stockList.map(item => ({ ...item, forecastQuantity: 1 }))
+
+        console.log('📋 [DEBUG] 设置表格数据后,stockTableData长度:', this.stockTableData.length)
+
         // 根据表格中已有的物料,过滤下拉选项
         this.updateStockSelectOptions()
         // 规范化分页并回显选择(新增模式首次加载)
@@ -1251,12 +1396,13 @@ export default {
         this.$message.error(e.message || '加载用户关联商品失败')
       } finally {
         this.tableLoading = false
+        console.log('✅ [DEBUG] loadUserLinkGoods 执行完成')
       }
     },
 
     /**
      * 导入所选物料到下方表格
-     * @description 仅在点击"导入物料"按钮后,将选择的物料行添加到表格,默认预测数量为 0
+     * @description 仅在点击"导入物料"按钮后,将选择的物料行添加到表格,默认预测数量为 1
      * @returns {void}
      * @this {ForecastFormMixinComponent & Vue}
      */
@@ -1273,24 +1419,10 @@ export default {
         return
       }
 
-      // 防止重复导入 - 使用多个字段进行更全面的重复检查
-      const exists = this.stockTableData.some(row => {
-        // 优先使用 id 进行匹配
-        if (row.id !== undefined && row.id !== null && stock.id !== undefined && stock.id !== null && String(row.id) === String(stock.id)) {
-          return true
-        }
-        // 使用 goodsId 进行匹配
-        if (row.goodsId !== undefined && row.goodsId !== null && stock.goodsId !== undefined && stock.goodsId !== null && String(row.goodsId) === String(stock.goodsId)) {
-          return true
-        }
-        // 使用 code 进行匹配
-        if (row.code && stock.code && String(row.code) === String(stock.code)) {
-          return true
-        }
-        return false
-      })
+      // 使用通用匹配器检查是否重复导入
+      const isDuplicate = this.stockTableData.some(this.createMaterialMatcher(stock))
 
-      if (exists) {
+      if (isDuplicate) {
         this.$message.warning('该物料已在列表中')
         this.selectedStockId = null
         return
@@ -1425,30 +1557,21 @@ export default {
 
     /**
      * 更新物料下拉选项(过滤已在表格中的物料)
+     * @description 使用优化的过滤算法,提高性能和可维护性
      * @returns {void}
      */
     updateStockSelectOptions() {
-      try {
-        const table = Array.isArray(this.stockTableData) ? this.stockTableData : []
-        const source = Array.isArray(this.stockDescList) ? this.stockDescList : []
-
-        const idSet = new Set(table.filter(r => r && r.id !== undefined && r.id !== null).map(r => String(r.id)))
-        const goodsIdSet = new Set(table.filter(r => r && r.goodsId !== undefined && r.goodsId !== null).map(r => String(r.goodsId)))
-        const codeSet = new Set(table.filter(r => r && r.code).map(r => String(r.code)))
-
-        const options = source
-          .filter(item => {
-            const byId = item && item.id !== undefined && item.id !== null && idSet.has(String(item.id))
-            const byGoods = item && item.goodsId !== undefined && item.goodsId !== null && goodsIdSet.has(String(item.goodsId))
-            const byCode = item && item.code && codeSet.has(String(item.code))
-            return !(byId || byGoods || byCode)
-          })
-          .map(item => ({
-            label: /** @type {any} */ (item.cname || item.code || ''),
-            value: /** @type {any} */ (String(item.id))
-          }))
+      console.log('🔄 [DEBUG] updateStockSelectOptions 被调用')
+      console.log('📊 [DEBUG] stockTableData 长度:', this.stockTableData?.length)
+      console.log('📊 [DEBUG] stockDescList 长度:', this.stockDescList?.length)
 
+      try {
+        // 使用优化的过滤算法
+        const options = this.optimizeStockSelectOptions(this.stockTableData, this.stockDescList)
         this.stockSelectOptions = options
+
+        console.log('✅ [DEBUG] 过滤后的选项数量:', options.length)
+
         // 如果当前选中不在可选项中,则清空
         const hasSelected = options.some(opt => opt && opt.value === this.selectedStockId)
         if (!hasSelected) {
@@ -1598,20 +1721,35 @@ export default {
      * @returns {Promise<void>}
      */
     async mergeEchoStoreInventory() {
+      console.log('🔄 [DEBUG] mergeEchoStoreInventory 开始执行');
       try {
-        if (!Array.isArray(this.stockTableData) || this.stockTableData.length === 0) return
+        if (!Array.isArray(this.stockTableData) || this.stockTableData.length === 0) {
+          console.log('⏭️ [DEBUG] 表格数据为空,跳过库存合并');
+          return
+        }
+
+        console.log('📡 [DEBUG] 调用 getUserLinkGoods API');
         const res = await getUserLinkGoods()
         const payload = res && res.data && res.data.data ? res.data.data : null
         const stockList = (payload && payload.pjpfStockDescList) || []
-        if (!Array.isArray(stockList) || stockList.length === 0) return
+        if (!Array.isArray(stockList) || stockList.length === 0) {
+          console.log('⚠️ [DEBUG] API返回的物料列表为空');
+          return
+        }
+
+        console.log('📦 [DEBUG] 获取到物料列表:', {
+          stockCount: stockList.length,
+          tableCount: this.stockTableData.length
+        });
 
         // 在编辑模式下,确保"导入物料"的选择器有数据可选
         // 不修改现有表格数据,仅补齐选择来源
         this.stockDescList = stockList
-        this.stockSelectOptions = stockList.map(item => ({
-          label: item.cname,
-          value: item.id
-        }))
+
+        // ✅ 修复:使用优化的过滤方法,正确过滤已在表格中的物料
+        this.updateStockSelectOptions()
+
+        console.log('✅ [DEBUG] 更新物料选择选项完成');
 
         // 构建基于 goodsId 与 code 的索引映射
         /** @type {Map<string, string|undefined>} */
@@ -1635,6 +1773,8 @@ export default {
           const value = (fromGoods !== undefined && fromGoods !== null && fromGoods !== '') ? fromGoods : ((fromCode !== undefined && fromCode !== null && fromCode !== '') ? fromCode : '0')
           return { ...row, storeInventory: String(value) }
         })
+
+        console.log('✅ [DEBUG] 库存合并完成');
       } catch (e) {
         console.warn('回显库存合并失败:', e)
       }