首页 > 其他分享 >el-select封装(单选框、多选框、全选功能)

el-select封装(单选框、多选框、全选功能)

时间:2022-09-21 18:14:25浏览次数:50  
标签:el hover 单选框 color -- 全选 var border select

先看看设计图:


网上找了一溜,都是扯淡,样式也没个

自己动手吧,先把样式搞定
popper-class="xx-option"
所有单选框都用 :after和:before类 + 定位 实现
样式逻辑复杂点,再加上:hover、:active伪类,看不惯还要封装
就出来了

.xx-option {
  .el-select-dropdown__list .el-select-dropdown__item {
    background-color: var(--select-bg);
    color: var(--select-txt);
    font-weight: 400;
    padding-left: 40px;
    &.hover {
      background-color: var(--select-hover-bg);
      color: var(--select-hover-txt);
    }
    &.selected {
      background-color: var(--select-active-bg);
      color: var(--select-active-txt);
    }
    &.selected.hover {
      background-color: var(--select-active-hover-bg);
      color: var(--select-active-hover-txt);
    }
    &.selected::before {
      height: 10px;
      width: 10px;
      box-sizing: border-box;
      content: "";
      display: inline-block;
      position: absolute;
      left: 17px;
      top: 50%;
      transform: translateY(-50%);
      background-color: var(--select-radio-bg);
      border-radius: 50%;
      transition: 0.2s;
    }
    &::after {
      height: 16px;
      width: 16px;
      box-sizing: border-box;
      position: absolute;
      font-family: element-icons;
      content: "";
      font-size: 12px;
      font-weight: 700;
      left: 14px;
      top: 50%;
      transform: translateY(-50%);
      border: 1px solid var(--select-checkbox-border);
      border-radius: 50%;
      text-align: center;
      line-height: 16px;
      transition: 0.2s;
    }
    &.hover::after {
      border-color: var(--select-checkbox-hover-border);
      background-color: transparent;
    }
    &.selected::after {
      border-color: var(--select-checkbox-bg);
    }
  }

  &.is-multiple .el-select-dropdown__list .el-select-dropdown__item {
    &::after {
      border-radius: 0;
    }
    &.selected::after {
      content: "\E6DA";
      color: #fff;
      background-color: var(--select-checkbox-bg);
    }
    &.hover.selected::after {
      border-color: var(--select-checkbox-bg);
      background-color: var(--select-checkbox-bg);
    }
  }
}

用看的肯定是看不懂的,要不就直接拿走换颜色用,要不就动动小手自己敲一遍,再比对一番~

接下来是全选功能,先看看代码

组件
    <el-select
      ref="selector"
      popper-class="xx-option"
      v-model="selectValue"
      v-bind="$attrs"
      v-on="$listeners"
      :multiple="multiple"
      collapse-tags
    >
      <div
        v-if="multiple"
        class="el-select-dropdown__item"
        @click="onAllClick"
        @mouseenter="onAllEnter"
        @mouseleave="hoverAll = false"
        :class="{
          selected: selectedAll,
          hover: hoverAll
        }"
      >
        <span>全选</span>
      </div>
      <el-option
        v-for="(item, key) in options"
        :key="key"
        :label="item[labelKey]"
        :value="item[valueKey]"
      >
      </el-option>
    </el-select>

el-options写在了封装组件内,也是因为全选功能的局限性导致
在调用组件时,要传展示的labelKey和取值的valueKey

事件
    onAllClick() {
      this.selectedAll = !this.selectedAll;
      // 选中全选
      if (this.selectedAll) {
        if (this.selectValue.length < this.options.length) {
          this.selectValue = !this.valueKey
            ? this.options
            : this.options.map(item => item[this.valueKey]);
        }
      } else {
        this.selectValue = [];
      }
      this.$emit("change", this.selectValue);
    },
    onAllEnter() {
      this.hoverAll = true;
      const options = this.$refs.selector.options;

      this.$nextTick(() => {
        this.$refs.selector.options = options.map(item => {
          item.hover = false;
          return item;
        });
      });
      this.$refs.selector.hoverIndex = -1;
    },

用v-bind和v-on接受所有的参数和事件,这里就有点繁琐了
对比vue3 :=$attrs 属性逻辑都搞定

v-bind的参数,如果已经通过props传了过来,那就不会出现在this.$attrs里面
另外$attrs拿到的是个对象{string: string},对Boolean类型的数据不太友好

v-on就不像v-bind,可以存在同名的事件emit出去,自己组件本身和v-on的事件都能调用到(其实也是坑)
另外,要想调用自身事件的而不用组件的,需要加水修饰符.native(扯远了..)

onAllClick就是全选和反选功能,再emit一个change事件
那为什么要有onAllEnter事件呢,样式有问题
看了下源码,ele对option的鼠标移入事件添加了个hoverIndex状态来记录
需要手动处理一下,重置hoverIndex和option组件内的hover属性

功能完成,基本看得过去,处理一些数据绑定的小bug
@visible-change="onVisibleChange"

    onVisibleChange(visible) {
      // 验证多选全选
      this.selectedAll =
        visible &&
        this.multiple &&
        this.selectValue.length >= this.options.length;
    },

