Browse Source

refactor(订单表单): 优化物料明细类型定义和混入逻辑

yz 3 weeks ago
parent
commit
e417279dc0
2 changed files with 106 additions and 171 deletions
  1. 66 34
      src/components/order-form/material-detail-mixin.js
  2. 40 137
      src/components/order-form/types.d.ts

+ 66 - 34
src/components/order-form/material-detail-mixin.js

@@ -2,6 +2,15 @@
  * @fileoverview 物料明细表格组件
  * @description 基于AvueJS的物料明细数据展示和操作组件,支持分页、搜索、删除等功能
  * @typedef {import('./types').MaterialDetailTableComponent} MaterialDetailTableComponent
+ * @typedef {import('./types').MaterialDetailRecord} MaterialDetailRecord
+ * @typedef {import('./types').MaterialUpdateEventData} MaterialUpdateEventData
+ * @typedef {import('./types').MaterialDeleteEventData} MaterialDeleteEventData
+ * @typedef {import('./types').MaterialDetailQueryParams} MaterialDetailQueryParams
+ * @typedef {import('./types').MaterialOption} MaterialOption
+ * @typedef {import('./types').PaginationConfig} PaginationConfig
+ * @typedef {import('smallwei__avue/crud').AvueCrudOption} AvueCrudOption
+ * @typedef {import('smallwei__avue/crud').AvueCrudColumn} AvueCrudColumn
+ * @typedef {import('smallwei__avue/crud').PageOption} PageOption
  */
 
 import { getMaterialDetailOption, DEFAULT_PAGINATION_CONFIG } from './material-detail-option'
@@ -28,25 +37,15 @@ import {
   NUMBER_TYPES
 } from './number-format-utils'
 
-/**
- * @typedef {import('./types').MaterialDetailRecord} MaterialDetailRecord
- * @typedef {import('./types').MaterialUpdateEventData} MaterialUpdateEventData
- * @typedef {import('./types').MaterialDeleteEventData} MaterialDeleteEventData
- * @typedef {import('./types').MaterialDetailQueryParams} MaterialDetailQueryParams
- * @typedef {import('smallwei__avue/crud').AvueCrudOption} AvueCrudOption
- * @typedef {import('smallwei__avue/crud').AvueCrudColumn} AvueCrudColumn
- * @typedef {import('smallwei__avue/crud').PageOption} PageOption
- */
-
 
 
-// 使用@types/smallwei__avue/crud中的PageOption类型代替PaginationConfig
+// 使用本地 PaginationConfig 作为分页数据结构描述
 
 /**
  * 组件数据类型定义
  * @typedef {Object} MaterialDetailTableData
  * @property {Partial<MaterialDetailRecord>} formData - 表单数据
- * @property {PageOption} page - 分页配置
+ * @property {PaginationConfig} page - 分页配置
  * @property {boolean} importDialogVisible - 导入弹窗显示状态
  */
 
