index.vue 4.53 KB
<template>
  <el-select :size="size" v-model="model" :disabled="disabled" :remote="remote" :remote-method="remote ? remoteMethod : undefined" :placeholder="placeholder" clearable :filterable="filterable" :multiple="multiple" :value-key="format == 'text' ? undefined : valueKey" @change="handleChange" @focus="handleFocus" :loading="loading">
    <el-option v-for="item in option" :key="format == 'text' ? item[valueProps.value] : item[valueKey]" :label="item[valueProps.label]" :value="format == 'text' ? item[valueProps.value] : item"></el-option>
  </el-select>
</template>
<script>
  export default {
    name: 'Select',
    props: {
      // 组件值
      value: [Object, String, Number, Boolean, Array],
      // 选择框禁用状态
      disabled: {
        type: Boolean,
        default: false
      },
      // 选择框提示
      placeholder: {
        type: String,
        default: '请选择'
      },
      // 选择框大小
      size: String,
      // 是否可搜索
      filterable: {
        type: Boolean,
        default: false
      },
      // 远程搜索
      remote: {
        type: Boolean,
        default: false,
      },
      // 数据源
      dataSource: {
        type: [Promise, Function, Array],
        required: true,
      },
      // 数据唯一标识
      valueKey: {
        type: String,
        default: 'value'
      },
      // 数据格式
      valueProps: {
        type: Object,
        default: () => {
          return { value: 'value', label: 'label' };
        }
      },
      // 格式化值类型 text object
      format: {
        type: String,
        default: 'text'
      },
      // 是否每次打开下拉框刷新数据
      uptodate: {
        type: Boolean,
        default: false
      },
      // 是否动态数据源
      dynamicSource: {
        type: Boolean,
        default: false
      },
      // 是否为多选
      multiple: {
        type: Boolean,
        default: false
      },
    },
    data () {
      return {
        // 绑定值(由于选择框直接绑定model后Vue会报错,因此不使用value直接绑定而使用model代替)
        model: this.multiple ? [] : undefined,
        // 下拉框数据
        option: [],
        // 加载状态
        loading: false,
      };
    },
    watch: {
      value(val) {
        if (this.multiple && !val) {
          this.model = [];
        } else {
          this.model = val;
          if (this.remote) {
            if (this.format === 'text') {
              this.remoteMethod(val, this.valueProps.value);
            } else {
              this.remoteMethod(val ? val[this.valueProps.label] : val);
            }
          }
        }
      },
      dataSource(val) {
        if (this.dynamicSource) {
          if (this.remote) {
            if (this.format === 'text') {
              this.remoteMethod(this.value, this.valueProps.value);
            } else {
              this.remoteMethod((this.value || {})[this.valueProps.label]);
            }
          } else {
            this.queryData();
          }
        }
      }
    },
    mounted() {
      this.model = !this.multiple ? this.value : this.value || [];
      if (this.remote) {
        if (this.format === 'text') {
          this.remoteMethod(this.value, this.valueProps.value);
        } else {
          this.remoteMethod((this.value || {})[this.valueProps.label]);
        }
      } else {
        this.queryData();
      }
    },
    methods: {
      // 查询数据
      async queryData() {
        this.loading = true;
        if (this.dataSource instanceof Array) {
          this.option = this.dataSource;
        } else {
          this.option = await this.dataSource();
        }
        this.loading = false;
      },
      // 远程数据方法
      async remoteMethod(query, value) {
        this.loading = true;
        const { label = 'name' } = this.valueProps || {};
        this.option = await this.dataSource({ [value || label]: query });
        this.loading = false;
      },
      // 聚焦选择框
      handleFocus() {
        if (this.remote) {
          if (this.format === 'text') {
            this.remoteMethod(this.value, this.valueProps.value);
          } else {
            this.remoteMethod((this.value || {})[this.valueProps.label]);
          }
        } else {
          if (this.uptodate || this.dataSource instanceof Array) {
            this.queryData();
          }
        }
      },
      // 选择
      handleChange(value) {
        this.model = this.value;
        this.$emit('input', value);
        this.$emit('change', value);
      },
    }
  };
</script>