目录
简述
效果
功能描述
代码实现
总结
简述
基于Element UI内置的Select下拉和Tree树形组件,组合封装的树状下拉选择器。
效果
先看效果:
下拉状态:
选择后状态:
选择的数据:
功能描述
1、加载树结构,实现树状下拉选择器;
2、可通过关键词实现本地和远程过滤;
3、高亮选择行;
4、设置默认选择行;
5、可直接应用在form表单;
代码实现
树状下拉组件代码:
<!--
树状下拉选择框:
1、加载树结构,实现树状下拉选择组件;
2、可通过关键词实现本地和远程过滤;
3、高亮选择行;
4、设置默认选择行;
5、可直接应用在form表单;
-->
<template>
<el-select
ref="selectRef"
clearable
:filterable="filterableFor || remoteFor"
:remote="filterableFor || remoteFor"
:remote-method="selectRemoteMethod"
v-model="currentLabel"
@visible-change="handleVisibleChange"
@clear="handleClear"
>
<el-option
style="height: 100%; padding: 0"
value=""
v-loading="loading"
element-loading-text="加载中..."
element-loading-spinner="el-icon-loading"
>
<el-tree
ref="treeRef"
:data="dataOfTree"
:node-key="defaultProps.value"
:props="defaultProps"
highlight-current
default-expand-all
:current-node-key="selectedNode.value"
:expand-on-click-node="false"
@node-click="handleNodeClicked"
:filter-node-method="filterNode"
>
</el-tree>
</el-option>
</el-select>
</template>
<script>
export default {
name: 'SelectTree',
components: {},
model: {
prop: 'inputValue',
event: 'myInputEvent'
},
props: {
// 默认选中值
defaultValue: {
type: Number
},
// 是否支持搜索,本地搜索,与远程搜索配置互斥。
filterable: {
type: Boolean,
default: false
},
// 是否远程搜索,要设置远程搜索方法
remote: {
type: Boolean,
default: false
},
// 远程方法
remoteMethod: {
type: Function
},
treeOptions: {
type: Array,
default: () => {
return []
}
},
defaultProps: {
type: Object,
default: () => {
return {
children: 'children',
label: 'label',
value: 'value'
}
}
}
},
watch: {
treeOptions: {
handler(newValue) {
this.loading = false
this.dataOfTree = JSON.parse(JSON.stringify(newValue))
// 保留源数据;
this.dataSource = JSON.parse(JSON.stringify(newValue))
},
deep: true,
immediate: false
},
defaultValue: {
handler(newValue) {
this.selectedNode = {}
this.currentLabel = undefined
this.currentValue = newValue
this.$nextTick(() => {
// 过滤方式是通过value还是label;
this.isFilterWithValue = true
if (this.dataOfTree) {
this.$refs.treeRef.filter(newValue)
}
})
},
deep: true,
immediate: true
}
},
computed: {
// 是否支持搜索,本地搜索,与远程搜索配置互斥。
filterableFor() {
return this.remote ? false : this.filterable
},
remoteFor() {
return this.filterable ? false : this.remote
}
},
data() {
return {
selectedNode: {},
loading: false,
currentValue: undefined,
currentLabel: undefined,
dataOfTree: []
}
},
created() {
this.dataOfTree = JSON.parse(JSON.stringify(this.treeOptions))
// 保留源数据;
this.dataSource = JSON.parse(JSON.stringify(this.treeOptions))
},
mounted() {
},
methods: {
selectRemoteMethod(val) {
this.isFilterWithValue = false
if (this.filterableFor) {
// 本地过滤
this.$refs.treeRef.filter(val)
} else if (this.remoteFor) {
// 远程搜索
this.loading = true
this.remoteMethod(val)
}
},
handleClear() {
// 如果内容被清空
this.selectedNode = {}
this.currentLabel = undefined
this.currentValue = undefined
const result = this.buildEmptyResult()
this.$emit('myInputEvent', result)
this.$emit('onNodeSelectEvent', result)
},
handleVisibleChange(visible) {
if (!visible) {
// 先移除所有数据;
this.dataOfTree.splice(0)
// 恢复原来的所有数据;
this.dataOfTree.splice(0, 0, ...this.dataSource)
// 本地过滤
this.$refs.treeRef.filter('')
}
},
filterNode(value, data) {
if (!value) {
return data
}
if (this.isFilterWithValue) {
if (data[this.defaultProps.value] === value) {
this.selectedNode = data
this.currentLabel = data[this.defaultProps.label]
this.$refs.treeRef.setCurrentKey(this.selectedNode[this.defaultProps.value])
const result = this.buildResultByNodeData(data)
this.$emit('myInputEvent', result)
}
} else {
return data[this.defaultProps.label].indexOf(value) !== -1
}
return data
},
closeSelect() {
this.$refs.selectRef.blur()
},
/**
* @param data
* @param node
* @param comp
*/
handleNodeClicked(data, node, comp) {
this.selectedNode = data
this.currentLabel = data[this.defaultProps.label]
this.currentValue = data[this.defaultProps.value]
const result = this.buildResultByNodeData(data)
this.$emit('myInputEvent', result)
this.$emit('onNodeSelectEvent', result)
this.closeSelect()
},
buildResultByNodeData(data) {
return {
node: data[this.defaultProps.value],
data: {
label: data[this.defaultProps.label],
value: data[this.defaultProps.value]
},
meta: data
}
},
buildEmptyResult() {
return {
node: undefined,
data: {
label: undefined,
value: undefined
},
meta: undefined
}
}
}
}
</script>
<style lang='scss' scoped>
</style>
应用示例1:
<template>
<div>
<div>测试表单</div>
<el-form
ref="demandFormRef"
:model="form"
label-suffix=":"
status-icon
label-position="left"
>
<el-form-item label="树" label-width="85px" prop="tree">
<select-tree
v-model="form.tree"
filterable
:tree-options="treeOptions"
:default-value="form.tree.node"
@onNodeSelectEvent="handleNodeSelectEvent($event)"
/>
</el-form-item>
</el-form>
<div>
<el-button @click="reset">重置</el-button>
<el-button @click="submit">提交</el-button>
</div>
</div>
</template>
<script>
import {Message} from 'element-ui'
import SelectTree from '@/views/select-tree/SelectTree.vue'
export default {
name: 'SelectTreeExample',
components: {
SelectTree
},
props: {},
data() {
return {
form: {
tree: {node: undefined, data: {}}
},
treeOptions: [{
value: 1,
label: '一级 1',
children: [{
value: 11,
label: '二级 1-1',
children: [{
value: 111,
label: '三级 1-1-1'
}]
}]
}, {
value: 2,
label: '一级 2',
children: [{
value: 21,
label: '二级 2-1'
}]
}, {
value: 3,
label: '一级 3',
children: [{
value: 31,
label: '二级 3-1',
children: [{
value: 311,
label: '三级 3-1-1'
}]
}, {
value: 32,
label: '二级 3-2',
children: [{
value: 321,
label: '三级 3-2-1'
}]
}]
}]
}
},
mounted() {
// 模拟接口请求,反显选择数据
setTimeout(() => {
this.form.tree.node = 2
}, 1000)
},
methods: {
reset() {
this.form.tree = {node: undefined, data: {}}
},
submit() {
const data = this.form.tree.data
Message.info(`选中节点名称是${data.label},值是${data.value}`)
},
handleNodeSelectEvent(dataSelected) {
}
}
}
</script>
<style lang='scss' scoped>
</style>
应用示例2:
<template>
<div>
<div>测试表单</div>
<el-form
ref="demandFormRef"
:model="form"
label-suffix=":"
status-icon
label-position="left"
>
<el-form-item label="树" label-width="85px" prop="tree">
<select-tree
v-model="form.tree"
remote
:remote-method="handleRemote"
:tree-options="treeOptions"
:default-value="form.tree.node"
@onNodeSelectEvent="handleNodeSelectEvent($event)"
/>
</el-form-item>
</el-form>
<div>
<el-button @click="reset">重置</el-button>
<el-button @click="submit">提交</el-button>
</div>
</div>
</template>
<script>
import {Message} from 'element-ui'
import SelectTree from '@/views/components/SelectTree.vue'
export default {
name: 'SelectTreeExample',
components: {
SelectTree
},
props: {},
data() {
return {
form: {
tree: {node: undefined, data: {}}
},
treeOptions: [{
value: 1,
label: '一级 1',
children: [{
value: 11,
label: '二级 1-1',
children: [{
value: 111,
label: '三级 1-1-1'
}]
}]
}, {
value: 2,
label: '一级 2',
children: [{
value: 21,
label: '二级 2-1'
}]
}, {
value: 3,
label: '一级 3',
children: [{
value: 31,
label: '二级 3-1',
children: [{
value: 311,
label: '三级 3-1-1'
}]
}, {
value: 32,
label: '二级 3-2',
children: [{
value: 321,
label: '三级 3-2-1'
}]
}]
}]
}
},
mounted() {
// 模拟接口请求,反显选择数据
setTimeout(() => {
this.form.tree.node = 2
}, 1000)
},
methods: {
reset() {
this.form.tree = {node: undefined, data: {}}
},
submit() {
const data = this.form.tree.data
Message.info(`选中节点名称是${data.label},值是${data.value}`)
},
handleRemote() {
setTimeout(() => {
this.treeOptions = [{
value: 1,
label: '一级 1',
children: [{
value: 11,
label: '二级 1-1',
children: [{
value: 111,
label: '三级 1-1-1'
}]
}]
}]
}, 3000)
},
handleNodeSelectEvent(dataSelected) {
}
}
}
</script>
<style lang='scss' scoped>
</style>
总结
本示例中,部分实现细节或者写法,可根据实际需要调整,树状下拉的实现方式有多种,这只是其中一种,只要符合实际需求就可以。
如果发现问题,欢迎随时提出,共同改进。