@@ -72,7 +71,7 @@ export default {
   props: {
     /**
      * 是否为编辑模式 - 控制表格是否可编辑
-     * @type {boolean}
+     * @type {import('vue').PropOptions<boolean>}
      */
     editMode: {
       type: Boolean,
@@ -80,7 +79,7 @@ export default {
     },
     /**
      * 订单ID - 关联的订单唯一标识符
-     * @type {string|number|null}
+     * @type {import('vue').PropOptions<string|number|null>}
      */
     orderId: {
       type: [String, Number],
@@ -90,7 +89,7 @@ export default {
 
     /**
      * 物料明细列表 - 要展示的物料明细数据
-     * @type {MaterialDetailRecord[]} 物料明细数据数组,每个元素包含物料的详细信息
+     * @type {import('vue').PropOptions<MaterialDetailRecord[]>} 物料明细数据数组,每个元素包含物料的详细信息
      */
     materialDetails: {
       type: Array,
@@ -118,7 +117,7 @@ export default {
       formData: {},
 
       /**
-       * 分页配置 - AvueJS表格分页相关配置
+       * 分页配置 - 表格分页相关配置
        * @type {PaginationConfig} 包含currentPage、pageSize、total等属性的分页配置对象
        */
       page: {
@@ -135,7 +134,7 @@ export default {
 
       /**
        * 物料选项列表 - 远程搜索返回的物料选项
-       * @type {ItemRecord[]}
+       * @type {MaterialOption[]}
        */
       materialOptions: [],
 
@@ -185,6 +184,7 @@ export default {
 
     /**
      * 当前页显示的数据 - 根据分页配置计算当前页应显示的数据
+     * @this {MaterialDetailTableComponent}
      * @returns {MaterialDetailRecord[]} 当前页的物料明细数据
      */
     currentPageData() {
@@ -206,6 +206,9 @@ export default {
      * @returns {void}
      */
     materialDetails: {
+      /**
+       * @this {MaterialDetailTableComponent}
+       */
       handler(newVal) {
         this.page.total = newVal.length
       },
@@ -346,6 +349,7 @@ export default {
      * @description 根据关键词远程搜索物料数据,支持防抖处理
      * @param {string} query - 搜索关键词
      * @returns {void}
+     * @this {MaterialDetailTableComponent}
      */
     remoteSearchMaterial(query) {
       // 清除之前的定时器
@@ -374,6 +378,7 @@ export default {
      * @param {string} keyword - 搜索关键词
      * @returns {Promise<void>}
      * @throws {Error} 当API调用失败时抛出异常
++     * @this {MaterialDetailTableComponent}
      */
     async searchMaterials(keyword) {
       try {
@@ -490,6 +495,7 @@ export default {
      * 处理导入选中物料
      * @description 将选中的物料导入到物料明细表中
      * @returns {void}
+     * @this {MaterialDetailTableComponent}
      */
     handleImportSelectedMaterial() {
       if (!this.selectedMaterialId) {
@@ -529,7 +535,7 @@ export default {
     /**
      * 准备物料明细数据
      * @description 将选中的物料数据转换为物料明细表所需的格式
-     * @param {import('@/api/types/order').SalesOrderItemListRecord} material - 物料数据(来自getMaterialFullList API)
+     * @param {import('./types').MaterialOption} material - 物料数据(来自getMaterialFullList API)
      * @returns {MaterialDetailRecord} 格式化后的物料明细数据
      * @private
      */
@@ -539,8 +545,9 @@ export default {
         itemCode: material.itemCode,
         itemName: material.itemName,
         specs: material.specs || '',
-        specification: material.specs || '',
+        // 移除不在 MaterialDetailItem 中的 specification 字段
         unit: material.unit || '',
+        // 使用标准化字段,兼容后备旧字段
         mainItemCategoryName: material.mainItemCategoryName || material.MainItemCategory_Name || '',
         mainItemCategoryId: material.mainItemCategoryId || material.MainItemCategory_ID,
         mainItemCategoryCode: material.mainItemCategoryCode || material.MainItemCategory_Code || '',
@@ -580,6 +587,7 @@ export default {
      * @description 触发刷新事件,通知父组件重新加载数据
      * @returns {void}
      * @emits refresh
++     * @this {MaterialDetailTableComponent}
      */
     handleRefresh() {
       this.$emit(MATERIAL_DETAIL_EVENTS.REFRESH)
@@ -591,6 +599,7 @@ export default {
      * 处理分页页码变化事件
      * @description 当用户切换页码时触发,更新当前页码
      * @param {number} currentPage - 新的页码,从1开始
+     * @this {MaterialDetailTableComponent}
      * @returns {void}
      */
     handleCurrentChange(currentPage) {
@@ -601,6 +610,7 @@ export default {
      * 处理分页大小变化事件
      * @description 当用户改变每页显示条数时触发,重置到第一页
      * @param {number} pageSize - 新的每页显示条数
+     * @this {MaterialDetailTableComponent}
      * @returns {void}
      */
     handleSizeChange(pageSize) {
@@ -645,7 +655,7 @@ export default {
     /**
      * 获取状态标签类型
      * @description 根据物料明细状态值返回对应的Element UI标签类型
-     * @param {typeof MaterialDetailStatus[keyof typeof MaterialDetailStatus]} itemStatus - 物料明细状态值
+     * @param {import('@/api/types/order').OrderItemStatus} itemStatus - 物料明细状态值
      * @returns {string} Element UI标签类型
      * @example
      * getStatusTagType(0) // 返回 'warning'
@@ -658,7 +668,7 @@ export default {
     /**
      * 获取状态文本
      * @description 根据物料明细状态值返回对应的中文描述
-     * @param {number} itemStatus - 物料明细状态值
+     * @param {import('@/api/types/order').OrderItemStatus} itemStatus - 物料明细状态值
      * @returns {string} 状态的中文描述文本
      * @example
      * getStatusText(0) // 返回 '待确认'
@@ -669,13 +679,14 @@ export default {
     },
 
     /**
-      * 处理删除物料操作
-      * @description 删除指定的物料明细记录,仅允许删除可删除的物料
-      * @param {MaterialDetailRecord} row - 要删除的物料明细记录
-      * @param {number} index - 记录在当前页的索引位置
-      * @returns {void}
-      * @emits material-delete
-      */
+       * 处理删除物料操作
+       * @description 删除指定的物料明细记录,仅允许删除可删除的物料
+       * @param {MaterialDetailRecord} row - 要删除的物料明细记录
+       * @param {number} index - 记录在当前页的索引位置
+       * @returns {Promise<void>}
+       * @emits material-delete
+       * @this {MaterialDetailTableComponent}
+       */
      async handleDeleteMaterial(row, index) {
        try {
          await this.$confirm(
@@ -705,6 +716,7 @@ export default {
      * @param {MaterialDetailRecord} row - 要删除的行数据
      * @param {number} index - 行索引
      * @returns {void}
++     * @this {MaterialDetailTableComponent}
      */
     handleRowDelete(row, index) {
       this.handleDeleteMaterial(row, index)
@@ -715,11 +727,12 @@ export default {
      * @description 当用户编辑表格行数据时触发,执行自动计算逻辑
      * @param {MaterialDetailRecord} row - 更新后的行数据
      * @param {number} index - 行索引
-     * @param {boolean} done - 完成回调函数
+     * @param {(result: any) => void} done - 完成回调函数
      * @returns {void}
      * @emits material-update
+     * @this {MaterialDetailTableComponent}
      */
-    async handleRowUpdate(row, index, done) {
+    handleRowUpdate(row, index, done) {
       try {
         // 执行自动计算
         const calculatedRow = this.calculateAmounts(row)
@@ -743,6 +756,7 @@ export default {
      * @param {MaterialDetailRecord} row - 行数据
      * @param {number} index - 行索引
      * @returns {void}
++     * @this {MaterialDetailTableComponent}
      */
     handleQuantityBlur(row, index) {
       // 如果 index 无效,尝试通过 row 数据找到正确的索引
@@ -756,6 +770,7 @@ export default {
      * @param {MaterialDetailRecord} row - 行数据
      * @param {number} index - 行索引
      * @returns {void}
++     * @this {MaterialDetailTableComponent}
      */
     handleQuantityChange(row, index) {
       const calculatedRow = this.calculateAmounts(row)
@@ -769,6 +784,7 @@ export default {
      * @param {MaterialDetailRecord} row - 行数据
      * @param {number} index - 行索引
      * @returns {void}
++     * @this {MaterialDetailTableComponent}
      */
     handleTaxRateChange(row, index) {
       this.calculateTaxAmount(row)
@@ -781,6 +797,7 @@ export default {
      * @param {MaterialDetailRecord} row - 行数据
      * @param {number} index - 行索引
      * @returns {void}
++     * @this {MaterialDetailTableComponent}
      */
     handleUnitPriceBlur(row, index) {
       // 先格式化数值
@@ -797,6 +814,7 @@ export default {
      * @param {MaterialDetailRecord} row - 行数据
      * @param {number} index - 行索引
      * @returns {void}
++     * @this {MaterialDetailTableComponent}
      */
     handleUnitPriceChange(row, index) {
       const calculatedRow = this.calculateAmounts(row)
@@ -810,11 +828,15 @@ export default {
      * @param {MaterialDetailRecord} row - 行数据
      * @param {number} index - 行索引
      * @returns {void}
++     * @this {MaterialDetailTableComponent}
      */
     handleTaxAmountChange(row, index) {
       // 当税额手动修改时,反推税率
-      if (row.totalAmount && row.totalAmount > 0) {
-        row.taxRate = ((row.taxAmount || 0) / row.totalAmount * 100).toFixed(2)
+      // 使用数值转换,避免字符串参与算术导致的类型错误
+      const totalAmountNum = Number(row.totalAmount) || 0
+      const taxAmountNum = Number(row.taxAmount) || 0
+      if (totalAmountNum > 0) {
+        row.taxRate = ((taxAmountNum / totalAmountNum) * 100).toFixed(2)
       }
       this.$emit('material-update', { row, index })
     },
@@ -825,6 +847,7 @@ export default {
      * @param {MaterialDetailRecord} row - 行数据
      * @param {number} index - 行索引
      * @returns {void}
++     * @this {MaterialDetailTableComponent}
      */
     handleTotalAmountChange(row, index) {
       // 当总金额手动修改时,重新计算税额
@@ -839,6 +862,7 @@ export default {
      * @param {string} prop - 编辑的属性名
      * @param {*} value - 当前值
      * @returns {void}
++     * @this {MaterialDetailTableComponent}
      */
     handleCellEditStart(row, prop, value) {
       // 记录编辑前的值,用于计算变化
@@ -853,6 +877,7 @@ export default {
      * @param {string} prop - 编辑的属性名
      * @param {*} value - 新值
      * @returns {void}
++     * @this {MaterialDetailTableComponent}
      */
     handleCellEditEnd(row, prop, value) {
       // 如果编辑的是影响计算的字段,执行自动计算
@@ -923,9 +948,12 @@ export default {
      * @description 根据行数据获取在当前页中的索引
      * @param {MaterialDetailRecord} row - 行数据
      * @returns {number} 行索引
+     * @this {MaterialDetailTableComponent}
      */
     getCurrentRowIndex(row) {
-      return this.currentPageData.findIndex(item => item.id === row.id)
+      /** @type {MaterialDetailRecord[]} */
+      const list = this.currentPageData || []
+      return list.findIndex((item) => item.id === row.id)
     },
 
     /**
@@ -934,6 +962,7 @@ export default {
       * @param {MaterialDetailRecord} row - 行数据
       * @param {number} providedIndex - 提供的索引
       * @returns {number} 实际索引
+      * @this {MaterialDetailTableComponent}
       */
      findRowIndex(row, providedIndex) {
        // 如果提供的索引有效,直接使用
@@ -942,7 +971,9 @@ export default {
        }
 
        // 否则通过行数据查找索引
-       const index = this.materialDetails.findIndex(item => {
+       /** @type {MaterialDetailRecord[]} */
+       const details = this.materialDetails || []
+       const index = details.findIndex((item) => {
          // 优先使用 id 进行匹配
          if (row.id && item.id) {
            return row.id === item.id
@@ -962,6 +993,7 @@ export default {
   /**
    * 组件销毁前的清理工作
    * @description 清除定时器,避免内存泄漏
+   * @this {MaterialDetailTableComponent}
    */
   beforeDestroy() {
     if (this.searchTimer) {

+ 40 - 137
src/components/order-form/types.d.ts

@@ -132,7 +132,7 @@ export type OrderStatusOption = SelectOption<OrderStatus>;
 
 // 使用@types/smallwei__avue库中的类型定义
 import { AvueFormColumn, AvueFormGroup, AvueFormOption } from '@types/smallwei__avue/form';
-import { AvueCrudColumn, AvueCrudDefaults, PageOption } from '@types/smallwei__avue/crud';
+import { AvueCrudColumn, AvueCrudDefaults, PageOption, AvueCrudOption } from '@types/smallwei__avue/crud';
 import { DicItem, DicProps } from '@types/smallwei__avue/dic';
 
 // Vue相关类型导入
@@ -533,7 +533,7 @@ export interface MaterialDetailFormData {
   /** 总金额,自动计算,精度2位小数 */
   totalAmount: number;
   /** 明细状态 */
-  status: typeof MaterialDetailStatus[keyof typeof MaterialDetailStatus];
+  status: MaterialDetailStatus;
 }
 
 /**
@@ -563,7 +563,7 @@ export interface MaterialDetailItem {
   /** 仓库名称 */
   warehouseName: string | null;
   /** 可用数量 */
-  availableQuantity: number;
+  availableQuantity?: number;
   /** 订单数量 */
   orderQuantity: number | string;
   /** 确认数量 */
@@ -615,6 +615,11 @@ export interface MaterialOption {
   mainItemCategoryName: string;
   mainItemCategoryId: number;
   mainItemCategoryCode: string;
+  // 新增:兼容可用数量与旧字段
+  availableQuantity?: number;
+  MainItemCategory_Name?: string;
+  MainItemCategory_ID?: number;
+  MainItemCategory_Code?: string;
   unitPrice: string;
   description: string;
   warehouseId: number | null;
@@ -657,17 +662,23 @@ export interface MaterialDetailTableComponent extends Vue {
   isRowEditable(row: MaterialDetailRecord): boolean;
   remoteSearchMaterial(query: string): void;
   searchMaterials(keyword: string): Promise<void>;
+  // 新增:品牌库存搜索
+  searchBrandStock(keyword: string): Promise<void>;
   handleImportSelectedMaterial(): void;
   prepareMaterialDetailData(material: MaterialOption): MaterialDetailRecord;
   calculateAmounts(materialDetail: MaterialDetailRecord): MaterialDetailRecord;
+  // 新增:计算税额
+  calculateTaxAmount(row: MaterialDetailRecord): void;
   handleRefresh(): void;
   handleCurrentChange(current: number): void;
   handleSizeChange(size: number): void;
   handleRowDelete(row: MaterialDetailRecord, index: number): void;
   handleDeleteMaterial(row: MaterialDetailRecord, index: number): void;
-  handleRowUpdate(row: MaterialDetailRecord, index: number, done: () => void, loading: () => void): void;
+  handleRowUpdate(row: MaterialDetailRecord, index: number, done: (result: any) => void): void;
   handleQuantityBlur(row: MaterialDetailRecord, index: number): void;
+  handleQuantityChange(row: MaterialDetailRecord, index: number): void;
   handleUnitPriceBlur(row: MaterialDetailRecord, index: number): void;
+  handleUnitPriceChange(row: MaterialDetailRecord, index: number): void;
   handleTaxRateChange(row: MaterialDetailRecord, index: number): void;
   handleTaxAmountChange(row: MaterialDetailRecord, index: number): void;
   handleTotalAmountChange(row: MaterialDetailRecord, index: number): void;
@@ -679,6 +690,9 @@ export interface MaterialDetailTableComponent extends Vue {
   formatUnitPrice(value: number | string): string;
   formatTaxRate(value: number | string, withSymbol?: boolean): string;
   formatAmount(value: number | string, withSymbol?: boolean): string;
+  // 新增:索引辅助方法
+  getCurrentRowIndex(row: MaterialDetailRecord): number;
+  findRowIndex(row: MaterialDetailRecord, providedIndex: number): number;
 }
 
 /**
@@ -750,137 +764,26 @@ export interface MaterialDetailQueryParams {
   itemCode?: string;
   /** 物料名称 */
   itemName?: string;
-  /** 主物料分类ID */
-  mainItemCategoryId?: string;
-  /** 子物料分类ID */
-  subItemCategoryId?: string;
-  /** 品牌ID */
-  brandId?: string;
-  /** 仓库ID */
-  warehouseId?: string;
-  /** 订单状态 */
-  status?: typeof MaterialDetailStatus[keyof typeof MaterialDetailStatus];
-  /** 页码 */
-  pageNum?: number;
-  /** 每页大小 */
-  pageSize?: number;
-}
-
-/**
- * 验证规则接口 - 已在上方定义,此处注释掉
- * @description 表单验证规则的类型定义
- */
-// 重复定义,已在上方定义
-// export interface ValidationRule {
-//   /** 是否必填 */
-//   required?: boolean;
-//   /** 验证失败消息 */
-//   message: string;
-//   /** 触发方式 */
-//   trigger?: string;
-//   /** 验证类型 */
-//   type?: string;
-//   /** 最小值 */
-//   min?: number;
-//   /** 最大值 */
-//   max?: number;
-//   /** 自定义验证函数 */
-//   validator?: (rule: ValidationRule, value: any, callback: (error?: Error) => void) => void;
-//}
-
-/**
- * 物料明细表格组件Props接口
- * @description 物料明细表格组件的属性定义
- */
-export interface MaterialDetailTableProps {
-  /** 物料明细列表 */
-  materialDetails: OrderItemRecord[];
-}
-
-/**
- * 物料明细表格组件Events接口
- * @description 物料明细表格组件的事件定义
- */
-export interface MaterialDetailTableEvents {
-  /** 刷新事件 */
-  refresh: () => void;
-  /** 物料导入事件 */
-  'material-import': (materials: MaterialDetailRecord[]) => void;
-  /** 物料明细更新事件 */
-  'material-update': (data: MaterialUpdateEventData) => void;
-  /** 物料明细删除事件 */
-  'material-delete': (data: MaterialDeleteEventData) => void;
-}
-
-/**
- * 分页配置接口
- */
-export interface PaginationConfig {
-  /** 当前页码 */
-  currentPage: number;
-  /** 每页条数 */
-  pageSize: number;
-  /** 总条数 */
-  total: number;
-}
-
-/**
- * 订单表单混入完整接口
- * @description 订单表单混入组件的完整类型定义
- */
-export interface OrderFormMixin extends OrderFormMixinData, OrderFormMixinComputed, OrderFormMixinMethods, OrderFormComponentProps {
-  /** Vue实例引用 */
-  $refs: {
-    [key: string]: any;
-  };
-  /** 事件发射器 */
-  $emit: (event: string, ...args: any[]) => void;
-  /** 消息提示 */
-  $message: {
-    success(message: string): void;
-    error(message: string): void;
-    warning(message: string): void;
-    info(message: string): void;
-  };
-  /** 下一个tick */
-  $nextTick(): Promise<void>;
-  /** Vue设置响应式属性 */
-  $set: (target: any, key: string | number, value: any) => any;
-}
-
-/**
- * 订单表单混入组件类型
- * @description 结合Vue实例和OrderFormMixin的完整类型
- */
-export interface OrderFormMixinComponent extends OrderFormMixin, Vue {
-  // 继承Vue实例的所有属性和方法
-}
-
-/**
- * 订单表单组件完整接口
- * @description 订单表单组件的完整类型定义
- */
-export interface OrderFormComponent extends OrderFormComponentData, OrderFormComponentComputed, OrderFormComponentMethods, OrderFormComponentProps {
-  /** Vue实例引用 */
-  $refs: {
-    orderForm?: any;
-    [key: string]: any;
-  };
-  /** 事件发射器 */
-  $emit: (event: string, ...args: any[]) => void;
-  /** 消息提示 */
-  $message: {
-    success(message: string): void;
-    error(message: string): void;
-    warning(message: string): void;
-    info(message: string): void;
-  };
-  /** 下一个tick */
-  $nextTick(): Promise<void>;
-  /** Vue设置响应式属性 */
-  $set: (target: any, key: string | number, value: any) => any;
-  /** 混入的方法和属性 */
-  $options: {
-    mixins: OrderFormMixin[];
-  };
+  /** 规格(兼容旧字段) */
+  specification?: string;
+  /** 仓库名称(筛选用,可选) */
+  warehouseName?: string;
+   /** 主物料分类ID */
+   mainItemCategoryId?: string;
+   /** 子物料分类ID */
+   subItemCategoryId?: string;
+   /** 品牌ID */
+   brandId?: string;
+   /** 仓库ID */
+   warehouseId?: string;
+   /** 订单状态 */
+   status?: MaterialDetailStatus;
+   /** 页码 */
+   pageNum?: number;
+   /** 每页大小 */
+   pageSize?: number;
+  /** 当前页(兼容旧字段) */
+  current?: number;
+  /** 每页大小(兼容旧字段) */
+  size?: number;
 }