基于Vue构建n级联动下拉框组件
利用递归组件实现
组件参数:
- v-model 用于双向绑定的属性,目前绑定option里的id
- options 一个数组,例如[{title:'a',id:1,children:[{title:'b',id:2}]}]
<template> <div class="selectors"> <template v-if="!hasChildren"> <select @change="onChanged" :value="mvalue"> <option v-for="option in options" :value="option.id" :key="option.id">{{option.title}}</option> </select> </template> <template v-else> <select @change="onParentChanged" :value="pvalue"> <option v-for="option in options" :value="option.id" :key="option.id">{{option.title}}</option> </select> <PropertySelector :mvalue="cvalue" @change="onChidrenChanged" :options="childOptions" /> </template> </div> </template> <script lang="ts"> import { Component, Prop, Vue, Model, Watch } from "vue-property-decorator"; @Component export default class PropertySelector extends Vue { private childOptions: any[] = []; private cvalue: string | number = ""; private pvalue: string | number = ""; @Model("change") private mvalue!: string | number; @Prop() private options!: any[]; @Watch("options") private onOptionsChanged(newOptions: any[], oldVal: any[]) { /*options更新时,处理相关内容*/ if (newOptions.length > 0) { // 更新子options const childOptions = newOptions[0].children; this.$set(this, "childOptions", childOptions); // 更新选中值 const pvalue = newOptions[0].id; if (this.hasChildren) { this.pvalue = pvalue; } else { this.$emit("change", "" + pvalue); } } } private onChidrenChanged(val: number | string) { this.cvalue = val; this.$emit("change", val); } private onParentChanged(event: any) { this.pvalue = event.target.value; this.updateChildren(event.target.value); } private onChanged(event: any) { this.$emit("change", event.target.value); } private updateChildren(pid: number | string) { const finded = this.options.filter((op: any) => { return Number(op.id) === Number(pid); }); if (finded.length > 0) { this.$set(this, "childOptions", finded[0].children); } } private mounted() { if (this.options && this.options.length > 0) { const pid = this.options[0].id; this.updateChildren(pid); if (this.hasChildren) { this.pvalue = pid; } else { this.$emit("change", pid); } } } private get hasChildren() { if (this.childOptions) { return this.childOptions.length > 0; } else { return false; } } } </script> <!-- Add "scoped" attribute to limit CSS to this component only --> <style scoped lang="scss"> .selectors { display: inline-flex; } </style>