基于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>

  

posted @ 2020-06-05 12:11  夕晖  阅读(958)  评论(0编辑  收藏  举报