Vue如何优雅地复制一行带附件的表格数据
作者:网罗开发
前言
在做 Vue 项目的时候,大家是不是经常遇到这样的需求:
“点一下复制按钮,把这行表格数据复制一份放在下方,除了附件字段之外其他都保留。”
听起来简单?但一不小心就会踩到“对象引用”的大坑 —— 你以为复制的是一份新数据,结果却把原来的数据也一块改了,尤其是像 poFile
这样的附件字段,本来是保留原数据,现在全被你“顺手”置空了。
这篇文章就来聊聊:如何优雅地复制一行带附件的数据,不破坏原始数据结构?
需求背景与常见问题
我们有一个表格,每一行代表一个订单物料。每一行可能有个附件字段 poFile
,类型是数组,像这样:
{ id: 'row-1', name: '电容', amount: 10, poFile: [{ name: '报价单.pdf', url: '...' }] }
现在我们要做一个“复制”功能:
- 把这行数据原样复制;
- 附件字段
poFile
需要置空; - 原始数据不受影响。
于是很多同学很自然地写了这样一段逻辑:
const tableobj = dataList.value[index] tableobj.poFile = [] // 清空附件 dataList.value.splice(index + 1, 0, cloneDeep(tableobj))
表面上看没问题,但运行完你会发现:原始那一行的 poFile
也被清空了!
为什么?因为你直接修改了原始对象的引用。
背后的 JS 原理小科普:对象是“引用类型”
在 JavaScript 里,对象是引用类型。当你执行:
const tableobj = dataList.value[index]
其实 tableobj
和 dataList.value[index]
指向的是同一个对象地址。
所以当你:
tableobj.poFile = []
其实也就等于把 dataList.value[index].poFile
清空了。
正确做法:用 cloneDeep 深拷贝新对象
解决这个问题的方法很简单:
修改前先 cloneDeep
一份副本,所有的“清空字段”都操作副本,原始对象不动。
我们来重写一下逻辑,分为两种场景处理:普通复制 和 合并单元格模式复制。
Demo 代码模块
import cloneDeep from 'lodash/cloneDeep' const copyData = (record, index) => { let tableobj if (props.isMergeCells) { const curIndex = dataList.value.findIndex(items => { return items.some(item => item.groupId === record.groupId) }) const groupId = generateUUID() // cloneDeep 是关键 tableobj = cloneDeep(dataList.value[curIndex]).map(item => { item.id = generateUUID() item.groupId = groupId item.overLimitApproval = '' item.poFile = [] // 附件置空 return item }) dataList.value.splice(curIndex + 2, 0, ...tableobj, initTotalRow(groupId)) } else { const originalRow = dataList.value[index] if (originalRow) { const copyRow = cloneDeep(originalRow) if (copyRow?.id) { delete copyRow.id copyRow.groupId = generateUUID() copyRow.overLimitApproval = '' copyRow.poFile = [] // 清空附件 dataList.value.splice(index, 0, copyRow) } } } emit('edit', { key: 'amount' }) }
拆解讲讲:为啥这段代码能解决问题
cloneDeep 是灵魂
lodash/cloneDeep
是做深拷贝的神器,它可以递归复制对象中的每一层结构,确保你拿到的是一个“全新”的副本。
const copyRow = cloneDeep(originalRow)
这一行就保证了:你对 copyRow
的任何改动,都不会影响原始 originalRow
。
清空 poFile 的方式建议用[]而不是''
附件字段通常是数组,代表可能有多个上传文件。如果你把它清空成 ''
:
copyRow.poFile = ''
虽然能通过某些后端校验,但可能会导致前端的 v-model
或 el-upload
报错。
建议统一使用:
copyRow.poFile = []
更符合结构预期,也方便后续前端判断文件上传是否为空。
更进一步:提取清理逻辑为复用函数
如果将来要清空的字段不止 poFile
和 overLimitApproval
,可以提取成一个统一的清理函数:
function resetCopyFields(row) { row.poFile = [] row.overLimitApproval = '' // 其他字段... return row }
调用时只需要:
resetCopyFields(copyRow)
让逻辑更清晰,也更方便维护。
实际场景举例:审批单据、销售订单、费用报销
这种“复制数据行但清空部分字段”的需求在很多系统里都有,比如:
- 审批系统:复制上次填写的数据,但需要清空附件和备注;
- 销售订单:客户下单的某个产品需要复制行修改数量,附件不带;
- 报销系统:上个月差旅报销复制,但要重新上传票据。
这些场景下,如果不小心用了原始对象引用去处理,一不小心就把原始数据也改坏了,影响用户体验。
总结
本文通过一个简单的复制逻辑,讲清了一个非常常见但又容易忽略的问题 —— 引用对象修改导致原数据被篡改。
最后送上一句话:
只要是对象类型数据,就别相信“复制了就是新的”这件事,除非你用 cloneDeep
!
到此这篇关于Vue如何优雅地复制一行带附件的表格数据的文章就介绍到这了,更多相关Vue复制表格数据内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!