index.vue 6.16 KB
<template>
  <div class="select-container">
    <ElSelect
      v-loading="loading"
      v-bind="propsData"
      :placeholder="placeholder"
      :remote-method="fetchData"
      :remote="remote"
      :filterable="filterable"
      :disabled="disabled"
      @input="handleInput"
      @focus="onFocus"
      @blur="onBlur"
    >
      <ElOption
        v-for="item in list"
        :key="item[option.key]"
        :label="item[option.label]"
        :value="item[option.value]"
        :disabled="disabledList && disabledList.includes(item[option.key])"
      />
    </ElSelect>
  </div>
</template>

<script>
/* eslint-disable */
export default {
  props: {
    value: [String, Number, Array, Boolean],
    multiple: {
      type: Boolean,
      default: false
    },
    clearable: {
      type: Boolean,
      default: true
    },
    defaultName: {
      type: String,
      required: false,
      default: null
    },
    placeholder: {
      type: String,
      required: false,
      default: '请选择'
    },
    selectType: { // 必传,请求的接口
      type: String,
      required: true
    },
    requestParams: { // 非必传,接口请求的参数
      type: [String, Number, Object, Array, Boolean],
      required: false,
      default: undefined
    },
    remote: { // 是否开启远程搜索
      type: Boolean,
      required: false,
      default: false
    },
    filterable: {
      type: Boolean,
      required: false,
      default: false
    },
    config: { // 非必传,设置options的value, name, label
      type: Object,
      required: false
    },
    exceptId: {//非必传,排除的属性
      type: [String, Number],
      required: false
    },
    optionFuncName: { // 非必传,如果一个api文件中有多个options方法,需要指定方法名
      type: String,
      required: false,
      default: 'fetchOptions'
    },
    disabled: { // 是否能编辑
      type: Boolean,
      required: false,
      default: false
    },
    customOption: { // 自定义选项
      type: [Object, Array],
      default: null
    },
    disabledList: { // 选项禁用列表
      type: Array,
      required: false,
      default: null
    },
    defaultFirst: {
      type: Boolean,
      required: false,
      default: false
    },
    remoteSearchKey: {
      type: String,
      default: 'value'
    },
    canInput: {
      type: Boolean,
      // eslint-disable-next-line vue/no-boolean-default
      default: false
    }
  },
  data() {
    return {
      loading: false,
      list: []
    }
  },
  computed: {
    defaultValue() {
      const defaultItem = this.list.length && this.list.find(item => item.name === this.defaultName)
      if (this.list.length && this.defaultFirst === true && !this.defaultName) {
        return this.list[0][this.option.key]
      } else {
        return defaultItem && defaultItem.id || null
      }
    },
    option() {
      return {
        key: this.config && this.config.key || 'id',
        label: this.config && this.config.label || 'name',
        value: this.config && this.config.value || 'id'
      }
    },
    remoteMethod() {
      const selectType = this.selectType
      const api = selectType && require('@/api/' + selectType)
      if (!api) {
        console.log('api不存在', selectType)
        return
      }
      const optionFuncName = this.optionFuncName
      const remoteMethod = optionFuncName && api['default'][optionFuncName]
      if (!remoteMethod) {
        console.log(api)
        console.log('method不存在', remoteMethod)
        return
      }
      return remoteMethod
    },
    propsData() {
      return {
        value: this.value,
        filterable: true,
        multiple: this.multiple,
        clearable: this.clearable,
        defaultFirstOption: true
      }
    }
  },
  created() {
    this.fetchData && this.fetchData()
  },
  methods: {
    onBlur(e) {
      console.log('onBlur ---e', e.target.value, this.value)
      if (this.canInput && !this.value) {
        const value = e.target.value
        this.handleInput(value)
      }
    },
    onFocus() {
      if (this.remote) {
        // 解决远程搜索输入关键字后输入框失去焦点,关键词清空,再次点击输入框选项为上次搜索内容
        this.fetchData()
      }
    },
    fetchData(value) {
      const remote = this.remote
      const remoteMethod = this.remoteMethod
      if (!remoteMethod) {
        return
      }
      const requestParams = this.requestParams || {}
      const query = (remote && value && typeof requestParams === 'object') ? {
        ...this.requestParams,
        p: {
          ...this.requestParams?.p,
          [this.remoteSearchKey]: value
        }
      } : this.requestParams
      this.loading = true
      remoteMethod(query).then(this.onFetchListSuccess)
    },
    onFetchListSuccess(response) {
      this.list = Array.isArray(response.data) === true ? response.data : response.data.list
      if (this.list && this.list.length === 0) {
        this.$emit('empty', true)
      }
      this.list = this.list.length && this.list.filter(item => item[this.option.value] !== this.exceptId)
      if (this.customOption) {
        this.list.unshift(this.customOption)
      }
      if (!this.multiple && this.value && !(this.list && this.list.find(item => item[this.option.value] === this.value))) {
        if (!this.canInput) {
          this.handleInput(this.defaultValue)
        }
      }
      if (this.multiple && this.value && this.value.length > 0) {
        if (!this.remote) {
          const value = this.value.filter(itemVal => this.list.find(item => item[this.option.value] === itemVal))
          this.handleInput(value && value.length > 0 ? value : this.defaultValue ? [this.defaultValue] : [])
        }
      }
      if (this.value === null || this.value === undefined) {
        this.handleInput(this.defaultValue)
      }
      this.loading = false
    },
    handleInput(value) {
      console.log('handleInput', value)
      this.$emit('input', value)
      if (!this.multiple) {
        const option = this.list?.length ? this.list.find(item => item[this.option.value] === value) : {}
        console.log('选择项', option)
        this.$emit('select', option)
      }
    }
  }
}
</script>