vue+andv实现一个带输入框的多选选择器组件

210 阅读1分钟

需求

实现一个带输入框,可手动输入的多选选择器。

需求

  • 下拉时出现输入框可以自定义修改选择项
  • 可以删除任意一个选择项
  • 选择器里只显示固定的选择,在右边显示总共的选择数量

输入时

image.png

点击空白处收起

image.png

组件代码

<template>
  <div>
    <a-select
      v-model="ipList"
      mode="multiple"
      :open="open"
      :showArrow="true"
      :placeholder="placeholder"
      :maxTagCount="2"
      :get-popup-container="getPopupContainer"
      @deselect="deselect"
      @focus="selectFocus"
    >
      <span slot="suffixIcon" class="suffixIcon">
        <span>共{{ ipList.length }}项</span>
        <a-icon type="down" :class="open ? 'suffixIconStart1' : 'suffixIconEnd1'" />
      </span>
      <div
        slot="dropdownRender"
        class="dropdownRender"
        @click="
          e => {
            e.stopPropagation();
          }
        "
      >
        <a-input
          class="child-input"
          v-for="(item, index) in ipArr"
          :key="index"
          :ref="'ipArr' + index"
          v-model="ipArr[index]"
          size="large"
          :allowClear="false"
          :placeholder="placeholder"
          @change="add()"
        >
          <a-icon slot="suffix" type="close" @click="close(item)" />
        </a-input>
      </div>
    </a-select>
  </div>
</template>

<script>
import { cloneDeep } from 'lodash';
export default {
  name: '',
  components: {},
  props: {
    data: {
      type: Array,
      default: () => [],
    },
    placeholder: {
      type: String,
      default: '',
    },
  },
  data() {
    return {
      open: false,
      ipArr: [''],
      ipList: this.data,
    };
  },
  watch: {
    data: {
      immediate: true,
      deep: true,
      handler(val) {
        this.ipList = val;
        if (this.ipList.length == 0) {
          this.ipArr = [''];
        } else {
          this.ipArr = [...val, ''];
        }
      },
    },
    ipList: {
      deep: true,
      handler(val) {
        this.$emit('update:data', val);
        this.onChange();
      },
    },
  },
  mounted() {
    // 点击空白处关闭下拉框
    this.clickHandle = e => {
      if (e.target && 'className' in e.target && this.open) {
        const className = e.target.className;
        if (className.indexOf('child-input') === -1) {
          this.open = false;
          if (this.open == false) {
            this.ipList = cloneDeep(this.ipArr).slice(0, this.ipArr.length - 1);
          }
          this.$emit('update:data', this.ipList);
          this.onChange();
        }
      }
    };
    document.body.addEventListener('click', this.clickHandle);
  },
  beforeDestroy() {
    document.body.removeEventListener('click', this.clickHandle);
    this.clickHandle = null;
  },
  methods: {
    // 传给父组件进行规则校验
    onChange() {
      this.$emit('change');
    },
    getPopupContainer() {
      return document.getElementsByClassName('childForm')[0];
    },
    selectFocus() {
      this.open = true;
    },
    clickSuffixIcon() {
      this.open = !this.open;
      if (this.open == false) {
        this.ipList = cloneDeep(this.ipArr).slice(0, this.ipArr.length - 1);
      }
      this.$emit('update:data', this.ipList);
      this.onChange();
    },
    // 前面都填完才增加一个input
    add() {
      const noEmpty = this.ipArr.slice(0, this.ipArr.length).every(item => item !== '');
      if (noEmpty) {
        this.ipArr.push('');
      }
    },
    // 删除时更新下拉框内容的input数量和数据
    deselect(value, option) {
      this.ipArr = this.ipArr.filter(item => item !== value);
    },
    // 删除下拉框某条数据时更新下拉框内容的input数量和数据
    close(value) {
      this.ipArr = this.ipArr.filter(item => item !== value);
    },
  },
};
</script>
<style lang="less" scoped>
.dropdownRender {
  max-height: 260px;
  overflow: auto;
  padding: 10px;
  .a-input-affix-wrapper {
    margin-bottom: 5px;
  }
}
/deep/ .a-select-selection__choice__disabled {
  display: none;
}
/deep/ .a-select-selection--multiple {
  max-height: 32px;
}
.suffixIcon {
  display: flex;
  align-items: center;
  span {
    margin-right: 10px;
  }
}
.suffixIconStart1 svg {
  transform: rotate(180deg);
  transition: all 0.3s ease-in-out;
}
.suffixIconEnd1 svg {
  transform: rotate(0deg);
  transition: all 0.3s ease-in-out;
}
// 滚动条样式
.webkitScrollbar();
</style>