最近接到一个任务,需求对tree数据结构进行动态编辑,看到这个需求时立马跑步前进到element-ui官网看有没有类似的例子:
perfect!
那就开始淦!
先上代码:
<el-form
:model="modalForm"
:rules="modalForm.modalRules"
ref="modalForm"
class="edit-tree-table"
>
<el-table
empty-text="暂无"
:data="modalForm.tableData"
row-key="rowkey"
default-expand-all
:tree-props="{
children: 'child',
hasChildren: 'hasChildren',
}"
border
:show-summary="true"
:summary-method="summaryMethod"
>
<el-table-column>
<template slot="header">
<div>
<span class="warning">*</span
>序号
</div>
</template>
<template slot-scope="scope">
{{ scope.row.customIndex }}
</template>
</el-table-column>
<el-table-column>
<template slot="header">
<div>
<span class="warning">*</span>
标题1
</div>
</template>
<template slot-scope="scope">
<el-form-item
:rules="
modalForm.modalRules.xxx
"
>
<el-input
v-model="scope.row.xxx"
placeholder="请输入"
/>
</el-form-item>
</template>
</el-table-column>
...
...
...
</el-table>
</el-form>
data() {
return {
modalForm: {
tableData: [], // 存储表格table信息
modalRules: {
xxx: [
{
required: true,
message: "请输入",
trigger: "blur",
},
],
},
},
};
},
效果如下:
这时候问题来了,当去编辑提交的时候,需要对表单进行校验,因为tree结构不知道有多少层级,el-form-item标签的prop 表单域该怎么写呢,通过代码测试发现
:prop="tableData.0.xxx"
:prop="tableData.0.child.0.xxx"
:prop="tableData.0.child.0.child.0.xxx"
...
...
这种方式是可以校验的。 于是在表格渲染的时候就直接为每个row去添加一个propKey就行。
想到了广度优先遍历, js如下:
// 广度优先遍历,以1,1.1,1.1.1的规则形式对树形列表生成确定唯一值的索引
setIndexDep(data) {
let queue = [...data];
let loop = 0;
while (queue.length > 0) {
loop++;
[...queue].forEach((child, i) => {
queue.shift();
if (loop == 1) {
child.currentIndex = i;
child.customIndex = i + 1 + "";
child.propKey = i + "";
}
if (child.child && child.child.length > 0) {
child.dataType = 1;
for (let ci = 0; ci < child.child.length; ci++) {
child.child[ci].currentIndex = ci;
child.child[ci].disabled = false;
child.child[ci].customIndex =
child.customIndex + "." + (ci + 1);
child.child[ci].propKey =
child.propKey + ".child." + ci;
}
queue.push(...child.child);
} else {
child.dataType = 2;
}
});
}
return data;
},
html如下:
<el-form
:model="modalForm"
:rules="modalForm.modalRules"
ref="modalForm"
class="edit-tree-table"
>
<el-table
empty-text="暂无"
:data="modalForm.tableData"
row-key="rowkey"
default-expand-all
:tree-props="{
children: 'child',
hasChildren: 'hasChildren',
}"
border
:show-summary="true"
:summary-method="summaryMethod"
>
<el-table-column>
<template slot="header">
<div>
<span class="warning">*</span
>序号
</div>
</template>
<template slot-scope="scope">
{{ scope.row.customIndex }}
</template>
</el-table-column>
<el-table-column>
<template slot="header">
<div>
<span class="warning">*</span>
标题1
</div>
</template>
<template slot-scope="scope">
<el-form-item
:rules="
modalForm.modalRules.xxx
"
:prop="`tableData.${scope.row.propKey}.xxx`"
>
<el-input
v-model="scope.row.xxx"
placeholder="请输入"
/>
</el-form-item>
</template>
</el-table-column>
...
...
...
</el-table>
</el-form>
效果如下:
万事大吉!
以为这就结束了,这时候需求又加了,当输入某个值时候需要动态去根据规则去改变tree上row的一个值,用递归方法可以实现,后面想到一个代码少的方法就是一样用广度优先遍历先预埋一个propAttrKey,然后根据事件去拿到propAttrKey改变对应的值,代码如下:
setIndexDep(data) {
let queue = [...data];
let loop = 0;
while (queue.length > 0) {
loop++;
[...queue].forEach((child, i) => {
queue.shift();
if (loop == 1) {
child.currentIndex = i;
child.customIndex = i + 1 + "";
child.propKey = i + "";
child.propAttrKey = "[" + i + "]" + "";
}
if (child.child && child.child.length > 0) {
child.dataType = 1;
for (let ci = 0; ci < child.child.length; ci++) {
child.child[ci].currentIndex = ci;
child.child[ci].disabled = false;
child.child[ci].customIndex =
child.customIndex + "." + (ci + 1);
child.child[ci].propKey =
child.propKey + ".child." + ci;
child.child[ci].propAttrKey =
child.propAttrKey + ".child" + "[" + ci + "]";
}
queue.push(...child.child);
} else {
child.dataType = 2;
}
});
}
return data;
},
eventFun(row) {
let str = `this.modalForm.tableData${row.propAttrKey}`;
this.$set(
eval(str),
"key",
"value"
);
},
后面校验又加了校验规则,需要下级之和等于上级,这种就是递归去校验了,分享下方法:
depFun(tree, node, rootNum) {
if (tree[0].customIndex === node.customIndex) {
// 根节点
...
...
...
// 根节点判定
return tree;
}
const dep = (source, customIndex) => {
// 开启递归
for (let i = 0; i < source.length; i++) {
const item = source[i];
if (item.customIndex === customIndex) {
let sameLevelTotal = 0; // 同级之和
source.forEach((v) => {
let num = v.xxx;
if (!num) {
num = 0;
} else {
num = parseInt(num);
}
sameLevelTotal += num;
});
console.log("当前节点:", item);
console.log("同级:", source);
console.log("父节点:", tree[0]);
console.log("同级之和sameLevelTotal:", sameLevelTotal);
console.log("父节点值:", tree[0].xxx);
// 根据规则去校验
if (item.xxx > this.totalNum) {
// 当前节点分不大于总分
item.xxxx = "";
item.xxxxx = "";
this.$message({
message: "提示xxx",
type: "warning",
});
} else if (sameLevelTotal > tree[0].xxx) {
// 同级总分不应大于上级
item.xxxx = "";
item.xxxxx = "";
this.$message({
message: "提示xxx",
type: "warning",
});
}
...
...
...
return;
}
if (item.child) {
// 只对非末端节点进行递归
searchId(item.child, customIndex);
}
}
};
dep(tree[0].child, node.customIndex);
return tree;
},
备注:文章只用于记录平常code中所碰到问题,无其他用途!