Выберите реализацию компонента

Vue.js

Если вариантов слишком много, используйте раскрывающееся меню для отображения и выбора содержимого.

Основные возможности компонента Select:
  • Двусторонняя привязка данных, как отображать выбранный элемент при изменении выпадающего списка;
  • Различие между одиночным и множественным выбором и соответствующей обработкой.

1. Примеры

最终效果

код

<fat-select v-model="inputValue">
    <fat-option
        v-for="item in options"
        :key="item.value"
        :label="item.label"
        :value="item.value"
    >{{ item.label }}</fat-option>
</fat-select>

Адрес экземпляра:Выберите экземпляр

Кодовый адрес:Github UI-Library

2. Принцип

SelectБазовая структура компонента выглядит следующим образом

最终效果

В основном его можно разделить на две части:

  • Окно отображения: используется для отображения выбранных элементов, включая кнопку отмены;
  • Раскрывающийся список: содержит выбранные выделенные элементы, отключенные элементы, параметры выбора по умолчанию и т. д., с операцией щелчка для выбора и повторного щелчка для отмены.

Поле дисплея с толстым выбором:

<template>
    <div
      :class="['select-wrapper', { 'is-disabled': disabled }]"
      tabindex="0"
      @click.stop="isOpen = !disabled && !isOpen"
      @blur="handleBlur"
    >
      <div class="select-top-part">
        <template v-if="!selectItems.length">
          <span class="placeholder">{{ placeholder }}</span>
        </template>

        <template v-else>
          <div>{{ selectItems[0].label }}</div>
        </template>
      </div>
      <!-- 下拉框 -->
      <div class="select-bottom-part" v-show="isOpen">
        <slot></slot>
      </div>
    </div>
</template>

<script>
export default {
  props: {
    placeholder: { type: String, default: "请选择" },
    optionKey: { type: String, default: "value" },
    value: { type: [String, Object, Number, Array] }
  },
  model: {
    prop: "value",
    event: "input"
  },
  data() {
    return {
      isOpen: false,

      selectValue: [],

      selectItems: []
    };
  },
  provide() {
    return {
      fatSelect: this
    };
  },
  watch: {
    value: {
      handler(value) {
        const { multiple } = this;
        const init = value ? value : multiple ? [] : "";
        this.selectValue = multiple ? [...init] : init;
      },
      immediate: true
    },
    selectValue: {
      handler(value) {
        this.selectItems = [];
      }
    }
  },
  methods: {
    handleDelete(item) {
      const { value } = item;
      this.selectValue = this.selectValue.filter(item => item !== value);
      this.$emit("input", this.selectValue);
      this.$emit("change", this.selectValue);
    },
    handleBlur(event) {
      this.isOpen = false;
      this.$emit('blur', event);
    }
    ...
  }
};
</script>

использоватьtabIndexатрибут делает внешнийdivспособный вызватьblurсобытие и сворачивает раскрывающийся список, если он не в фокусе.

<div
    :class="['select-wrapper', { 'is-disabled': disabled }]"
    tabindex="0"
    @click.stop="isOpen = !disabled && !isOpen"
    @blur="handleBlur"
>
    ...
    <!-- 下拉框 -->
    <div class="select-bottom-part" v-show="isOpen">
        <slot></slot>
    </div>
</div>

handleBlur(event) {
    this.isOpen = false;
    this.$emit('blur', event);
}

Компонент реализует двустороннюю привязку данных, когдаv-modelПри изменении соответствующего значенияSelectЗначение компонента также изменяется, но выбранный элемент отображается в поле отображения.labelсвойство, поэтому значение будет выбраноselectValueи выбранные элементыselectItemsдифференцировать.

Настроить заодноv-modelсвязанные свойства во время мониторингаwatchСвязанныйvalueдетали следующим образом

