!数据格式与官网的树状图格式一致;
需求
1、可以通过输入框过滤,刷新树状图的数据,只显示其父级与子级(包括孙级),如搜索橙汁时树状图层级显示水->果汁->橙汁->汇源橙汁;(filter方法)
2、全选多选框只选择与判断当前显示的节点是否被全选;搜索后,点击全选也只选择当前显示的所有节点;
比如搜索奶茶后,点击全选,只全选了水—>奶茶—>奶茶的子级;删除搜索值(奶茶),全选按钮应为没被全选状态;
3、树状图旁边的文字按钮,点击全选时,子级都被全选,同时切换为取消;
点击取消时,子级全部不被选择,同时切换为全选按钮。
效果图:
<template>
<div>
<el-input
:placeholder="$t('common.searchPlaceholder')"
v-model.trim="filterText"
></el-input>
<el-checkbox
v-model="checkAllValue"
@change="isCheckAll"
style="margin: 10px 0 0px"
v-show="!filterText"
>{{ $t("button.check_all") }}</el-checkbox
>
<el-checkbox
v-model="checkAllResponse"
@change="isCheckResponse"
style="margin: 10px 0 0px"
v-show="filterText"
>{{ $t("button.check_all") }}</el-checkbox
>
<el-scrollbar
v-if="source"
wrap-class="scrollbar-wrapper"
style="height: 275px"
>
<el-tree
v-loading="LD_Tree"
ref="valueTree"
show-checkbox
:node-key="nodeKey"
check-strictly
:data="source"
:expand-on-click-node="isClickExpand"
:default-expand-all="isExpandAll"
:props="treeProps"
:highlight-current="isHighlightCurrent"
:default-checked-keys="checkKeys"
@check="treeCheckNode"
style="height: 280px; min-width: 20%"
class="tree-select"
>
<div class="custom-tree-node" slot-scope="{ node, data }">
<span>{{ data.name }}</span>
<span v-show="node.childNodes.length !== 0">
<el-link
size="mini"
@click.stop="nodeClick(node)"
:type="(nodeIsAllCheck(node)) ? 'warning' : ''"
>
{{ nodeIsAllCheck(node) ? "取消" : "全选" }}
</el-link>
</span>
</div>
</el-tree>
</el-scrollbar>
</div>
</template>
<script>
export default {
name: "SgoTreeSelect",
props: {
// 树状图的懒加载
LD_Tree: {
type: Boolean,
default: false,
},
// 节点的key
nodeKey: {
type: String,
default: "",
},
// 树状图的数据值
source: {
type: Array,
default: () => [],
},
// 树状图的prop
treeProps: {
type: Object,
default: null,
},
// 是否高亮当前行
isHighlightCurrent: {
type: Boolean,
default: false,
},
// 树状图的选中的值
checkKeys: {
type: Array,
default: () => [],
},
// 是否展开全部
isExpandAll: {
type: Boolean,
default: false,
},
// 是否点击展开节点
isClickExpand: {
type: Boolean,
default: true,
},
},
watch: {
// 过滤选中的值
filterText(val) {
// primeCheck 把没有过滤时所选择的值保存下来时,防止后面取消选择的时候可以恢复之前的值
this.primeCheck = this.$refs.valueTree.getCheckedKeys();
this.filter(val);
this.getVisibleNode();
if (!val) {
this.checkAllResponse = false;
return;
}
},
// 默认选中的值,当值改变时重新设置树的值
checkKeys: {
handler(val) {
// 设置值是异步操作,需要设置完后再去判断是否全选值
new Promise((resolve) => {
this.$refs.valueTree.setCheckedNodes(val);
resolve();
}).then((res) => {
this.getVisibleNode();
});
},
},
},
data() {
return {
filterText: "", // 搜索框绑定的值
checkAllValue: false, // 全选值
checkAllResponse: false, // 过滤后的值配置是否全选
treeVisibleValue: [], // 值配置当前所有显示节点的值
primeCheck: [], // 值配置没有过滤时候的选择
treeAllKey: [], // 全选id的值
};
},
methods: {
// 初始化组件里的值
init() {
this.filterText = "";
this.$refs.valueTree.setCheckedKeys([]);
this.checkAllResponse = false;
this.checkAllValue = false;
},
// 获取当前的节点值
getCheckedKeys() {
return this.$refs.valueTree.getCheckedKeys();
},
// 返回目前半选中的节点的 key 所组成的数组
getHalfCheckedKeys() {
return this.$refs.valueTree.getHalfCheckedKeys();
},
// 递归获取全部的key值
getTreeAllKey(tree) {
let treeKey = [];
let getTreeId = function (tree) {
tree.forEach((item) => {
treeKey.push(item.id);
if (item.children && item.children.length > 0) {
getTreeId(item.children);
}
});
};
getTreeId(tree);
return treeKey;
},
// 是否全选值配置的值
isCheckAll() {
if (this.checkAllValue == true) {
// 遍历获取选择全部的id 通过this.treeAllKey 获取
this.treeAllKey = this.getTreeAllKey(this.source);
this.$refs.valueTree.setCheckedKeys(this.treeAllKey);
} else {
// // 取消选择全部的
this.$refs.valueTree.setCheckedKeys([]);
}
},
// 树状图节点改变时
treeCheckNode(nodeObject, checkObject) {
// checkObject.checkedKeys 为当前选中的值
this.checkValueIsAll(checkObject.checkedKeys);
},
// 树状图搜索过滤节点
filterNode(value, data) {
if (!value) {
return true;
}
return data.name.indexOf(value) !== -1;
},
// 通过输入框的值修改是否显示节点
filter(value) {
const traverse = (node) => {
const childNodes = node.root ? node.root.childNodes : node.childNodes;
childNodes.forEach((child) => {
child.visible = this.filterNode(value, child.data, child);
traverse(child);
});
// node.visible为真,子节点也为真
if (node.visible && node.childNodes.length > 0) {
findChildNodes(node.childNodes);
}
// 递归循环子节点
function findChildNodes(data) {
for (let item of data) {
item.visible = true;
if (item.childNodes.length) {
findChildNodes(item.childNodes);
}
}
}
// 设置父节点是否可见
if (!node.visible && childNodes.length) {
let allHidden = true;
allHidden = !childNodes.some((child) => child.visible);
if (node.root) {
node.root.visible = allHidden === false;
} else {
node.visible = allHidden === false;
}
}
if (!value) return;
if (node.visible && !node.isLeaf) {
node.expand();
}
};
traverse(this.$refs.valueTree.store);
},
// 获取值配置树当前显示的节点的值
getVisibleNode() {
let allNodeObject = this.$refs.valueTree.store.nodesMap; // 获取当前显示的所有节点对象
let visibleNode = []; // 当前显示的节点
this.treeVisibleValue = []; // 因为要显示新的节点,要将之前的数据清空
// 获取显示的节点的id
for (let item in allNodeObject) {
// 判断是否可见
if (allNodeObject[item].visible == true) {
// 获取当前可见的节点
visibleNode.push(allNodeObject[item]);
// 判断过滤后可见节点是否都被选中
this.checkAllResponse = visibleNode.every((item) => {
return item.checked === true;
});
// 当没输入值时,判断是否被全选
if (!this.filterText) {
this.checkAllValue = visibleNode.every((item) => {
return item.checked === true;
});
}
// 添加当前显示节点的id
this.treeVisibleValue.push(allNodeObject[item].data.id);
}
}
},
// 是否全选过滤后值配置的值
isCheckResponse() {
// 全选过滤后值配置的值
if (this.checkAllResponse === true) {
// 将过滤后的节点值与之前所选择的节点值进行合并去重
this.$refs.valueTree.setCheckedKeys([
...new Set(this.treeVisibleValue.concat(this.primeCheck)),
]);
} else {
// 取消全选过滤后的值
// 判断未过滤选择的节点里是否包含了当前显示的节点,如果有则删除
for (let item in this.primeCheck) {
if (this.treeVisibleValue.includes(this.primeCheck[item])) {
delete this.primeCheck[item];
}
}
//设置当前值为未过滤时候所选中的值
this.$refs.valueTree.setCheckedKeys(this.primeCheck);
}
},
// 全选按钮
nodeClick(nodeObject) {
this.treeAllKey = [];
if (this.nodeIsAllCheck(nodeObject)) {
// 取消全选
nodeObject.expand();
this.allCheck(nodeObject, false);
} else {
// 全选
nodeObject.expand();
this.allCheck(nodeObject, true);
}
// this.$refs.valueTree.getCheckedKeys() 为当前选中的值
this.checkValueIsAll(this.$refs.valueTree.getCheckedKeys());
},
// 遍历父节点设置是否选择
// 通过isCheck来决定子节点是否选择
allCheck(params, isCheck) {
if(params.childNodes===0) {
params.checked = isCheck;
}
params.childNodes.forEach((item) => {
if (item.visible === true) {
if (item.childNodes) {
item.checked = isCheck;
this.allCheck(item, isCheck);
}
}
});
},
//判断节点是否被全选
nodeIsAllCheck(nodeObject) {
let isAllCheck = null;
// 过滤获取当前的可见子节点
let visibleNode = nodeObject.childNodes.filter((item) => {
return item.visible === true;
});
// 判断当前节点的子节点是否都为true
isAllCheck = visibleNode.every((item) => {
// 没有子节点时直接等于当前节点是否被选择
if(item.childNodes.length==0) {
return isAllCheck = item.checked
// 递归当前节点的孙节点
// 当前节点被全选时,所有子节点也必须勾选所以加上了&&item.checked
} else {
return this.nodeIsAllCheck(item)&&item.checked;
}
});
return isAllCheck;
},
// 判断当前选中的值是否触发复选框全选功能
checkValueIsAll(checkValue) {
// 遍历获取全部的id 通过this.treeAllKey 获取
this.treeAllKey = this.getTreeAllKey(this.source);
// 判断是否选择了全部的值
this.checkAllValue = this.treeAllKey.every((item) => {
return checkValue.includes(item);
});
// 判断是否选择了过滤后全部的值
this.checkAllResponse = this.treeVisibleValue.every((item) => {
return checkValue.includes(item);
});
},
},
};
</script>
<style scoped>
.custom-tree-node {
flex: 1;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 14px;
padding-right: 8px;
}
.tree-select >>> .el-tree-node__content{
height: 34px;
}
.tree-select >>> .el-link{
margin: 0 6px;
}
</style>