数据一进来,先判断是否全部选择,勾上全选按钮
每次数据change,除了绑定到外层v-model上,再加个全选判断

  computed: {
    selectValue: {
      get() {
        return this.value;
      },
      set(val) {
        this.selectedAll = this.multiple && val.length >= this.options.length;
        this.$emit("input", val);
      }
    }
  },

最后就是一些小细节,hover边框,active边框
上完整代码!!

<template>
  <div
    :class="[
      'xx-select',
      focusing && 'xx-select-focus',
      !title && 'xx-select-no-title'
    ]"
  >
    <span class="title" v-if="title">{{ title }}</span>
    <el-select
      ref="selector"
      class="select"
      popper-class="xx-option"
      v-model="selectValue"
      v-bind="$attrs"
      v-on="$listeners"
      :multiple="multiple"
      :placeholder="placeholder"
      :clearable="clearable"
      collapse-tags
      @visible-change="onVisibleChange"
      @blur="blur"
      @focus="focus"
    >
      <div
        v-if="multiple"
        class="el-select-dropdown__item"
        @click="onAllClick"
        @mouseenter="onAllEnter"
        @mouseleave="hoverAll = false"
        :class="{
          selected: selectedAll,
          hover: hoverAll
        }"
      >
        <span>全选</span>
      </div>
      <el-option
        v-for="(item, key) in options"
        :key="key"
        :label="labelKey ? item[labelKey] : item"
        :value="valueKey ? (valueKey === '$key' ? key : item[valueKey]) : item"
      >
      </el-option>
    </el-select>
  </div>
</template>

<script>
export default {
  name: "XxSelect",
  props: {
    title: {
      type: String,
      default: ""
    },
    placeholder: {
      type: String,
      default: ""
    },
    value: {
      type: [String, Number, Object, Array],
      required: true
    },
    multiple: {
      type: Boolean,
      default: false
    },
    clearable: {
      type: Boolean,
      default: true
    },
    options: {
      type: [Array, Object, Number],
      default: () => {
        return [];
      }
    },
    // valueKey
    // - 不传则为整个item赋值
    // - 传`$key` 返回index(array)或key(object)
    valueKey: {
      type: String
    },
    labelKey: {
      type: String
    }
  },
  data() {
    return {
      focusing: false,
      hoverAll: false,
      selectedAll: false
    };
  },
  computed: {
    selectValue: {
      get() {
        return this.value;
      },
      set(val) {
        this.selectedAll = this.multiple && val.length >= this.options.length;
        this.$emit("input", val);
      }
    }
  },
  methods: {
    onAllEnter() {
      this.hoverAll = true;
      const options = this.$refs.selector.options;

      this.$nextTick(() => {
        this.$refs.selector.options = options.map(item => {
          item.hover = false;
          return item;
        });
      });
      this.$refs.selector.hoverIndex = -1;
    },
    onAllClick() {
      this.selectedAll = !this.selectedAll;
      // 选中全选
      if (this.selectedAll) {
        if (this.selectValue.length < this.options.length) {
          this.selectValue = !this.valueKey
            ? this.options
            : this.options.map(item => item[this.valueKey]);
        }
      } else {
        this.selectValue = [];
      }
      this.$emit("change", this.selectValue);
    },
    onVisibleChange(visible) {
      // 验证多选全选
      this.selectedAll =
        visible &&
        this.multiple &&
        this.selectValue.length >= this.options.length;
    },
    focus() {
      this.focusing = true;
      this.$refs.selector.focus();
    },
    blur() {
      this.focusing = false;
    }
  }
};
</script>

