Bläddra i källkod

refactor(order-form): 统一物料明细状态字段名并增强功能

yz 1 månad sedan
förälder
incheckning
ca986c28aa

+ 22 - 33
src/components/order-form/material-detail-option.js

@@ -10,24 +10,17 @@ import {
   MATERIAL_DETAIL_STATUS_OPTIONS
 } from './constants'
 
+// 从types.js导入类型定义
+import { MaterialDetailDataSource } from './types'
+
 // 重新导出常量供其他模块使用
 export { MaterialDetailStatus, DEFAULT_PAGINATION_CONFIG, MATERIAL_DETAIL_STATUS_OPTIONS }
 
 /**
- * @typedef {Object} MaterialDetailQueryParams
- * @description 物料明细查询参数类型定义
- * @property {string} itemName - 物料名称,支持模糊查询
- * @property {string} itemCode - 物料编码,支持模糊查询
- * @property {string} specification - 规格型号,支持模糊查询
- * @property {string} warehouseName - 仓库名称,支持模糊查询
- * @property {number} current - 当前页码,从1开始
- * @property {number} size - 每页条数,范围1-100
- */
-
-/**
- * @typedef {import('./types').MaterialDetailFormData} MaterialDetailFormData
- * @typedef {import('./types').MaterialDetailItem} MaterialDetailItem
- * @typedef {import('./types').MaterialDetailStatus} MaterialDetailStatus
+ * @typedef {import('./types').MaterialDetailRecord} MaterialDetailRecord
+ * @typedef {import('./types').MaterialDetailQueryParams} MaterialDetailQueryParams
+ * @typedef {import('./types').ValidationRule} ValidationRule
+ * @typedef {import('./types').OrderFormModel} OrderFormModel
  */
 
 /**
@@ -36,10 +29,6 @@ export { MaterialDetailStatus, DEFAULT_PAGINATION_CONFIG, MATERIAL_DETAIL_STATUS
  */
 
 /**
- * @typedef {import('./types').ValidationRule} ValidationRule
- */
-
-/**
  * AvueJS分页配置选项类型定义
  * @typedef {Object} AvuePaginationOption
  * @description AvueJS表格分页配置对象
@@ -92,17 +81,11 @@ export { MaterialDetailStatus, DEFAULT_PAGINATION_CONFIG, MATERIAL_DETAIL_STATUS
  */
 
 /**
- * 获取物料明细表格配置选项
- * @description 返回AvueJS表格组件的配置对象,表格始终为只读模式,不支持编辑、新增、删除操作
- * @returns {AvueTableOption} AvueJS表格配置对象
- * @example
- * // 使用示例
- * const tableOption = getMaterialDetailOption()
- * // tableOption.addBtn === false
- * // tableOption.editBtn === false
- * // tableOption.delBtn === false
+ * 获取物料明细表格配置
+ * @param {boolean} [isEditMode=false] - 是否为编辑模式
+ * @returns {Object} AvueJS表格配置对象
  */
