Bläddra i källkod

feat(forecast): 新增年月重复校验并禁用保存按钮功能

yz 2 veckor sedan
förälder
incheckning
eda86d21e3

+ 58 - 7
src/components/forecast-form/forecast-form-mixin.js

@@ -59,7 +59,7 @@
 
 // API接口导入
 import { addForecast, updateForecast, getForecastDetail } from '@/api/forecast'
-import { addSalesForecastMain, updateSalesForecastMain } from '@/api/forecast/forecast-summary'
+import { addSalesForecastMain, updateSalesForecastMain, getSalesForecastSummaryByMonth } from '@/api/forecast/forecast-summary'
 import { getUserLinkGoods } from '@/api/order/sales-order'
 
 // 常量和枚举导入
@@ -102,7 +102,9 @@ export const FORECAST_FORM_EVENTS = {
   /** 表单提交失败事件 */
   SUBMIT_ERROR: 'submit-error',
   /** 更新可见性事件 */
-  UPDATE_VISIBLE: 'update:visible'
+  UPDATE_VISIBLE: 'update:visible',
+  /** 保存禁用状态变更(用于父级按钮禁用控制) */
+  SAVE_DISABLED_CHANGE: 'save-disabled-change'
 }
 
 /**
@@ -327,7 +329,8 @@ export default {
             if (this.initialFormData) {
               this.formData = this.cleanAndFormatFormData(this.initialFormData)
             } else {
-              this.formData = this.createInitialFormData()
+              // 使用 initFormData,确保新增模式默认填入“下个月”而不是当前月
+              this.initFormData()
             }
 
             // 如果是编辑模式且有ID,则加载详情数据
@@ -344,6 +347,11 @@ export default {
             if (!this.isEdit) {
               this.loadCurrentCustomerInfo()
             }
+
+            // 新增模式下进行年月预测存在性检查(默认年月)
+            if (!this.isEdit) {
+              this.checkForecastByMonthAndEmit && this.checkForecastByMonthAndEmit()
+            }
           })
         }
       },
@@ -422,10 +430,30 @@ export default {
         if (!newVal) {
           this.initFormData()
         }
+        // 切换为编辑态时,通知父级恢复保存按钮可点击
+        if (newVal && this.$emit) {
+          this.$emit(FORECAST_FORM_EVENTS.SAVE_DISABLED_CHANGE, false)
+        }
       },
       immediate: true
     },
 
+    // 新增:监听年份与月份变更以触发按月校验(仅新增模式)
+    'formData.year': {
+      handler() {
+        if (this.visible && !this.isEdit) {
+          this.checkForecastByMonthAndEmit && this.checkForecastByMonthAndEmit()
+        }
+      }
+    },
+    'formData.month': {
+      handler() {
+        if (this.visible && !this.isEdit) {
+          this.checkForecastByMonthAndEmit && this.checkForecastByMonthAndEmit()
+        }
+      }
+    },
+
     /**
      * 监听预测ID变化
      * @param {string|number} val - 新的预测ID
@@ -1056,7 +1084,7 @@ export default {
 
         // 新增模式下需要至少一条有效明细;编辑模式下仅提交主表四个字段,不校验明细条数
         if (!this.isEdit && !items.length) {
-          this.$message && this.$message.warning('请至少填写一条有效的预测数量')
+        //   this.$message && this.$message.warning('请至少填写一条有效的预测数量')
           // 通知父组件失败,便于父侧重置保存按钮loading
           this.$emit && this.$emit(FORECAST_FORM_EVENTS.SUBMIT_ERROR, { message: '未填写有效的预测明细' })
           return
@@ -1181,7 +1209,7 @@ export default {
         this.$message.error('未找到所选物料数据,请重新选择')
         return
       }
-      
+
       // 防止重复导入 - 使用多个字段进行更全面的重复检查
       const exists = this.stockTableData.some(row => {
         // 优先使用 id 进行匹配
@@ -1198,13 +1226,13 @@ export default {
         }
         return false
       })
-      
+
       if (exists) {
         this.$message.warning('该物料已在列表中')
         this.selectedStockId = null
         return
       }
-      
+
       // 添加到表格,默认预测数量为 0
       this.stockTableData.push({ ...stock, forecastQuantity: 0 })
       // 清空已选
@@ -1345,6 +1373,29 @@ export default {
       } catch (e) {
         console.warn('回显库存合并失败:', e)
       }
+    },
+
+    /**
+     * 新增模式:检查指定年月是否已有预测,并通过事件通知父组件控制保存按钮禁用
+     * @returns {Promise<void>}
+     */
+    async checkForecastByMonthAndEmit() {
+      try {
+        const year = typeof this.formData.year === 'string' ? parseInt(this.formData.year, 10) : Number(this.formData.year)
+        const month = Number(this.formData.month)
+        if (!year || !month) return
+        // 仅新增模式需要校验
+        if (this.isEdit) {
+          this.$emit && this.$emit(FORECAST_FORM_EVENTS.SAVE_DISABLED_CHANGE, false)
+          return
+        }
+        const res = await getSalesForecastSummaryByMonth(year, month)
+        const hasData = !!(res && res.data && res.data.success && Array.isArray(res.data.data) && res.data.data.length > 0)
+        this.$emit && this.$emit(FORECAST_FORM_EVENTS.SAVE_DISABLED_CHANGE, hasData)
+      } catch (e) {
+        // 异常时不阻塞新增,默认允许保存
+        this.$emit && this.$emit(FORECAST_FORM_EVENTS.SAVE_DISABLED_CHANGE, false)
+      }
     }
   }
 }

