Vue.js 多级联动 Select 组件实现指南
在后台管理系统、表单录入等场景中,多级联动选择是非常常见的需求,比如区域选择(省-市-区)、分类选择(一级分类-二级分类-三级分类)等。本文将以省-市-区三级联动为例,讲解如何在 Vue.js 中实现一个通用的多级联动 Select 组件,支持动态数据加载和自定义层级。
需求分析
我们的多级联动组件需要满足以下核心需求:
- 支持自定义层级数量,不局限于三级,可扩展为二级、四级等
- 每一级的选择会影响下一级的选项,清空上级时下级选项同步清空
- 支持初始值回显,比如在编辑场景中需要默认选中已保存的省-市-区
- 数据结构可配置,默认支持树形结构的选项数据,也支持接口动态加载
组件设计与实现
1. 基础结构与数据定义
首先我们定义组件的基础结构,接收几个核心 props:options 为初始的选项树形数据,value 为当前选中的值数组(比如['广东省', '深圳市', '南山区']),level 为联动的层级数量,placeholder 为未选择时的提示文本。
// 多级联动 Select 组件
Vue.component('multi-level-select', {
props: {
// 树形结构的选项数据,每个节点包含 label 和 children 字段
options: {
type: Array,
default: () => []
},
// 当前选中的值数组,v-model 绑定使用
value: {
type: Array,
default: () => []
},
// 联动层级数量
level: {
type: Number,
default: 3
},
// 每一级的 placeholder
placeholder: {
type: String,
default: '请选择'
}
},
data() {
return {
// 每一级当前选中的值
selectedValues: [],
// 每一级的选项列表
levelOptions: []
}
},
created() {
// 初始化时处理初始值
this.initSelectedValues()
// 初始化每一级的选项
this.initLevelOptions()
},
methods: {
// 初始化选中的值
initSelectedValues() {
// 如果传入了初始 value,截断到指定层级
this.selectedValues = this.value.slice(0, this.level)
// 不足层级的部分用空字符串补全
while (this.selectedValues.length < this.level) {
this.selectedValues.push('')
}
},
// 初始化每一级的选项
initLevelOptions() {
// 第一级的选项就是传入的 options
this.levelOptions[0] = this.options
// 根据初始选中值生成后续层级的选项
for (let i = 1; i < this.level; i++) {
this.updateLevelOptions(i)
}
},
// 更新指定层级的选项
updateLevelOptions(levelIndex) {
const prevValue = this.selectedValues[levelIndex - 1]
if (!prevValue) {
// 上级未选中,当前层级选项为空
this.levelOptions[levelIndex] = []
return
}
// 找到上级选中的节点,取它的 children 作为当前层级的选项
const prevOptions = this.levelOptions[levelIndex - 1]
const selectedNode = prevOptions.find(item => item.label === prevValue)
this.levelOptions[levelIndex] = selectedNode ? (selectedNode.children || []) : []
},
// 处理某一级选择变化
handleSelectChange(levelIndex, value) {
// 更新当前层级的选中值
this.selectedValues[levelIndex] = value
// 清空当前层级之后的所有选中值
for (let i = levelIndex + 1; i < this.level; i++) {
this.selectedValues[i] = ''
}
// 更新后续层级的选项
for (let i = levelIndex + 1; i < this.level; i++) {
this.updateLevelOptions(i)
}
// 触发 input 事件,实现 v-model 双向绑定
this.$emit('input', this.selectedValues.filter(item => item !== ''))
}
}
})2. 模板渲染逻辑
模板部分需要根据层级数量动态渲染对应数量的 <select> 元素,每一级的选项和选中值都和 data 中的对应字段绑定。
<template>
<div class="multi-level-select">
<select
v-for="(level, index) in level"
:key="index"
v-model="selectedValues[index]"
@change="handleSelectChange(index, $event.target.value)"
class="level-select"
>
<option value="" disabled>{{ placeholder }}</option>
<option
v-for="option in levelOptions[index]"
:key="option.label"
:value="option.label"
>
{{ option.label }}
</option>
</select>
</div>
</template>3. 样式简单美化
给组件添加一些基础样式,让它看起来更规整,你可以根据实际项目需求调整。
.multi-level-select {
display: flex;
gap: 10px;
}
.level-select {
padding: 6px 12px;
border: 1px solid #dcdfe6;
border-radius: 4px;
font-size: 14px;
min-width: 120px;
outline: none;
}
.level-select:focus {
border-color: #409eff;
}使用示例
下面我们来看如何在页面中使用这个多级联动组件,首先准备省-市-区的树形数据,然后绑定 v-model 即可。
<div id="app">
<h3>省-市-区三级联动选择</h3>
<multi-level-select
v-model="selectedArea"
:options="areaOptions"
:level="3"
placeholder="请选择区域"
></multi-level-select>
<p>当前选中的区域:{{ selectedArea }}</p>
</div>new Vue({
el: '#app',
data() {
return {
selectedArea: [],
// 省-市-区树形数据
areaOptions: [
{
label: '广东省',
children: [
{
label: '深圳市',
children: [
{ label: '南山区' },
{ label: '福田区' },
{ label: '罗湖区' }
]
},
{
label: '广州市',
children: [
{ label: '天河区' },
{ label: '越秀区' }
]
}
]
},
{
label: '浙江省',
children: [
{
label: '杭州市',
children: [
{ label: '西湖区' },
{ label: '余杭区' }
]
}
]
}
]
}
}
})扩展说明
如果需要支持接口动态加载每一级的数据,只需要修改 updateLevelOptions 方法即可,比如当上级选中后,调用接口获取下一级的选项,再赋值给对应的 levelOptions 字段。另外如果需要在选中完成后触发回调,可以添加一个 change 事件,在 handleSelectChange 方法最后触发即可。
这个组件的设计足够灵活,你可以根据实际需求调整选项数据的字段名,比如把 label 改成 name,children 改成 subList,只需要在查找节点的时候对应修改字段名即可。