Browse Source

refactor(订单表单): 重构订单表单组件结构并优化代码

yz 2 months ago
parent
commit
fe70c156c6

+ 96 - 0
src/components/order-form/constants.js

@@ -0,0 +1,96 @@
+/**
+ * @fileoverview 订单表单相关常量定义
+ * @description 定义订单表单组件使用的枚举值和常量
+ */
+
+/**
+ * 订单类型枚举
+ * @readonly
+ * @enum {number}
+ */
+export const OrderType = {
+  /** 普通订单 */
+  NORMAL: 1,
+  /** 紧急订单 */
+  URGENT: 2,
+  /** 预订订单 */
+  RESERVATION: 3
+}
+
+/**
+ * 订单状态枚举
+ * @readonly
+ * @enum {number}
+ */
+export const OrderStatus = {
+  /** 草稿 */
+  DRAFT: 0,
+  /** 待审核 */
+  PENDING: 1,
+  /** 已审核 */
+  APPROVED: 2,
+  /** 已发货 */
+  SHIPPED: 3,
+  /** 已完成 */
+  COMPLETED: 4,
+  /** 已取消 */
+  CANCELLED: 5
+}
+
+/**
+ * 物料明细状态枚举
+ * @readonly
+ * @enum {string}
+ */
+export const MaterialDetailStatus = {
+  /** 待确认 */
+  PENDING: '0',
+  /** 已确认 */
+  CONFIRMED: '1',
+  /** 已取消 */
+  CANCELLED: '2'
+}
+
+/**
+ * 订单类型选项列表
+ * @type {Array<{label: string, value: number}>}
+ */
+export const ORDER_TYPE_OPTIONS = [
+  { label: '普通订单', value: OrderType.NORMAL },
+  { label: '紧急订单', value: OrderType.URGENT },
+  { label: '预订订单', value: OrderType.RESERVATION }
+]
+
+/**
+ * 订单状态选项列表
+ * @type {Array<{label: string, value: number}>}
+ */
+export const ORDER_STATUS_OPTIONS = [
+  { label: '草稿', value: OrderStatus.DRAFT },
+  { label: '待审核', value: OrderStatus.PENDING },
+  { label: '已审核', value: OrderStatus.APPROVED },
+  { label: '已发货', value: OrderStatus.SHIPPED },
+  { label: '已完成', value: OrderStatus.COMPLETED },
+  { label: '已取消', value: OrderStatus.CANCELLED }
+]
+
+/**
+ * 默认分页配置
+ * @description 通用的AvueJS分页配置,可在多个组件中复用
+ * @type {Object}
+ */
+export const DEFAULT_PAGINATION_CONFIG = {
+  pageSize: 10,
+  pageSizes: [5, 10, 20, 50],
+  layout: 'total, sizes, prev, pager, next, jumper'
+}
+
+/**
+ * 物料明细状态选项列表
+ * @type {Array<{label: string, value: string}>}
+ */
+export const MATERIAL_DETAIL_STATUS_OPTIONS = [
+  { label: '待确认', value: MaterialDetailStatus.PENDING },
+  { label: '已确认', value: MaterialDetailStatus.CONFIRMED },
+  { label: '已取消', value: MaterialDetailStatus.CANCELLED }
+]

+ 0 - 1
src/components/order-form/form-option.js

@@ -289,7 +289,6 @@ export function getFormOption(isEdit = false) {
     
     return option
   } catch (error) {
-    console.error('获取表单配置失败:', error)
     throw new Error(`表单配置生成失败: ${error.message}`)
   }
 }

+ 45 - 86
src/components/order-form/material-detail-option.js

@@ -3,24 +3,15 @@
  * @description 定义物料明细表格的AvueJS配置选项、数据类型和相关常量
  */
 
-/**
- * 物料明细状态枚举
- * @readonly
- * @enum {string}
- */
-export const MATERIAL_DETAIL_STATUS = {
-  /** 待确认 */
-  PENDING: '0',
-  /** 已确认 */
-  CONFIRMED: '1',
-  /** 已取消 */
-  CANCELLED: '2'
-}
+// 从constants.js导入常量
+import { 
+  MaterialDetailStatus, 
+  DEFAULT_PAGINATION_CONFIG, 
+  MATERIAL_DETAIL_STATUS_OPTIONS 
+} from './constants'
 