+ 8 - 2
src/components/forecast-form/index.js

@@ -319,10 +319,16 @@ export default {
      * @private
      */
     createInitialFormData() {
+      // 默认使用“下个月”的年月,与 mixin 中的逻辑保持一致
+      const now = new Date()
+      const currentYear = now.getFullYear()
+      const currentMonth = now.getMonth() + 1
+      const nextYear = currentMonth === 12 ? currentYear + 1 : currentYear
+      const nextMonth = currentMonth === 12 ? 1 : currentMonth + 1
       return {
         forecastCode: '',
-        year: new Date().getFullYear(),
-        month: new Date().getMonth() + 1,
+        year: nextYear,
+        month: nextMonth,
         customerId: 0,
         customerCode: '',
         customerName: '',

+ 35 - 1
src/views/forecast/forecastIndex.js

@@ -54,6 +54,8 @@ export default {
       submitting: false,
       /** @type {boolean} 保存按钮加载状态 */
       saveLoading: false,
+      /** @type {boolean} 保存按钮禁用状态(按年月重复校验) */
+      saveDisabled: false,
       /** @type {boolean} 添加/编辑弹窗显示状态(保留兼容性) */
       dialogVisible: false,
       /** @type {boolean} 是否为编辑模式(保留兼容性) */
@@ -660,6 +662,9 @@ export default {
       // 生成预测编码
       this.generateForecastCode()
 
+      // 进入新增:先禁用保存,待子组件按默认年月校验完成后再由事件恢复
+      this.saveDisabled = true
+
       // 保持兼容性
       this.isEdit = false
       this.dialogVisible = true
@@ -678,8 +683,19 @@ export default {
         return
       }
 
-      this.isEdit = true
+      this.editMode = 'edit'
+      this.editTitle = '编辑预测申报'
       this.currentForecastId = row.id
+      this.editVisible = true
+
+      // 设置表单数据
+      this.form = { ...row }
+
+      // 编辑模式不需要按月校验禁用
+      this.saveDisabled = false
+
+      // 保持兼容性
+      this.isEdit = true
       this.dialogVisible = true
     },
 
@@ -705,6 +721,9 @@ export default {
       // 设置表单数据
       this.form = { ...row }
 
+      // 编辑模式不需要按月校验禁用
+      this.saveDisabled = false
+
       // 保持兼容性
       this.isEdit = true
       this.dialogVisible = true
@@ -723,6 +742,9 @@ export default {
       this.currentForecastId = null
       this.form = { ...DEFAULT_FORECAST_FORM }
 
+      // 重置保存禁用状态
+      this.saveDisabled = false
+
       // 保持兼容性
       this.dialogVisible = false
       this.isEdit = false
@@ -734,6 +756,10 @@ export default {
      * @returns {void}
      */
     handleSave() {
+      if (this.saveDisabled) {
+        this.$message && this.$message.warning('所选年月已存在预测,无法重复保存')
+        return
+      }
       if (this.$refs.forecastForm) {
         this.saveLoading = true
         this.$refs.forecastForm.handleSubmit()
@@ -741,6 +767,14 @@ export default {
     },
 
     /**
+     * 子组件发出的保存禁用状态变更
+     * @param {boolean} disabled
+     */
+    onSaveDisabledChange(disabled) {
+      this.saveDisabled = !!disabled
+    },
+
+    /**
      * 处理表单提交成功
      * @this {Vue & {saveLoading: boolean, editMode: string, $message: any, handleBackToList: () => void, refreshChange: () => void}}
      * @param {{msg?: string}} data - 提交成功的数据

+ 2 - 0
src/views/forecast/index.vue

@@ -156,6 +156,7 @@
           icon="el-icon-check"
           size="small"
           :loading="saveLoading"
+          :disabled="saveDisabled"
           @click="handleSave"
         >
           保存
@@ -171,6 +172,7 @@
         @close="handleFormClose"
         @submit="handleFormSubmit"
         @submit-error="handleFormError"
+        @save-disabled-change="onSaveDisabledChange"
       />
     </div>
   </basic-container>