/** * @fileoverview 物料明细表格组件 * @description 基于AvueJS的物料明细数据展示和操作组件,支持分页、搜索、删除等功能 * @typedef {import('./types').MaterialDetailTableComponent} MaterialDetailTableComponent */ import { getMaterialDetailOption, DEFAULT_PAGINATION_CONFIG } from './material-detail-option' import { MaterialDetailStatus, getOrderItemStatusLabel as getMaterialDetailStatusLabel, getOrderItemStatusTagType as getMaterialDetailStatusTagType, getOrderItemStatusColor as getMaterialDetailStatusColor, MaterialDetailDataSource } from '@/constants/order' import { MATERIAL_DETAIL_EVENTS, DIALOG_EVENTS } from './events' import { getMaterialFullList } from '@/api/order/sales-order' import { formatAmount, formatFloatNumber, formatIntegerNumber, formatUnitPrice, formatTaxRate, preciseMultiply, preciseDivide, preciseRound, validateNumber, 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 /** * 组件数据类型定义 * @typedef {Object} MaterialDetailTableData * @property {Partial} formData - 表单数据 * @property {PageOption} page - 分页配置 * @property {boolean} importDialogVisible - 导入弹窗显示状态 */ // 状态处理已统一使用明细管理中的工具函数,无需本地映射常量 /** * 物料明细表格组件 * @description 用于展示和编辑订单的物料明细信息,支持物料导入和实时编辑功能 * 当物料数量、单价、税率等字段变更时,自动计算总金额和税额,并触发父组件重新计算订单总计 * @emits {MaterialDetailRecord[]} material-import - 物料导入事件 * @emits {Object} material-update - 物料明细更新事件,包含更新的行数据和索引 * @emits {Object} material-delete - 物料明细删除事件 * @emits {void} refresh - 刷新事件 */ export default { name: 'MaterialDetailTable', components: {}, /** * 组件属性定义 * @description 定义组件接收的外部属性 */ props: { /** * 是否为编辑模式 - 控制表格是否可编辑 * @type {boolean} */ editMode: { type: Boolean, default: false }, /** * 订单ID - 关联的订单唯一标识符 * @type {string|number|null} */ orderId: { type: [String, Number], default: null, validator: (value) => value === null || value === undefined || (typeof value === 'string' && value.length > 0) || (typeof value === 'number' && value > 0) }, /** * 物料明细列表 - 要展示的物料明细数据 * @type {MaterialDetailRecord[]} 物料明细数据数组,每个元素包含物料的详细信息 */ materialDetails: { type: Array, required: true, default: () => [], validator: (value) => Array.isArray(value) && value.every(item => typeof item === 'object' && item !== null && typeof item.id === 'string' && typeof item.itemCode === 'string' ) } }, /** * 组件数据 * @returns {MaterialDetailTableData} 组件响应式数据对象 * @this {MaterialDetailTableComponent} */ data() { return { /** * 表单数据 - 当前编辑行的数据 * @type {Partial} 物料明细表单数据对象 */ formData: {}, /** * 分页配置 - AvueJS表格分页相关配置 * @type {PaginationConfig} 包含currentPage、pageSize、total等属性的分页配置对象 */ page: { currentPage: 1, pageSize: DEFAULT_PAGINATION_CONFIG.pageSize, total: 0 }, /** * 选中的物料ID - 当前在下拉框中选中的物料ID * @type {string|null} */ selectedMaterialId: null, /** * 物料选项列表 - 远程搜索返回的物料选项 * @type {ItemRecord[]} */ materialOptions: [], /** * 物料搜索加载状态 - 控制远程搜索时的加载状态 * @type {boolean} */ materialLoading: false, /** * 搜索防抖定时器 - 用于防抖处理远程搜索 * @type {number|null} */ searchTimer: null, /** * 事件常量 */ DIALOG_EVENTS, /** * 正在编辑的行数据 - 用于记录编辑前的状态 * @type {MaterialDetailRecord|null} */ editingRow: null, /** * 正在编辑的属性名 - 用于记录当前编辑的字段 * @type {string|null} */ editingProp: null } }, /** * 计算属性 * @this {MaterialDetailTableComponent} */ computed: { /** * 表格配置选项 - 获取AvueJS表格的配置对象 * @returns {AvueCrudOption} AvueJS表格配置对象,根据编辑模式配置 */ tableOption() { return getMaterialDetailOption(this.editMode) }, /** * 当前页显示的数据 - 根据分页配置计算当前页应显示的数据 * @returns {MaterialDetailRecord[]} 当前页的物料明细数据 */ currentPageData() { const { currentPage, pageSize } = this.page const startIndex = (currentPage - 1) * pageSize const endIndex = startIndex + pageSize return this.materialDetails.slice(startIndex, endIndex) } }, /** * 监听器 * @this {MaterialDetailTableComponent} */ watch: { /** * 监听物料明细变化 * @param {MaterialDetailRecord[]} newVal - 新的物料明细列表 * @returns {void} */ materialDetails: { handler(newVal) { this.page.total = newVal.length }, immediate: true } }, /** * 组件方法 * @this {MaterialDetailTableComponent} */ methods: { /** * 验证整数输入 * @param {string} value - 输入值 * @param {Object} row - 当前行数据 * @param {string} field - 字段名 * @param {number} min - 最小值 * @param {number} max - 最大值 */ validateIntegerInput(value, row, field, min = 0, max = 999999) { // 允许空值和部分输入(如正在输入的数字) if (value === '' || value === '-') { return } // 移除所有非数字字符(除了负号) let cleanValue = value.replace(/[^-\d]/g, '') // 确保负号只能在开头 if (cleanValue.indexOf('-') > 0) { cleanValue = cleanValue.replace(/-/g, '') } // 如果有值,转换为整数 if (cleanValue !== '' && cleanValue !== '-') { const numValue = parseInt(cleanValue, 10) // 检查范围 if (numValue < min) { row[field] = min } else if (numValue > max) { row[field] = max } else { row[field] = numValue } } }, /** * 验证浮点数输入(输入时验证) * @param {string} value - 输入值 * @param {Object} row - 当前行数据 * @param {string} field - 字段名 * @param {number} min - 最小值 * @param {number} max - 最大值 */ validateFloatInput(value, row, field, min = 0, max = 999999.99) { // 允许空值和部分输入(包括单独的小数点、负号等) if (value === '' || value === '-' || value === '.' || value === '-.') { row[field] = value return } // 移除无效字符,只保留数字、小数点和负号 let cleanValue = value.replace(/[^-\d.]/g, '') // 确保负号只能在开头 if (cleanValue.indexOf('-') > 0) { cleanValue = cleanValue.replace(/-/g, '') } // 确保只有一个小数点 const parts = cleanValue.split('.') if (parts.length > 2) { cleanValue = parts[0] + '.' + parts.slice(1).join('') } // 限制小数位数为2位(但允许继续输入) if (parts.length === 2 && parts[1].length > 2) { cleanValue = parts[0] + '.' + parts[1].substring(0, 2) } // 更新字段值,但不进行范围检查(留到blur时处理) row[field] = cleanValue }, /** * 验证并格式化浮点数(失焦时验证) * @param {Object} row - 当前行数据 * @param {string} field - 字段名 * @param {number} min - 最小值 * @param {number} max - 最大值 * @param {number} precision - 小数位数,默认2位 */ validateAndFormatFloatOnBlur(row, field, min = 0, max = 999999.99, precision = 2) { const value = row[field] // 如果是空值或无效输入,设置为最小值 if (value === '' || value === '.' || value === '-' || value === '-.' || isNaN(parseFloat(value))) { row[field] = min return } const numValue = parseFloat(value) // 范围检查 if (numValue < min) { row[field] = min } else if (numValue > max) { row[field] = max } else { // 格式化为指定小数位数 const multiplier = Math.pow(10, precision) const roundedValue = Math.round(numValue * multiplier) / multiplier row[field] = roundedValue } }, /** * 判断行是否可编辑 * @description 根据数据来源判断物料明细行是否允许编辑,远程数据(订单ID获取)不可编辑 * @param {MaterialDetailRecord} row - 物料明细行数据 * @returns {boolean} 是否可编辑,true表示可编辑,false表示不可编辑 */ isRowEditable(row) { // 如果没有数据来源信息,默认可编辑 if (!row || !row.dataSource) { return true } // 只有导入的物料可以编辑,远程数据(订单ID获取)不可编辑 return row.dataSource === MaterialDetailDataSource.IMPORTED }, /** * 远程搜索物料 * @description 根据关键词远程搜索物料数据,支持防抖处理 * @param {string} query - 搜索关键词 * @returns {void} */ remoteSearchMaterial(query) { // 清除之前的定时器 if (this.searchTimer) { clearTimeout(this.searchTimer) } // 如果查询为空,清空选项 if (!query) { this.materialOptions = [] return } // 设置防抖定时器 this.searchTimer = setTimeout(async () => { await this.searchMaterials(query) }, 300) }, /** * 搜索物料数据 * @description 调用API搜索物料数据 * @param {string} keyword - 搜索关键词 * @returns {Promise} * @throws {Error} 当API调用失败时抛出异常 */ async searchMaterials(keyword) { try { this.materialLoading = true const response = await getMaterialFullList({ itemName: keyword }) if (response?.data?.success && response.data.data) { // 转换API返回的字段名称为组件所需的格式 // getMaterialFullList返回的是SalesOrderItemListRecord[]数组 this.materialOptions = response.data.data.map(item => ({ id: item.id, itemId: item.Item_ID, itemCode: item.Item_Code, itemName: item.Item_Name, specs: item.Item_PECS || '', unit: item.InventoryInfo_Name, mainItemCategoryName: item.MainItemCategory_Name, mainItemCategoryId: item.MainItemCategory_ID, mainItemCategoryCode: item.MainItemCategory_Code, unitPrice: item.Item_Price || '0', description: item.Item_Description || '', warehouseId: item.Warehouse_ID, warehouseCode: item.Warehouse_Code, warehouseName: item.Warehouse_Name, orgId: item.ORG_ID, orgCode: item.ORG_CODE, orgName: item.ORG_NAME, // 保留原始数据以备后用 _raw: item })) } else { this.materialOptions = [] const errorMsg = response?.data?.msg || '搜索物料失败' this.$message.warning(errorMsg) } } catch (error) { this.materialOptions = [] this.$message.error('网络错误,搜索物料失败') } finally { this.materialLoading = false } }, /** * 处理导入选中物料 * @description 将选中的物料导入到物料明细表中 * @returns {void} */ handleImportSelectedMaterial() { if (!this.selectedMaterialId) { this.$message.warning('请先选择要导入的物料') return } // 查找选中的物料数据 const selectedMaterial = this.materialOptions.find(item => item.id === this.selectedMaterialId) if (!selectedMaterial) { this.$message.warning('未找到选中的物料数据') return } // 检查是否已存在相同物料 const existingMaterial = this.materialDetails.find(item => item.itemCode === selectedMaterial.itemCode) if (existingMaterial) { this.$message.warning(`物料 ${selectedMaterial.itemName} 已存在,请勿重复导入`) return } // 构造物料明细数据 let materialDetail = this.prepareMaterialDetailData(selectedMaterial) // 导入时自动计算金额 materialDetail = this.calculateAmounts(materialDetail) // 触发导入事件 this.$emit(MATERIAL_DETAIL_EVENTS.MATERIAL_IMPORT, [materialDetail]) this.$emit(MATERIAL_DETAIL_EVENTS.REFRESH) // 清空选择 this.selectedMaterialId = null this.materialOptions = [] }, /** * 准备物料明细数据 * @description 将选中的物料数据转换为物料明细表所需的格式 * @param {import('@/api/types/order').SalesOrderItemListRecord} material - 物料数据(来自getMaterialFullList API) * @returns {MaterialDetailRecord} 格式化后的物料明细数据 * @private */ prepareMaterialDetailData(material) { return { itemId: material.itemId, itemCode: material.itemCode, itemName: material.itemName, specs: material.specs || '', specification: material.specs || '', unit: material.unit || '', mainItemCategoryName: material.mainItemCategoryName || material.MainItemCategory_Name || '', mainItemCategoryId: material.mainItemCategoryId || material.MainItemCategory_ID, mainItemCategoryCode: material.mainItemCategoryCode || material.MainItemCategory_Code || '', description: material.description || '', warehouseId: material.warehouseId, warehouseCode: material.warehouseCode, warehouseName: material.warehouseName, orgId: material.orgId, orgCode: material.orgCode, orgName: material.orgName, unitPrice: material.unitPrice || 0, orderQuantity: 1, confirmQuantity: 1, availableQuantity: 0, taxRate: 0, taxAmount: 0, totalAmount: 0, itemStatus: MaterialDetailStatus.UNCONFIRMED, dataSource: MaterialDetailDataSource.IMPORTED, isDeletable: true, remark: '' } }, /** * 生成唯一ID * @description 生成物料明细的唯一标识符 * @returns {string} 唯一ID * @private */ generateUniqueId() { return 'material_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9) }, /** * 处理表格刷新事件 * @description 触发刷新事件,通知父组件重新加载数据 * @returns {void} * @emits refresh */ handleRefresh() { this.$emit(MATERIAL_DETAIL_EVENTS.REFRESH) }, /** * 处理分页页码变化事件 * @description 当用户切换页码时触发,更新当前页码 * @param {number} currentPage - 新的页码,从1开始 * @returns {void} */ handleCurrentChange(currentPage) { this.page.currentPage = currentPage }, /** * 处理分页大小变化事件 * @description 当用户改变每页显示条数时触发,重置到第一页 * @param {number} pageSize - 新的每页显示条数 * @returns {void} */ handleSizeChange(pageSize) { this.page.pageSize = pageSize this.page.currentPage = 1 }, /** * 格式化浮点数显示 * @description 格式化浮点数为4位小数的字符串 * @param {number|string|null|undefined} value - 数值 * @returns {string} 格式化后的字符串 */ formatFloatNumber(value) { return formatFloatNumber(value) }, /** * 格式化金额显示 * @description 格式化金额为带货币符号的字符串 * @param {number|string|null|undefined} amount - 金额数值 * @param {boolean} withSymbol - 是否显示货币符号 * @returns {string} 格式化后的金额字符串 */ formatAmount(amount, withSymbol = true) { return formatAmount(amount, withSymbol) }, formatUnitPrice, formatTaxRate, /** * 格式化整数显示 * @description 格式化整数为字符串 * @param {number|string|null|undefined} value - 整数数值 * @returns {string} 格式化后的整数字符串 */ formatIntegerNumber(value) { return formatIntegerNumber(value) }, /** * 获取状态标签类型 * @description 根据物料明细状态值返回对应的Element UI标签类型 * @param {typeof MaterialDetailStatus[keyof typeof MaterialDetailStatus]} itemStatus - 物料明细状态值 * @returns {string} Element UI标签类型 * @example * getStatusTagType(0) // 返回 'warning' * getStatusTagType(1) // 返回 'success' */ getStatusTagType(itemStatus) { return getMaterialDetailStatusTagType(itemStatus) }, /** * 获取状态文本 * @description 根据物料明细状态值返回对应的中文描述 * @param {number} itemStatus - 物料明细状态值 * @returns {string} 状态的中文描述文本 * @example * getStatusText(0) // 返回 '待确认' * getStatusText(1) // 返回 '已确认' */ getStatusText(itemStatus) { return getMaterialDetailStatusLabel(itemStatus) }, /** * 处理删除物料操作 * @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_DETAIL_EVENTS.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) }, /** * 处理行更新事件 * @description 当用户编辑表格行数据时触发,执行自动计算逻辑 * @param {MaterialDetailRecord} row - 更新后的行数据 * @param {number} index - 行索引 * @param {boolean} done - 完成回调函数 * @returns {void} * @emits material-update */ async handleRowUpdate(row, index, done) { try { // 执行自动计算 const calculatedRow = this.calculateAmounts(row) // 触发更新事件,传递计算后的数据 this.$emit(MATERIAL_DETAIL_EVENTS.MATERIAL_UPDATE, { row: calculatedRow, index }) // 完成编辑 done(calculatedRow) this.$message.success('物料明细更新成功') } catch (error) { this.$message.error('更新失败:' + error.message) done(false) } }, /** * 处理订单数量失焦事件 * @description 当订单数量输入框失焦时,触发数量变更处理 * @param {MaterialDetailRecord} row - 行数据 * @param {number} index - 行索引 * @returns {void} */ handleQuantityBlur(row, index) { // 如果 index 无效,尝试通过 row 数据找到正确的索引 const actualIndex = this.findRowIndex(row, index) this.handleQuantityChange(row, actualIndex) }, /** * 处理订单数量变更 * @description 当订单数量发生变化时,自动计算总金额和税额,并触发父组件重新计算订单总计 * @param {MaterialDetailRecord} row - 行数据 * @param {number} index - 行索引 * @returns {void} */ handleQuantityChange(row, index) { const calculatedRow = this.calculateAmounts(row) Object.assign(row, calculatedRow) this.$emit(MATERIAL_DETAIL_EVENTS.MATERIAL_UPDATE, { row, index }) }, /** * 处理税率变更 * @description 当税率发生变化时,重新计算税额,并触发父组件重新计算订单总计 * @param {MaterialDetailRecord} row - 行数据 * @param {number} index - 行索引 * @returns {void} */ handleTaxRateChange(row, index) { this.calculateTaxAmount(row) this.$emit('material-update', { row, index }) }, /** * 处理单价失焦事件 * @description 当单价输入框失焦时,先格式化数值,再计算总金额和税额,并触发父组件重新计算订单总计 * @param {MaterialDetailRecord} row - 行数据 * @param {number} index - 行索引 * @returns {void} */ handleUnitPriceBlur(row, index) { // 先格式化数值 this.validateAndFormatFloatOnBlur(row, 'unitPrice') // 如果 index 无效,尝试通过 row 数据找到正确的索引 const actualIndex = this.findRowIndex(row, index) // 再处理单价变更 this.handleUnitPriceChange(row, actualIndex) }, /** * 处理单价变更 * @description 当单价发生变化时,自动计算总金额和税额,并触发父组件重新计算订单总计 * @param {MaterialDetailRecord} row - 行数据 * @param {number} index - 行索引 * @returns {void} */ handleUnitPriceChange(row, index) { const calculatedRow = this.calculateAmounts(row) Object.assign(row, calculatedRow) this.$emit('material-update', { row, index }) }, /** * 处理税额变更 * @description 当税额手动修改时,反推税率,并触发父组件重新计算订单总计 * @param {MaterialDetailRecord} row - 行数据 * @param {number} index - 行索引 * @returns {void} */ handleTaxAmountChange(row, index) { // 当税额手动修改时,反推税率 if (row.totalAmount && row.totalAmount > 0) { row.taxRate = ((row.taxAmount || 0) / row.totalAmount * 100).toFixed(2) } this.$emit('material-update', { row, index }) }, /** * 处理总金额变更 * @description 当总金额手动修改时,重新计算税额,并触发父组件重新计算订单总计 * @param {MaterialDetailRecord} row - 行数据 * @param {number} index - 行索引 * @returns {void} */ handleTotalAmountChange(row, index) { // 当总金额手动修改时,重新计算税额 this.calculateTaxAmount(row) this.$emit('material-update', { row, index }) }, /** * 处理单元格编辑开始事件 * @description 当用户开始编辑单元格时触发 * @param {MaterialDetailRecord} row - 编辑的行数据 * @param {string} prop - 编辑的属性名 * @param {*} value - 当前值 * @returns {void} */ handleCellEditStart(row, prop, value) { // 记录编辑前的值,用于计算变化 this.editingRow = { ...row } this.editingProp = prop }, /** * 处理单元格编辑结束事件 * @description 当用户结束编辑单元格时触发,执行实时计算 * @param {MaterialDetailRecord} row - 编辑后的行数据 * @param {string} prop - 编辑的属性名 * @param {*} value - 新值 * @returns {void} */ handleCellEditEnd(row, prop, value) { // 如果编辑的是影响计算的字段,执行自动计算 if (['orderQuantity', 'unitPrice', 'taxRate'].includes(prop)) { const calculatedRow = this.calculateAmounts(row) // 更新行数据 Object.assign(row, calculatedRow) // 触发更新事件 this.$emit(MATERIAL_DETAIL_EVENTS.MATERIAL_UPDATE, { row: calculatedRow, index: this.getCurrentRowIndex(row) }) } }, /** * 自动计算金额 * @description 根据订单数量、单价和税率自动计算总金额和税额,使用精确计算避免浮点数精度问题 * @param {MaterialDetailRecord} row - 物料明细记录 * @returns {MaterialDetailRecord} 计算后的物料明细记录 */ calculateAmounts(row) { const calculatedRow = { ...row } // 验证并获取数值 const quantityValidation = validateNumber(calculatedRow.orderQuantity) const priceValidation = validateNumber(calculatedRow.unitPrice) const rateValidation = validateNumber(calculatedRow.taxRate) const orderQuantity = quantityValidation.isValid ? Math.round(quantityValidation.value) : 0 const unitPrice = priceValidation.isValid ? priceValidation.value : 0 const taxRate = rateValidation.isValid ? rateValidation.value : 0 // 使用精确计算:订单数量 * 单价 const totalAmount = preciseMultiply(orderQuantity, unitPrice) calculatedRow.totalAmount = preciseRound(totalAmount, 2) // 使用精确计算:总金额 * 税率 / 100 const taxAmount = preciseMultiply(totalAmount, preciseDivide(taxRate, 100)) calculatedRow.taxAmount = preciseRound(taxAmount, 2) return calculatedRow }, /** * 计算税额 * @description 根据总金额和税率计算税额,使用精确计算避免浮点数精度问题 * @param {MaterialDetailRecord} row - 物料明细记录 * @returns {void} */ calculateTaxAmount(row) { const amountValidation = validateNumber(row.totalAmount) const rateValidation = validateNumber(row.taxRate) if (amountValidation.isValid && rateValidation.isValid) { const totalAmount = amountValidation.value const taxRate = rateValidation.value // 使用精确计算:总金额 * 税率 / 100 const taxAmount = preciseMultiply(totalAmount, preciseDivide(taxRate, 100)) row.taxAmount = preciseRound(taxAmount, 2) } else { row.taxAmount = 0 } }, /** * 获取当前行索引 * @description 根据行数据获取在当前页中的索引 * @param {MaterialDetailRecord} row - 行数据 * @returns {number} 行索引 */ getCurrentRowIndex(row) { return this.currentPageData.findIndex(item => item.id === row.id) }, /** * 查找行索引 * @description 根据行数据查找在物料明细列表中的正确索引 * @param {MaterialDetailRecord} row - 行数据 * @param {number} providedIndex - 提供的索引 * @returns {number} 实际索引 */ findRowIndex(row, providedIndex) { // 如果提供的索引有效,直接使用 if (providedIndex >= 0 && providedIndex < this.materialDetails.length) { return providedIndex } // 否则通过行数据查找索引 const index = this.materialDetails.findIndex(item => { // 优先使用 id 进行匹配 if (row.id && item.id) { return row.id === item.id } // 如果没有 id,使用物料编码进行匹配 if (row.itemCode && item.itemCode) { return row.itemCode === item.itemCode } // 最后使用对象引用进行匹配 return row === item }) return index >= 0 ? index : -1 } }, /** * 组件销毁前的清理工作 * @description 清除定时器,避免内存泄漏 */ beforeDestroy() { if (this.searchTimer) { clearTimeout(this.searchTimer) this.searchTimer = null } } }