当前位置: 首页 > news >正文

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. 效果

在这里插入图片描述

求关注
在这里插入图片描述
http://www.lryc.cn/news/599382.html

相关文章:

  • 激光频率梳 3D 轮廓检测在深凹槽检测的应用有哪些
  • AI-调查研究-38-多模态大模型量化 主流视觉语言任务的量化评估策略分析
  • 在kdb+x中使用SQL
  • Python高效操作Kafka实战指南
  • 专为小靶面工业相机的抗振微距镜头
  • C++ string:准 STL Container
  • Java线程基础面试复习笔记
  • 相机ROI 参数
  • 力扣-32.最长有效括号
  • Python(32)Python内置函数全解析:30个核心函数的语法、案例与最佳实践
  • 188.买卖股票的最佳时机IV 309.最佳买卖股票时机含冷冻期 714.买卖股票的最佳时机含手续费
  • 《C++初阶之STL》【vector容器:详解 + 实现】
  • Python应用append()方法向列表末尾添加元素
  • 深入解析HBase如何保证强一致性:WAL日志与MVCC机制
  • selenium 元素定位
  • 【unitrix】 6.15 “非零非负一“的整数类型(NonZeroNonMinusOne)特质(non_zero_non_minus_one.rs)
  • XCTF-crypto-幂数加密
  • Docker 实战大纲
  • Windows Installer安全深度剖析
  • SQL基础⑭ | 变量、流程控制与游标篇
  • 解放生产力:Amazon API Gateway 与 Amazon Lambda 的优雅组合
  • adb 下载并安装
  • 使用Python绘制金融数据可视化工具
  • SR9900低功耗USB 2.0转百兆以太网控制器芯片,SR9900规格书,SR9900原理图
  • 【第四章:大模型(LLM)】01.神经网络中的 NLP-(1)RNN、LSTM 和 GRU 的基本原理和应用
  • Linux网络框架分析
  • 使用vllm创建相同模型的多个实例,使用nginx进行负载均衡,提高模型吞吐量
  • RabbitMQ—HAProxy负载均衡
  • 数仓主题域划分
  • [linux]Haproxy七层代理