element-ui 树型结构筛选框
下拉筛选功能在后台管理系统中是非常常见的。
有单纯的下拉筛选:
这种单纯的下拉筛选功能可以通过select
嵌套option
来实现。
举例如下:
<el-select v-model="listQuery.param.status" clearable
style="width: 140px; margin-right: 6px"
class="filter-item"
placeholder="请选择订单状态">
<el-option
v-for="item in statusOptions"
:key="item.val"
:label="item.lab"
:value="item.val" />
</el-select>
树型结构的筛选可以说是稍微有点复杂的,我没有写过,只是借用过公司大神已经写好的组件,在此将组件及使用方法奉上,为自己多多积累经验。
SelectTree
组件如下:
<!-- 树状选择器 -->
<template>
<el-popover
ref="popover"
placement="bottom-start"
trigger="click"
:disabled="disabled"
@show="onShowPopover"
@hide="onHidePopover"
>
<el-tree
ref="tree"
class="select-tree"
highlight-current
:style="`min-width: ${treeWidth}`"
:data="data"
:props="props"
:expand-on-click-node="false"
:filter-node-method="filterNode"
:default-expand-all="defaultExpandAll"
@node-click="onClickNode"
/>
<el-input
slot="reference"
ref="input"
v-model="labelModel"
clearable
:style="`width: ${width}px`"
:class="{ 'rotate': showStatus }"
suffix-icon="el-icon-arrow-down"
:placeholder="placeholder" :disabled="disabled"
/>
</el-popover>
</template>
<script>
export default {
name: "SelectTree",
// 设置绑定参数
model: {
prop: "value",
event: "selected"
},
props: {
// 父节点是否可选
isOptionalParent: { default: true, type: Boolean },
// 是否默认展开所有节点
defaultExpandAll: { default: false, type: Boolean },
disabled: { default: false, type: Boolean },
// 接收绑定参数
value: null,
// 输入框宽度
width: String,
// 选项数据
options: {
type: Array,
required: true
},
// 输入框占位符
placeholder: {
type: String,
required: false,
default: "请选择"
},
// 树节点配置选项
props: {
type: Object,
required: false,
default: () => ({
parent: "pid",
value: "id",
label: "title",
children: "children"
})
}
},
data() {
return {
// 树状菜单显示状态
showStatus: false,
// 菜单宽度
treeWidth: "auto",
// 输入框显示值
labelModel: null,
// 实际请求传值
valueModel: null
};
},
computed: {
// 是否为树状结构数据
dataType() {
const jsonStr = JSON.stringify(this.options);
return jsonStr.indexOf(this.props.children) !== -1;
},
// 若非树状结构,则转化为树状结构数据
data() {
return this.dataType ? this.options : this.switchTree();
}
},
watch: {
labelModel(val) {
if (!val) {
this.valueModel = "";
}
this.$refs.tree.filter(val);
},
value(val) {
this.labelModel = this.queryTree(this.data, val);
}
},
created() {
// 检测输入框原有值并显示对应 label
if (this.value) {
this.labelModel = this.queryTree(this.data, this.value);
}
// 获取输入框宽度同步至树状菜单宽度
this.$nextTick(() => {
this.treeWidth = `${(this.width || this.$refs.input.$refs.input.clientWidth) - 24}px`;
});
},
methods: {
// 单击节点
onClickNode(node) {
this.labelModel = node[this.props.label];
this.valueModel = node[this.props.value];
this.onCloseTree();
this.$emit("change", this.valueModel);
},
// 偏平数组转化为树状层级结构
switchTree() {
return this.cleanChildren(this.buildTree(this.options, "0"));
},
// 隐藏树状菜单
onCloseTree() {
this.$refs.popover.showPopper = false;
},
// 显示时触发
onShowPopover() {
this.showStatus = true;
this.$refs.tree.filter(false);
},
// 隐藏时触发
onHidePopover() {
this.showStatus = false;
this.$emit("selected", this.valueModel);
},
// 树节点过滤方法
filterNode(query, data) {
if (!query) return true;
return data[this.props.label].indexOf(query) !== -1;
},
// 搜索树状数据中的 ID
queryTree(tree, id) {
let stark = [];
stark = stark.concat(tree);
while (stark.length) {
const temp = stark.shift();
if (temp[this.props.children]) {
stark = stark.concat(temp[this.props.children]);
}
if (temp[this.props.value] === id) {
return temp[this.props.label];
}
}
return "";
},
// 将一维的扁平数组转换为多层级对象
buildTree(data, id = "0") {
const fa = (parentId) => {
const temp = [];
for (let i = 0; i < data.length; i++) {
const n = data[i];
if (n[this.props.parent] === parentId) {
n.children = fa(n.rowGuid);
temp.push(n);
}
}
return temp;
};
return fa(id);
},
// 清除空 children项
cleanChildren(data) {
const fa = (list) => {
list.map((e) => {
if (e.children.length) {
fa(e.children);
} else {
delete e.children;
}
return e;
});
return list;
};
return fa(data);
}
}
};
</script>
<style>
/* .el-input.el-input--suffix {
cursor: pointer;
overflow: hidden;
}
.el-input.el-input--suffix.rotate .el-input__suffix {
transform: rotate(180deg);
} */
.select-tree {
max-height: 350px;
overflow-y: scroll;
}
/* 菜单滚动条 */
.select-tree::-webkit-scrollbar {
z-index: 11;
width: 6px;
}
.select-tree::-webkit-scrollbar-track,
.select-tree::-webkit-scrollbar-corner {
background: #fff;
}
.select-tree::-webkit-scrollbar-thumb {
border-radius: 5px;
width: 6px;
background: #b4bccc;
}
.select-tree::-webkit-scrollbar-track-piece {
background: #fff;
width: 6px;
}
</style>
组件的引入及注册应该大家都会的。
组件引入:
import SelectTree from "@/components/SelectTree";
组件注册:
components: {SelectTree}
组件的使用:
<SelectTree
v-model="listQuery.param.categoryId"
:placeholder="'请选择商品分类'"
:options="treeData"
:is-optional-parent="false"
style="width: 200px; margin-right: 6px"
:props="defaultSelectTreeProps"
class="filter-item"
/>
script部分:
data(){
return{
treeData: [],
// 这个defaultSelectTreeProps是将默认的参数改变一下,适合之前定义的参数。
defaultSelectTreeProps: {
parent: "pid", // 父级唯一标识
value: "id", // 唯一标识
label: "name", // 标签显示
children: "children"// 子级
},
}
},
mounted(){
this.getTree();
},
methods:{
getTree() {
tree({}).then(res => {
this.treeData = res.result;
this.treeData.forEach(item => {
let obj = {};
obj[item.id] = item.name;
this.categoryList.push(obj);
});
});
},
}