Browse Source

feat(销售预测): 添加基于AvueJS的销售预测表单实现

yz 1 month ago
parent
commit
c41a8c4e0b

+ 10 - 0
src/components/common/events.js

@@ -29,4 +29,14 @@ export const COMMON_EVENTS = {
 export const CUSTOMER_SELECT_EVENTS = {
   /** 客户选择事件 */
   CUSTOMER_SELECTED: 'customer-selected'
+}
+
+/**
+ * 物料选择组件事件常量
+ * @readonly
+ * @enum {string}
+ */
+export const MATERIAL_SELECT_EVENTS = {
+  /** 物料选择事件 */
+  MATERIAL_SELECTED: 'material-selected'
 }

+ 380 - 0
src/components/common/material-select.vue

@@ -0,0 +1,380 @@
+<template>
+  <el-select
+    v-model="currentValue"
+    :placeholder="placeholder"
+    :disabled="disabled"
+    :clearable="clearable"
+    :filterable="true"
+    :remote="true"
+    :remote-method="remoteSearch"
+    :loading="loading"
+    :size="size"
+    :class="selectClass"
+    @change="handleChange"
+    @clear="handleClear"
+    @focus="handleFocus"
+    @blur="handleBlur"
+  >
+    <el-option
+      v-for="item in options"
+      :key="item.Item_ID"
+      :label="item.Item_Name"
+      :value="item.Item_ID"
+    >
+      <div style="display: flex; justify-content: space-between">
+        <span>{{ item.Item_Code }} - {{ item.Item_Name }}</span>
+        <span style="color: #8492a6; font-size: 13px">
+          {{ item.Item_PECS || '无规格' }}
+        </span>
+      </div>
+    </el-option>
+  </el-select>
+</template>
+
+<script>
+import { getItemList } from '@/api/common/index'
+import { COMMON_EVENTS, MATERIAL_SELECT_EVENTS } from './events'
+
+/**
+ * 物料选择组件
+ * @description 基于Element UI Select的物料选择组件,支持远程搜索和数据回显
+ * @component
+ */
+export default {
+  name: 'MaterialSelect',
+  
+  /**
+   * 组件属性定义
+   * @description 定义组件接收的外部属性
+   */
+  props: {
+    /**
+     * 绑定值
+     * @description 当前选中的物料ID
+     */
+    value: {
+      type: [String, Number],
+      default: ''
+    },
+    /**
+     * 占位符文本
+     * @description 输入框的占位符提示文本
+     */
+    placeholder: {
+      type: String,
+      default: '请选择物料'
+    },
+    /**
+     * 是否禁用
+     * @description 控制组件是否处于禁用状态
+     */
+    disabled: {
+      type: Boolean,
+      default: false
+    },
+    /**
+     * 是否可清空
+     * @description 控制是否显示清空按钮
+     */
+    clearable: {
+      type: Boolean,
+      default: true
+    },
+    /**
+     * 组件尺寸
+     * @description 控制组件的显示尺寸
+     */
+    size: {
+      type: String,
+      default: 'small',
+      validator: (value) => ['large', 'medium', 'small', 'mini'].includes(value)
+    }
+  },
+  
+  /**
+   * 组件响应式数据
+   * @description 定义组件的内部状态数据
+   * @returns {Object} 组件数据对象
+   */
+  data() {
+    return {
+      /**
+       * 当前选中值
+       * @type {string|number}
+       */
+      currentValue: this.value,
+      /**
+       * 物料选项列表
+       */
+      options: [],
+      /**
+       * 加载状态
+       * @type {boolean}
+       */
+      loading: false,
+      /**
+       * 搜索防抖定时器
+       * @type {number|null}
+       */
+      searchTimer: null
+    }
+  },
+  
+  /**
+   * 计算属性
+   * @description 组件的响应式计算属性
+   */
+  computed: {
+    /**
+     * 选择器样式类
+     * @description 返回与Avue组件一致的样式类名
+     * @returns {Object} 样式类对象
+     */
+    selectClass() {
+      return {
+        'avue-select': true,
+        'el-select': true
+      }
+    }
+  },
+  /**
+   * 监听器
+   * @description 监听属性变化并执行相应的处理逻辑
+   */
+  watch: {
+    /**
+     * 监听value属性变化
+     * @description 当外部传入的value发生变化时,同步更新内部状态并加载物料信息
+     * @param {string|number} newVal - 新的物料ID值
+     * @param {string|number} oldVal - 旧的物料ID值
+     */
+    value(newVal, oldVal) {
+      if (newVal !== oldVal) {
+        this.currentValue = newVal
+        // 当value变化时,如果有值且当前选项为空,则加载对应的物料信息
+        if (newVal && this.options.length === 0) {
+          this.loadMaterialById(newVal)
+        }
+      }
+    },
+    /**
+     * 监听currentValue变化
+     * @description 当内部值发生变化时,向父组件发送input事件
+     * @param {string|number} newVal - 新的值
+     */
+    currentValue(newVal) {
+      this.$emit(COMMON_EVENTS.INPUT, newVal)
+    }
+  },
+  
+  /**
+   * 组件挂载钩子
+   * @description 组件挂载完成后执行的初始化逻辑
+   */
+  mounted() {
+    // 组件挂载时,如果有初始值,加载对应的物料信息
+    if (this.value) {
+      this.loadMaterialById(this.value)
+    }
+  },
+  
+  /**
+   * 组件销毁钩子
+   * @description 组件销毁前清理定时器等资源
+   */
+  beforeDestroy() {
+    if (this.searchTimer) {
+      clearTimeout(this.searchTimer)
+      this.searchTimer = null
+    }
+  },
+  
+  /**
+   * 组件方法
+   * @description 组件的业务逻辑方法集合
+   */
+  methods: {
+    /**
+     * 远程搜索方法
+     * @description 根据输入关键词远程搜索物料数据
+     * @param {string} keyword - 搜索关键词
+     * @returns {void}
+     */
+    remoteSearch(keyword) {
+      // 清除之前的定时器
+      if (this.searchTimer) {
+        clearTimeout(this.searchTimer)
+      }
+      
+      // 设置防抖定时器
+      this.searchTimer = setTimeout(async () => {
+        if (!keyword) {
+          this.options = []
+          return
+        }
+        
+        await this.searchMaterials(keyword)
+      }, 300)
+    },
+    
+    /**
+     * 搜索物料
+     * @description 调用API搜索物料数据
+     * @param {string} keyword - 搜索关键词
+     * @returns {Promise<void>}
+     * @throws {Error} 当API调用失败时抛出异常
+     */
+    async searchMaterials(keyword) {
+      try {
+        this.loading = true
+        
+        const response = await getItemList(1, 20, {
+          itemName: keyword
+        })
+        
+        if (response?.data?.success && response.data.data?.records) {
+          this.options = response.data.data.records
+        } else {
+          this.options = []
+          const errorMsg = response?.data?.msg || '搜索物料失败'
+          this.$message.warning(errorMsg)
+        }
+      } catch (error) {
+        console.error('搜索物料API调用失败:', error)
+        this.options = []
+        this.$message.error('网络错误,搜索物料失败')
+        throw error
+      } finally {
+        this.loading = false
+      }
+    },
+    
+    /**
+     * 根据ID加载物料信息
+     * @description 通过物料ID查询并加载单个物料的详细信息,用于数据回显
+     * @param {string|number} itemId - 物料ID
+     * @returns {Promise<void>}
+     * @throws {Error} 当API调用失败时抛出异常
+     */
+    async loadMaterialById(itemId) {
+      if (!itemId) {
+        return
+      }
+      
+      try {
+        this.loading = true
+        
+        const response = await getItemList(1, 200, {
+          id: itemId
+        })
+        
+        if (response?.data?.success && response.data.data?.records) {
+          const allMaterials = response.data.data.records
+          const material = allMaterials.find(item => item.Item_ID == itemId)
+          
+          if (material) {
+            // 只保留匹配的物料选项
+            this.options = [material]
+            // 确保选中状态
+            this.currentValue = String(material.Item_ID)
+            console.log('物料回显成功:', material.Item_Name, 'ID:', material.Item_ID)
+          } else {
+            // 如果在当前页没找到,可能需要查询更多页面
+            console.warn(`未找到物料ID为 ${itemId} 的物料信息`)
+            this.options = []
+          }
+        } else {
+          const errorMsg = response?.data?.msg || '获取物料列表失败'
+          console.error('获取物料列表失败:', errorMsg)
+          this.options = []
+          this.$message.warning(errorMsg)
+        }
+      } catch (error) {
+        console.error('加载物料信息API调用失败:', error)
+        this.options = []
+        this.$message.error('网络错误,加载物料信息失败')
+        throw error
+      } finally {
+        this.loading = false
+      }
+    },
+    
+    /**
+     * 选择变化处理
+     * @description 当用户选择物料时触发,更新内部状态并向父组件发送相关事件
+     * @param {string|number} value - 选中的物料ID
+     * @returns {void}
+     */
+    handleChange(value) {
+      this.currentValue = value
+      this.$emit(COMMON_EVENTS.CHANGE, value)
+      
+      // 查找选中的物料对象
+      const selectedMaterial = this.options.find(item => item.Item_ID == value)
+      
+      if (selectedMaterial) {
+        // 发送物料选择事件,包含完整的物料信息
+        this.$emit(MATERIAL_SELECT_EVENTS.MATERIAL_SELECTED, {
+          itemId: selectedMaterial.Item_ID,
+          itemCode: selectedMaterial.Item_Code,
+          itemName: selectedMaterial.Item_Name,
+          specification: selectedMaterial.Item_PECS,
+          materialData: selectedMaterial // 传递完整的物料对象
+        })
+      } else {
+        // 如果没有找到对应的物料,发送空数据
+        this.$emit(MATERIAL_SELECT_EVENTS.MATERIAL_SELECTED, {
+          itemId: '',
+          itemCode: '',
+          itemName: '',
+          specification: '',
+          materialData: null
+        })
+      }
+    },
+    
+    /**
+     * 清空处理
+     */
+    handleClear() {
+      this.currentValue = ''
+      this.options = []
+      this.$emit(COMMON_EVENTS.CLEAR)
+      this.$emit(MATERIAL_SELECT_EVENTS.MATERIAL_SELECTED, {
+        itemId: '',
+        itemCode: '',
+        itemName: '',
+        specification: ''
+      })
+    },
+    
+    /**
+     * 获得焦点处理
+     */
+    handleFocus() {
+      this.$emit(COMMON_EVENTS.FOCUS)
+    },
+    
+    /**
+     * 失去焦点处理
+     */
+    handleBlur() {
+      this.$emit(COMMON_EVENTS.BLUR)
+    }
+  }
+}
+</script>
+
+<style scoped>
+/* 确保样式与Avue select保持一致 */
+.avue-select {
+  width: 100%;
+}
+
+/* 选项样式优化 */
+.el-select-dropdown__item {
+  height: auto;
+  line-height: 1.5;
+  padding: 8px 20px;
+}
+</style>

+ 423 - 0
src/components/forecast-form/forecast-form-avue.vue

@@ -0,0 +1,423 @@
+<template>
+  <!-- 销售预测表单容器 - AvueJS版本 -->
+  <div class="forecast-form-container basic-container">
+    <!-- 表单内容区域 -->
+    <div class="forecast-form-content">
+      <avue-form
+        ref="forecastForm"
+        v-model="formData"
+        :option="formOption"
+        @submit="handleSubmit"
+        @reset-change="handleReset"
+      >
+        <!-- 客户选择器插槽 -->
+        <template #customerId>
+          <customer-select
+            v-model="formData.customerId"
+            placeholder="请选择客户"
+            @customer-selected="handleCustomerSelected"
+          />
+        </template>
+
+        <!-- 品牌选择器插槽 -->
+        <template #brandId>
+          <el-select
+            v-model="formData.brandId"
+            placeholder="请选择品牌"
+            :disabled="isEdit"
+            filterable
+            remote
+            :remote-method="remoteBrandSearch"
+            :loading="brandLoading"
+            @change="handleBrandChange"
+            style="width: 100%"
+          >
+            <el-option
+              v-for="item in brandOptions"
+              :key="item.id"
+              :label="item.name"
+              :value="item.id"
+            >
+              <span>{{ item.code }} - {{ item.name }}</span>
+            </el-option>
+          </el-select>
+        </template>
+
+        <!-- 物料选择器插槽 -->
+        <template #itemId>
+          <material-select
+            v-model="formData.itemId"
+            placeholder="请选择物料"
+            @material-selected="handleMaterialSelected"
+          />
+        </template>
+
+        <!-- 预测数量字段的库存显示 -->
+        <template #forecastQuantityForm>
+          <div class="forecast-quantity-section">
+            <el-input-number
+              v-model="formData.forecastQuantity"
+              :min="1"
+              :precision="0"
+              :step="1"
+              controls-position="right"
+              style="width: 100%"
+              class="quantity-input"
+            ></el-input-number>
+            <div class="inventory-display" v-if="currentInventory !== null">
+              当前库存: {{ currentInventory }}
+            </div>
+          </div>
+        </template>
+      </avue-form>
+    </div>
+  </div>
+</template>
+
+<script>
+import forecastFormMixin from './index.js'
+import { getFormOption } from './form-option'
+import { getApprovalStatusLabel, getApprovalStatusType } from '@/constants/forecast'
+import { addForecast, updateForecast } from '@/api/forecast/index'
+import CustomerSelect from '@/components/common/customer-select.vue'
+import MaterialSelect from '@/components/common/material-select.vue'
+
+/**
+ * 销售预测表单组件 - AvueJS版本
+ * @description 基于AvueJS的销售预测表单组件,支持新增和编辑销售预测功能
+ */
+export default {
+  name: 'ForecastFormAvue',
+  mixins: [forecastFormMixin],
+
+  /**
+   * 组件注册
+   */
+  components: {
+    CustomerSelect,
+    MaterialSelect
+  },
+
+  /**
+   * 组件属性
+   */
+  props: {
+    /**
+     * 是否为编辑模式
+     */
+    isEdit: {
+      type: Boolean,
+      default: false
+    },
+    /**
+     * 编辑时的表单数据
+     */
+    editData: {
+      type: Object,
+      default: () => ({})
+    }
+  },
+
+  /**
+   * 组件数据
+   */
+  data() {
+    return {
+      // 表单配置
+      formOption: {},
+      // 当前库存
+      currentInventory: null
+    }
+  },
+
+  /**
+   * 计算属性
+   */
+  computed: {
+    /**
+     * 表单标题
+     */
+    formTitle() {
+      return this.isEdit ? '编辑销售预测' : '新增销售预测'
+    }
+  },
+
+  /**
+   * 侦听器
+   */
+  watch: {
+    /**
+     * 监听编辑数据变化
+     */
+    editData: {
+      handler(newData) {
+        if (newData && this.isEdit) {
+          this.formData = { ...newData }
+        }
+      },
+      immediate: true,
+      deep: true
+    },
+
+    /**
+     * 监听编辑模式变化
+     */
+    isEdit: {
+      handler(newVal) {
+        this.initFormOption()
+        if (!newVal) {
+          this.initFormData()
+        }
+      },
+      immediate: true
+    }
+  },
+
+  /**
+   * 组件创建时
+   */
+  created() {
+    this.initFormOption()
+    this.initFormData()
+  },
+
+  /**
+   * 组件方法
+   */
+  methods: {
+    /**
+     * 初始化表单配置
+     */
+    initFormOption() {
+      this.formOption = getFormOption(this.isEdit)
+    },
+
+    /**
+     * 初始化表单数据
+     */
+    initFormData() {
+      if (this.isEdit && this.editData) {
+        // 编辑模式:使用传入的数据
+        this.formData = { ...this.editData }
+      } else {
+        // 新增模式:使用默认数据,自动填入下个月
+        const now = new Date()
+        const currentYear = now.getFullYear()
+        const currentMonth = now.getMonth() + 1
+        
+        let nextYear, nextMonth
+        if (currentMonth === 12) {
+          // 当前是12月,下个月是明年1月
+          nextYear = currentYear + 1
+          nextMonth = 1
+        } else {
+          // 其他月份,直接 +1
+          nextYear = currentYear
+          nextMonth = currentMonth + 1
+        }
+        
+        this.formData = {
+          forecastCode: '',
+          year: nextYear,
+          month: nextMonth,
+          customerId: '',
+          customerCode: '',
+          customerName: '',
+          brandId: '',
+          brandCode: '',
+          brandName: '',
+          itemId: '',
+          itemCode: '',
+          itemName: '',
+          itemSpecs: '',
+          forecastQuantity: 1,
+          approvalStatus: '',
+          approvalRemark: ''
+        }
+        
+        // 生成预测编码
+        this.generateForecastCode()
+      }
+    },
+
+    /**
+     * 生成预测编码
+     */
+    generateForecastCode() {
+      const now = new Date()
+      const year = this.formData.year || now.getFullYear()
+      const month = this.formData.month || (now.getMonth() + 1)
+      const monthStr = month.toString().padStart(2, '0')
+      const random = Math.floor(1000 + Math.random() * 9000) // 生成1000-9999之间的随机数
+      
+      this.formData.forecastCode = `FC-${year}${monthStr}-${random}`
+    },
+
+    /**
+     * 客户选择事件处理
+     */
+    handleCustomerSelected(customerData) {
+      if (customerData && customerData.customerId) {
+        this.formData.customerId = customerData.customerId
+        this.formData.customerCode = customerData.customerCode
+        this.formData.customerName = customerData.customerName
+      } else {
+        this.formData.customerId = ''
+        this.formData.customerCode = ''
+        this.formData.customerName = ''
+      }
+    },
+
+    /**
+     * 品牌变更处理
+     */
+    handleBrandChange(brandId) {
+      const selectedBrand = this.brandOptions.find(brand => brand.id === brandId)
+      if (selectedBrand) {
+        this.formData.brandCode = selectedBrand.code
+        this.formData.brandName = selectedBrand.name
+      } else {
+        this.formData.brandCode = ''
+        this.formData.brandName = ''
+      }
+    },
+
+    /**
+     * 物料变更处理
+     */
+    handleItemChange(itemId) {
+      const selectedItem = this.itemOptions.find(item => item.id === itemId)
+      if (selectedItem) {
+        this.formData.itemCode = selectedItem.code
+        this.formData.itemName = selectedItem.name
+        this.formData.itemSpecs = selectedItem.specs || ''
+        // 获取当前库存
+        this.getCurrentInventory(itemId)
+      } else {
+        this.formData.itemCode = ''
+        this.formData.itemName = ''
+        this.formData.itemSpecs = ''
+        this.currentInventory = null
+      }
+    },
+
+    /**
+     * 物料选择处理
+     * @description 处理MaterialSelect组件的物料选择事件
+     * @param {Object} materialData - 物料选择数据
+     */
+    handleMaterialSelected(materialData) {
+      if (materialData && materialData.itemId) {
+        this.formData.itemId = materialData.itemId
+        this.formData.itemCode = materialData.itemCode
+        this.formData.itemName = materialData.itemName
+        this.formData.itemSpecs = materialData.specification || ''
+        // 获取当前库存
+        this.getCurrentInventory(materialData.itemId)
+      } else {
+        this.formData.itemId = ''
+        this.formData.itemCode = ''
+        this.formData.itemName = ''
+        this.formData.itemSpecs = ''
+        this.currentInventory = null
+      }
+    },
+
+    /**
+     * 表单提交处理
+     */
+    async handleSubmit() {
+      try {
+        // 表单验证
+        const valid = await this.$refs.forecastForm.validate()
+        if (!valid) {
+          return
+        }
+
+        // 提交数据
+        const submitData = { ...this.formData }
+        
+        if (this.isEdit) {
+          await updateForecast(submitData)
+          this.$message.success('更新销售预测成功')
+        } else {
+          await addForecast(submitData)
+          this.$message.success('新增销售预测成功')
+        }
+
+        // 触发成功事件
+        this.$emit('success', submitData)
+      } catch (error) {
+        console.error('提交表单失败:', error)
+        this.$message.error(error.message || '操作失败,请重试')
+      }
+    },
+
+    /**
+     * 表单重置处理
+     */
+    handleReset() {
+      this.initFormData()
+      this.currentInventory = null
+      this.$emit('reset')
+    },
+
+    /**
+     * 获取审批状态标签
+     */
+    getApprovalStatusLabel,
+
+    /**
+     * 获取审批状态类型
+     */
+    getApprovalStatusType
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.forecast-form-container {
+  .forecast-form-content {
+    padding: 20px;
+    background: #fff;
+    border-radius: 4px;
+    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+  }
+
+  .forecast-quantity-section {
+    .quantity-input {
+      margin-bottom: 8px;
+    }
+
+    .inventory-display {
+      font-size: 12px;
+      color: #909399;
+      line-height: 1.5;
+    }
+  }
+}
+
+// AvueJS 表单样式调整
+:deep(.avue-form) {
+  .el-form-item__label {
+    font-weight: 500;
+    color: #303133;
+  }
+
+  .avue-group {
+    margin-bottom: 20px;
+
+    .avue-group__header {
+      margin-bottom: 15px;
+      padding-bottom: 8px;
+      border-bottom: 1px solid #ebeef5;
+
+      .avue-group__title {
+        font-size: 16px;
+        font-weight: 600;
+        color: #303133;
+      }
+    }
+  }
+}
+</style>

+ 316 - 0
src/components/forecast-form/form-option.js

@@ -0,0 +1,316 @@
+import { APPROVAL_STATUS_OPTIONS } from '@/constants/forecast'
+
+/**
+ * 销售预测表单配置
+ * @description 基于AvueJS的销售预测表单配置,支持新增和编辑销售预测功能
+ */
+
+// 月份选项
+const MONTH_OPTIONS = [
+  { label: '1月', value: 1 },
+  { label: '2月', value: 2 },
+  { label: '3月', value: 3 },
+  { label: '4月', value: 4 },
+  { label: '5月', value: 5 },
+  { label: '6月', value: 6 },
+  { label: '7月', value: 7 },
+  { label: '8月', value: 8 },
+  { label: '9月', value: 9 },
+  { label: '10月', value: 10 },
+  { label: '11月', value: 11 },
+  { label: '12月', value: 12 }
+]
+
+export const forecastFormOption = {
+  // 基础配置
+  submitBtn: false, // 不显示默认提交按钮
+  emptyBtn: false,  // 不显示默认清空按钮
+  labelWidth: 120,  // 标签宽度
+  gutter: 20,       // 栅格间隔
+  menuBtn: false,   // 不显示菜单按钮
+  size: 'small',    // 使用小尺寸组件
+
+  // 分组配置
+  group: [
+    {
+      label: '基本信息',
+      icon: 'el-icon-document',
+      prop: 'basic',
+      column: [
+        {
+          label: '预测编码',
+          prop: 'forecastCode',
+          type: 'input',
+          span: 12,
+          placeholder: '请输入预测编码',
+          disabled: true,
+          display: false, // 新增时不显示
+          rules: [{
+            required: true,
+            message: '请输入预测编码',
+            trigger: 'blur'
+          }]
+        },
+        {
+          label: '年份',
+          prop: 'year',
+          type: 'year',
+          span: 12,
+          placeholder: '请选择年份',
+          valueFormat: 'yyyy',
+          rules: [{
+            required: true,
+            message: '请选择年份',
+            trigger: 'change'
+          }]
+        },
+        {
+          label: '月份',
+          prop: 'month',
+          type: 'select',
+          span: 12,
+          placeholder: '请选择月份',
+          dicData: MONTH_OPTIONS,
+          rules: [{
+            required: true,
+            message: '请选择月份',
+            trigger: 'change'
+          }]
+        },
+        {
+          label: '客户',
+          prop: 'customerId',
+          type: 'select',
+          span: 12,
+          slot: true,
+          placeholder: '请选择客户',
+          rules: [{
+            required: true,
+            message: '请选择客户',
+            trigger: 'change'
+          }]
+        },
+        {
+          label: '客户编码',
+          prop: 'customerCode',
+          type: 'input',
+          span: 12,
+          placeholder: '选择客户后自动填充',
+          disabled: true
+        },
+        {
+          label: '客户名称',
+          prop: 'customerName',
+          type: 'input',
+          span: 12,
+          placeholder: '选择客户后自动填充',
+          disabled: true
+        }
+      ]
+    },
+    {
+      label: '产品信息',
+      icon: 'el-icon-goods',
+      prop: 'product',
+      column: [
+        {
+          label: '品牌',
+          prop: 'brandId',
+          type: 'select',
+          span: 12,
+          slot: true,
+          placeholder: '请选择品牌',
+          rules: [{
+            required: true,
+            message: '请选择品牌',
+            trigger: 'change'
+          }]
+        },
+        {
+          label: '品牌编码',
+          prop: 'brandCode',
+          type: 'input',
+          span: 12,
+          placeholder: '选择品牌后自动填充',
+          disabled: true
+        },
+        {
+          label: '品牌名称',
+          prop: 'brandName',
+          type: 'input',
+          span: 12,
+          placeholder: '选择品牌后自动填充',
+          disabled: true
+        },
+        {
+          label: '物料',
+          prop: 'itemId',
+          type: 'select',
+          span: 12,
+          slot: true,
+          placeholder: '请选择物料',
+          rules: [{
+            required: true,
+            message: '请选择物料',
+            trigger: 'change'
+          }]
+        },
+        {
+          label: '物料编码',
+          prop: 'itemCode',
+          type: 'input',
+          span: 12,
+          placeholder: '选择物料后自动填充',
+          disabled: true
+        },
+        {
+          label: '物料名称',
+          prop: 'itemName',
+          type: 'input',
+          span: 12,
+          placeholder: '选择物料后自动填充',
+          disabled: true
+        },
+        {
+          label: '物料规格',
+          prop: 'itemSpecs',
+          type: 'input',
+          span: 12,
+          placeholder: '选择物料后自动填充',
+          disabled: true
+        },
+        {
+          label: '预测数量',
+          prop: 'forecastQuantity',
+          type: 'number',
+          span: 12,
+          placeholder: '请输入预测数量',
+          min: 1,
+          precision: 0,
+          step: 1,
+          controlsPosition: 'right',
+          rules: [{
+            required: true,
+            message: '请输入预测数量',
+            trigger: 'blur'
+          }, {
+            type: 'number',
+            min: 1,
+            message: '预测数量不能小于1',
+            trigger: 'blur'
+          }]
+        }
+      ]
+    },
+    {
+      label: '审批信息',
+      icon: 'el-icon-check',
+      prop: 'approval',
+      display: false, // 新增时不显示
+      column: [
+        {
+          label: '审批状态',
+          prop: 'approvalStatus',
+          type: 'select',
+          span: 8,
+          placeholder: '审批状态',
+          dicData: APPROVAL_STATUS_OPTIONS,
+          disabled: true
+        },
+        {
+          label: '审批人',
+          prop: 'approver',
+          type: 'input',
+          span: 8,
+          placeholder: '审批人',
+          disabled: true
+        },
+        {
+          label: '审批时间',
+          prop: 'approvalTime',
+          type: 'datetime',
+          span: 8,
+          placeholder: '审批时间',
+          disabled: true,
+          valueFormat: 'yyyy-MM-dd HH:mm:ss'
+        },
+        {
+          label: '审批备注',
+          prop: 'approvalRemark',
+          type: 'textarea',
+          span: 24,
+          placeholder: '审批备注',
+          disabled: true,
+          minRows: 2,
+          maxRows: 4
+        }
+      ]
+    }
+  ]
+}
+
+/**
+ * 获取表单配置
+ * @param {boolean} isEdit 是否为编辑模式
+ * @returns {Object} 表单配置对象
+ */
+export function getFormOption(isEdit = false) {
+  // 深拷贝配置对象
+  const option = JSON.parse(JSON.stringify(forecastFormOption))
+  
+  if (isEdit) {
+    adjustFieldsForEditMode(option)
+  } else {
+    adjustFieldsForCreateMode(option)
+  }
+  
+  return option
+}
+
+/**
+ * 调整编辑模式下的字段配置
+ * @param {Object} option 表单配置对象
+ */
+function adjustFieldsForEditMode(option) {
+  option.group.forEach(group => {
+    // 显示审批信息分组
+    if (group.prop === 'approval') {
+      group.display = true
+    }
+    
+    group.column.forEach(field => {
+      // 显示预测编码字段
+      if (field.prop === 'forecastCode') {
+        field.display = true
+      }
+      // 年份和月份在编辑模式下禁用
+      if (field.prop === 'year' || field.prop === 'month') {
+        field.disabled = true
+      }
+      // 品牌在编辑模式下禁用
+      if (field.prop === 'brandId') {
+        field.disabled = true
+      }
+    })
+  })
+}
+
+/**
+ * 调整新增模式下的字段配置
+ * @param {Object} option 表单配置对象
+ */
+function adjustFieldsForCreateMode(option) {
+  option.group.forEach(group => {
+    // 隐藏审批信息分组
+    if (group.prop === 'approval') {
+      group.display = false
+    }
+    
+    group.column.forEach(field => {
+      // 隐藏预测编码字段
+      if (field.prop === 'forecastCode') {
+        field.display = false
+      }
+    })
+  })
+}

+ 285 - 0
src/views/forecast-form-comparison.vue

@@ -0,0 +1,285 @@
+<template>
+  <div class="forecast-form-comparison">
+    <div class="page-header">
+      <h1>销售预测表单实现对比</h1>
+      <p class="description">对比传统Element UI实现与AvueJS实现的差异</p>
+    </div>
+
+    <div class="comparison-container">
+      <!-- 切换按钮 -->
+      <div class="toggle-section">
+        <el-radio-group v-model="currentImplementation" @change="handleImplementationChange">
+          <el-radio-button label="element">Element UI 实现</el-radio-button>
+          <el-radio-button label="avue">AvueJS 实现</el-radio-button>
+        </el-radio-group>
+        
+        <div class="mode-toggle">
+          <el-switch
+            v-model="isEditMode"
+            active-text="编辑模式"
+            inactive-text="新增模式"
+            @change="handleModeChange"
+          ></el-switch>
+        </div>
+      </div>
+
+      <!-- 表单展示区域 -->
+      <div class="form-display-area">
+        <!-- Element UI 版本 -->
+        <div v-if="currentImplementation === 'element'" class="form-wrapper">
+          <div class="implementation-header">
+            <h3>Element UI 实现</h3>
+            <el-tag type="info">传统实现方式</el-tag>
+          </div>
+          <forecast-form
+            :is-edit="isEditMode"
+            :edit-data="editModeData"
+            @success="handleFormSuccess"
+            @reset="handleFormReset"
+          />
+        </div>
+
+        <!-- AvueJS 版本 -->
+        <div v-if="currentImplementation === 'avue'" class="form-wrapper">
+          <div class="implementation-header">
+            <h3>AvueJS 实现</h3>
+            <el-tag type="success">配置化实现</el-tag>
+          </div>
+          <forecast-form-avue
+            :is-edit="isEditMode"
+            :edit-data="editModeData"
+            @success="handleFormSuccess"
+            @reset="handleFormReset"
+          />
+        </div>
+      </div>
+
+      <!-- 对比说明 -->
+      <div class="comparison-info">
+        <el-card class="info-card">
+          <div slot="header">
+            <span>实现方式对比</span>
+          </div>
+          <div class="comparison-table">
+            <el-table :data="comparisonData" border>
+              <el-table-column prop="aspect" label="对比维度" width="150"></el-table-column>
+              <el-table-column prop="element" label="Element UI 实现">
+                <template slot-scope="scope">
+                  <div v-html="scope.row.element"></div>
+                </template>
+              </el-table-column>
+              <el-table-column prop="avue" label="AvueJS 实现">
+                <template slot-scope="scope">
+                  <div v-html="scope.row.avue"></div>
+                </template>
+              </el-table-column>
+            </el-table>
+          </div>
+        </el-card>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import ForecastForm from '@/components/forecast-form/index.vue'
+import ForecastFormAvue from '@/components/forecast-form/forecast-form-avue.vue'
+
+export default {
+  name: 'ForecastFormComparison',
+  
+  components: {
+    ForecastForm,
+    ForecastFormAvue
+  },
+
+  data() {
+    return {
+      // 当前实现方式
+      currentImplementation: 'element',
+      // 是否为编辑模式
+      isEditMode: false,
+      // 编辑模式的示例数据
+      editModeData: {
+        forecastCode: 'FC202401001',
+        year: '2024',
+        month: 1,
+        customerId: 'C001',
+        customerCode: 'CUST001',
+        customerName: '示例客户有限公司',
+        brandId: 'B001',
+        brandCode: 'BRAND001',
+        brandName: '示例品牌',
+        itemId: 'I001',
+        itemCode: 'ITEM001',
+        itemName: '示例物料',
+        itemSpecs: '规格A',
+        forecastQuantity: 100,
+        approvalStatus: 1,
+        approver: '张三',
+        approvalTime: '2024-01-15 10:30:00',
+        approvalRemark: '审批通过'
+      },
+      // 对比数据
+      comparisonData: [
+        {
+          aspect: '代码量',
+          element: '<span style="color: #f56c6c;">较多</span><br/>需要手写大量模板代码',
+          avue: '<span style="color: #67c23a;">较少</span><br/>主要是配置对象'
+        },
+        {
+          aspect: '维护性',
+          element: '<span style="color: #f56c6c;">一般</span><br/>模板和逻辑耦合',
+          avue: '<span style="color: #67c23a;">良好</span><br/>配置与逻辑分离'
+        },
+        {
+          aspect: '可读性',
+          element: '<span style="color: #e6a23c;">中等</span><br/>需要阅读大量模板',
+          avue: '<span style="color: #67c23a;">良好</span><br/>配置结构清晰'
+        },
+        {
+          aspect: '扩展性',
+          element: '<span style="color: #e6a23c;">中等</span><br/>需要修改模板代码',
+          avue: '<span style="color: #67c23a;">良好</span><br/>修改配置即可'
+        },
+        {
+          aspect: '学习成本',
+          element: '<span style="color: #67c23a;">较低</span><br/>基于Element UI',
+          avue: '<span style="color: #e6a23c;">中等</span><br/>需要学习AvueJS'
+        },
+        {
+          aspect: '灵活性',
+          element: '<span style="color: #67c23a;">高</span><br/>完全自定义',
+          avue: '<span style="color: #e6a23c;">中等</span><br/>受配置限制'
+        }
+      ]
+    }
+  },
+
+  methods: {
+    /**
+     * 切换实现方式
+     */
+    handleImplementationChange(value) {
+      console.log('切换到:', value === 'element' ? 'Element UI' : 'AvueJS')
+    },
+
+    /**
+     * 切换编辑模式
+     */
+    handleModeChange(value) {
+      console.log('切换到:', value ? '编辑模式' : '新增模式')
+    },
+
+    /**
+     * 表单提交成功
+     */
+    handleFormSuccess(data) {
+      this.$message.success('表单提交成功')
+      console.log('提交的数据:', data)
+    },
+
+    /**
+     * 表单重置
+     */
+    handleFormReset() {
+      this.$message.info('表单已重置')
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.forecast-form-comparison {
+  padding: 20px;
+  background: #f5f5f5;
+  min-height: 100vh;
+
+  .page-header {
+    text-align: center;
+    margin-bottom: 30px;
+
+    h1 {
+      font-size: 28px;
+      color: #303133;
+      margin-bottom: 10px;
+    }
+
+    .description {
+      font-size: 16px;
+      color: #606266;
+      margin: 0;
+    }
+  }
+
+  .comparison-container {
+    max-width: 1200px;
+    margin: 0 auto;
+
+    .toggle-section {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      margin-bottom: 20px;
+      padding: 15px 20px;
+      background: #fff;
+      border-radius: 8px;
+      box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+
+      .mode-toggle {
+        display: flex;
+        align-items: center;
+        gap: 10px;
+      }
+    }
+
+    .form-display-area {
+      margin-bottom: 30px;
+
+      .form-wrapper {
+        background: #fff;
+        border-radius: 8px;
+        box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+        overflow: hidden;
+
+        .implementation-header {
+          display: flex;
+          justify-content: space-between;
+          align-items: center;
+          padding: 15px 20px;
+          background: #f8f9fa;
+          border-bottom: 1px solid #ebeef5;
+
+          h3 {
+            margin: 0;
+            font-size: 18px;
+            color: #303133;
+          }
+        }
+      }
+    }
+
+    .comparison-info {
+      .info-card {
+        .comparison-table {
+          :deep(.el-table) {
+            .el-table__header {
+              th {
+                background: #f8f9fa;
+                color: #303133;
+                font-weight: 600;
+              }
+            }
+
+            .el-table__body {
+              td {
+                padding: 12px 0;
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+}
+</style>

+ 2 - 2
src/views/forecast/forecastIndex.js

@@ -12,7 +12,7 @@ import {
   DEFAULT_FORECAST_FORM
 } from '@/constants/forecast'
 import { mapGetters } from 'vuex'
-import ForecastForm from '@/components/forecast-form/index.vue'
+import ForecastFormAvue from '@/components/forecast-form/forecast-form-avue.vue'
 
 /**
  * 经销商销售预测申报页面业务逻辑混入
@@ -23,7 +23,7 @@ export default {
    * 组件注册
    */
   components: {
-    ForecastForm
+    ForecastFormAvue
   },
   data() {
     return {

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

@@ -92,8 +92,8 @@
         </el-button>
       </div>
 
-      <!-- 使用销售预测表单组件 -->
-      <forecast-form
+      <!-- 使用销售预测表单组件 - AvueJS版本 -->
+      <forecast-form-avue
         ref="forecastForm"
         :visible="true"
         :is-edit="editMode === 'edit'"