可自定义设置以下属性:
可选项数据源(options),类型:Array,默认 []
(v-model)级联选中项(selectedValue),类型:Array,默认[]
下拉字典项的文本字段名(label),类型:string,默认 'label'
下拉字典项的值字段名(value),类型:string,默认 'value'
下拉字典项的后代字段名(children),类型:string,默认 'children'
当此项为true时,点选每级菜单选项值(v-model)都会发生变化;否则只有选择三级选项后选项值才会变化(changeOnSelect),类型:boolean,默认 false
下拉层级(zIndex),类型:number,默认 1
级联下拉框相互间隙宽度,类型:number,单位px,默认 8px
三级下拉各自宽度(width),类型:number|number[],默认 160
下拉框高度(height),类型:number,单位px,默认 36px
三级各自是否禁用(disabled),类型:boolean|boolean[],默认 false
三级下拉各自占位文本(placeholder),类型:string|string[],默认 '请选择'
下拉面板最多能展示的下拉项数,超过后滚动显示(num),类型:number,默认 6
效果如下图:
①创建级联下拉选择器Cascader.vue:
<template>
<div class="m-cascader-wrap" :style="`height: ${height}px;`">
<VueAmazingSelector
:style="`margin-right: ${gap}px; z-index: ${zIndex};`"
:options="firstOptions"
v-model="selectedValue[0]"
:label="label"
:value="value"
:disabled="disabled"
:width="Array.isArray(width) ? width[0] : width"
:height="height"
:num="num"
:placeholder="Array.isArray(placeholder) ? placeholder[0] : placeholder"
@change="onFirstChange" />
<VueAmazingSelector
:style="`margin-right: ${gap}px; z-index: ${zIndex};`"
:options="secondOptions"
v-model="selectedValue[1]"
:label="label"
:value="value"
:disabled="disabled"
:width="Array.isArray(width) ? width[1] : width"
:height="36"
:num="num"
:placeholder="Array.isArray(placeholder) ? placeholder[1] : placeholder"
@change="onSecondChange" />
<VueAmazingSelector
:style="`z-index: ${zIndex};`"
:options="thirdOptions"
v-model="selectedValue[2]"
:label="label"
:value="value"
:disabled="disabled"
:width="Array.isArray(width) ? width[2] : width"
:height="height"
:num="num"
:placeholder="Array.isArray(placeholder) ? placeholder[2]:placeholder"
@change="onThirdChange" />
</div>
</template>
<script>
// yarn add VueAmazingSelector
import { VueAmazingSelector } from 'vue-amazing-selector'
export default {
name: 'Cascader',
components: {
VueAmazingSelector
},
model: {
prop: 'selectedValue',
event: 'model'
},
props: {
options: { // 可选项数据源
type: Array,
default: () => []
},
selectedValue: { // (v-model)级联选中项
type: Array,
default: () => []
},
label: { // 下拉字典项的文本字段名
type: String,
default: 'label'
},
value: { // 下拉字典项的值字段名
type: String,
default: 'value'
},
children: { // 下拉字典项的后代字段名
type: String,
default: 'children'
},
changeOnSelect: { // 当此项为true时,点选每级菜单选项值(v-model)都会发生变化;否则只有选择三级选项后选项值才会变化
type: Boolean,
default: false
},
zIndex: { // 下拉层级
type: Number,
default: 1
},
gap: { // 级联下拉框相互间隙宽度,单位px,默认8px
type: Number,
default: 8
},
width: { // 三级下拉各自宽度
type: [Number, Array],
default: 160
},
height: { // 下拉框高度
type: Number,
default: 36
},
disabled: { // 统一是否全部禁用
type: Boolean,
default: false
},
placeholder: { // 三级下拉各自占位文本
type: [String, Array],
default: '请选择'
},
num: { // 下拉面板最多能展示的下拉项数,超过后滚动显示
type: Number,
default: 6
}
},
data () {
return {
values: this.selectedValue,
labels: [],
firstOptions: this.options,
secondOptions: [],
thirdOptions: []
}
},
watch: {
selectedValue (to) {
this.values = to
if (to.length) {
this.initCascader(to)
this.initLabels(to)
}
}
},
mounted () {
if (this.selectedValue.length) {
this.initCascader(this.selectedValue)
this.initLabels(this.selectedValue)
}
},
methods: {
findChildren (options, index) {
const len = options.length
for (let i = 0; i < len; i++) {
if (options[i][this.value] === this.selectedValue[index]) {
return options[i][this.children] || []
}
}
return []
},
initCascader (selectedValue) {
this.secondOptions = this.findChildren(this.firstOptions, 0)
this.thirdOptions = []
if (selectedValue.length > 1) {
this.thirdOptions = this.findChildren(this.secondOptions, 1)
}
},
findLabel (options, index) {
const len = options.length
for (let i = 0; i < len; i++) {
if (options[i][this.value] === this.selectedValue[index]) {
return options[i][this.label]
}
}
return this.selectedValue[index]
},
initLabels (selectedValue) {
this.labels[0] = this.findLabel(this.firstOptions, 0)
if (selectedValue.length > 1) {
this.labels[1] = this.findLabel(this.secondOptions, 1)
}
if (selectedValue.length > 2) {
this.labels[2] = this.findLabel(this.thirdOptions, 2)
}
},
onFirstChange (value, label, index) { // 一级下拉回调
this.values = [value]
this.labels = [label]
if (this.changeOnSelect) {
this.$emit('model', this.values)
this.$emit('change', this.values, this.labels)
}
// 获取二级下拉选项
this.secondOptions = this.firstOptions[index][this.children] || []
this.thirdOptions = []
},
onSecondChange (value, label, index) { // 二级下拉回调
this.values = [this.values[0], value]
this.labels = [this.labels[0], label]
if (this.changeOnSelect) {
this.$emit('model', this.values)
this.$emit('change', this.values, this.labels)
}
// 获取三级下拉选项
this.thirdOptions = this.secondOptions[index][this.children] || []
},
onThirdChange (value, label) { // 三级下拉回调
this.values[2] = value
this.labels[2] = label
this.$emit('model', this.values)
this.$emit('change', this.values, this.labels)
}
}
}
</script>
<style lang="less" scoped>
.m-cascader-wrap {
display: inline-block;
}
</style>
②在要使用的页面引入:
<template>
<div class="selector">
<Cascader
v-model="selectedValue"
label="label"
value="value"
children="children"
changeOnSelect
:options="options"
:zIndex="9"
:gap="8"
:provinceWidth="120"
:cityWidth="120"
:areaWidth="120"
:width="120"
:height="36"
:provinceDisabled="false"
:cityDisabled="false"
:disabled="false"
:num="6"
@change="onChange" />
</div>
</template>
<script>
import Cascader from '@/components/Cascader'
export default {
name: 'Selector',
components: {
Cascader
},
data () {
return {
options: [
{
value: '1',
label: '北京',
children: [
{
value: '11',
label: '北京市',
children: [
{
value: '111',
label: '东城区'
},
{
value: '112',
label: '西城区'
}
]
}
]
},
{
value: '2',
label: '浙江',
children: [
{
value: '21',
label: '杭州市',
children: [
{
value: '211',
label: '西湖区'
},
{
value: '212',
label: '余杭区'
}
]
},
{
value: '22',
label: '湖州市',
children: [
{
value: '221',
label: '吴兴区'
},
{
value: '222',
label: '安吉区'
}
]
}
]
}
],
selectedValue: []
}
},
watch: {
selectedValue (to) {
console.log('selectedValue:', to)
}
},
mounted () {
setTimeout(() => {
this.selectedValue = ['1', '11', '112']
}, 1000)
},
methods: {
onChange (value, label) {
console.log('value:', value)
console.log('label:', label)
}
}
}
</script>
<style lang="less" scoped>
</style>