基于elementUI封装的带复选框el-checkbox的下拉多选el-select组件

263 阅读1分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

3种不同效果展示

项目中多次用到,网上也看了一些别人封装的,都不太满意,于是自己动手封装了一个,不足之处,还请大家多多指点,如果项目中能用到的话,还望点个赞,直接上代码。 1.父组件

<template>
  <div id="app">
    <div>{{value1}}</div>
    <MultipleSelectCheckbox v-model="value1" :order="1" placeholder="请选择" collapse-tags :options="options" style="width: 30%;" @change="change">
    </MultipleSelectCheckbox>
    <br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>

    <div>{{value2}}</div>
    <MultipleSelectCheckbox v-model="value2" :order="2" is-inline placeholder="请选择" collapse-tags :options="options" style="width: 50%;">
    </MultipleSelectCheckbox>
    <br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>

    <div>{{value3}}</div>
    <MultipleSelectCheckbox v-model="value3" :order="3" is-inline :text-width="100" placeholder="请选择" collapse-tags :options="options"
                            style="width: 80%;">
    </MultipleSelectCheckbox>
  </div>
</template>

<script>
import MultipleSelectCheckbox from "./components/MultipleSelectCheckbox"
export default {
  name: 'App',
  components: {
    MultipleSelectCheckbox
  },
  data() {
    return {
      value1: ["选项1"],
      value2: [],
      value3: [],
      options: [
        {
          value: '选项1',
          label: '黄金糕11111111111111111111111'
        },
        {
          value: '选项2',
          label: '双皮奶'
        },
        {
          value: '选项3',
          label: '蚵仔煎333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333'
        },
        {
          value: '选项4',
          label: '龙须面'
        },
        {
          value: '选项5',
          label: '北京烤鸭'
        },
        {
          value: '选项6',
          label: '武汉热干面6666666666666666666'
        },
        {
          value: '选项7',
          label: '蔡甸牛肉面7777777777777777777'
        }
      ]
    }
  },
  methods: {
    change() {
      console.log(this.value1)
    }
  }
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  color: #2c3e50;
  margin-top: 60px;
  padding-inline-start: 100px;
}
</style>

2.子组件

<template>
  <el-select v-model="selectValues" :popper-class="'multiple_select_popper_' + order" v-bind="$attrs" v-on="$listeners" multiple
             :class="'multiple_select_checkbox_' + order" @visible-change="visibleChange" @change="changeSelect">
    <el-option v-if="options_.length" label="全选" value="全选">
      <el-checkbox v-model="isSelectAll" @click.native.prevent>全选</el-checkbox>
    </el-option>
    <el-option v-for="item in options_" :key="item[props.value]" :label="item[props.label]" :value="item[props.value]">
      <el-tooltip :disabled="!item.isExceed" :content="item[props.label]" placement="top" effect="light">
        <el-checkbox :value="selectValues.includes(item[props.value])" @click.native.prevent>{{item[props.label]}}</el-checkbox>
      </el-tooltip>
    </el-option>
  </el-select>
</template>

<script>
export default {
  name: "MultipleSelectCheckbox",
  inheritAttrs: false,// 似乎设不设置都可以
  props: {
    value: {
      type: Array,
      default: () => []
    },
    // 下拉选项
    options: {
      type: Array,
      default: () => []
    },
    // 选项键值对
    props: {
      type: Object,
      default: () => {
        return {
          label: "label",
          value: "value"
        }
      }
    },
    // 选项是否独占一行
    isInline: {
      type: Boolean,
      default: false
    },
    // 选项的宽度
    textWidth: {
      type: Number,
    },
    // 组件唯一标识 (避免同一个页面引用多次,发生耦合)
    order: {
      type: Number,
      default: 0
    }
  },
  data() {
    return {
      selectValues: this.value,
      isSelectAll: false,
      multipleSelectCheckboxMaxWidth: 0,
      options_: []
    }
  },
  mounted() {
    const multipleSelectCheckbox = document.querySelector(`.multiple_select_checkbox_${this.order}.el-select`)
    const elSelectDropdown = document.querySelector(`.multiple_select_popper_${this.order}.el-select-dropdown`)
    const multipleSelectCheckboxMaxWidth = multipleSelectCheckbox.scrollWidth
    this.multipleSelectCheckboxMaxWidth = multipleSelectCheckboxMaxWidth
    elSelectDropdown.style["max-width"] = multipleSelectCheckboxMaxWidth + "px"
  },
  watch: {
    // 监听(全选or全不选)
    value: {
      handler(arr) {
        this.selectValues = arr
        this.isSelectAll = arr.length === this.options_.length
      }
    },
    options: {
      handler(arr) {
        this.options_ = JSON.parse(JSON.stringify(arr))
        this.isSelectAll = this.selectValues.length === this.options_.length
      },
      immediate: true
    }
  },
  methods: {
    changeSelect(val) {
      if (val.includes("全选")) {
        // 说明已经全选了,所以全不选
        if (val.length > this.options_.length) {
          this.selectValues = []
        }
        // 反之,全选
        else {
          this.selectValues = this.options_.map(item => item[this.props.value])
        }
      }
      this.$emit("input", this.selectValues)
    },
    // 判断元素是否溢出,溢出加上toolTip
    async visibleChange(visible) {
      await this.$nextTick()
      if (visible) {
        const maxLableWidth = this.multipleSelectCheckboxMaxWidth - 80
        const labels = document.querySelectorAll(`.multiple_select_popper_${this.order}.el-select-dropdown .el-checkbox.el-tooltip .el-checkbox__label`)
        labels.forEach((label, index) => {
          // eslint-disable-next-line no-prototype-builtins
          if (!this.options_[index].hasOwnProperty("isExceed")) {
            this.$set(this.options_[index], "isExceed", label.scrollWidth - 10 > (this.textWidth || maxLableWidth))
            label.style["width"] = this.textWidth + "px"
            label.style["max-width"] = (this.textWidth || maxLableWidth) + "px"
            label.style["vertical-align"] = "middle"
            label.style["overflow"] = "hidden"
            label.style["text-overflow"] = "ellipsis"
            label.style["white-space"] = "nowrap"
          }
        })
        const items = document.querySelectorAll(`.multiple_select_popper_${this.order} .el-select-dropdown__item:not(:first-child)`)
        items.forEach(item => {
          item.style.display = this.isInline ? "inline-block" : "block"
        })
      }
    }
  }
}
</script>

<style scoped>
[class*="multiple_select_popper"].el-select-dropdown.is-multiple .el-select-dropdown__item.selected::after {
  content: "";
}
</style>