1.实现效果
stree.gif
2.实现原理
2.1前端实现搜索过滤
indexOf:indexOf() 方法可返回某个指定的字符串值在字符串中首次出现的位置,如果没有找到匹配的字符串则返回 -1。(区分大小写)
语法:string.indexOf(searchvalue,start)
eg:
var str="Hello world, welcome to the universe.";
var n=str.indexOf("welcome");
// n=13
前端进行数据过滤:
1.浅拷贝数据,每次过滤数据都对此数组进行操作。
this.originData = JSON.stringify(this.initData([res.data]));//res.data为返回的tree对象。
2.遍历tree,indexOf过滤key字段(如name),去重之后组合成一个新的tree。
/**
* 搜索树
* @param {*} key 搜索匹配字段,如name
* @param {*} primary 数组去重字段 唯一
* @param {*} value 搜索字段
* @param {*} arr 树列表
* @returns
*/
searchTree(key, primary, value, arr) {
let newarr = [];
arr.forEach((item) => {
if (item.children && item.children.length) {
if (item[key].indexOf(value) > -1) {
item.expand = true;
newarr.push(item);
}
const child = this.duplicateList(this.searchTree(key, primary, value, item.children), primary);
const obj = {
...item,
expand: true,
children: child,
};
if (child && child.length) {
newarr.push(obj);
}
} else {
if (item[key].indexOf(value) > -1) {
newarr.push(item);
}
}
});
return newarr;
},
数组去重方法:
/**
* 数组去重
* @param {*} arr 数组
* @param {*} key 去重key值
*/
duplicateList(arr, key) {
let obj = {};
arr = arr.reduce((item, next) => {
if (!obj[next[key]]) {
item.push(next);
obj[next[key]] = true;
}
return item;
}, []);
return arr;
},
2.2搜索选中
Tree组件的events,@on-check-change="checkChange",可拿到相应的节点。
image.png
2.2.1 获取已勾选数据(只包括子节点)
定义一个数组,存放已勾选数据:
data() {
return {
checkedList: [], //已勾选数据列表
filterText: "",//搜索词
};
},
勾选节点时触发checkChange事件:
let nodes = this.$refs.sTree.getCheckedNodes();//获取被勾选的节点
过滤该数组拿到当前勾选的所有子节点数据:
若存在父节点,当查询所有数据时候,会造成不必要选中。
let l = nodes.filter((item) => !item.children);
将每次勾选节点push进checkedList并去重:
l.forEach((item) => {
this.checkedList.push(item);
});
this.checkedList = this.tools.duplicateList(this.checkedList, "id");
已勾选被不勾选时,删除已勾选列表中相应的数据
获取当前被勾选项的ID合集:
let unCheckedIdList = this.tools.getUnCheckedList([curr],"checked","id",[]);//curr为当前勾选对象
/**
* 获取满足条件的树列表下面的所有target字段
* @param {*} data
* @param {*} key
* @param {*} target
* @param {*} outList
* @returns
*/
getUnCheckedList(data, key, target, outList) {
data.forEach((item) => {
if (!item[key]) {
outList.push(item[target]);
}
if (item.children && item.children.length) {
this.getUnCheckedList(item.children, key, target, outList);
}
});
return outList;
},
根据ID,批量删除已勾选数据中符合条件的数据
if (unCheckedIdList.length) {
this.checkedList = this.deleteALl(this.checkedList, unCheckedIdList);
}
/**
* 数组-删除所有满足条件的数据
*/
deleteALl(data, e) {
let len = data.length;
while (len--) {
if (e.includes(data[len].id)) {
data.splice(len, 1);
}
}
return data;
},
2.2.2 点击搜索,回显已勾选数据
已勾选数据列表,即this.checkedList,遍历tree列表,将满足条件的数据checked 设置为true。
/**
* 回显已选中的树
* @param {*} key 判断是否选择的字段
* @param {*} data 树
* @param {*} list 已选中列表
* @returns
*/
getCheckedTree(key, data, list) {
data.forEach((item) => {
if (
list.filter((it) => {
return it[key] == item[key];
}).length > 0
) {
item.checked = true;
} else {
item.checked = false;
}
if (item.children && item.children.length) {
item.children = this.getCheckedTree(key, item.children, list);
}
});
return data;
},
2.2.3 获取所有被选中的节点(包括父节点)
与上述方法一致,去掉对父节点的过滤。
let l = nodes.filter((item) => !item.children);
2.2.4 获取所有被选中及半选中的节点(包括父节点)
let immNodes = this.$refs.sTree.getCheckedAndIndeterminateNodes();
immNodes.forEach((item) => {
this.immNodesList.push(item);
});
this.immNodesList = this.tools.duplicateList(this.immNodesList, "id");
3.封装组件实现代码
<template>
<div>
<div>
<div class="flex mb10">
<Input placeholder="请输入" v-model="filterText" clearable class="mr20" />
<Button type="primary" class="box-content-btn" @click="searchOneTree()">查询</Button>
</div>
<Tree ref="sTree" :data="list" multiple :show-checkbox="true" @on-check-change="checkChange" @on-select-change="selectChange"></Tree>
<slot></slot>
</div>
</div>
</template>
<script>
export default {
name: "stree",
props: {
treeList: {
type: Array,
default: () => [],
},
originList: {
type: String,
default: "",
},
},
data() {
return {
list: [],
checkedList: [], //已勾选数据列表
checkedListIncludeParent: [], //已勾选数据的列表-包括父节点
immNodesList: [], //获取选中及半选节点列表
filterText: "",
};
},
watch: {
treeList: {
deep: true,
handler: function (val) {
this.list = val;
},
},
},
methods: {
/**
* 搜索
*/
searchOneTree() {
if (!this.filterText) {
this.list = JSON.parse(this.originList);
} else {
this.list = this.tools.searchTree(
"name",
"id",
this.filterText,
JSON.parse(this.originList)
);
}
this.list = this.tools.getCheckedTree("id", this.list, this.checkedList);
this.$emit("update:treeList", this.list);
},
/**
* 点击树节点时触发 当前已选中的节点数组、当前项
*/
selectChange() {
// selected为true的数据
let nodes0 = this.$refs.sTree.getSelectedNodes();
console.log(nodes0);
},
/**
* 勾选树
*/
checkChange(s, curr) {
let immNodes = this.$refs.sTree.getCheckedAndIndeterminateNodes();
immNodes.forEach((item) => {
this.immNodesList.push(item);
});
this.immNodesList = this.tools.duplicateList(this.immNodesList, "id");
let unCheckedIdList = this.tools.getUnCheckedList(
[curr],
"checked",
"id",
[]
);
let nodes = this.$refs.sTree.getCheckedNodes();
nodes.forEach((item) => {
this.checkedListIncludeParent.push(item);
});
this.checkedListIncludeParent = this.tools.duplicateList(
this.checkedListIncludeParent,
"id"
);
let l = nodes.filter((item) => !item.children);
l.forEach((item) => {
this.checkedList.push(item);
});
this.checkedList = this.tools.duplicateList(this.checkedList, "id");
if (unCheckedIdList.length) {
this.immNodesList = this.deleteALl(this.immNodesList, unCheckedIdList);
this.checkedList = this.deleteALl(this.checkedList, unCheckedIdList);
this.checkedListIncludeParent = this.deleteALl(
this.checkedListIncludeParent,
unCheckedIdList
);
}
this.$emit(
"on-check-change",
s,
curr,
this.checkedList,
this.checkedListIncludeParent,
this.immNodesList
);
},
/**
* 数组-删除所有满足条件的数据
*/
deleteALl(data, e) {
let len = data.length;
while (len--) {
if (e.includes(data[len].id)) {
data.splice(len, 1);
}
}
return data;
},
},
};
</script>
<style lang="less" scoped>
.mr20 {
margin-right: 20px;
}
.mb10 {
margin-bottom: 10px;
}
</style>
4.父组件引用实现代码
<template>
<div class="contentBox">
<s-tree :treeList.sync="list" :originList="originList" class="stree" @on-check-change="change"></s-tree>
</div>
</template>
<script>
import sTree from "./components/stree.vue";
export default {
nqme: "searchTree",
components: {
sTree,
},
data() {
return {
list: [],
originList: "",
};
},
created() {
this.getTreeData();
},
methods: {
change(s, cur, e1, e2, e3) {
console.log("子节点勾选中", e1);
console.log("勾选中的所有数据", e2);
console.log("获取选中及半选节点ID", e3);
},
// 获取树列表
async getTreeData() {
const { data, code } = await this.$api.getTreeData("getTreeDataTwo");
if (code == 200) {
this.list = this.initData([data], 0);
this.originList = JSON.stringify(this.initData([data], 0));
}
},
// 树列表格式化
initData(data, e) {
data.forEach((item) => {
item.title = item.name;
item.checked = false;
item.expand = e == 0;
if (item.children && item.children.length) {
this.initData(item.children, 1);
}
});
return data;
},
},
};
</script>
<style lang="less" scoped>
.stree {
width: 500px;
}
</style>