二次封装组件element-ui的el-select多选tag数值多余隐藏

本文介绍了一种自定义Element UI的el-select组件的方法,使其在多选模式下可以根据容器宽度动态调整显示的标签数量,并优雅地隐藏超出部分的标签,同时提供额外的标签计数指示未完全展示的数量。

改造el-select默认显示一个tag问题

原组件
在这里插入图片描述

  • 前面的全部展示,而后面的只会展示一个而隐藏其他
  • 改为按照宽度来判断显示隐藏

效果

在这里插入图片描述

代码

  • 子组件
<template>
  <el-select
    v-model="values"
    multiple
    style="width:100%"
    placeholder="请选择"
    @change="handleChange"
  >
    <el-option
      v-for="item in options"
      :key="item.value"
      :label="item.label"
      :value="item.value"
    >
    </el-option>
  </el-select>
</template>

<script>
var observer= null
export default {
  props: {
    options: {
      type: Array,
      default: () => []
    },
    value: {
      type: Array,
      default: () => []
    }
  },
  data() {
    return {};
  },
  mounted() {
    let tagLIstDom = document.querySelector(".el-select__tags");
    let tagSpanDom = document.querySelector(".el-select__tags > span"); 
    let hideDom = document.createElement("span");
    hideDom.classList = ["count-node"]; //设置样式
    tagSpanDom.append(hideDom); //插入到span中
    var config = { childList: true };

    // 当观察到突变时执行的回调函数
    var callback = function(mutationsList) {
      mutationsList.forEach(function(item, index) {
        if (item.type == "childList") {
          let tagList = item.target.childNodes;
          let tagWidth = 0; //标签总宽度
          let tagNum = 0; //标签多余个数

          for (let i = 0; i < tagList.length; i++) {
            const e = tagList[i];
            if (tagWidth > tagLIstDom.offsetWidth) {
              e.style.display = "none"; //隐藏多余标签
            } else {
              e.style.display = "inline-block"; //显示标签
            }
            tagWidth += e.offsetWidth + 5;
            if (tagWidth > tagLIstDom.offsetWidth) {
              e.style.display = "none"; //隐藏多余标签
            } else {
              e.style.display = "inline-block"; //显示标签
            }
            if (e.style.display != "none") {
              tagNum++;
              hideDom.style.display = "none"; //隐藏多余标签个数
            } else {
              hideDom.style.display = "inline-block"; //显示多余标签个数
              hideDom.innerHTML = `+${tagList.length - tagNum}`;  //显示多余标签个数
            }
          }
        }
      });
    };

    // 创建一个链接到回调函数的观察者实例
     observer = new MutationObserver(callback);

    // 开始观察已配置突变的目标节点
    observer.observe(tagSpanDom, config);

    // 随后,您还可以停止观察
    // observer.disconnect();
  },
  methods: {
    handleChange() {
      this.$emit("change", this.value);
    }
  },
  computed: {
    values: {
      get() {
        return this.value;
      },
      set(val) {
        this.$emit("input", val);
      }
    }
  },
  //销毁时
  beforeDestroy() {
    // 停止观察
    observer.disconnect();
  }
};
</script>
<style lang="scss" scoped>
>>> .count-node {
  position: absolute;
  right: 0;
  top: 2px;
  display: none;
  height: 24px;
  padding: 0 8px;
  line-height: 22px;
  margin-left: 6px;
  background-color: #f4f4f5;
  border: 1px solid #e9e9eb;
  border-radius: 4px;
  color: #909399;
  font-size: 12px;
  box-sizing: border-box;
}
</style>

  • 父组件
//引入
  components: {
    select: () => import("./components/select.vue"),
  },
  //使用
<select
      v-model="value"
      :options="options"
      @change="handleChange"
/>

💖代码可能存在比较大的性能消耗,谨慎使用

