接上一篇table列表复现框【勾选-搜索-再勾选】功能,存在数据结构非【数组】而是【树形结构】的数据,在实现【勾选-搜索-再勾选】功能时,有一定的差异点,做记录如下:
一、代码实现
第一步:封装查询企业名称弹窗组件
<template>
<el-dialog
width="40%"
title="企业查询"
append-to-body
class="search-enterprise"
:visible.sync="enterpriseDialogVisible"
:before-close="closeDialog"
>
<div class="search-box">
<span class="search-box-name">企业名称:</span>
<el-input
suffix-icon="el-icon-search"
v-model.trim="enterpriseName"
placeholder="请输入"
></el-input>
<el-button type="primary" class="search-btn" @click="handQueryOrgs">
查 询
</el-button>
</div>
<div class="tree-box" ref="treeBox" @scroll="handleScroll">
<el-tree
ref="tree"
show-checkbox
:data="treedata"
node-key="pk_org"
default-expand-all
:expand-on-click-node="false"
:props="defaultProps"
@check="handleCheckChange"
></el-tree>
</div>
<div slot="footer">
<el-button @click="submitVal" type="primary" class="submit-btn">
确 认
</el-button>
<el-button @click="closeDialog()" class="cancel-btn">取 消</el-button>
</div>
</el-dialog>
</template>
<script>
import commonService from '@/api/common.js';
export default {
props: {
enterpriseDialogVisible: {
type: [Boolean],
default: false,
},
pkGroups: {
type: [Array],
default: [], // 企业基于集团
},
pkPlans: {
type: [Array],
default: [], // 集团基于计划
},
pkOrgs: {
type: [Array],
default: [], // 企业
},
},
data() {
return {
scrollTop: 0,
treedata: [],
searchSaveSelectId: [],
searchSaveSelectData: [],
selectedId: [], // 选中的节点的主键集合
selectedData: [], // 选中的树结构
enterpriseName: '',
defaultProps: {
children: 'children',
label: 'org_name',
},
};
},
watch: {
// 弹窗出现标识
enterpriseDialogVisible(val) {
if (val) {
this.enterpriseName = '';
if (this.pkGroups && this.pkGroups.length) {
this.queryOrgs();
} else {
this.$nextTick(() => {
this.$refs.tree.setCheckedKeys([]);
this.treedata = [];
});
}
}
},
// 集团选中pks
pkGroups(val, oldval) {
let haveOldValFlag = oldval && oldval.length; // 老数据有值
let haveValFlag = val && val.length && val.join(',') != oldval.join(','); // 新老数据不同
this.$nextTick(() => {
if (haveOldValFlag && haveValFlag) {
this.$refs.tree.setCheckedKeys([]);
}
});
},
// 企业选中id
selectedId(val, old) {
if (this.pkGroups && this.pkGroups.length) {
this.$refs.tree.setCheckedKeys(val);
}
},
},
methods: {
// 手动搜索
handQueryOrgs() {
this.pagenumber = 1; // 重置页码
if (this.pkGroups && this.pkGroups.length) {
this.searchSaveSelectId = this.selectedId; // 手动搜索时保存之前选中的id
this.searchSaveSelectData = this.selectedData; // 手动搜索时保存之前选中的数据
this.queryOrgs();
} else {
this.$message.error('请先选择集团名称!');
}
},
// 获取企业名称
queryOrgs() {
this.treedata = [];
let params = {
pk_groups: this.pkGroups,
pk_plans: this.pkPlans,
org_name: this.enterpriseName ? this.enterpriseName : '',
};
commonService.queryOrgs(params).then((res) => {
if (res && res.code == 200) {
if (res.data) {
this.treedata = res.data;
}
} else {
this.$message.error(res.message);
}
});
},
// 实现的是勾选-搜索-再勾选的逻辑
handleCheckChange() {
this.selectedId = this.$refs.tree.getCheckedKeys(); // 旧的id
this.selectedId = this.searchSaveSelectId.concat(this.selectedId); // 再次赋值id
this.selectedData = this.$refs.tree.getCheckedNodes(); // 旧的数据
this.selectedData = this.searchSaveSelectData.concat(this.selectedData); // 再次赋值数据
},
// el-tree将选中的节点以tree结构形式数组返回
getDeepTree() {
const getC = this.$refs.tree.getCheckedNodes();
const getHC = this.$refs.tree.getHalfCheckedNodes();
let selected_nodes = getHC.concat(getC);
let selected_ids = new Set(); // 记录选中的节点tree_id
let used_ids = new Set(); // 记录已经查询过的节点
// 遍历初始化selected_ids
for (let node of selected_nodes) {
selected_ids.add(node.$treeNodeId);
}
// 最终的结果数据用一个根节点存储, 为了对齐递归的数据结构
let root_node = {
treeNodeId: -1,
children: [],
};
// 添加给parent_node节点一个新节点 node
// 注意:node 的类型是原始的 tree 节点
// 而parent_node 的类型是真正的结果节点
function AddNode(node, parent_node) {
if (
!selected_ids.has(node.$treeNodeId) ||
used_ids.has(node.$treeNodeId)
) {
return;
}
used_ids.add(node.$treeNodeId); // 加过的要存入 used_ids 做标记
let real_node = {
...node,
children: [],
treeNodeId: node.$treeNodeId,
// 存储一下节点的数据
};
// // 添加子节点
if (node.children) {
for (let child_node of node.children) {
AddNode(child_node, real_node);
}
}
if (real_node.children.length === 0) {
delete real_node.children;
}
// 如果数据没有时,删除数据
if (!real_node.pk_org) {
delete real_node.pk_org;
}
if (!real_node.org_name) {
delete real_node.org_name;
}
if (!real_node.org_code) {
delete real_node.org_code;
}
if (!real_node.org_level) {
delete real_node.org_level;
}
if (!real_node.parent_pk) {
delete real_node.parent_pk;
}
if (!real_node.one_level_org) {
delete real_node.one_level_org;
}
parent_node.children.push(real_node);
}
for (let node of selected_nodes) {
AddNode(node, root_node);
}
return root_node.children;
},
// 确认选中的企业,回显在父组件输入框
submitVal() {
if (this.selectedData.length == 0) {
this.$message.error('请选择企业名称!');
return false;
} else {
let sendObjInfo = this.getDeepTree();
this.$emit('submit', this.selectedData, sendObjInfo);
this.closeDialog();
}
},
// 关闭弹窗
closeDialog() {
this.$emit('close');
},
// 弹窗高度
handleScroll() {
this.scrollTop = this.$refs.treeBox.scrollTop;
},
},
};
</script>
<style scoped>
.search-enterprise >>> .el-dialog__body {
padding: 10px 30px 0px;
border-top: 1px solid #ececec;
}
.search-enterprise >>> .el-dialog__header {
padding: 15px 20px;
}
.search-enterprise >>> .el-dialog__headerbtn {
top: 12px;
}
.search-enterprise >>> .el-dialog__headerbtn .el-dialog__close {
font-size: 25px;
display: block;
}
.search-box >>> .el-input {
width: 230px;
}
.search-box >>> .el-input--suffix .el-input__inner {
height: 32px;
}
.search-box {
display: flex;
flex-direction: row;
align-items: center;
padding-left: 10px;
}
.search-box >>> .el-input {
width: 266px;
}
.search-box >>> .el-input input {
width: 266px;
height: 32px;
}
.search-box >>> .el-input__icon {
line-height: 32px;
}
.search-box-name {
flex-shrink: 0;
}
.tree-box {
margin-top: 16px;
padding: 0 12px;
min-height: 300px;
max-height: 500px;
overflow: auto;
}
.search-btn {
width: 85px;
height: 32px;
padding: 0;
margin-left: 16px;
background-color: #2e78ff;
}
.submit-btn {
width: 64px;
height: 32px;
padding: 0;
background-color: #2e78ff;
}
.cancel-btn {
width: 64px;
height: 32px;
padding: 0;
background-color: #fafafa;
border-color: #ddd;
color: #666;
margin-left: 4px;
}
</style>
第二步:组件应用
<template>
<EnterpriseDialog
:enterpriseDialogVisible="enterpriseDialogVisible"
@close="closeEnterpriseDialog"
@submit="getEnterpriseData"
:pkGroups="form.pk_groups"
:pkPlans="form.pk_plans"
:pkOrgs="form.pk_orgs"
></EnterpriseDialog>
</template>
第三步:组件方法
<script>
import EnterpriseDialog from '@/components/searchEnterprise';
export default {
components: {
EnterpriseDialog
},
data(){
return {
form: {
pk_plans: [],
plan_name: '',
pk_groups: [],
group_name: '',
org_name: '',
pk_orgs: [],
},
enterpriseDialogVisible: false, // 企业弹窗
}
},
methods:{
// 企业名称递归
getAllOrgPks (tree) => {
let pksResult = [];
let pkNamesResult = [];
for (const i in tree) {
pksResult.push(tree[i].pk_org); // 遍历项目满足条件后的操作
pkNamesResult.push(tree[i].org_name); // 遍历项目满足条件后的操作
if (tree[i].children) {
this.getAllOrgPks(tree[i].children); // 存在子节点就递归
}
}
return [pksResult, pkNamesResult];
},
// 清空企业组件数据
clearEnterpriseInfo() {
this.form.org_name = '';
this.form.pk_orgs = [];
},
// 选中企业
getEnterpriseData(val, info) {
this.clearEnterpriseInfo(); // 清空企业组件数据
let [pksResult, pkNamesResult] = this.getAllOrgPks(val);
this.form.pk_orgs = pksResult;
this.form.org_name = pkNamesResult.join(',');
this.treedata = info;
},
// 关闭企业弹框
closeEnterpriseDialog() {
this.enterpriseDialogVisible = false;
},
}
}
</script>
第四步:企业数据树再次展示(具体问题具体分析:不确定反复【扁平化】或者【成树操作】方法的相互影响)
- 在企业名称弹窗中拿到的数据,需要再次以树形结构的形式展示出来
1.1 现象:存在父子关系的children数据会被单独拎出来作为一项展示;
1.2 思路:打印企业名称选择时获取到的数据和后续拿到数据后再次进行数据checkId去重、数据递归、成数操作等各个方法处理时得到的数据;
1.3 结论:在最初选中企业名称时使用的getCheckNodes方法,拿到的数据就是【现象】呈现出来的效果 - 由得出的结论,思考解决问题
2.1 递归数据:拿到所有的有children的每一个数据项(思路梳理不清晰,无法找出合适的节点,找到对应的筛选条件;将满足条件的数据给存放起来)
2.2找度娘:el-tree将选中的节点以tree结构形式数组返回,整理思路,方法getDeepTree拿到的就是纯选中数据的树形结构
二、总结
- 组件封装,关注每次弹窗数据【重置】或【清空】按钮时,组件中各个方面数据的情况问题;
- 遇到问题时,除了梳理自己思路以外,借助网络查询到的结果,切勿眼高手低嫌麻烦不去尝试,试着理解别人文章中的注释,放置自己场景中进行打印,查看效果。