-/**
- * @typedef {keyof typeof MATERIAL_DETAIL_STATUS} MaterialDetailStatusKey
- * @typedef {typeof MATERIAL_DETAIL_STATUS[MaterialDetailStatusKey]} MaterialDetailStatus
- */
+// 重新导出常量供其他模块使用
+export { MaterialDetailStatus, DEFAULT_PAGINATION_CONFIG, MATERIAL_DETAIL_STATUS_OPTIONS }
 
 /**
  * @typedef {Object} MaterialDetailQueryParams
@@ -34,50 +25,27 @@ export const MATERIAL_DETAIL_STATUS = {
  */
 
 /**
- * @typedef {Object} MaterialDetailFormData
- * @description 物料明细表单数据类型定义
- * @property {string} itemCode - 物料编码,必填,最大长度50
- * @property {string} itemName - 物料名称,必填,最大长度100
- * @property {string} specification - 规格型号,可选,最大长度100
- * @property {string} mainCategoryName - 主物料分类名称,只读
- * @property {string} warehouseName - 仓库名称,只读
- * @property {number} availableQuantity - 可用数量,非负数,精度2位小数
- * @property {number} orderQuantity - 订单数量,非负数,精度2位小数
- * @property {number} confirmQuantity - 确认数量,非负数,精度2位小数
- * @property {number} unitPrice - 单价,非负数,精度2位小数
- * @property {number} taxRate - 税率,范围0-100,精度2位小数
- * @property {number} taxAmount - 税额,自动计算,精度2位小数
- * @property {number} totalAmount - 总金额,自动计算,精度2位小数
- * @property {MaterialDetailStatus} detailStatus - 明细状态
+ * @typedef {import('./types').MaterialDetailFormData} MaterialDetailFormData
+ * @typedef {import('./types').MaterialDetailItem} MaterialDetailItem
+ * @typedef {import('./types').MaterialDetailStatus} MaterialDetailStatus
  */
 
 /**
- * @typedef {Object} MaterialDetailItem
- * @description 物料明细列表项类型定义
- * @property {string} id - 明细ID,唯一标识符
- * @property {string} itemId - 物料ID,关联物料表
- * @property {string} itemCode - 物料编码,业务唯一标识
- * @property {string} itemName - 物料名称
- * @property {string} specification - 规格型号
- * @property {string} mainCategoryId - 主物料分类ID
- * @property {string} mainCategoryName - 主物料分类名称
- * @property {string} warehouseId - 仓库ID
- * @property {string} warehouseName - 仓库名称
- * @property {number} availableQuantity - 可用数量
- * @property {number} orderQuantity - 订单数量
- * @property {number} confirmQuantity - 确认数量
- * @property {number} unitPrice - 单价
- * @property {number} taxRate - 税率
- * @property {number} taxAmount - 税额
- * @property {number} totalAmount - 总金额
- * @property {MaterialDetailStatus} detailStatus - 明细状态
- * @property {string} createTime - 创建时间,ISO 8601格式
- * @property {string} updateTime - 更新时间,ISO 8601格式
+ * @typedef {import('@smallwei/avue').AvueCrudOption} AvueCrudOption
+ * @typedef {import('@smallwei/avue').AvueCrudColumn} AvueCrudColumn
  */
 
 /**
- * @typedef {import('@smallwei/avue').AvueCrudOption} AvueCrudOption
- * @typedef {import('@smallwei/avue').AvueCrudColumn} AvueCrudColumn
+ * @typedef {import('./types').ValidationRule} ValidationRule
+ */
+
+/**
+ * AvueJS分页配置选项类型定义
+ * @typedef {Object} AvuePaginationOption
+ * @description AvueJS表格分页配置对象
+ * @property {number} pageSize - 每页显示条数,默认10
+ * @property {number[]} pageSizes - 每页显示个数选择器的选项设置
+ * @property {string} layout - 组件布局,子组件名用逗号分隔
  */
 
 /**
@@ -86,22 +54,23 @@ export const MATERIAL_DETAIL_STATUS = {
  * @description 物料明细表格的AvueJS配置对象
  * @property {boolean} border - 是否显示边框
  * @property {boolean} index - 是否显示序号列
- * @property {boolean} indexLabel - 序号列标题
+ * @property {string} indexLabel - 序号列标题
  * @property {boolean} stripe - 是否显示斑马纹
- * @property {boolean} menuAlign - 操作列对齐方式
- * @property {boolean} align - 表格内容对齐方式
- * @property {boolean} refreshBtn - 是否显示刷新按钮
- * @property {boolean} columnBtn - 是否显示列设置按钮
- * @property {boolean} searchBtn - 是否显示搜索按钮
+ * @property {'left'|'center'|'right'} menuAlign - 操作列对齐方式
+ * @property {'left'|'center'|'right'} align - 表格内容对齐方式
+ * @property {boolean} [refreshBtn] - 是否显示刷新按钮
+ * @property {boolean} [columnBtn] - 是否显示列设置按钮
+ * @property {boolean} searchShow - 是否显示搜索区域
  * @property {boolean} addBtn - 是否显示新增按钮
  * @property {boolean} editBtn - 是否显示编辑按钮
  * @property {boolean} delBtn - 是否显示删除按钮
  * @property {boolean} viewBtn - 是否显示查看按钮
- * @property {boolean} selection - 是否显示多选框
- * @property {boolean} reserveSelection - 是否保留选择状态
- * @property {string} height - 表格高度
- * @property {string} calcHeight - 计算高度的偏移量
- * @property {Array<AvueColumnOption>} column - 列配置数组
+ * @property {boolean} [selection] - 是否显示多选框
+ * @property {boolean} [reserveSelection] - 是否保留选择状态
+ * @property {string} [height] - 表格高度
+ * @property {string} [calcHeight] - 计算高度的偏移量
+ * @property {AvuePaginationOption|boolean} page - 分页配置,true启用默认分页,对象为详细配置
+ * @property {AvueColumnOption[]} column - 列配置数组
  */
 
 /**
@@ -110,24 +79,22 @@ export const MATERIAL_DETAIL_STATUS = {
  * @description 表格列的配置对象
  * @property {string} label - 列标题
  * @property {string} prop - 列属性名
- * @property {string} [type] - 列类型
+ * @property {'text'|'number'|'select'|'date'|'datetime'} [type] - 列类型
  * @property {number} [minWidth] - 最小宽度
  * @property {number} [width] - 固定宽度
  * @property {boolean} [sortable] - 是否可排序
- * @property {boolean} [search] - 是否可搜索
- * @property {string} [align] - 对齐方式
+ * @property {'left'|'center'|'right'} [align] - 对齐方式
  * @property {boolean} [overHidden] - 是否隐藏溢出内容
- * @property {string} [searchPlaceholder] - 搜索框占位符
- * @property {Array<{label: string, value: string}>} [dicData] - 字典数据
- * @property {Object} [props] - 字典属性配置
- * @property {string} [formatter] - 格式化函数名
+ * @property {boolean} [hide] - 是否隐藏列
+ * @property {number} [precision] - 数字精度
+ * @property {{label: string, value: string|number}[]} [dicData] - 字典数据
+ * @property {ValidationRule[]} [rules] - 验证规则
  */
 
 /**
  * 获取物料明细表格配置选项
  * @description 返回AvueJS表格组件的配置对象,表格始终为只读模式,不支持编辑、新增、删除操作
  * @returns {AvueTableOption} AvueJS表格配置对象
- * @since 2.0.0
  * @example
  * // 使用示例
  * const tableOption = getMaterialDetailOption()
@@ -147,13 +114,12 @@ export function getMaterialDetailOption() {
     editBtn: false,
     delBtn: false,
     viewBtn: true,
-    searchShow: true,
-    searchMenuSpan: 6,
+    searchShow: false,
+    page: DEFAULT_PAGINATION_CONFIG,
     column: [
       {
         label: '物料编码',
         prop: 'itemCode',
-        search: true,
         width: 120,
         rules: [{
           required: true,
@@ -164,7 +130,6 @@ export function getMaterialDetailOption() {
       {
         label: '物料名称',
         prop: 'itemName',
-        search: true,
         width: 150,
         rules: [{
           required: true,
@@ -175,7 +140,6 @@ export function getMaterialDetailOption() {
       {
         label: '规格型号',
         prop: 'specification',
-        search: true,
         width: 120
       },
       {
@@ -186,7 +150,6 @@ export function getMaterialDetailOption() {
       {
         label: '仓库名称',
         prop: 'warehouseName',
-        search: true,
         width: 120
       },
       {
@@ -259,11 +222,7 @@ export function getMaterialDetailOption() {
         label: '明细状态',
         prop: 'detailStatus',
         type: 'select',
-        dicData: [
-          { label: '待确认', value: '0' },
-          { label: '已确认', value: '1' },
-          { label: '已取消', value: '2' }
-        ],
+        dicData: MATERIAL_DETAIL_STATUS_OPTIONS,
         width: 100
       },
       {
@@ -454,5 +413,5 @@ export const DEFAULT_FORM_DATA = {
   taxRate: 0,
   taxAmount: 0,
   totalAmount: 0,
-  detailStatus: '0'
+  detailStatus: '0' // MaterialDetailStatus.PENDING
 }

+ 72 - 58
src/components/order-form/material-detail-table.vue

@@ -31,10 +31,12 @@
       <avue-crud
         ref="materialDetailCrud"
         v-model="formData"
-        :data="materialDetails"
+        :data="currentPageData"
         :option="tableOption"
         :page.sync="page"
         @refresh-change="handleRefresh"
+        @current-change="handleCurrentChange"
+        @size-change="handleSizeChange"
       >
 
         <!-- 总金额列自定义渲染 -->
@@ -67,50 +69,33 @@
 </template>
 
 <script>
-import { getMaterialDetailOption, MATERIAL_DETAIL_STATUS } from './material-detail-option'
+import { getMaterialDetailOption, DEFAULT_PAGINATION_CONFIG } from './material-detail-option'
+import { MaterialDetailStatus } from './constants'
 import MaterialImportDialog from './material-import-dialog.vue'
+import { formatAmount } from './utils'
+// 类型定义在JSDoc注释中提供,无需导入
 
 /**
  * 状态标签类型映射常量
- * @type {Record<MaterialDetailStatus, TagType>}
+ * @type {Object} 物料明细状态对应的标签类型
  */
 const STATUS_TAG_TYPE_MAP = {
-  [MATERIAL_DETAIL_STATUS.PENDING]: 'warning',   // 待确认
-  [MATERIAL_DETAIL_STATUS.CONFIRMED]: 'success', // 已确认
-  [MATERIAL_DETAIL_STATUS.CANCELLED]: 'danger'   // 已取消
+  [MaterialDetailStatus.PENDING]: 'warning',   // 待确认
+  [MaterialDetailStatus.CONFIRMED]: 'success', // 已确认
+  [MaterialDetailStatus.CANCELLED]: 'danger'   // 已取消
 }
 
 /**
  * 状态文本映射常量
- * @type {Record<MaterialDetailStatus, string>}
+ * @type {Object} 物料明细状态对应的文本描述
  */
 const STATUS_TEXT_MAP = {
-  [MATERIAL_DETAIL_STATUS.PENDING]: '待确认',
-  [MATERIAL_DETAIL_STATUS.CONFIRMED]: '已确认',
-  [MATERIAL_DETAIL_STATUS.CANCELLED]: '已取消'
+  [MaterialDetailStatus.PENDING]: '待确认',
+  [MaterialDetailStatus.CONFIRMED]: '已确认',
+  [MaterialDetailStatus.CANCELLED]: '已取消'
 }
 
 /**
- * @typedef {import('./material-detail-option').MaterialDetailItem} MaterialDetailItem
- * @typedef {import('./material-detail-option').MaterialDetailFormData} MaterialDetailFormData
- * @typedef {import('./material-detail-option').AvueTableOption} AvueTableOption
- * @typedef {import('./material-detail-option').MaterialDetailStatus} MaterialDetailStatus
- */
-
-/**
- * 分页配置类型定义
- * @typedef {Object} PaginationConfig
- * @property {number} currentPage - 当前页码,从1开始
- * @property {number} pageSize - 每页显示条数
- * @property {number} total - 总记录数
- */
-
-/**
- * 状态标签类型映射
- * @typedef {'warning'|'success'|'danger'|'info'} TagType
- */
-
-/**
  * 物料明细表格组件
  * @description 用于展示订单的物料明细信息,支持物料导入功能(只读模式)
  * 该组件已移除所有编辑、新增、删除功能,仅支持数据展示和物料导入
@@ -122,9 +107,10 @@ export default {
   components: {
     MaterialImportDialog
   },
-
+  
   /**
    * 组件属性定义
+   * @description 定义组件接收的外部属性
    */
   props: {
     /**
@@ -137,16 +123,19 @@ export default {
       validator: (value) => value === null || value === undefined || (typeof value === 'string' && value.length > 0) || (typeof value === 'number' && value > 0)
     },
 
-
-
     /**
      * 物料明细列表 - 要展示的物料明细数据
-     * @type {Array<MaterialDetailItem>}
+     * @type {Array} 物料明细数据数组,每个元素包含物料的详细信息
      */
     materialDetails: {
       type: Array,
+      required: true,
       default: () => [],
-      validator: (value) => Array.isArray(value)
+      validator: (value) => Array.isArray(value) && value.every(item => 
+        typeof item === 'object' && item !== null && 
+        typeof item.id === 'string' && 
+        typeof item.itemCode === 'string'
+      )
     }
   },
 
