RemoteTreeSelect.vue
<template>
<el-select
size="small"
class="component remote-tree-select"
popper-class="component remote-tree-select dropdown-wrapper"
:placeholder="placeholder"
:loading="loading"
:value="localValue"
multiple
collapse-tags
filterable
:filter-method="searchFunc"
@visible-change="e => !e && searchFunc()"
@remove-tag="removeItem">
<el-option v-show="false" value=""></el-option>
<el-option
v-for="item in selectedOptions"
:key="item[defaultProps.key]"
:value="item[defaultProps.key]"
:label="item[defaultProps.label]"
v-show="false">
</el-option>
<el-tree
ref="treeEl"
lazy
:load="syncFunc"
show-checkbox
check-strictly
@check="selectNode"
:data="localOptions"
:props="defaultProps"
:node-key="defaultProps.key"
:default-checked-keys="localValue"
:render-content="renderTreeLabel">
</el-tree>
</el-select>
</template>
<script>
export default {
name: 'RemoteTreeSelect',
props: {
placeholder: {
type: String,
default: '请选择'
},
value: {
types: Object || String,
default: () => ({})
},
props: {
type: Object,
default: () => ({
key: 'id',
children: 'children',
label: 'label'
})
},
syncOptionsFunc: {
type: Function
},
remoteSearchFunc: {
type: Function
}
},
computed: {
defaultProps () {
return Object.assign({
key: 'id',
children: 'children',
label: 'label'
}, this.props)
},
localValue () {
return this.value[this.defaultProps.key] && [this.value[this.defaultProps.key]] || []
}
},
data: () => ({
loading: false,
searchTxt: '',
localOptions: [],
selectedOptions: [],
option: []
}),
methods: {
checkNode (key, obj = this.option) {
if (this.option.length === 0) {
throw undefined
}
const index = obj.findIndex(item => item[this.defaultProps.key] == key)
if (index !== -1) {
throw obj[index][this.defaultProps.children]
}
for (let i = 0; i < obj.length; i++) {
obj[i][this.defaultProps.children] && this.checkNode(key, obj[i][this.defaultProps.children])
}
throw undefined
},
setNode (key, node, obj = this.option) {
if (this.option.length === 0) {
this.option = node
throw '无记录'
}
const index = obj.findIndex(item => item[this.defaultProps.key] == key)
if (index !== -1) {
obj[index][this.defaultProps.children] = node
throw '命中'
}
for (let i = 0; i < obj.length; i++) {
obj[i][this.defaultProps.children] && this.setNode(key, node, obj[i][this.defaultProps.children])
}
},
syncFunc (/* 节点 node */ node = {}, /* 请求成功返回节点值 */ resolve) {
try {
this.checkNode(node.key)
} catch (result) {
if (result) {
resolve(result)
return
}
}
this.syncOptionsFunc(node, e => {
try {
this.setNode(node.key, e)
} catch (err) {
// console.log(err)
}
resolve(e)
})
},
searchFunc (v = '') {
this.searchTxt = v
if (v) {
this.loading = true
this.remoteSearchFunc(v, e => {
this.localOptions = e
this.loading = false
})
} else {
this.localOptions = this.option
}
},
removeItem () {
this.selectedOptions = []
this.$refs.treeEl.setCheckedKeys([])
this.$emit('input', '')
},
selectNode (e) {
if (!this.localValue.includes(e[this.defaultProps.key])) {
this.selectedOptions = [{
[this.defaultProps.key]: e[this.defaultProps.key],
[this.defaultProps.label]: e[this.defaultProps.label]
}]
this.$emit('input', {
[this.defaultProps.key]: e[this.defaultProps.key],
[this.defaultProps.label]: e[this.defaultProps.label]
})
this.$refs.treeEl.setCheckedKeys([e[this.defaultProps.key]])
} else {
this.selectedOptions = []
this.$emit('input', '')
}
},
renderTreeLabel (h, { node }) {
const { label } = node
let renderContent = [ label ]
if (
this.searchTxt
&& label.indexOf(this.searchTxt) !== -1
) {
let temp = label.split(this.searchTxt)
let iterator = temp.length * 2 - 2
for (let i = 0; i < iterator; i += 2) {
temp.splice(
i + 1,
0,
h('span', { style: 'color: #4B8BEE' }, this.searchTxt)
)
}
renderContent = [ ...temp ]
}
return h('span', {}, renderContent)
}
}
}
</script>
App.vue
{{selected}}