<think> 我们参考了引用[1]中的实现方法,其中在el-select内部直接放置了el-checkbox来实现全功能。但是,根据之前的分析,这样直接放置会导致el-checkbox没有el-select-dropdown__item类,从而可能引发样式和布局问题。 解决方案:将全el-checkbox放入一个el-option中,这样它就会生成带有el-select-dropdown__item类的DOM结构。 同时,我们参考引用[2]中提到的封装思路,我们可以将全功能封装成一个可复用的组件。 下面我们将实现一个带有全功能的el-select组件: 步骤: 1.el-select内部,首先放置一个el-option用于全框。 2. 然后循环生成其他项,每个项内放置一个复框。 3. 使用计算属性来处理全状态和反状态。 4. 注意事件处理,避免事件冒泡导致的问题。 具体代码实现如下: ```vue <template> <el-select multiple v-model="selectedValues" placeholder="请择" @remove-tag="handleRemoveTag" @clear="handleClear" > <!----> <el-option class="select-option-all"> <el-checkbox v-model="checkAll" @change="handleCheckAllChange" >全</el-checkbox> </el-option> <!--项(如果需要的话,根据需求添加) --> <!-- 或者可以将反功能放在全旁边,但通常反并不常见,这里省略 --> <!-- 普通--> <el-option v-for="item in options" :key="item.value" :value="item.value" :label="item.label" class="select-option-item" > <el-checkbox v-model="selectedValues" :label="item.value" @change="handleItemChange(item.value)" >{{ item.label }}</el-checkbox> </el-option> </el-select> </template> <script> export default { props: { options: { type: Array, default: () => [], }, value: { type: Array, default: () => [], }, }, data() { return { // 选中的值,使用v-model绑定 selectedValues: this.value, // 全状态 checkAll: false, }; }, watch: { value(newVal) { this.selectedValues = newVal; }, selectedValues(newVal) { // 当选中值变化时,更新全状态 this.checkAll = newVal.length === this.options.length; // 将值传递给父组件 this.$emit('input', newVal); }, }, methods: { // 全处理 handleCheckAllChange(isChecked) { if (isChecked) { // 全:将options的所有value赋值给selectedValues this.selectedValues = this.options.map(item => item.value); } else { // 取消全 this.selectedValues = []; } }, // 单个项改变时,不需要特别处理,因为v-model已经双向绑定了 handleItemChange() { // 这里可以留空,因为selectedValues变化会触发watch }, // 当移除某个标签时(点击tag的删除图标),需要更新全状态 handleRemoveTag(tag) { // 不需要额外处理,因为selectedValues会变化,watch会处理 }, // 清除所有择 handleClear() { this.selectedValues = []; }, }, }; </script> <style scoped> /* 注意:由于el-option内部结构,我们需要调整样式 */ /* 使用深度择器来覆盖样式 */ ::v-deep .select-option-all.el-select-dropdown__item { height: auto; padding: 0; line-height: normal; } ::v-deep .select-option-all .el-checkbox { display: block; padding-left: 20px; } /* 普通项的复框样式 */ ::v-deep .select-option-item .el-checkbox { width: 100%; margin-right: 0; } </style> ``` ### 关键点说明 1. **全项**:将全的`el-checkbox`放在一个`el-option`内,这样它就会生成`.el-select-dropdown__item`类,样式和布局与普通项一致。 2. **双向绑定**:使用`v-model`绑定`selectedValues`数组,同时监听其变化,当数组长度等于项总数时,全框被勾。 3. **样式调整**:通过深度择器调整全项的内部样式,使其与普通项的样式保持一致。 4. **事件处理**:全按钮的`@change`事件用于设置所有项的选中状态;普通项的复框直接绑定到`selectedValues`数组,利用`v-model`的双向绑定特性。 ### 注意事项 - **反功能**:上述代码没有实现反,因为引用[1]中虽然提到了反,但实际使用中反需求较少。如果需要反,可以添加一个反按钮(同样放在一个`el-option`中),并实现反逻辑(即遍历所有项,将不在当前选中的值添加到选中数组,已选中的则移除)。 - **性能考虑**:当项非常时(如上千条),这种实现方式可能会有性能问题,因为每个项都是一个组件。此时建议使用虚拟滚动,但Element UIel-select本身不支持虚拟滚动,可能需要更换组件或自行封装- **样式穿透**:在Vue3中,`::v-deep`的语法可能有所改变,应使用`:deep()`代替。 ### 相关问题 1. 如何实现反功能? 2.项数量很大时,如何优化性能? 3.Vue3中如何正确使用样式穿透? [^1]: vue:遇到的坑之-----el-select/el-tree 下拉项全element-ui) [^2]: VUE:依赖element-uiel-select组件封装 </think> # Element UIel-select 组件下拉项添加全功能实现 在使用 Element UI 的 `el-select` 组件时,为下拉项添加内置全功能可以通过以下方式实现。我将提供一个完整的解决方案,包含样式调整和功能逻辑。 ## 完整实现方案 ```vue <template> <el-select v-model="selectedValues" multiple filterable collapse-tags placeholder="请择" @change="handleSelectChange" > <!--项放在第一位 --> <el-option class="select-all-option"> <el-checkbox v-model="allSelected" @change="handleSelectAll" > 全 </el-checkbox> </el-option> <!-- 普通--> <el-option v-for="item in options" :key="item.value" :value="item.value" :label="item.label" > <el-checkbox v-model="selectedValues" :label="item.value" style="pointer-events: none;" > {{ item.label }} </el-checkbox> </el-option> </el-select> </template> <script> export default { data() { return { // 项数据 options: [ { value: 1, label: '项1' }, { value: 2, label: '项2' }, { value: 3, label: '项3' }, { value: 4, label: '项4' }, ], // 已selectedValues: [], // 全状态 allSelected: false }; }, watch: { // 监听选中值变化,更新全状态 selectedValues(newVal) { this.allSelected = newVal.length === this.options.length; } }, methods: { // 全/取消全处理 handleSelectAll(checked) { if (checked) { // 全操作 this.selectedValues = this.options.map(item => item.value); } else { // 取消全 this.selectedValues = []; } }, // 项变更处理 handleSelectChange(value) { // 如果择了全部项,自动勾 if ([...value].sort().toString() === [...this.options.map(i => i.value)].sort().toString()) { this.allSelected = true; } } } }; </script> <style> /* 全项样式 */ .select-all-option.el-select-dropdown__item { border-bottom: 1px solid #eee; padding: 0 !important; height: auto !important; } .select-all-option .el-checkbox { width: 100%; padding: 0 20px; height: 34px; line-height: 34px; } /* 普通项样式 */ .el-select-dropdown__item .el-checkbox { width: 100%; pointer-events: none; } /* 选中样式调整 */ .el-select-dropdown__item.selected .el-checkbox__input.is-checked .el-checkbox__inner { background-color: #409EFF; border-color: #409EFF; } </style> ``` ## 关键实现原理 ### 1. 结构设计 - **全项**:将 `el-checkbox` 封装在 `el-option` 内,确保继承正确的 DOM 结构和样式[^1] - **普通项**:每个项内部嵌入 `el-checkbox` 组件,使用 `pointer-events: none` 防止事件冲突 ### 2. 状态管理 - **全状态**:使用 `allSelected` 变量控制全- **双向绑定**:普通项的 `v-model` 直接绑定到 `selectedValues` 数组 - **状态同步**:通过 `watch` 监听 `selectedValues` 变化,自动更新全状态 ### 3. 事件处理 - **全事件**:`handleSelectAll` 方法处理全/取消全逻辑 - **变更事件**:`handleSelectChange` 确保所有项被选中时自动勾 ### 4. 样式优化 - **全分隔线**:添加底部边框区分全与普通- **项宽度**:确保复框占据全部宽度 - **禁用事件**:普通项使用 `pointer-events: none` 避免与 `el-option` 原生事件冲突 ## 常见问题解决 ### 为什么全项没有正确的样式? 将 `el-checkbox` 直接放在 `el-select` 内而不是 `el-option` 中是常见错误,会导致缺少 `.el-select-dropdown__item` 类名[^1]。正确做法是将全封装在 `el-option` 内。 ### 点击复框无效怎么办? 使用 `pointer-events: none;` 可以防止复框与 `el-option` 的点击事件冲突,同时保留通过 `v-model` 的状态绑定。 ### 如何添加反功能? 如果需要添加反功能,可以添加另一个项: ```vue <el-option class="select-reverse-option"> <el-checkbox v-model="reverseSelected" @change="handleReverseSelect"> 反 </el-checkbox> </el-option> ``` 并实现反方法: ```js handleReverseSelect() { const allValues = this.options.map(item => item.value); this.selectedValues = allValues.filter(value => !this.selectedValues.includes(value) ); } ``` ## 相关问题 1. 如何实现级嵌套的全功能? 2. Element UI 的下拉框虚拟滚动如何实现? 3.Vue3 中如何正确使用样式穿透代替 `::v-deep`? 4. 如何动态加载 el-select 的下拉项数据? 5. el-select 时如何优化大量项的性能? [^1]: vue:遇到的坑之-----el-select/el-tree 下拉项全element-ui) [^2]: VUE:依赖element-uiel-select组件封装
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值