最近项目中需要使用树状结构,可以无限层级递增,并且还要实现搜索和搜索后选中指定层级。本篇讲总结一下实现思路以及在实现过程中遇到的一些问题和解决办法。
一,步先看看数据结构如下图 ,左侧就是数据解析后展示出来的树状表,三角符号说明有子级可点击展开查看子级内数据。
屏幕快照 2019-03-10 下午4.04.32.png
二,数据解析部分
handelData(tree,depth,id){
let that = this;
let subArr=[];
for(let i in tree){
let p=tree[i];
let obj={
functionCode: p.functionCode,
label: p.functionName,
functionNameAbc:p.functionNameAbc,
functionSortId: p.functionSortId,
functionUrl: p.functionUrl,
parentCode: p.parentCode,
selected:that.editObj&&that.editObj.functionCode==p.functionCode?true:false,
key:that.$v4(),
depth:depth+1,
showChildren:false,
pid:id,
id:id+'0'+that.typeSearch.pid,
nodes:p.subFuncList.length?that.handelData(p.subFuncList,depth+1,id+'0'+that.typeSearch.pid):[]
}
that.typeSearch.pid=that.typeSearch.pid+2;
subArr.push(obj)
}
return subArr;
},
在外部调用递归解析, handelData(tree,depth,id){}函数有三个参数分别是tree(含有子级的节点);depth(当前节点的层级);id(生成父子级对应关系的id,一直是有规律的变化着的)
注意组装subArr数据时
label(当前显示文字内容),
selected(是否选中),
key(唯一标识),
showChildren(是否展示子级,一般设置第0级别为true,其余都为false),
depth(层级关系,注意从0开始为第一级),
id(搜索时匹配使用,id是有规律的,当前id能辨别是否包含父级的id以及父级的父级的id等)
详细结果可以对比一下控制台打印的解析后数据截图:
屏幕快照 2019-03-10 下午4.24.17.png
三,完成了数据组装,接下来就讲一讲vue部分。由于项目内涉及到复用,所以我就单独将树状表单独封装成了组件,这样很大的好处就是后期如果对树状表有什么新的需求改动,也可以更加方便。例如这个页面需要搜索,那个页面只是存展示,这样会更加方便使用参数单独控制即可。
<div class="search_con" v-if="typeSearch&&typeSearch.clickType=='search'">
<Input class="searchInput" v-model="searchKey" search clearable @on-change="searchClick(typeSearch.eventClick)" @on-focus="focusClick" :placeholder="typeSearch.placeholder"/>
</div>
<div :class="searchKey&&!hasClick?'result_con':'disppear'">
<template v-for="(item,key) in searchTree">
<!--:key="random+key" 随机数+key 形式 解决控制台报'Duplicate keys detected: '0.7069392510570331'. This may cause an update error.'-->
<search-result-tree :item="item" :keyStr="searchKey" :key="random+key" :onClick="resultClick"></search-result-tree>
</template>
<template v-if="searchTree.length==0">
<span class="noneC">无搜索结果</span>
</template>
</div>
<div class="data_con">
<template v-for="value in dataData">
<handle-tree
:treeData="value"
:onClick="groupClick"
></handle-tree>
</template>
</div>
<br/>
这里主要涉及两个组件,handle-tree是树状表的展示部分,另外就是search-result-tree 就是展示搜索结果的浮动框。用两个class控制隐藏(disppear)与显示(result_con)。
四,重点说一下搜索高亮。
这部分踩了一个坑。因为是使用了v-html,而且还涉及到了vue的计算属性computed,计算属性是基于它们的依赖进行缓存的。只在相关依赖发生改变时它们才会重新求值。贴部分代码出来:
<template>
<div class="label_con" v-html="ruleTitle" @click="dataClick"></div>
</template>
computed: {
ruleTitle() {//搜索关键字匹配
let that = this;
let titleString = that.title;
// console.log(titleString)
if (!titleString) {
return '';
}
if (that.searchKey && that.searchKey.length > 0) {
// 匹配关键字正则
let replaceReg = new RegExp(that.searchKey, 'g');
// 高亮替换v-html值
let replaceString = "<span style='color: #1e67db; font-weight: bold;'>" + that.searchKey + "</span>";
// 开始替换
titleString = titleString.replace(replaceReg, replaceString);
}
return titleString;
},
// 每次传入不一样的key 强制刷新Vue(实时搜索需要)
key() {
return this.$route.name !== undefined? this.$route.name +new Date(): this.$route +new Date()
}
},
搜索关键字高亮是通过v-html="ruleTitle"这种方式实现,但是实时搜索必须要加一个强制刷新vue的属性,所以就多加了一个key。反观第三步内:key="random+key" 就是触发刷新的部分,random是随机数,采用+key的形式要不然控制台会误以为有相同的key报错。
五,点击搜索结果,树状表展开且选中搜索结果。
handelData(tree,e,s){//递归遍历
let that = this;
for(let i in tree){
//改变选中状态
tree[i].selected=false;
if(tree[i].key==e.key){
tree[i].selected=true;
if(e.showChildren){
tree[i].showChildren = true;
}else {
tree[i].showChildren = false;
}
} else {
//点击搜索结果才执行
if(s&&e.id.includes(tree[i].id)){
tree[i].showChildren = true;
}
}
that.handelData(tree[i].nodes,e,s)
}
}
handelData(tree,e,s)参数tree为全部树状表数据,e是搜索后点击的那个节点,s是搜索的内容且每次s变化都会触发重新遍历
if(s&&e.id.includes(tree[i].id)){
tree[i].showChildren = true;
}
这段就是利用id的规律性控制哪些该展开哪些不该展开或保持原样。
至此树状表实时搜索就实现了,搜索的结果呢是没有分父子级别的,所有搜索的结果也是通过递归遍历获取,如果有子级的话就把子级剔除放到搜索结果内。最终看下搜索后的截图:
屏幕快照 2019-03-10 下午5.12.28.png 屏幕快照 2019-03-10 下午5.12.58.png
网友评论