el-select 下拉树封装
安装教程
npm i zwd-vue2-el-plus
使用说明
import Vue from "vue";
import zwdElPlus from "zwd-vue2-el-plus";
Vue.use(zwdElPlus);
组件说明
ZwdTreeSelect
使用方式和 element-plus的el-tree-select 一样。
API
Attributes
组件是el-tree
和el-select
的结合体,原始属性未被更改。请跳转查看原组件的相应文档。
属性 | 方法 | 事件 | 插槽 |
---|---|---|---|
tree | tree | tree | tree |
select | select | select | select |
<template>
<div id="app">
<div>
<ZwdTreeSelect
check-on-click-node
collapse-tags
show-checkbox
multiple
clearable
highlight-current
v-model="value"
:data="data"
node-key="value"
></ZwdTreeSelect>
</div>
</div>
</template>
<script>
export default {
data() {
return {
value: [44],
data: [
{
label: "212",
value: 11,
children: [
{ label: "234", value: 33 },
{ label: "444", value: 44 },
],
},
{ label: "333", value: 22 },
],
};
},
};
</script>
组件使用参考element-plus
https://element-plus.org/zh-CN/component/tree-select.html
源码
<!--
* @Author: zhaowendi
* @Date: 2023-10-07 16:14:27
* @LastEditTime: 2023-10-20 15:42:39
* @Description:
-->
<template>
<el-select
ref="select"
v-bind="selectProps"
v-on="$listeners"
:filter-method="selectFilterMethod"
>
<template #prefix>
<slot name="prefix"></slot>
</template>
<template #empty>
<slot name="empty"></slot>
</template>
<el-option
v-for="item in treeFlatData"
:key="item[propsMap.value]"
:label="item[propsMap.label]"
:value="item[propsMap.value]"
hidden
>
</el-option>
<el-tree
ref="tree"
v-bind="treeProps"
@check="check"
@node-click="nodeClick"
:filter-node-method="treeFilterNodeMethod"
v-on="$listeners"
>
<template #default="slotProps">
<slot v-bind="slotProps"></slot>
</template>
</el-tree>
</el-select>
</template>
<script>
import { Select, Tree } from "element-ui";
import { useSelect } from "./select.js";
import { useTree } from "./tree.js";
import { getForestFlatData } from "zwd-tree-utils";
import { isEqual } from "lodash";
import { isValidArray, toValidArray, isValidValue, treeFind } from "./utils";
export default {
name: "ZwdTreeSelect",
componentName: "ZwdTreeSelect",
// disable `ElSelect` inherit current attrs
inheritAttrs: false,
props: {
...Select.props,
...Tree.props,
},
data() {
return {};
},
watch: {
value: {
immediate: true,
deep: true,
handler() {
if (this.showCheckbox) {
this.$nextTick(() => {
let tree = this.$refs.tree;
if (
tree &&
!isEqual(tree.getCheckedKeys(), toValidArray(this.value))
) {
tree.setCheckedKeys(toValidArray(this.value));
}
});
}
},
},
},
computed: {
treeFlatData() {
return getForestFlatData(JSON.parse(JSON.stringify(this.data)));
},
selectProps() {
return useSelect(this.$props);
},
treeProps() {
return useTree(this.$props);
},
propsMap() {
return {
value: this.$props.nodeKey || this.$props.valueKey || "value",
label: "label",
children: "children",
disabled: "disabled",
isLeaf: "isLeaf",
...this.$props.props,
};
},
},
methods: {
selectFilterMethod(keyword) {
if (this.filterMethod) this.filterMethod(keyword);
this.$nextTick(() => {
let tree = this.$refs.tree;
// let tree node expand only, same with tree filter
tree && tree.filter(keyword);
});
},
treeFilterNodeMethod(value, data, node) {
if (this.filterNodeMethod)
return this.filterNodeMethod(value, data, node);
if (!value) return true;
return this.getNodeValByProp("label", data).includes(value);
},
//无复选框点击 通过handleOptionSelect设置select选中值
nodeClick(data, node, e) {
// `onCheck` is trigger when `checkOnClickNode`
if (this.showCheckbox && this.checkOnClickNode) return;
// now `checkOnClickNode` is false, only no checkbox and `checkStrictly` or `isLeaf`
let select = this.$refs.select;
if (!this.showCheckbox && (this.checkStrictly || node.isLeaf)) {
if (!this.getNodeValByProp("disabled", data)) {
const option = select.getOption(this.getNodeValByProp("value", data));
select.handleOptionSelect(option, true);
}
} else if (this.expandOnClickNode) {
e.handleExpandIconClick();
}
},
//当复选框被点击的时候触发
check(data, params) {
// ignore when no checkbox, like only `checkOnClickNode` is true
if (!this.showCheckbox) return;
const dataValue = this.getNodeValByProp("value", data);
let tree = this.$refs.tree;
const uncachedCheckedKeys = params.checkedKeys;
const cachedKeys = this.multiple
? this.value.filter(
(item) => !tree.getNode(item) && !uncachedCheckedKeys.includes(item)
)
: [];
const checkedKeys = uncachedCheckedKeys.concat(cachedKeys);
if (this.checkStrictly) {
this.$emit(
"input",
this.multiple
? checkedKeys
: checkedKeys.includes(dataValue)
? dataValue
: undefined
);
}
// only can select leaf node
else {
if (this.multiple) {
this.$emit("input", tree.getCheckedKeys(true));
} else {
// select first leaf node when check parent
const firstLeaf = treeFind(
[data],
(data) =>
!isValidArray(this.getNodeValByProp("children", data)) &&
!this.getNodeValByProp("disabled", data),
(data) => this.getNodeValByProp("children", data)
);
const firstLeafKey = firstLeaf
? this.getNodeValByProp("value", firstLeaf)
: undefined;
// unselect when any child checked
const hasCheckedChild =
isValidValue(this.value) &&
!!treeFind(
[data],
(data) => this.getNodeValByProp("value", data) === this.value,
(data) => this.getNodeValByProp("children", data)
);
this.$emit(
"input",
firstLeafKey === this.value || hasCheckedChild
? undefined
: firstLeafKey
);
}
}
this.$nextTick(() => {
const checkedKeys = toValidArray(this.value);
tree.setCheckedKeys(checkedKeys);
// console.log(this.$attrs);
// this.$attrs.onCheck(data, {
// checkedKeys: tree.getCheckedKeys(),
// checkedNodes: tree.getCheckedNodes(),
// halfCheckedKeys: tree.getHalfCheckedKeys(),
// halfCheckedNodes: tree.getHalfCheckedNodes(),
// });
});
},
getNodeValByProp(prop, data) {
const propVal = this.propsMap[prop];
let tree = this.$refs.tree;
if (typeof propVal == "function") {
return propVal(
data,
tree.getNode(this.getNodeValByProp("value", data))
);
} else {
return data[propVal];
}
},
},
};
</script>
<style lang="scss" scoped></style>