vue中el-select 和el-tree二次封裝實(shí)現(xiàn)
前言
本文章是本人在開發(fā)過程中,遇到使用樹形數(shù)據(jù),動態(tài)單選或多選的需求,element中沒有這種組件,故自己封裝一個,歡迎多多指教
開發(fā)環(huán)境: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: '請輸入關(guān)鍵字'
},
//----- 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
},
/* 樹節(jié)點(diǎn)點(diǎn)擊事件 */
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 + '項(xiàng)')
}
}
},
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>到此這篇關(guān)于vue中el-select 和el-tree二次封裝實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)vue el-select 和el-tree封裝內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
關(guān)于element?ui中的el-scrollbar橫向滾動問題
這篇文章主要介紹了關(guān)于element?ui中的el-scrollbar橫向滾動問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-08-08
vue + element-ui的分頁問題實(shí)現(xiàn)
這篇文章主要介紹了vue + element-ui的分頁問題實(shí)現(xiàn),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-12-12
Vue3?封裝擴(kuò)展并簡化Vuex在組件中的調(diào)用問題
這篇文章主要介紹了Vue3?封裝擴(kuò)展并簡化Vuex在組件中的調(diào)用,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-01-01
Vue3中SetUp函數(shù)的參數(shù)props、context詳解
我們知道setup函數(shù)是組合API的核心入口函數(shù),下面這篇文章主要給大家介紹了關(guān)于Vue3中SetUp函數(shù)的參數(shù)props、context的相關(guān)資料,需要的朋友可以參考下2021-07-07
vue3中實(shí)現(xiàn)異步組件的方法實(shí)例
前端開發(fā)經(jīng)常遇到異步的問題,請求函數(shù)、鏈接庫等,下面這篇文章主要給大家介紹了關(guān)于vue3中實(shí)現(xiàn)異步組件的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-06-06

