普通嵌套
表单和表格是项目中最常见的项目,有时候就会遇到表格嵌套表单的情况,比如
这种形式的其实挺好处理的,如下:
<el-form
:model="states"
ref="tableFormRef"
label-width="80px"
label-position="top"
>
<el-table :data="states.tableData" row-key="id" border height="400px">
<el-table-column prop="unitDosage" label="单位用量" width="320">
<template #header>
<span>
<span class="require-star">*</span>
<span>单位用量</span>
</span>
</template>
<template v-slot="{ row,$index }">
<el-form-item
:prop="`tableData[${$index}].unitDosage`"
:rules="e_rule('required', '单位用量不能为空')"
>
<el-input-number
v-model.trim="row.unitDosage"
:clearable="false"
placeholder="请输入"
:controls="false"
:min="0"
:precision="3"
/>
</el-form-item>
</template>
</el-table-column>
const states=reactive({
tableData:[]
})
就按照上面的这种格式写就可以实现,因为是平级的,在表单校验时可以很容易的在表格数组中找到属性的值。
树形表格嵌套
最近把又碰见了树形表格嵌套的形式,如下图:
然后呢,就按照老套路那样写,结果一直校验不通过。最后发现是绑定的属性不对。以下面的代码为例
let list = [
{
id: "1",
children: [
{
id: "1-1",
children: [],
},
],
},
];
id
为1-1
的属性应该是list[0].children[0].id
,所以问题就转换为如何获取到属性准确的路径问题
方法肯定是用递归,但是水平太低不会写(也就会写一点简单的递归)。
想到最近 new bing 这么火,我自己不会写,让new bing帮我写吧。结果真的给我写好了
当然 如果想要new bing给你答案的话,你需要准确的向他描述这个问题,你描述的越准确,他给你的答案越准确。反复尝试了很多次,最后给我了想要的答案。
下面是他给我的例子:
// 定义一个递归函数,接受一个对象或数组,一个目标id值和一个路径数组作为参数
function findPath(obj, targetId, path = []) {
// 如果obj是数组,就遍历每个元素
if (Array.isArray(obj)) {
for (let i = 0; i < obj.length; i++) {
// 将当前索引添加到路径数组中
path.push(`[${i}]`)
// 对当前元素递归调用findPath函数,并将结果赋值给result
const result = BomTool.findPath(obj[i], targetId, path)
// 如果result不为空,说明找到了匹配的id,就返回result
if (result) return result
// 否则,就从路径数组中移除最后一个元素(即当前索引)
path.pop()
}
}
// 如果obj是对象,就遍历每个属性
else if (typeof obj === 'object') {
for (const key in obj) {
// 将当前键添加到路径数组中
path.push(`.${key}`)
// 对当前值递归调用findPath函数,并将结果赋值给result
const result = BomTool.findPath(obj[key], targetId, path, key)
// 如果result不为空,说明找到了匹配的id,就返回result
if (result) return result
// 否则,就从路径数组中移除最后一个元素(即当前键)
path.pop()
}
}
// 如果obj是基本类型(如字符串、数字等),就判断是否等于目标id
else {
// 如果相等,就将路径数组连接成字符串并返回
if (obj === targetId) return path.join('')
// 否则,就返回空字符串
else return ''
}
}
// 定义一个测试用的列表对象
let list = [
{
id: "1",
children: [
{
id: "1-1",
children: [],
},
],
},
];
//删除最后的id
let str = findPath(list, "1-1")
// 调用findPath函数,并打印结果list[0].children[0].id
console.log(str);
这里需要注意一个问题,函数的值必须是唯一的。这里id
是唯一的,最后返回值里有一个id
,如果不是想要的属性的话,可以使用replace
函数替换一下。
如果id值不是唯一的,这种情况也是会出现的。建议是往对象里再添加一个key
,比如 key
的值是id
值加上当前的时间戳加上一个随机数拼接起来的字符串。
下面是在事件项目里应用的代码
<template v-slot="{ row }">
<el-form-item
:prop="`tableData${findPath(
states.tableData,
row.materialNo,
).replace('materialNo', 'unitDosage')}`"
:rules="e_rule('required', '单位用量不能为空')"
v-if="states.updateType != 'read'"
>
<el-input-number
v-model.trim="row.unitDosage"
:clearable="false"
placeholder="请输入"
:controls="false"
:min="0"
:precision="3"
/>
</el-form-item>
与上面提到平级基本一致,就是改一下prop
属性值,下面看一下debugger
的情况,可以看出路径是对的
补充
- 根据路径获取层级的方法
findLevel(list, value) {
const path = this.findPath(list, value)
// 根据点的个数判断层级和序号
let level = 0
let index = 0
if (path.length > 0) {
level = (path.match(/]/g) || []).length
index = parseInt(path.substring(path.lastIndexOf('[') + 1, path.length - 2)) + 1
}
return `${level}.${index}`
}