-export function getMaterialDetailOption() {
+export function getMaterialDetailOption(isEditMode = false) {
   return {
     border: true,
     index: true,
@@ -113,9 +96,10 @@ export function getMaterialDetailOption() {
     addBtn: false,
     editBtn: false,
     delBtn: false,
-    viewBtn: true,
+    viewBtn: false,
     searchShow: false,
     page: DEFAULT_PAGINATION_CONFIG,
+    menuWidth: 120,
     column: [
       {
         label: '物料编码',
@@ -220,7 +204,7 @@ export function getMaterialDetailOption() {
       },
       {
         label: '明细状态',
-        prop: 'detailStatus',
+        prop: 'status',
         type: 'select',
         dicData: MATERIAL_DETAIL_STATUS_OPTIONS,
         width: 100
@@ -396,9 +380,12 @@ export const DEFAULT_QUERY_PARAMS = {
   size: 10
 }
 
+// 重新导出MaterialDetailDataSource供其他模块使用
+export { MaterialDetailDataSource }
+
 /**
- * 默认物料明细表单数据
- * @type {MaterialDetailFormData}
+ * 默认表单数据
+ * @type {Readonly<MaterialDetailRecord>}
  */
 export const DEFAULT_FORM_DATA = {
   itemCode: '',
@@ -413,5 +400,7 @@ export const DEFAULT_FORM_DATA = {
   taxRate: 0,
   taxAmount: 0,
   totalAmount: 0,
-  detailStatus: '0' // MaterialDetailStatus.PENDING
+  status: '0', // MaterialDetailStatus.PENDING
+  dataSource: MaterialDetailDataSource.IMPORTED,
+  isDeletable: true
 }

+ 69 - 13
src/components/order-form/material-detail-table.vue

@@ -37,6 +37,7 @@
         @refresh-change="handleRefresh"
         @current-change="handleCurrentChange"
         @size-change="handleSizeChange"
+        @row-del="handleRowDelete"
       >
 
         <!-- 总金额列自定义渲染 -->
@@ -45,7 +46,7 @@
         </template>
 
         <!-- 明细状态列自定义渲染 -->
-        <template slot="detailStatus" slot-scope="{ row }">
+        <template slot="status" slot-scope="{ row }">
           <el-tag
             :type="getStatusTagType(row.status)"
             size="mini"
@@ -53,6 +54,20 @@
             {{ getStatusText(row.status) }}
           </el-tag>
         </template>
+
+        <!-- 操作列自定义渲染 -->
+        <template slot="menu" slot-scope="{ row, index }">
+          <el-button
+            v-if="row.isDeletable"
+            type="text"
+            size="mini"
+            icon="el-icon-delete"
+            class="delete-btn"
+            @click="handleDeleteMaterial(row, index)"
+          >
+            删除
+          </el-button>
+        </template>
       </avue-crud>
     </div>
 
@@ -69,6 +84,11 @@
 </template>
 
 <script>
+/**
+ * @fileoverview 物料明细表格组件
+ * @description 基于AvueJS的物料明细数据展示和操作组件,支持分页、搜索、删除等功能
+ */
+
 import { getMaterialDetailOption, DEFAULT_PAGINATION_CONFIG } from './material-detail-option'
 import {
   MaterialDetailStatus,
@@ -78,22 +98,16 @@ import {
 } from './constants'
 import MaterialImportDialog from './material-import-dialog.vue'
 import { formatAmount } from './utils'
+import { MaterialDetailDataSource } from './types'
 
 /**
- * 物料明细记录类型定义
- * @typedef {Object} MaterialDetailRecord
- * @property {string} id - 明细ID
- * @property {string} itemCode - 物料编码
- * @property {string} itemName - 物料名称
- * @property {string} specs - 规格型号
- * @property {string} unit - 计量单位
- * @property {number} quantity - 数量
- * @property {number} unitPrice - 单价
- * @property {number} totalAmount - 总金额
- * @property {0|1|2|3} detailStatus - 明细状态
- * @property {string} [remark] - 备注
+ * @typedef {import('./types').MaterialDetailRecord} MaterialDetailRecord
+ * @typedef {import('./types').MaterialDeleteEventData} MaterialDeleteEventData
+ * @typedef {import('./types').MaterialDetailQueryParams} MaterialDetailQueryParams
  */
 
+
+
 /**
  * 分页配置类型定义
  * @typedef {Object} PaginationConfig
@@ -328,6 +342,48 @@ export default {
      */
     getStatusText(status) {
       return getMaterialDetailStatusLabel(status)
+    },
+
+    /**
+      * 处理删除物料操作
+      * @description 删除指定的物料明细记录,仅允许删除可删除的物料
+      * @param {MaterialDetailRecord} row - 要删除的物料明细记录
+      * @param {number} index - 记录在当前页的索引位置
+      * @returns {void}
+      * @emits material-delete
+      */
+     async handleDeleteMaterial(row, index) {
+       try {
+         await this.$confirm(
+           `确定要删除物料 "${row.itemName}" 吗?`,
+           '删除确认',
+           {
+             confirmButtonText: '确定',
+             cancelButtonText: '取消',
+             type: 'warning'
+           }
+         )
+
+         // 触发删除事件,传递物料记录和索引
+         this.$emit('material-delete', { row, index })
+         this.$message.success('物料删除成功')
+       } catch (error) {
+         // 用户取消删除操作
+         if (error !== 'cancel') {
+           this.$message.error('删除操作失败')
+         }
+       }
+     },
+
+    /**
+     * 处理表格行删除事件
+     * @description AvueJS表格的删除事件处理器,委托给自定义删除方法
+     * @param {MaterialDetailRecord} row - 要删除的行数据
+     * @param {number} index - 行索引
+     * @returns {void}
+     */
+    handleRowDelete(row, index) {
+      this.handleDeleteMaterial(row, index)
     }
   }
 }

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

@@ -198,9 +198,6 @@ import { generateUniqueId } from './utils'
 /**
  * 物料导入弹窗组件
  * @description 用于从明细管理接口搜索和选择物料进行导入
- * @author 系统开发团队
- * @version 1.0.0
- * @since 2024-01-15
  */
 export default {
   name: 'MaterialImportDialog',
@@ -509,7 +506,7 @@ export default {
           taxRate: 0,
           taxAmount: 0,
           totalAmount: 0,
-          detailStatus: MaterialDetailStatus.PENDING, // 默认状态为待确认
+          status: material.status,
           createTime: new Date().toISOString(),
           updateTime: new Date().toISOString()
         }))

+ 90 - 13
src/components/order-form/order-form-mixin.js

@@ -6,22 +6,21 @@ import {
   ORDER_TYPE_OPTIONS,
   ORDER_STATUS_OPTIONS
 } from '@/constants/order'
+import { MaterialDetailDataSource } from './types'
 
 /**
+ * @typedef {import('./types').MaterialDetailRecord} MaterialDetailRecord
  * @typedef {import('./types').OrderFormModel} OrderFormModel
- * @typedef {import('./types').ValidationRule} ValidationRule
- * @typedef {import('./types').OrderFormRules} OrderFormRules
- * @typedef {import('./types').OrderFormData} OrderFormMixinData
- */
-
-/**
- * @typedef {Object} ApiResponse
- * @property {number} code - 响应状态码
- * @property {string} message - 响应消息
- * @property {*} data - 响应数据
+ * @typedef {import('./types').MaterialDeleteEventData} MaterialDeleteEventData
+ * @typedef {import('./types').ApiResponse} ApiResponse
+ * @typedef {import('./types').PaginatedResponse} PaginatedResponse
  */
 
 /**
+ * @typedef {import('./types').OrderFormModel} OrderFormModel
+ * @typedef {import('./types').ValidationRule} ValidationRule
+ * @typedef {import('./types').OrderFormRules} OrderFormRules
+ * @typedef {import('./types').OrderFormData} OrderFormMixinData
  * @typedef {import('./types').OrderTypeOption} OrderTypeOption
  * @typedef {import('./types').OrderStatusOption} OrderStatusOption
  */
@@ -339,9 +338,9 @@ export default {
 
     /**
      * 加载订单物料明细数据
-     * @description 根据订单ID从服务器获取物料明细列表,用于编辑模式下的数据回显
+     * @description 根据订单ID从服务器获取物料明细列表,远程数据不可删除
      * @param {string|number} orderId - 订单唯一标识符
-     * @returns {Promise<OrderItemRecord[]>} 物料明细记录列表,失败时返回空数组
+     * @returns {Promise<MaterialDetailRecord[]>} 物料明细记录列表,失败时返回空数组
      * @private
      */
     async loadMaterialDetails(orderId) {
@@ -352,9 +351,17 @@ export default {
           throw new Error('物料明细数据格式错误')
         }
 
-        return response.data.data.records || []
+        const materialDetails = response.data.data.records || []
+        
+        // 为远程加载的物料数据添加数据来源标识
+        return materialDetails.map(material => ({
+          ...material,
+          dataSource: MaterialDetailDataSource.REMOTE,
+          isDeletable: false // 远程加载的数据不可删除
+        }))
       } catch (error) {
         this.$message.warning('加载物料明细失败,请稍后重试')
+        console.error('加载物料明细失败:', error)
         return []
       }
     },
@@ -548,6 +555,76 @@ export default {
       })
 
       return cleanedData
+    },
+
+    /**
+      * 处理物料删除事件
+      * @description 从物料明细列表中删除指定的物料记录,仅允许删除可删除的物料
+      * @param {Object} deleteData - 删除数据对象
+      * @param {MaterialDetailRecord} deleteData.row - 要删除的物料记录
+      * @param {number} deleteData.index - 记录在当前页的索引
+      * @returns {void}
+      * @public
+      */
+     handleMaterialDelete({ row, index }) {
+       if (!row) {
+         this.$message.warning('删除数据无效')
+         return
+       }
+
+       // 检查物料是否可删除
+       if (!row.isDeletable) {
+         this.$message.warning('该物料不允许删除')
+         return
+       }
+
+       try {
+         // 从物料明细列表中移除该记录
+         const materialIndex = this.materialDetails.findIndex(item => 
+           item.itemCode === row.itemCode && 
+           item.dataSource === row.dataSource
+         )
+
+         if (materialIndex !== -1) {
+           this.materialDetails.splice(materialIndex, 1)
+           this.$message.success(`物料 "${row.itemName}" 删除成功`)
+         } else {
+           this.$message.warning('未找到要删除的物料记录')
+         }
+       } catch (error) {
+         this.$message.error('删除物料失败,请重试')
+         console.error('删除物料失败:', error)
+       }
+     },
+
+    /**
+     * 处理物料导入事件
+     * @description 将导入的物料数据添加到物料明细列表中,导入数据可删除
+     * @param {MaterialDetailRecord[]} importedMaterials - 导入的物料数据数组
+     * @returns {void}
+     * @public
+     */
+    handleMaterialImport(importedMaterials) {
+      if (!Array.isArray(importedMaterials) || importedMaterials.length === 0) {
+        this.$message.warning('没有有效的物料数据可导入')
+        return
+      }
+
+      try {
+        // 为导入的物料添加数据来源标识
+        const processedMaterials = importedMaterials.map(material => ({
+          ...material,
+          dataSource: MaterialDetailDataSource.IMPORTED,
+          isDeletable: true // 导入的数据可以删除
+        }))
+
+        // 添加到现有物料列表中
+        this.materialDetails.push(...processedMaterials)
+        this.$message.success(`成功导入 ${importedMaterials.length} 条物料记录`)
+      } catch (error) {
+        this.$message.error('导入物料失败,请重试')
+        console.error('导入物料失败:', error)
+      }
     }
   }
 }

+ 10 - 4
src/components/order-form/order-form.vue

@@ -47,6 +47,7 @@
           :material-details="materialDetails"
           @material-change="handleMaterialChange"
           @material-import="handleMaterialImport"
+          @material-delete="handleMaterialDelete"
         />
       </div>
     </div>
@@ -62,6 +63,8 @@ import MaterialDetailTable from './material-detail-table.vue'
  * @typedef {import('@/api/order/order-item').OrderItemRecord} OrderItemRecord
  * @typedef {import('./types').OrderFormModel} OrderFormModel
  * @typedef {import('./types').OrderFormProps} OrderFormProps
+ * @typedef {import('./types').MaterialDetailRecord} MaterialDetailRecord
+ * @typedef {import('./types').MaterialDeleteEventData} MaterialDeleteEventData
  */
 
 /**
@@ -89,8 +92,8 @@ export default {
     return {
       /**
        * 物料明细列表
-       * @type {OrderItemRecord[]}
-       * @description 存储当前订单的物料明细数据,在编辑模式下会自动从服务器加载
+       * @type {MaterialDetailRecord[]}
+       * @description 存储当前订单的物料明细数据,包含数据来源和删除权限标识
        */
       materialDetails: []
     }
@@ -248,8 +251,11 @@ export default {
      * @returns {void}
      */
     handleMaterialImport(importedMaterials) {
-      // 将导入的物料添加到现有明细列表中
-      this.materialDetails = [...this.materialDetails, ...importedMaterials]
+      // 调用mixin中的方法来正确处理导入物料(设置数据来源和删除权限)
+      // mixin中的方法会直接更新this.materialDetails
+      orderFormMixin.methods.handleMaterialImport.call(this, importedMaterials)
+      
+      // 重新计算订单总金额
       this.calculateOrderTotal()
       this.$message.success(`成功导入 ${importedMaterials.length} 条物料明细`)
     },

+ 2 - 2
src/components/order-form/types.d.ts

@@ -428,7 +428,7 @@ export interface MaterialDetailFormData {
   /** 总金额,自动计算,精度2位小数 */
   totalAmount: number;
   /** 明细状态 */
-  detailStatus: MaterialDetailStatus;
+  status: MaterialDetailStatus;
 }
 
 /**
@@ -468,7 +468,7 @@ export interface MaterialDetailItem {
   /** 总金额 */
   totalAmount: number;
   /** 明细状态 */
-  detailStatus: MaterialDetailStatus;
+  status: MaterialDetailStatus;
   /** 创建时间,ISO 8601格式 */
   createTime: string;
   /** 更新时间,ISO 8601格式 */

+ 117 - 0
src/components/order-form/types.js

@@ -0,0 +1,117 @@
+/**
+ * @fileoverview 订单表单组件类型定义
+ * @description 定义订单表单相关的TypeScript类型和JSDoc类型注释
+ */
+
+/**
+ * 物料明细数据来源枚举
+ * @readonly
+ * @enum {string}
+ */
+export const MaterialDetailDataSource = {
+  /** 远程加载的数据(从订单获取) */
+  REMOTE: 'REMOTE',
+  /** 用户导入的数据 */
+  IMPORTED: 'IMPORTED'
+}
+
+/**
+ * 物料明细状态枚举
+ * @readonly
+ * @enum {string}
+ */
+export const MaterialDetailStatus = {
+  /** 待处理 */
+  PENDING: '0',
+  /** 已确认 */
+  CONFIRMED: '1',
+  /** 已取消 */
+  CANCELLED: '2'
+}
+
+/**
+ * @typedef {Object} MaterialDetailRecord
+ * @description 物料明细记录数据结构
+ * @property {string} itemCode - 物料编码,唯一标识
+ * @property {string} itemName - 物料名称
+ * @property {string} specification - 规格型号
+ * @property {string} mainCategoryName - 主分类名称
+ * @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} status - 明细状态
+ * @property {MaterialDetailDataSource} dataSource - 数据来源
+ * @property {boolean} isDeletable - 是否可删除
+ */
+
+/**
+ * @typedef {Object} OrderFormModel
+ * @description 订单表单数据模型
+ * @property {string} orderCode - 订单编码
+ * @property {string} orderType - 订单类型
+ * @property {string} orderStatus - 订单状态
+ * @property {string} customerName - 客户名称
+ * @property {string} customerCode - 客户编码
+ * @property {string} contactPerson - 联系人
+ * @property {string} contactPhone - 联系电话
+ * @property {string} deliveryAddress - 交货地址
+ * @property {string} expectedDeliveryDate - 预期交货日期
+ * @property {string} remarks - 备注信息
+ * @property {number} totalAmount - 订单总金额
+ * @property {MaterialDetailRecord[]} materialDetails - 物料明细列表
+ */
+
+/**
+ * @typedef {Object} MaterialDetailQueryParams
+ * @description 物料明细查询参数
+ * @property {string} itemName - 物料名称(模糊查询)
+ * @property {string} itemCode - 物料编码(模糊查询)
+ * @property {string} specification - 规格型号(模糊查询)
+ * @property {string} warehouseName - 仓库名称(模糊查询)
+ * @property {number} current - 当前页码,从1开始
+ * @property {number} size - 每页条数,范围1-100
+ */
+
+/**
+ * @typedef {Object} MaterialDeleteEventData
+ * @description 物料删除事件数据
+ * @property {MaterialDetailRecord} row - 要删除的物料记录
+ * @property {number} index - 记录在当前页的索引位置
+ */
+
+/**
+ * @typedef {Object} ValidationRule
+ * @description 表单验证规则
+ * @property {boolean} required - 是否必填
+ * @property {string} message - 验证失败提示信息
+ * @property {string} trigger - 触发验证的事件类型
+ * @property {number} [min] - 最小长度
+ * @property {number} [max] - 最大长度
+ * @property {RegExp} [pattern] - 正则表达式验证
+ */
+
+/**
+ * @typedef {Object} ApiResponse
+ * @description API响应数据结构
+ * @template T
+ * @property {boolean} success - 请求是否成功
+ * @property {string} message - 响应消息
+ * @property {T} data - 响应数据
+ * @property {number} code - 响应状态码
+ */
+
+/**
+ * @typedef {Object} PaginatedResponse
+ * @description 分页响应数据结构
+ * @template T
+ * @property {T[]} records - 数据记录列表
+ * @property {number} total - 总记录数
+ * @property {number} current - 当前页码
+ * @property {number} size - 每页大小
+ * @property {number} pages - 总页数
+ */