model: {
    prop: "value",
    event: "input"
},
watch: {
    value: {
        handler(value) {
            const { multiple } = this;
            const init = value ? value : multiple ? [] : "";
            this.selectValue = multiple ? [...init] : init;
        },
        immediate: true
    }
}

использовать одновременноprovideВнедрите зависимость во все его раскрывающиеся списки для доступаselectValueа такжеselectItemsЖдатьpropа такжеdata.

provide() {
    return {
        fatSelect: this
    };
}

По умолчаниюoptionKey: { type: String, default: "value" }В качестве уникального идентификатора раскрывающегося элемента используется значение по умолчанию value , которое также можно настроить.

раскрывающийся список толстых опций:

Используйте слот, чтобы вставить раскрывающийся списокSelectВ компоненте его конкретное определение выглядит следующим образом

<template>
  <div
    :class="['select-option-wrapper', { 'is-selected': isSelect }, { 'is-disabled': disabled }]"
    @click.stop="handleClick"
  >
    <slot></slot>
  </div>
</template>
<script>
export default {
  props: {
    value: { type: [Object, String, Number], required: true },
    label: { type: String },
    disabled: { type: Boolean, defa: false }
  },
  inject: ["fatSelect"],
  computed: {
    isSelect() {
      const {
        fatSelect: { optionKey, selectItems }
      } = this;
      const key = this[optionKey] || this.$attrs[optionKey];

      return selectItems.find(item => item.key === key);
    }
  },
  watch: {
    ["fatSelect.selectValue"]: {
      handler(newValue) {
        const {
          value,
          label,
          fatSelect: { optionKey, multiple, selectValue }
        } = this;
        const key = this[optionKey] || this.$attrs[optionKey];

        if (
          newValue === value ||
          (Array.isArray(newValue) && newValue.find(item => item === value))
        ) {
          if (!multiple) {
            this.fatSelect.selectItems = [
              {
                key,
                label,
                value
              }
            ];
          } else {
            this.fatSelect.selectItems.push({
              key,
              label,
              value
            });
          }
        }
      },
      immediate: true
    }
  },
  methods: {
    ...
  }
};
</script>

использоватьinject: ["fatSelect"]поставить вышеprovideизSelectкомпонент вводится в текущую опцию,

пройти черезthis.fatSelectдля доступа к родительскому компонентуselectItemsчтобы определить, выбран ли текущий параметр.

isSelect() {
    const {
        fatSelect: { optionKey, selectItems }
    } = this;
    const key = this[optionKey] || this.$attrs[optionKey];

    return selectItems.find(item => item.key === key);
}

При этом смотретьfatSelect.selectValueТо есть выбранное значение.Как было сказано ранее, этот компонент реализует двустороннюю привязку данных.Selectкомпонентыv-modelКогда связанное значение изменяется, его необходимо синхронизировать с раскрывающимся элементом.

["fatSelect.selectValue"]: {
    handler(newValue) {
        const {
          value,
          label,
          fatSelect: { optionKey, multiple, selectValue }
        } = this;
        const key = this[optionKey] || this.$attrs[optionKey];

        if (
          newValue === value ||
          (Array.isArray(newValue) && newValue.find(item => item === value))
        ) {
          if (!multiple) {
            this.fatSelect.selectItems = [
              {
                key,
                label,
                value
              }
            ];
        } else {
            this.fatSelect.selectItems.push({
              key,
              label,
              value
            });
          }
        }
    },
    immediate: true
}

если соответствующийfatSelect.selectValueПри изменении необходимо судить о текущем вариантеoptionKeyВы вselectValue, если он существует, то будет

this.fatSelect.selectItems = [
    {
        key,
        label,
        value
    }
];

3. Заключение

игнорируетсяSelectНекоторые детали компонента, такие как логика множественного выбора и одиночного выбора, сосредоточены на логике проектирования компонента и реализации привязки данных, а также обобщают некоторые проблемы, возникающие в реальном бизнесе.

Оригинальное заявление: Данная статья является оригинальной, при перепечатке просьба указывать источник.