|
@@ -1,6 +1,14 @@
|
|
|
+/**
|
|
|
+ * @fileoverview 订单表单混入组件
|
|
|
+ * @description 提供订单表单的数据管理、验证规则和业务逻辑的混入组件,支持新增和编辑模式
|
|
|
+ */
|
|
|
+
|
|
|
+// API接口导入
|
|
|
import { add, update, getDetail } from '@/api/order/order'
|
|
|
import { getList as getOrderItemList } from '@/api/order/order-item'
|
|
|
import { createSalesOrder } from '@/api/order/sales-order'
|
|
|
+
|
|
|
+// 常量和枚举导入
|
|
|
import {
|
|
|
ORDER_TYPES,
|
|
|
ORDER_STATUS,
|
|
@@ -8,12 +16,12 @@ import {
|
|
|
ORDER_STATUS_OPTIONS
|
|
|
} from '@/constants/order'
|
|
|
|
|
|
-// 导入本地常量定义
|
|
|
+// 本地常量定义导入
|
|
|
import {
|
|
|
MaterialDetailDataSource
|
|
|
} from './constants'
|
|
|
|
|
|
-// 导入数字格式化工具
|
|
|
+// 数字格式化工具导入
|
|
|
import {
|
|
|
formatAmount,
|
|
|
formatFloatNumber,
|
|
@@ -28,17 +36,63 @@ import {
|
|
|
} from './number-format-utils'
|
|
|
|
|
|
/**
|
|
|
+ * 类型定义导入
|
|
|
+ * @description 导入所有必要的TypeScript类型定义,确保类型安全
|
|
|
+ */
|
|
|
+
|
|
|
+/**
|
|
|
* @typedef {import('./constants').MaterialDetailRecord} MaterialDetailRecord
|
|
|
+ * @description 物料明细记录类型
|
|
|
+ */
|
|
|
+
|
|
|
+/**
|
|
|
* @typedef {import('./constants').OrderFormModel} OrderFormModel
|
|
|
+ * @description 订单表单数据模型类型
|
|
|
+ */
|
|
|
+
|
|
|
+/**
|
|
|
* @typedef {import('./constants').MaterialDeleteEventData} MaterialDeleteEventData
|
|
|
+ * @description 物料删除事件数据类型
|
|
|
+ */
|
|
|
+
|
|
|
+/**
|
|
|
* @typedef {import('./constants').ApiResponse} ApiResponse
|
|
|
+ * @description API响应数据类型
|
|
|
+ */
|
|
|
+
|
|
|
+/**
|
|
|
* @typedef {import('./constants').PaginatedResponse} PaginatedResponse
|
|
|
+ * @description 分页响应数据类型
|
|
|
+ */
|
|
|
+
|
|
|
+/**
|
|
|
* @typedef {import('./constants').ValidationRule} ValidationRule
|
|
|
+ * @description 表单验证规则类型
|
|
|
+ */
|
|
|
+
|
|
|
+/**
|
|
|
* @typedef {import('./constants').OrderFormMixinData} OrderFormMixinData
|
|
|
+ * @description 订单表单混入数据类型
|
|
|
+ */
|
|
|
+
|
|
|
+/**
|
|
|
* @typedef {import('@/api/types/order').SalesOrderCreateForm} SalesOrderCreateForm
|
|
|
+ * @description 销售订单创建表单类型
|
|
|
+ */
|
|
|
+
|
|
|
+/**
|
|
|
* @typedef {import('@/api/types/order').SalesOrderItemCreateForm} SalesOrderItemCreateForm
|
|
|
+ * @description 销售订单明细创建表单类型
|
|
|
+ */
|
|
|
+
|
|
|
+/**
|
|
|
* @typedef {import('@/constants/order').ORDER_TYPES} OrderTypeValue
|
|
|
+ * @description 订单类型枚举值类型
|
|
|
+ */
|
|
|
+
|
|
|
+/**
|
|
|
* @typedef {import('@/constants/order').ORDER_STATUS} OrderStatusValue
|
|
|
+ * @description 订单状态枚举值类型
|
|
|
*/
|
|
|
|
|
|
/**
|
|
@@ -214,14 +268,25 @@ export default {
|
|
|
trigger: 'blur'
|
|
|
},
|
|
|
{
|
|
|
+ /**
|
|
|
+ * 手机号码格式验证器
|
|
|
+ * @description 验证手机号码格式是否正确,支持1开头的11位数字
|
|
|
+ * @param {Object} rule - 验证规则对象
|
|
|
+ * @param {string} value - 待验证的值
|
|
|
+ * @param {Function} callback - 验证回调函数
|
|
|
+ * @returns {void}
|
|
|
+ */
|
|
|
validator: (rule, value, callback) => {
|
|
|
- if (!value) {
|
|
|
+ if (!value || typeof value !== 'string') {
|
|
|
callback()
|
|
|
return
|
|
|
}
|
|
|
+
|
|
|
+ // 手机号码正则表达式:1开头,第二位为3-9,总共11位数字
|
|
|
const phoneRegex = /^1[3-9]\d{9}$/
|
|
|
- if (!phoneRegex.test(value)) {
|
|
|
- callback(new Error('请输入正确的手机号码'))
|
|
|
+
|
|
|
+ if (!phoneRegex.test(value.trim())) {
|
|
|
+ callback(new Error('请输入正确的手机号码格式(1开头的11位数字)'))
|
|
|
} else {
|
|
|
callback()
|
|
|
}
|
|
@@ -299,69 +364,94 @@ export default {
|
|
|
|
|
|
/**
|
|
|
* 初始化表单数据
|
|
|
- * @description 根据编辑模式初始化表单,编辑模式加载数据,新增模式重置表单
|
|
|
+ * @description 根据编辑模式初始化表单,编辑模式加载订单详情数据,新增模式重置表单为初始状态
|
|
|
* @returns {Promise<void>}
|
|
|
+ * @throws {Error} 当初始化过程中发生错误时抛出异常
|
|
|
* @public
|
|
|
*/
|
|
|
async initForm() {
|
|
|
try {
|
|
|
if (this.isEdit && this.orderId) {
|
|
|
+ // 编辑模式:加载现有订单数据
|
|
|
await this.loadOrderDetail(this.orderId)
|
|
|
} else {
|
|
|
- this.resetForm()
|
|
|
+ // 新增模式:重置表单为初始状态
|
|
|
+ await this.resetForm()
|
|
|
}
|
|
|
} catch (error) {
|
|
|
- this.$message.error('初始化表单失败,请刷新页面重试')
|
|
|
+ console.error('初始化表单失败:', error)
|
|
|
+ const errorMessage = error.message || '初始化表单失败,请刷新页面重试'
|
|
|
+ this.$message.error(errorMessage)
|
|
|
+ throw error
|
|
|
}
|
|
|
},
|
|
|
|
|
|
/**
|
|
|
* 重置表单数据
|
|
|
- * @description 将表单数据重置为初始状态,并清除所有验证错误信息
|
|
|
+ * @description 将表单数据重置为初始状态,清除所有验证错误信息,并重置物料明细列表
|
|
|
* @returns {Promise<void>}
|
|
|
+ * @throws {Error} 当重置过程中发生严重错误时抛出异常
|
|
|
* @public
|
|
|
*/
|
|
|
async resetForm() {
|
|
|
try {
|
|
|
// 重置表单数据为初始状态
|
|
|
this.formData = this.createInitialFormData()
|
|
|
+
|
|
|
+ // 重置物料明细列表(如果存在)
|
|
|
+ if (Array.isArray(this.materialDetails)) {
|
|
|
+ this.materialDetails = []
|
|
|
+ }
|
|
|
+
|
|
|
+ // 重置保存状态
|
|
|
+ this.saveLoading = false
|
|
|
|
|
|
// 等待DOM更新后清除表单验证
|
|
|
await this.$nextTick()
|
|
|
|
|
|
+ // 清除表单验证状态
|
|
|
if (this.$refs.orderForm && typeof this.$refs.orderForm.clearValidate === 'function') {
|
|
|
this.$refs.orderForm.clearValidate()
|
|
|
}
|
|
|
} catch (error) {
|
|
|
- // 重置表单时发生错误,静默处理
|
|
|
+ console.error('重置表单失败:', error)
|
|
|
+ // 重置表单时发生严重错误,抛出异常
|
|
|
+ throw new Error('重置表单失败,请刷新页面重试')
|
|
|
}
|
|
|
},
|
|
|
|
|
|
/**
|
|
|
* 加载订单详情数据
|
|
|
- * @description 根据订单ID从服务器获取订单详情并填充到表单中,同时加载物料明细数据
|
|
|
- * @param {string|number} orderId - 订单唯一标识
|
|
|
+ * @description 根据订单ID从服务器获取订单详情并填充到表单中,同时并行加载物料明细数据以提高性能
|
|
|
+ * @param {string|number} orderId - 订单唯一标识符
|
|
|
* @returns {Promise<void>}
|
|
|
- * @throws {Error} 当API调用失败或数据格式错误时抛出异常
|
|
|
+ * @throws {Error} 当订单ID无效、API调用失败或数据格式错误时抛出异常
|
|
|
* @public
|
|
|
*/
|
|
|
async loadOrderDetail(orderId) {
|
|
|
- if (!orderId) {
|
|
|
- throw new Error('订单ID不能为空')
|
|
|
+ // 参数验证
|
|
|
+ if (!orderId || (typeof orderId !== 'string' && typeof orderId !== 'number')) {
|
|
|
+ throw new Error('订单ID不能为空且必须是有效的字符串或数字')
|
|
|
}
|
|
|
|
|
|
try {
|
|
|
- // 并行加载订单详情和物料明细数据
|
|
|
+ // 并行加载订单详情和物料明细数据以提高性能
|
|
|
/**
|
|
|
- * @type {[AxiosResponse<ApiResponse<OrderItem>>, MaterialDetailRecord[]]}
|
|
|
+ * @type {[import('axios').AxiosResponse<ApiResponse<import('@/api/types/order').OrderRecord>>, MaterialDetailRecord[]]}
|
|
|
*/
|
|
|
const [orderResponse, materialResponse] = await Promise.all([
|
|
|
getDetail(orderId),
|
|
|
this.loadMaterialDetails(orderId)
|
|
|
])
|
|
|
|
|
|
- if (!orderResponse || !orderResponse.data || !orderResponse.data.data) {
|
|
|
- throw new Error('服务器返回数据格式错误')
|
|
|
+ // 验证订单详情响应数据
|
|
|
+ if (!orderResponse?.data?.success) {
|
|
|
+ const errorMsg = orderResponse?.data?.msg || '获取订单详情失败'
|
|
|
+ throw new Error(errorMsg)
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!orderResponse.data.data) {
|
|
|
+ throw new Error('订单数据不存在或已被删除')
|
|
|
}
|
|
|
|
|
|
const orderData = orderResponse.data.data
|
|
@@ -369,67 +459,107 @@ export default {
|
|
|
// 安全地映射订单数据到表单,确保数据类型正确
|
|
|
this.formData = this.mapOrderDataToForm(orderData)
|
|
|
|
|
|
- // 设置物料明细数据
|
|
|
- this.materialDetails = materialResponse
|
|
|
+ // 设置物料明细数据(确保是数组类型)
|
|
|
+ this.materialDetails = Array.isArray(materialResponse) ? materialResponse : []
|
|
|
+
|
|
|
+ console.log(`成功加载订单详情,订单编码: ${orderData.orderCode || orderId}`)
|
|
|
|
|
|
} catch (error) {
|
|
|
- const errorMessage = error.message || '加载订单详情失败'
|
|
|
- this.$message.error(`${errorMessage},请重试`)
|
|
|
+ console.error('加载订单详情失败:', error)
|
|
|
+ const errorMessage = error.message || '加载订单详情失败,请检查网络连接后重试'
|
|
|
+ this.$message.error(errorMessage)
|
|
|
throw error
|
|
|
}
|
|
|
},
|
|
|
|
|
|
/**
|
|
|
- * 加载订单物料明细数据
|
|
|
- * @description 根据订单ID从服务器获取物料明细列表,远程数据不可删除
|
|
|
- * @param {string|number} orderId - 订单唯一标识符
|
|
|
- * @returns {Promise<MaterialDetailRecord[]>} 物料明细记录列表,失败时返回空数组
|
|
|
- * @private
|
|
|
- */
|
|
|
- /**
|
|
|
* 加载物料明细数据
|
|
|
- * @description 根据订单ID获取物料明细列表,并为每个物料添加数据来源标识
|
|
|
- * @param {string|number} orderId - 订单ID
|
|
|
- * @returns {Promise<MaterialDetailRecord[]>} 处理后的物料明细数组
|
|
|
+ * @description 根据订单ID获取物料明细列表,并对数值字段进行格式化和验证,确保数据精确性和类型安全
|
|
|
+ * @param {string|number} orderId - 订单唯一标识符
|
|
|
+ * @returns {Promise<MaterialDetailRecord[]>} 格式化后的物料明细数组,数值字段已进行精度处理
|
|
|
+ * @throws {Error} 当订单ID无效或API调用失败时抛出异常
|
|
|
* @private
|
|
|
*/
|
|
|
async loadMaterialDetails(orderId) {
|
|
|
+ // 参数验证
|
|
|
+ if (!orderId || (typeof orderId !== 'string' && typeof orderId !== 'number')) {
|
|
|
+ console.error('loadMaterialDetails: 订单ID无效', orderId)
|
|
|
+ return []
|
|
|
+ }
|
|
|
+
|
|
|
try {
|
|
|
/**
|
|
|
- * @type {AxiosResponse<ApiResponse<PageResult<OrderItemRecord>>>}
|
|
|
+ * @type {import('axios').AxiosResponse<ApiResponse<PageResult<OrderItemRecord>>>}
|
|
|
*/
|
|
|
const response = await getOrderItemList(1, 1000, { orderId })
|
|
|
|
|
|
- if (!response || !response.data || !response.data.data) {
|
|
|
- throw new Error('物料明细数据格式错误')
|
|
|
+ // 验证响应数据结构
|
|
|
+ if (!response?.data?.success) {
|
|
|
+ const errorMsg = response?.data?.msg || '获取物料明细失败'
|
|
|
+ throw new Error(errorMsg)
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!response.data.data) {
|
|
|
+ console.warn('物料明细数据为空')
|
|
|
+ return []
|
|
|
}
|
|
|
|
|
|
- const materialDetails = response.data.data.records || []
|
|
|
+ const materialDetails = response.data.data.records
|
|
|
+
|
|
|
+ // 确保返回的是数组类型
|
|
|
+ if (!Array.isArray(materialDetails)) {
|
|
|
+ console.warn('物料明细数据格式异常,返回空数组')
|
|
|
+ return []
|
|
|
+ }
|
|
|
|
|
|
// 为远程加载的物料数据添加数据来源标识并格式化数字字段
|
|
|
- return materialDetails.map(material => {
|
|
|
- // 验证和格式化数字字段
|
|
|
- const orderQuantityValidation = validateNumber(material.orderQuantity)
|
|
|
- const unitPriceValidation = validateNumber(material.unitPrice)
|
|
|
- const taxRateValidation = validateNumber(material.taxRate)
|
|
|
- const taxAmountValidation = validateNumber(material.taxAmount)
|
|
|
- const totalAmountValidation = validateNumber(material.totalAmount)
|
|
|
-
|
|
|
- return {
|
|
|
- ...material,
|
|
|
- dataSource: MaterialDetailDataSource.REMOTE,
|
|
|
- isDeletable: false, // 远程加载的数据不可删除
|
|
|
- // 格式化数字字段
|
|
|
- orderQuantity: orderQuantityValidation.isValid ? Math.round(orderQuantityValidation.value) : 0,
|
|
|
- unitPrice: unitPriceValidation.isValid ? preciseRound(unitPriceValidation.value, 2) : 0,
|
|
|
- taxRate: taxRateValidation.isValid ? preciseRound(taxRateValidation.value, 4) : 0,
|
|
|
- taxAmount: taxAmountValidation.isValid ? preciseRound(taxAmountValidation.value, 2) : 0,
|
|
|
- totalAmount: totalAmountValidation.isValid ? preciseRound(totalAmountValidation.value, 2) : 0
|
|
|
+ return materialDetails.map((material, index) => {
|
|
|
+ try {
|
|
|
+ // 验证和格式化数字字段,确保类型安全
|
|
|
+ const orderQuantityValidation = validateNumber(material.orderQuantity)
|
|
|
+ const unitPriceValidation = validateNumber(material.unitPrice)
|
|
|
+ const taxRateValidation = validateNumber(material.taxRate)
|
|
|
+ const taxAmountValidation = validateNumber(material.taxAmount)
|
|
|
+ const totalAmountValidation = validateNumber(material.totalAmount)
|
|
|
+
|
|
|
+ return {
|
|
|
+ ...material,
|
|
|
+ dataSource: MaterialDetailDataSource.REMOTE,
|
|
|
+ isDeletable: false, // 远程加载的数据不可删除
|
|
|
+ // 格式化数字字段,确保精度和类型正确
|
|
|
+ orderQuantity: orderQuantityValidation.isValid ? Math.round(orderQuantityValidation.value) : 0,
|
|
|
+ unitPrice: unitPriceValidation.isValid ? preciseRound(unitPriceValidation.value, 2) : 0,
|
|
|
+ taxRate: taxRateValidation.isValid ? preciseRound(taxRateValidation.value, 4) : 0,
|
|
|
+ taxAmount: taxAmountValidation.isValid ? preciseRound(taxAmountValidation.value, 2) : 0,
|
|
|
+ totalAmount: totalAmountValidation.isValid ? preciseRound(totalAmountValidation.value, 2) : 0,
|
|
|
+ // 确保必要的字段存在
|
|
|
+ itemCode: material.itemCode || '',
|
|
|
+ itemName: material.itemName || '',
|
|
|
+ specs: material.specs || material.specification || '',
|
|
|
+ unit: material.unit || ''
|
|
|
+ }
|
|
|
+ } catch (itemError) {
|
|
|
+ console.error(`格式化物料明细第${index + 1}项失败:`, itemError)
|
|
|
+ // 返回默认的物料明细项,确保数据完整性
|
|
|
+ return {
|
|
|
+ ...material,
|
|
|
+ dataSource: MaterialDetailDataSource.REMOTE,
|
|
|
+ isDeletable: false,
|
|
|
+ orderQuantity: 0,
|
|
|
+ unitPrice: 0,
|
|
|
+ taxRate: 0,
|
|
|
+ taxAmount: 0,
|
|
|
+ totalAmount: 0,
|
|
|
+ itemCode: material.itemCode || '',
|
|
|
+ itemName: material.itemName || '',
|
|
|
+ specs: material.specs || material.specification || '',
|
|
|
+ unit: material.unit || ''
|
|
|
+ }
|
|
|
}
|
|
|
})
|
|
|
} catch (error) {
|
|
|
- this.$message.warning('加载物料明细失败,请稍后重试')
|
|
|
console.error('加载物料明细失败:', error)
|
|
|
+ this.$message.warning('加载物料明细失败,请稍后重试')
|
|
|
return []
|
|
|
}
|
|
|
},
|
|
@@ -452,6 +582,7 @@ export default {
|
|
|
orderCode: String(orderData.orderCode || ''),
|
|
|
orgCode: String(orderData.orgCode || ''),
|
|
|
orgName: String(orderData.orgName || ''),
|
|
|
+ customerId: Number(orderData.customerId) || null,
|
|
|
customerCode: String(orderData.customerCode || ''),
|
|
|
customerName: String(orderData.customerName || ''),
|
|
|
orderType: Number(orderData.orderType) || ORDER_TYPES.NORMAL,
|