<style lang="scss" scoped>
.xx-select {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 100%;
  width: 24%;
  box-sizing: border-box;
  padding-left: 16px;
  border: 1px solid var(--default-border);
  border-radius: 4px;
  background-color: #fff;
  overflow: hidden;
  &:hover {
    border-color: var(--default-hover-border);
  }
  &-focus {
    border-color: var(--default-active-border) !important;
  }
  &-no-title {
    padding-left: 0px;
  }
  .title {
    flex: 2;
    font-size: 16px;
    color: #333;
  }
  .select {
    flex: 5;
    height: 100%;
    :deep(.el-select__tags) {
      margin-top: 1px;
    }
    :deep(.el-input--suffix) {
      line-height: 1;
      .el-input__inner {
        border: none;
      }
      .el-input__icon {
        line-height: 1;
      }
      .el-select__caret:not(.el-icon-circle-close) {
        transform: rotateZ(90deg);
        &::before {
          content: "\E6E1";
        }
      }
      &.is-focus {
        .el-select__caret:not(.el-icon-circle-close) {
          transform: rotateZ(0deg);
        }
      }
    }
  }
}
.xx-option {
  .el-select-dropdown__list .el-select-dropdown__item {
    background-color: var(--select-bg);
    color: var(--select-txt);
    font-weight: 400;
    padding-left: 40px;
    &.hover {
      background-color: var(--select-hover-bg);
      color: var(--select-hover-txt);
    }
    &.selected {
      background-color: var(--select-active-bg);
      color: var(--select-active-txt);
    }
    &.selected.hover {
      background-color: var(--select-active-hover-bg);
      color: var(--select-active-hover-txt);
    }
    &.selected::before {
      height: 10px;
      width: 10px;
      box-sizing: border-box;
      content: "";
      display: inline-block;
      position: absolute;
      left: 17px;
      top: 50%;
      transform: translateY(-50%);
      background-color: var(--select-radio-bg);
      border-radius: 50%;
      transition: 0.2s;
    }
    &::after {
      height: 16px;
      width: 16px;
      box-sizing: border-box;
      position: absolute;
      font-family: element-icons;
      content: "";
      font-size: 12px;
      font-weight: 700;
      left: 14px;
      top: 50%;
      transform: translateY(-50%);
      border: 1px solid var(--select-checkbox-border);
      border-radius: 50%;
      text-align: center;
      line-height: 16px;
      transition: 0.2s;
    }
    &.hover::after {
      border-color: var(--select-checkbox-hover-border);
      background-color: transparent;
    }
    &.selected::after {
      border-color: var(--select-checkbox-bg);
    }
  }

  &.is-multiple .el-select-dropdown__list .el-select-dropdown__item {
    &::after {
      border-radius: 0;
    }
    &.selected::after {
      content: "\E6DA";
      color: #fff;
      background-color: var(--select-checkbox-bg);
    }
    &.hover.selected::after {
      border-color: var(--select-checkbox-bg);
      background-color: var(--select-checkbox-bg);
    }
  }
}
</style>

options的label和value为啥这么复杂呢,主要是为了通用
支持数组、对象、数字,还可以通过传$key拿到key(对象)、index(数组、数字)
试试就明白了
给两个调用示例

<xx-select
  :ref="`productCode${index}`"
  v-model="product.productCode"
  filterable
  remote
  :remote-method="
    $event =>
      onFilterProduct(product.typeCode, 'productCode', $event)
  "
  :loading="productLoading"
  @visible-change="onProductVisibleChange($event, product.typeCode)"
  @change="onProductChange($event, 'productCode', index)"
  :options="products"
  valueKey="productCode"
  labelKey="productName"
/>
<xx-select v-model="product.priority" :options="9" />

好的。

标签:el,hover,单选框,color,--,全选,var,border,select
From: https://www.cnblogs.com/rion1234567/p/16716318.html

相关文章

  • Docker 安装ELK
    ELK配置文件ES端口修改./es/config/elasticsearch.ymlKibana配置,ES地址,kibana账号密码./kibana/config/kibana.ymlLogstash配置修改,ES地址,ES账号密码./logstash......
  • ElasticSearch安装使用
    要吃多少根冰棍才能说出如此冰冷刺骨的话语简介有了mysql,为什么还要用elasticsearch?mysql更多是用来存储数据,在数据量过多的时候,使用ES来检索数据(快)。ES基本概念In......
  • HelloWorld
    Java语言特点面向对象性:  两个要素:类、对象  三个特征:封装、继承、多态健壮性:①去除了C语言中的指针②自动的垃圾回收机制-->仍然会出现内存溢出、内存泄漏跨平台......
  • Label Smoothing(标签平移)
    作用标签平滑(Labelsmoothing),像L1、L2和dropout一样,是机器学习领域的一种正则化方法,通常用于分类问题,目的是防止模型在训练时过于自信地预测标签,改善泛化能力差的问题。......
  • 【Vue项目实践】套用github 上的项目运行 electron + vite + vue3
    最终版gitclonehttps://gitee.com/mywink/vite-electron.gityarnyarnbuildyarndev从github上下载这个项目,运行,就得到了electron桌面版的项目,采用vue3来......
  • Failed to download 'https://github.com/xerial/sqlite-jdbc/releases/download/3.39
    1.原因外网原因无法下载2.解决办法下载sqlite-jdbc-3.39.2.0.jar,放这个目录下:C:\用户\你的用户名\AppData\Roaming\JetBrains\PyCharm2021.1\jdbc-drivers\XerialS......
  • 第九章 ELK常见报错及解决
    一、常见报错:shared小了1.报错#ES日志报错:ValidationFailed:1:thisactionwouldadd[2]totalshards,butthisclustercurrentlyhas[4999]/[5000]maximums......
  • 使用element-tree实现id相同的选择
    最近做一个项目,需要树形结构中 id相同的数据,选中或取消一个,其他的也要选中和取消。上网查案例,有一个需求相同的,但是需要修改源码。我觉得不靠谱。于是乎,自己写了一个,供......
  • el-upload 只执行一次上传就没反应了
    原因是浏览器还保存着我们已经上传的文件,所以应该在上传成功后清除掉这样就可以继续上传了......
  • 您在 Elasticsearch 中的数据库数据
    您在Elasticsearch中的数据库数据如果您像我一样喜欢Elasticsearch的灵活性和可扩展性。与Kibana搭配使用,它成为利用数据的完美工具,即使对于非技术人员也是如此。......