vue中el-select 和el-tree二次封装实现
作者:程序猴老王
本文介绍了vue中el-select 和el-tree二次封装实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
前言
本文章是本人在开发过程中,遇到使用树形数据,动态单选或多选的需求,element中没有这种组件,故自己封装一个,欢迎多多指教
开发环境:element-UI、vue2
组件效果
单选
多选
组件引用
<treeselect v-model="form.parentId" :options="deptOptions" :props="{value:'id',label:'name',children: 'children'}" :placeholder="'选择上级部门'" />
组件代码
<template> <div> <el-select ref="treeSelect" popper-class="custom-select-popper" style="width: 100%" v-model="valueLabel" :clearable="clearable" :placeholder="placeholder" :multiple="multiple" @clear="handleClear" @remove-tag="handleRemoveTag" > <el-input v-if="filter" v-model="filterText" :placeholder="filterPlaceholder" style="margin-top: -6px;" /> <el-option :value="value" :label="option.name" class="select-options"> <el-tree id="tree-option" ref="treeSelectTree" :accordion="accordion" :data="options" :props="props" :node-key="props.value" :highlight-current="!multiple" :show-checkbox="multiple" :check-strictly="checkStrictly" :default-expand-all="expandAll" :expand-on-click-node="multiple" :filter-node-method="filterNode" @node-click="handleNodeClick" @check="handleNodeCheckbox" > <span slot-scope="{ node, data }" class="tree_label"> {{ node.label }} </span> </el-tree> </el-option> </el-select> </div> </template> <script> export default { name: 'TreeSelect', model: { prop: 'value', event: 'change' }, props: { value: { type: [String, Number, Object, Array], default: () => { return '' } }, clearable: { type: Boolean, default: true }, placeholder: { type: String, default: '请选择' }, multipleLimit: { type: Number, default: 2 }, //--------- filter props ----- filter: { type: Boolean, default: true }, filterPlaceholder: { type: String, default: '请输入关键字' }, //----- tree props ----- accordion: { type: Boolean, default: false }, options: { type: Array, default: () => { return [] } }, props: { type: Object, default: () => { return { value: 'id', label: 'label', children: 'children' } } }, expandAll: { type: Boolean, default: false }, checkStrictly: { type: Boolean, default: false } }, data() { return { tp: { value: 'id', label: 'label', children: 'children', prentId: 'parentId' }, multiple: false, valueLabel: [], option: { id: '', name: '' }, filterText: undefined, valueId: [], treeIds: [] } }, watch: { valueId() { if (this.multiple) { let valueStr = '' if (this.value instanceof Array) { valueStr = this.value.join() } else { valueStr = '' + this.value } if (valueStr !== this.valueId.join()) { this.$emit('change', this.valueId) } } else { let id = this.valueId.length > 0 ? this.valueId[0] : undefined if (id !== this.value) { this.$emit('change', id) } } }, value: { handler(newVal, oldVal) { if (newVal !== oldVal) { this.init() } } }, filterText: { handler(newVal, oldVal) { if (newVal !== oldVal) { this.$refs.treeSelectTree.filter(newVal) } } } }, mounted() { for (let key in this.tp) { if (this.props[key] !== undefined) { this.tp[key] = this.props[key] } } this.multiple = this.multipleLimit > 1 this.init() this.$nextTick(() => { if (this.multiple) { document.getElementsByClassName('el-select__tags')[0].style.maxHeight = document.getElementsByClassName('el-select')[0].offsetHeight * 2 - 4 + 'px' } }) }, methods: { init() { if (this.value instanceof Array) { this.valueId = this.value } else if (this.value === undefined) { this.valueId = [] } else { this.valueId = [this.value] } if (this.multiple) { for (let id of this.valueId) { this.$refs.treeSelectTree.setChecked(id, true, false) } } else { this.$refs.treeSelectTree.setCurrentKey(this.valueId.length > 0 ? this.valueId[0] : undefined) } this.initValueLabel() this.initTreeIds() this.initScroll() }, // 初始化滚动条 initScroll() { this.$nextTick(() => { let scrollWrap = document.querySelectorAll('.el-scrollbar .el-select-dropdown__wrap')[0] scrollWrap.style.cssText = 'margin: 0px; max-height: none; overflow: hidden;' let scrollBar = document.querySelectorAll('.el-scrollbar .el-scrollbar__bar') scrollBar.forEach((ele) => (ele.style.width = 0)) }) }, initTreeIds() { let treeIds = [] let _this = this function traverse(nodes) { for (let node of nodes) { treeIds.push(node[_this.tp.value]) if (node[_this.tp.children]) { traverse(node[_this.tp.children]) } } } traverse(this.options) this.treeIds = treeIds }, initValueLabel() { let labels = [] let _this = this for (let id of this.valueId) { let node = this.traverse(this.options, node => node[_this.tp.value] === id) if (node) { labels.push(node[_this.tp.label]) } } if (this.multiple) { this.valueLabel = labels this.option.name = labels.join() } else { this.valueLabel = labels.length > 0 ? labels[0] : undefined this.option.name = this.valueLabel } }, traverse(tree, func) { for (let node of tree) { if (func(node)) { return node } if (node[this.tp.children]) { let result = this.traverse(node[this.tp.children], func) if (result !== undefined) { return result } } } return undefined }, handleClear() { this.valueLabel = [] this.valueId = [] if (this.multiple) { for (let id of this.treeIds) { this.$refs.treeSelectTree.setChecked(id, false, false) } } else { this.$refs.treeSelectTree.setCurrentKey(null) } }, /* 树filter方法 */ filterNode(value, data) { if (!value) return true return data[this.props.label].indexOf(value) !== -1 }, /* 树节点点击事件 */ handleNodeClick(data, node) { if (!this.multiple) { this.filterText = '' this.valueId = [data[this.tp.value]] } if(node.childNodes){ node.expanded = true } }, handleNodeCheckbox(data, node) { if (this.multiple) { if (this.multipleLimit >= node.checkedKeys.length) { this.valueId = node.checkedKeys } else { this.$refs.treeSelectTree.setChecked(data, false, !this.checkStrictly) this.$message.error('最多选择' + this.multipleLimit + '项') } } }, handleRemoveTag(tag) { let n = this.traverse(this.options, node => node[this.tp.label] === tag) if (n) { this.$refs.treeSelectTree.setChecked(n[this.tp.value], false, !this.checkStrictly) } this.valueId = this.$refs.treeSelectTree.getCheckedKeys() } } } </script> <style scoped lang="scss"> ::v-deep .el-select__tags { overflow: auto; } .custom-select-popper{ } .el-scrollbar { .el-scrollbar__view { .el-select-dropdown__item { height: auto; max-height: 300px; padding: 0; overflow: hidden; overflow-y: auto; } .el-select-dropdown__item.selected { font-weight: normal; } } } ul li { .el-tree { .el-tree-node__content { height: auto; padding: 0 20px; } .el-tree-node__label { font-weight: normal; } .is-current > .el-tree-node__label{ color: #409eff; font-weight: 700; } } } .tree_label { line-height: 23px; .label_index { background-color: rgb(0, 175, 255); width: 22px; height: 22px; display: inline-flex; border-radius: 4px; .label_index_font { color: #ffffff; width: 100%; text-align: center; } } } </style>
到此这篇关于vue中el-select 和el-tree二次封装实现的文章就介绍到这了,更多相关vue el-select 和el-tree封装内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!