vue2用elementUI做单选下拉树
1. 需求
elementUI的form表单如果实现一个下拉树的需求,也不想用级联的功能,就得自己手动搓代码了。
2. 代码
组件代码
<template><div ref="treeSelect" class="tree-select"><div class="tree-select-dom" @click="handleIconClick"><el-inputv-model="inputValue":placeholder="placeholder":clearable="clearable":disabled="disabled"readonly@click.native="handleInputClick"@clear="clearSelection"><islot="suffix":class="['el-input__icon', showPopover ? 'el-icon-arrow-up' : 'el-icon-arrow-down']"/></el-input></div><el-popoverref="popover"v-model="showPopover"v-click-outside="handleClickOutside"placement="bottom-start"trigger="manual":width="popoverWidth"popper-class="tree-select-popover"><div class="popover-content"><div class="tree-filter-input"><el-inputv-if="filterable"v-model="filterText"placeholder="输入关键字过滤"prefix-icon="el-icon-search"size="small"/></div><el-treeref="tree"class="tree-select-tree":data="data":props="defaultProps":highlight-current="true":filter-node-method="filterNode":node-key="nodeKey":default-expand-all="defaultExpandAll":expand-on-click-node="false"@node-click="handleNodeClick"><div slot-scope="{ node }" class="custom-tree-node"><span :class="['tree-node-label', { 'is-leaf': node.isLeaf }]"><i v-if="!node.isLeaf" class="el-icon-folder" /><i v-else class="el-icon-document" />{{ node.label }}</span></div></el-tree></div></el-popover></div>
</template>
export default {name: 'TreeSelect',directives: {// 自定义指令:点击外部区域关闭下拉框'click-outside': {bind: function(el, binding, vnode) {el.clickOutsideEvent = function(event) {if (!(el === event.target || el.contains(event.target))) {vnode.context[binding.expression](event);}};document.body.addEventListener('click', el.clickOutsideEvent);},unbind: function(el) {document.body.removeEventListener('click', el.clickOutsideEvent);}}},props: {// 树形数据data: {type: Array,default: () => []},// 树节点配置props: {type: Object,default: () => ({})},// 选中的节点值value: {type: [String, Number],default: null},// 占位文本placeholder: {type: String,default: '请选择'},// 是否可清空clearable: {type: Boolean,default: true},// 是否禁用disabled: {type: Boolean,default: false},// 是否可搜索filterable: {type: Boolean,default: false},// 是否默认展开所有节点defaultExpandAll: {type: Boolean,default: false},// 节点唯一标识字段nodeKey: {type: String,default: 'id'},// 弹出框宽度popoverWidth: {type: [String, Number],default: 300}},data() {return {showPopover: false,filterText: '',currentValue: null,currentLabel: '',defaultProps: {children: 'children',label: 'label'}};},computed: {inputValue() {return this.currentLabel || '';}},watch: {value: {immediate: true,handler(newVal) {this.currentValue = newVal;if (newVal) {this.setCurrentLabel();} else {this.currentLabel = '';}}},props: {immediate: true,handler(newProps) {this.defaultProps = Object.assign({}, this.defaultProps, newProps);}},filterText(val) {this.$refs.tree.filter(val);}},methods: {// 处理输入框点击handleInputClick() {if (this.disabled) return;this.togglePopover();},// 处理图标点击handleIconClick(e) {e.stopPropagation();if (this.disabled) return;this.togglePopover();},// 切换下拉框显示状态togglePopover() {this.showPopover = !this.showPopover;if (this.showPopover) {this.$nextTick(() => {if (this.currentValue) {this.$refs.tree.setCurrentKey(this.currentValue);}});}},// 节点点击事件handleNodeClick(data, node) {if (node.isLeaf) {this.currentValue = data[this.nodeKey];this.currentLabel = node.label;this.$emit('input', this.currentValue);this.$emit('change', this.currentValue, node);this.showPopover = false;} else {// 非叶子节点点击展开/折叠node.expanded = !node.expanded;}},// 设置当前显示的标签setCurrentLabel() {if (!this.data.length || !this.currentValue) return;const findNode = (data, value) => {for (const node of data) {if (node[this.nodeKey] === value) {return node;}if (node[this.defaultProps.children] && node[this.defaultProps.children].length) {const result = findNode(node[this.defaultProps.children], value);if (result) return result;}}return null;};const node = findNode(this.data, this.currentValue);if (node) {this.currentLabel = node[this.defaultProps.label];}},// 清空选择clearSelection() {this.currentValue = null;this.currentLabel = '';this.$emit('input', null);this.$emit('change', null);this.showPopover = false;},// 过滤节点filterNode(value, data, node) {if (!value) return true;return node.label && node.label.toLowerCase().indexOf(value.toLowerCase()) !== -1;},// 处理点击外部区域handleClickOutside() {this.showPopover = false;},// 获取当前选中的节点getCurrentNode() {return this.$refs.tree.getCurrentNode();}}
};
<style lang="less" scoped>
.tree-select {position: relative;display: inline-block;width: 100%;
}
.tree-select-dom{position: relative;&::after{position: absolute;top: 0;left: 0;width: 100%;height: 100%;content: '';cursor: pointer;}
}
.tree-select .el-input {cursor: pointer;
}.tree-select .el-input__icon {transition: transform 0.3s;cursor: pointer;
}.popover-content {padding: 10px;max-height: 300px;overflow-y: auto;background: #fff;border-radius: 4px;box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}.tree-filter-input {margin-bottom: 10px;
}.custom-tree-node {flex: 1;display: flex;align-items: center;font-size: 14px;padding: 5px 0;
}.tree-node-label {display: flex;align-items: center;transition: all 0.2s;
}.tree-node-label:hover {color: #3498db;
}.tree-node-label i {margin-right: 8px;
}.tree-node-label.is-leaf i {color: #2ecc71;
}
</style><style lang="less">
.tree-select-popover{background-color: #4e5470;padding: 0;.popover-content{background-color: #4e5470;display: flex;flex-direction: column;.tree-filter-input{flex-shrink: 0;}}.tree-select-tree{flex: 1;overflow-x: hidden;overflow-y: auto;}.el-input__inner{padding-left: 30px;}
}
</style>
3. 使用
<template><TreeSingleSelectv-model="selectedValue1":data="treeData":prop="{children: 'subitems',label: 'name'}"placeholder="请选择部门"node-key="id"filterable:default-expand-all="true"class="demo-component"/>
</template>
import TreeSingleSelect from '@/components/treeSelect/TreeSingleSelect.vue';export default {components: {TreeSingleSelect },data() {return {selectedValue1: '',treeData: [{id: 1,label: '集团总部',children: [{id: 101,label: '总裁办公室',children: [{ id: 1011, label: '秘书处' },{ id: 1012, label: '行政科' }]},{id: 102,label: '人力资源部',children: [{ id: 1021, label: '招聘组' },{ id: 1022, label: '培训组' },{ id: 1023, label: '薪酬组' }]}]}]}}
}
4. 效果
求关注 |
---|
![]() |