@@ -157,18 +146,21 @@ export default {
   data() {
     return {
       /**
-       * 表单数据 - AvueJS组件的表单数据绑定
-       * @type {MaterialDetailFormData}
+       * 表单数据 - 当前编辑行的数据
+       * @type {Object} 物料明细表单数据对象
        */
       formData: {},
 
       /**
-       * 分页配置 - 表格分页相关配置
-       * @type {PaginationConfig}
+       * 分页配置 - AvueJS表格分页相关配置
+       * @type {Object} 包含currentPage、pageSize、total等属性的分页配置对象
+       * @property {number} currentPage - 当前页码,从1开始
+       * @property {number} pageSize - 每页显示条数
+       * @property {number} total - 总记录数
        */
       page: {
         currentPage: 1,
-        pageSize: 10,
+        pageSize: DEFAULT_PAGINATION_CONFIG.pageSize,
         total: 0
       },
 
@@ -190,6 +182,17 @@ export default {
      */
     tableOption() {
       return getMaterialDetailOption()
+    },
+
+    /**
+     * 当前页显示的数据 - 根据分页配置计算当前页应显示的数据
+     * @returns {Array} 当前页的物料明细数据
+     */
+    currentPageData() {
+      const { currentPage, pageSize } = this.page
+      const startIndex = (currentPage - 1) * pageSize
+      const endIndex = startIndex + pageSize
+      return this.materialDetails.slice(startIndex, endIndex)
     }
   },
 
@@ -199,7 +202,8 @@ export default {
   watch: {
     /**
      * 监听物料明细变化
-     * @param {Array<MaterialDetailItem>} newVal - 新的物料明细列表
+     * @param {OrderItemRecord[]} newVal - 新的物料明细列表
+     * @returns {void}
      */
     materialDetails: {
       handler(newVal) {
@@ -235,13 +239,13 @@ export default {
     /**
      * 处理物料导入确认事件
      * @description 处理物料导入确认,将导入的物料数据传递给父组件并关闭弹窗
-     * @param {Array<MaterialDetailItem>} importedMaterials - 导入的物料列表,必须为有效的物料明细数组
+     * @param {OrderItemRecord[]} importedMaterials - 导入的物料列表,必须为有效的物料明细数组
      * @returns {void}
      * @emits material-import
      */
     handleImportConfirm(importedMaterials) {
       if (!Array.isArray(importedMaterials)) {
-        console.warn('导入的物料数据必须为数组格式')
+        this.$message.warning('导入的物料数据格式不正确')
         return
       }
       this.$emit('material-import', importedMaterials)
@@ -258,25 +262,35 @@ export default {
     },
 
     /**
-     * 格式化金额显示
-     * @description 将数字金额格式化为带货币符号的字符串,保留2位小数
-     * @param {number|string|null|undefined} amount - 金额数值,支持数字、字符串或空值
-     * @returns {string} 格式化后的金额字符串,格式为 ¥XX.XX
-     * @example
-     * formatAmount(123.456) // 返回 "¥123.46"
-     * formatAmount(null) // 返回 "¥0.00"
-     * formatAmount('100') // 返回 "¥100.00"
+     * 处理分页页码变化事件
+     * @description 当用户切换页码时触发,更新当前页码
+     * @param {number} currentPage - 新的页码,从1开始
+     * @returns {void}
      */
-    formatAmount(amount) {
-      const numAmount = Number(amount || 0)
-      if (isNaN(numAmount)) {
-        console.warn(`无效的金额值: ${amount}`)
-        return '¥0.00'
-      }
-      return `¥${numAmount.toFixed(2)}`
+    handleCurrentChange(currentPage) {
+      this.page.currentPage = currentPage
     },
 
     /**
+     * 处理分页大小变化事件
+     * @description 当用户改变每页显示条数时触发,重置到第一页
+     * @param {number} pageSize - 新的每页显示条数
+     * @returns {void}
+     */
+    handleSizeChange(pageSize) {
+      this.page.pageSize = pageSize
+      this.page.currentPage = 1
+    },
+
+    /**
+     * 格式化金额显示
+     * @description 使用公共工具函数格式化金额
+     * @param {number|string|null|undefined} amount - 金额数值
+     * @returns {string} 格式化后的金额字符串
+     */
+    formatAmount,
+
+    /**
      * 获取状态标签类型
      * @description 根据物料明细状态值返回对应的Element UI标签类型
      * @param {MaterialDetailStatus} status - 物料明细状态值

+ 5 - 5
src/components/order-form/material-import-dialog.vue

@@ -144,7 +144,9 @@
 
 <script>
 import { getMaterialImportOption, DEFAULT_QUERY_PARAMS } from './material-detail-option'
+import { MaterialDetailStatus } from './constants'
 import { getItemList } from '@/api/common'
+import { generateUniqueId } from './utils'
 
 /**
  * @typedef {import('./material-detail-option').MaterialDetailItem} MaterialDetailItem
@@ -394,7 +396,6 @@ export default {
           this.page.total = 0
         }
       } catch (error) {
-        console.error('加载物料列表失败:', error)
         this.$message.error('加载物料列表失败,请重试')
         this.materialList = []
         this.page.total = 0
@@ -491,8 +492,8 @@ export default {
         this.confirmLoading = true
 
         // 转换为物料明细格式
-        const importedMaterials = this.selectedMaterials.map((material, index) => ({
-          id: Date.now().toString() + Math.random().toString(36).substr(2, 9) + index,
+        const importedMaterials = this.selectedMaterials.map((material) => ({
+          id: generateUniqueId(),
           itemId: material.itemId || material.id,
           itemCode: material.itemCode,
           itemName: material.itemName,
@@ -508,7 +509,7 @@ export default {
           taxRate: 0,
           taxAmount: 0,
           totalAmount: 0,
-          detailStatus: '0', // 默认状态为待确认
+          detailStatus: MaterialDetailStatus.PENDING, // 默认状态为待确认
           createTime: new Date().toISOString(),
           updateTime: new Date().toISOString()
         }))
@@ -516,7 +517,6 @@ export default {
         this.$emit('confirm', importedMaterials)
         this.dialogVisible = false
       } catch (error) {
-        console.error('导入物料失败:', error)
         this.$message.error('导入物料失败,请重试')
       } finally {
         this.confirmLoading = false

+ 60 - 55
src/components/order-form/order-form-mixin.js

@@ -1,4 +1,5 @@
 import { add, update, getDetail } from '@/api/order/order'
+import { getList as getOrderItemList } from '@/api/order/order-item'
 import {
   ORDER_TYPES,
   ORDER_STATUS,
@@ -7,37 +8,10 @@ import {
 } from '@/constants/order'
 
 /**
- * @typedef {Object} OrderFormModel
- * @property {string|number|null} id - 订单唯一标识
- * @property {string} orderCode - 订单编码
- * @property {string} orgCode - 组织编码
- * @property {string} orgName - 组织名称
- * @property {string} customerCode - 客户编码
- * @property {string} customerName - 客户名称
- * @property {number} orderType - 订单类型
- * @property {number|null} totalAmount - 订单总金额
- * @property {number|null} totalQuantity - 订单总数量
- * @property {string} receiverName - 收货人姓名
- * @property {string} receiverPhone - 收货人电话
- * @property {string} receiverAddress - 收货地址
- * @property {number} status - 订单状态
- * @property {string} remark - 备注信息
- */
-
-/**
- * @typedef {Object} ValidationRule
- * @property {boolean} [required] - 是否必填
- * @property {string} [message] - 验证失败提示信息
- * @property {string} [trigger] - 触发验证的事件
- * @property {number} [min] - 最小长度
- * @property {number} [max] - 最大长度
- * @property {string} [type] - 数据类型
- * @property {number} [minimum] - 最小值
- * @property {number} [maximum] - 最大值
- */
-
-/**
- * @typedef {Object.<string, ValidationRule[]>} OrderFormRules
+ * @typedef {import('./types').OrderFormModel} OrderFormModel
+ * @typedef {import('./types').ValidationRule} ValidationRule
+ * @typedef {import('./types').OrderFormRules} OrderFormRules
+ * @typedef {import('./types').OrderFormData} OrderFormMixinData
  */
 
 /**
@@ -48,24 +22,20 @@ import {
  */
 
 /**
- * @typedef {Object} OrderSelectOption
- * @property {string} label - 显示标签
- * @property {number} value - 选项值
+ * @typedef {import('./types').OrderTypeOption} OrderTypeOption
+ * @typedef {import('./types').OrderStatusOption} OrderStatusOption
  */
 
 /**
  * 订单表单混入组件
  * @description 提供订单表单的数据管理、验证规则和业务逻辑的混入组件
- * @author 系统开发团队
- * @version 2.0.0
- * @since 2024-01-15
  * @mixin
  */
 export default {
   /**
    * 组件响应式数据
    * @description 定义组件的响应式数据状态
-   * @returns {Object} 组件数据对象
+   * @returns {OrderFormMixinData} 组件数据对象
    */
   data() {
     return {
@@ -86,14 +56,14 @@ export default {
       /**
        * 订单类型选项列表
        * @description 订单类型下拉选择器的选项数据
-       * @type {OrderSelectOption[]}
+       * @type {OrderTypeOption[]}
        */
       orderTypeOptions: Object.freeze([...ORDER_TYPE_OPTIONS]),
 
       /**
        * 订单状态选项列表
        * @description 订单状态下拉选择器的选项数据
-       * @type {OrderSelectOption[]}
+       * @type {OrderStatusOption[]}
        */
       orderStatusOptions: Object.freeze([...ORDER_STATUS_OPTIONS])
     }
@@ -302,7 +272,6 @@ export default {
           this.resetForm()
         }
       } catch (error) {
-        console.error('初始化表单失败:', error)
         this.$message.error('初始化表单失败,请刷新页面重试')
       }
     },
@@ -325,13 +294,13 @@ export default {
           this.$refs.orderForm.clearValidate()
         }
       } catch (error) {
-        console.warn('重置表单时发生错误:', error)
+        // 重置表单时发生错误,静默处理
       }
     },
 
     /**
      * 加载订单详情数据
-     * @description 根据订单ID从服务器获取订单详情并填充到表单中
+     * @description 根据订单ID从服务器获取订单详情并填充到表单中,同时加载物料明细数据
      * @param {string|number} orderId - 订单唯一标识
      * @returns {Promise<void>}
      * @throws {Error} 当API调用失败或数据格式错误时抛出异常
@@ -343,26 +312,54 @@ export default {
       }
 
       try {
-        const response = await getDetail(orderId)
+        // 并行加载订单详情和物料明细数据
+        const [orderResponse, materialResponse] = await Promise.all([
+          getDetail(orderId),
+          this.loadMaterialDetails(orderId)
+        ])
 
-        if (!response || !response.data || !response.data.data) {
+        if (!orderResponse || !orderResponse.data || !orderResponse.data.data) {
           throw new Error('服务器返回数据格式错误')
         }
 
-        const orderData = response.data.data
+        const orderData = orderResponse.data.data
 
         // 安全地映射订单数据到表单,确保数据类型正确
         this.formData = this.mapOrderDataToForm(orderData)
 
+        // 设置物料明细数据
+        this.materialDetails = materialResponse
+
       } catch (error) {
         const errorMessage = error.message || '加载订单详情失败'
-        console.error('加载订单详情失败:', error)
         this.$message.error(`${errorMessage},请重试`)
         throw error
       }
     },
 
     /**
+     * 加载订单物料明细数据
+     * @description 根据订单ID从服务器获取物料明细列表,用于编辑模式下的数据回显
+     * @param {string|number} orderId - 订单唯一标识符
+     * @returns {Promise<OrderItemRecord[]>} 物料明细记录列表,失败时返回空数组
+     * @private
+     */
+    async loadMaterialDetails(orderId) {
+      try {
+        const response = await getOrderItemList(1, 1000, { orderId })
+
+        if (!response || !response.data || !response.data.data) {
+          throw new Error('物料明细数据格式错误')
+        }
+
+        return response.data.data.records || []
+      } catch (error) {
+        this.$message.warning('加载物料明细失败,请稍后重试')
+        return []
+      }
+    },
+
+    /**
      * 映射订单数据到表单格式
      * @description 将API返回的订单数据安全地映射为表单数据格式
      * @param {Object} orderData - 从API获取的原始订单数据
@@ -449,7 +446,6 @@ export default {
 
       } catch (error) {
         const errorMessage = this.isEdit ? '订单更新失败,请重试' : '订单创建失败,请重试'
-        console.error('保存订单失败:', error)
         this.$message.error(errorMessage)
         throw error
       } finally {
@@ -480,16 +476,12 @@ export default {
      */
     async validateForm() {
       if (!this.$refs.orderForm) {
-        console.warn('表单引用不存在,无法进行验证')
         return false
       }
 
       try {
-        const isValid = await new Promise((resolve) => {
-          this.$refs.orderForm.validate((valid) => {
-            resolve(Boolean(valid))
-          })
-        })
+        // 使用更简洁的Promise包装器函数
+        const isValid = await this.validateFormFields()
 
         if (!isValid) {
           this.$message.warning('请检查表单填写是否正确')
@@ -497,13 +489,26 @@ export default {
 
         return isValid
       } catch (error) {
-        console.warn('表单验证失败:', error)
         this.$message.warning('请检查表单填写是否正确')
         return false
       }
     },
 
     /**
+     * 验证表单字段
+     * @description 封装表单验证逻辑,提供更清晰的异步处理
+     * @returns {Promise<boolean>} 验证结果
+     * @private
+     */
+    validateFormFields() {
+      return new Promise((resolve) => {
+        this.$refs.orderForm.validate((valid) => {
+          resolve(Boolean(valid))
+        })
+      })
+    },
+
+    /**
      * 准备提交数据
      * @description 处理表单数据,移除空值字段并确保数据类型正确
      * @returns {Object} 格式化后的提交数据对象

+ 12 - 25
src/components/order-form/order-form.vue

@@ -59,26 +59,9 @@ import { getFormOption } from './form-option'
 import MaterialDetailTable from './material-detail-table.vue'
 
 /**
- * @typedef {Object} OrderFormData
- * @property {string} orderCode - 订单编码
- * @property {string} customerName - 客户名称
- * @property {string} orderType - 订单类型
- * @property {string} orderStatus - 订单状态
- * @property {number} totalAmount - 订单总金额
- * @property {number} paidAmount - 已付金额
- * @property {string} receiverName - 收货人姓名
- * @property {string} receiverPhone - 收货人电话
- * @property {string} receiverAddress - 收货地址
- * @property {string} remark - 备注信息
- * @property {string} createTime - 创建时间
- * @property {string} updateTime - 更新时间
- */
-
-/**
- * @typedef {Object} OrderFormProps
- * @property {boolean} visible - 表单是否可见
- * @property {boolean} isEdit - 是否为编辑模式
- * @property {string|number|null} orderId - 订单ID(编辑模式时使用)
+ * @typedef {import('@/api/order/order-item').OrderItemRecord} OrderItemRecord
+ * @typedef {import('./types').OrderFormModel} OrderFormModel
+ * @typedef {import('./types').OrderFormProps} OrderFormProps
  */
 
 /**
@@ -106,7 +89,8 @@ export default {
     return {
       /**
        * 物料明细列表
-       * @type {Array}
+       * @type {OrderItemRecord[]}
+       * @description 存储当前订单的物料明细数据,在编辑模式下会自动从服务器加载
        */
       materialDetails: []
     }
@@ -248,7 +232,8 @@ export default {
     /**
      * 处理物料明细数据变化
      * @description 当物料明细表格数据发生变化时的回调处理
-     * @param {Array} materialDetails - 更新后的物料明细列表
+     * @param {OrderItemRecord[]} materialDetails - 更新后的物料明细列表
+     * @returns {void}
      */
     handleMaterialChange(materialDetails) {
       this.materialDetails = materialDetails
@@ -259,7 +244,8 @@ export default {
     /**
      * 处理物料导入事件
      * @description 当从物料导入弹窗确认导入物料时的回调处理
-     * @param {Array} importedMaterials - 导入的物料列表
+     * @param {OrderItemRecord[]} importedMaterials - 导入的物料列表
+     * @returns {void}
      */
     handleMaterialImport(importedMaterials) {
       // 将导入的物料添加到现有明细列表中
@@ -270,11 +256,12 @@ export default {
 
     /**
      * 计算订单总金额
-     * @description 根据物料明细计算订单总金额
+     * @description 根据物料明细计算订单总金额并更新表单数据
+     * @returns {void}
      */
     calculateOrderTotal() {
       const totalAmount = this.materialDetails.reduce((sum, item) => {
-        return sum + (item.totalAmount || 0)
+        return sum + (Number(item.totalAmount) || 0)
       }, 0)
       
       // 更新表单中的总金额字段

+ 147 - 0
src/components/order-form/types.d.ts

@@ -247,6 +247,27 @@ export interface PaginationData<T = any> {
 }
 
 /**
+ * AvueJS分页配置接口
+ * @description 用于AvueJS表格组件的分页配置
+ */
+export interface AvuePaginationConfig {
+  /** 当前页码,从1开始 */
+  currentPage: number;
+  /** 每页显示条数 */
+  pageSize: number;
+  /** 总记录数 */
+  total: number;
+  /** 页码按钮的数量,当总页数超过该值时会折叠 */
+  pagerCount?: number;
+  /** 是否为分页按钮添加背景色 */
+  background?: boolean;
+  /** 组件布局,子组件名用逗号分隔 */
+  layout?: string;
+  /** 每页显示个数选择器的选项设置 */
+  pageSizes?: number[];
+}
+
+/**
  * 订单详情API响应类型
  */
 export type OrderDetailResponse = ApiResponse<OrderFormModel>;
@@ -365,3 +386,129 @@ export interface OrderFormComponent extends OrderFormProps, OrderFormData, Order
   /** Vue下一个tick */
   $nextTick(): Promise<void>;
 }
+
+/**
+ * 物料明细状态枚举
+ */
+export enum MaterialDetailStatus {
+  /** 待确认 */
+  PENDING = '0',
+  /** 已确认 */
+  CONFIRMED = '1',
+  /** 已取消 */
+  CANCELLED = '2'
+}
+
+/**
+ * 物料明细表单数据接口
+ */
+export interface MaterialDetailFormData {
+  /** 物料编码,必填,最大长度50 */
+  itemCode: string;
+  /** 物料名称,必填,最大长度100 */
+  itemName: string;
+  /** 规格型号,可选,最大长度100 */
+  specification: string;
+  /** 主物料分类名称,只读 */
+  mainCategoryName: string;
+  /** 仓库名称,只读 */
+  warehouseName: string;
+  /** 可用数量,非负数,精度2位小数 */
+  availableQuantity: number;
+  /** 订单数量,非负数,精度2位小数 */
+  orderQuantity: number;
+  /** 确认数量,非负数,精度2位小数 */
+  confirmQuantity: number;
+  /** 单价,非负数,精度2位小数 */
+  unitPrice: number;
+  /** 税率,范围0-100,精度2位小数 */
+  taxRate: number;
+  /** 税额,自动计算,精度2位小数 */
+  taxAmount: number;
+  /** 总金额,自动计算,精度2位小数 */
+  totalAmount: number;
+  /** 明细状态 */
+  detailStatus: MaterialDetailStatus;
+}
+
+/**
+ * 物料明细列表项接口
+ */
+export interface MaterialDetailItem {
+  /** 明细ID,唯一标识符 */
+  id: string;
+  /** 物料ID,关联物料表 */
+  itemId: string;
+  /** 物料编码,业务唯一标识 */
+  itemCode: string;
+  /** 物料名称 */
+  itemName: string;
+  /** 规格型号 */
+  specification: string;
+  /** 主物料分类ID */
+  mainCategoryId: string;
+  /** 主物料分类名称 */
+  mainCategoryName: string;
+  /** 仓库ID */
+  warehouseId: string;
+  /** 仓库名称 */
+  warehouseName: string;
+  /** 可用数量 */
+  availableQuantity: number;
+  /** 订单数量 */
+  orderQuantity: number;
+  /** 确认数量 */
+  confirmQuantity: number;
+  /** 单价 */
+  unitPrice: number;
+  /** 税率 */
+  taxRate: number;
+  /** 税额 */
+  taxAmount: number;
+  /** 总金额 */
+  totalAmount: number;
+  /** 明细状态 */
+  detailStatus: MaterialDetailStatus;
+  /** 创建时间,ISO 8601格式 */
+  createTime: string;
+  /** 更新时间,ISO 8601格式 */
+  updateTime: string;
+}
+
+/**
+ * 订单项记录类型别名
+ * @description MaterialDetailItem的类型别名,用于更好的语义化
+ */
+export type OrderItemRecord = MaterialDetailItem;
+
+/**
+ * 物料明细表格组件Props接口
+ * @description 物料明细表格组件的属性定义
+ */
+export interface MaterialDetailTableProps {
+  /** 物料明细列表 */
+  materialDetails: OrderItemRecord[];
+}
+
+/**
+ * 物料明细表格组件Events接口
+ * @description 物料明细表格组件的事件定义
+ */
+export interface MaterialDetailTableEvents {
+  /** 刷新事件 */
+  refresh: () => void;
+  /** 物料导入事件 */
+  'material-import': (materials: OrderItemRecord[]) => void;
+}
+
+/**
+ * 分页配置接口
+ */
+export interface PaginationConfig {
+  /** 当前页码 */
+  currentPage: number;
+  /** 每页条数 */
+  pageSize: number;
+  /** 总条数 */
+  total: number;
+}

+ 144 - 0
src/components/order-form/utils.js

@@ -0,0 +1,144 @@
+/**
+ * @fileoverview 订单表单组件工具函数
+ * @description 提供订单表单相关组件的公共工具函数
+ */
+
+/**
+ * 格式化金额显示
+ * @description 将数字金额格式化为带货币符号的字符串,保留2位小数
+ * @param {number|string|null|undefined} amount - 金额数值,支持数字、字符串或空值
+ * @returns {string} 格式化后的金额字符串,格式为 ¥XX.XX
+ * @example
+ * formatAmount(123.456) // 返回 "¥123.46"
+ * formatAmount(null) // 返回 "¥0.00"
+ * formatAmount('100') // 返回 "¥100.00"
+ */
+export function formatAmount(amount) {
+  const numAmount = Number(amount || 0)
+  return `¥${numAmount.toFixed(2)}`
+}
+
+/**
+ * 格式化数量显示
+ * @description 将数字数量格式化为字符串,保留4位小数并去除尾随零
+ * @param {number|string|null|undefined} quantity - 数量数值
+ * @returns {string} 格式化后的数量字符串
+ * @example
+ * formatQuantity(123.4567) // 返回 "123.4567"
+ * formatQuantity(100.0000) // 返回 "100"
+ * formatQuantity(null) // 返回 "0"
+ */
+export function formatQuantity(quantity) {
+  const numQuantity = Number(quantity || 0)
+  return numQuantity.toFixed(4).replace(/\.?0+$/, '')
+}
+
+/**
+ * 格式化百分比显示
+ * @description 将数字格式化为百分比字符串
+ * @param {number|string|null|undefined} rate - 百分比数值(0-100)
+ * @returns {string} 格式化后的百分比字符串
+ * @example
+ * formatPercentage(13.5) // 返回 "13.5%"
+ * formatPercentage(0) // 返回 "0%"
+ */
+export function formatPercentage(rate) {
+  const numRate = Number(rate || 0)
+  return `${numRate}%`
+}
+
+/**
+ * 生成唯一ID
+ * @description 生成基于时间戳和随机数的唯一标识符
+ * @returns {string} 唯一ID字符串
+ * @example
+ * generateUniqueId() // 返回类似 "1704067200000_abc123def"
+ */
+export function generateUniqueId() {
+  return Date.now().toString() + '_' + Math.random().toString(36).substr(2, 9)
+}
+
+/**
+ * 深度克隆对象
+ * @description 创建对象的深度副本,避免引用问题
+ * @param {any} obj - 要克隆的对象
+ * @returns {any} 克隆后的对象
+ * @example
+ * const cloned = deepClone({ a: 1, b: { c: 2 } })
+ */
+export function deepClone(obj) {
+  if (obj === null || typeof obj !== 'object') {
+    return obj
+  }
+  
+  if (obj instanceof Date) {
+    return new Date(obj.getTime())
+  }
+  
+  if (obj instanceof Array) {
+    return obj.map(item => deepClone(item))
+  }
+  
+  if (typeof obj === 'object') {
+    const clonedObj = {}
+    for (const key in obj) {
+      if (obj.hasOwnProperty(key)) {
+        clonedObj[key] = deepClone(obj[key])
+      }
+    }
+    return clonedObj
+  }
+  
+  return obj
+}
+
+/**
+ * 验证手机号码格式
+ * @description 验证中国大陆手机号码格式
+ * @param {string} phone - 手机号码
+ * @returns {boolean} 验证结果
+ * @example
+ * validatePhone('13812345678') // 返回 true
+ * validatePhone('12345678901') // 返回 false
+ */
+export function validatePhone(phone) {
+  const phoneRegex = /^1[3-9]\d{9}$/
+  return phoneRegex.test(phone)
+}
+
+/**
+ * 防抖函数
+ * @description 创建一个防抖函数,在指定延迟后执行
+ * @param {Function} func - 要防抖的函数
+ * @param {number} delay - 延迟时间(毫秒)
+ * @returns {Function} 防抖后的函数
+ * @example
+ * const debouncedSearch = debounce(searchFunction, 300)
+ */
+export function debounce(func, delay) {
+  let timeoutId
+  return function (...args) {
+    clearTimeout(timeoutId)
+    timeoutId = setTimeout(() => func.apply(this, args), delay)
+  }
+}
+
+/**
+ * 节流函数
+ * @description 创建一个节流函数,限制函数执行频率
+ * @param {Function} func - 要节流的函数
+ * @param {number} limit - 时间间隔(毫秒)
+ * @returns {Function} 节流后的函数
+ * @example
+ * const throttledScroll = throttle(scrollHandler, 100)
+ */
+export function throttle(func, limit) {
+  let inThrottle
+  return function (...args) {
+    if (!inThrottle) {
+      func.apply(this, args)
+      inThrottle = true
+      setTimeout(() => inThrottle = false, limit)
+    }
+  }
+}