Bootstrap

基于Vue和Element-UI中Select 选择器的分组全选以及样式修改问题

基于Vue和Element-UI中Select 选择器的分组全选以及样式修改问题

首先我们来看看最终表现出来的效果图

在这里插入图片描述

(下面是新更新的最终效果图,更新于2022年3月30日)
在这里插入图片描述

本人前端写的比较少,因为有需求是需要做到如上图所示的效果,所以特意研究了一下。

Select选择器分组

关于el-select 选择器分组这个,在element-ui的官网也给出了简单的demo了,所以本博文也是基于这个demo的基础上进行改造的。

<template>
  <el-select v-model="value" placeholder="请选择">
    <el-option-group
      v-for="group in options"
      :key="group.label"
      :label="group.label">
      <el-option
        v-for="item in group.options"
        :key="item.value"
        :label="item.label"
        :value="item.value">
      </el-option>
    </el-option-group>
  </el-select>
</template>

<script>
  export default {
    data() {
      return {
        options: [{
          label: '热门城市',
          options: [{
            value: 'Shanghai',
            label: '上海'
          }, {
            value: 'Beijing',
            label: '北京'
          }]
        }, {
          label: '城市名',
          options: [{
            value: 'Chengdu',
            label: '成都'
          }, {
            value: 'Shenzhen',
            label: '深圳'
          }, {
            value: 'Guangzhou',
            label: '广州'
          }, {
            value: 'Dalian',
            label: '大连'
          }]
        }],
        value: ''
      }
    }
  }
</script>

在这里插入图片描述

Select选择器分组加全选

至于全选的问题,其实就是加上checkbox,然后在select框change的时候,还有checkBox勾选的时候去处理全选的逻辑。所以我们需要在<el-select>上加上@change的事件,还有在<el-checkbox>上加上@change的事件。

  1. <el-select>@change的事件处理:当分组下的全部选项勾选的时候,全选框需要对应勾选,当分组下的全部选项取消勾选的时候,全选框需要取消对应勾选

  2. <el-checkbox>@change的事件处理:当勾选全选框的时候,需要把分组下的全部选项都选择,当全选框取消勾选的时候,需要把分组下的全部选项取消勾选。

为此我们的data的数据需要加上checked的标记位来标志该选择项有没有被勾选。数据如下:

[{
        id: 1,
        label: '热门城市',
        checked: false,
        options: [{
          value: 'Shanghai',
          label: '上海'
        }, {
          value: 'Beijing',
          label: '北京'
        }]
      }, {
        id: 2,
        label: '城市名',
        checked: false,
        options: [{
          value: 'Chengdu',
          label: '成都'
        }, {
          value: 'Shenzhen',
          label: '深圳'
        }, {
          value: 'Guangzhou',
          label: '广州'
        }, {
          value: 'Dalian',
          label: '大连'
        },
        {
          value: 'Huizhou',
          label: '惠州'
        }]
      },
      {
        id: 3,
        label: '城市名2',
        checked: false,
        options: [{
          value: 'Chengdu2',
          label: '成都2'
        }, {
          value: 'Shenzhen2',
          label: '深圳2'
        }, {
          value: 'Guangzhou2',
          label: '广州2'
        }, {
          value: 'Dalian2',
          label: '大连2'
        },
        {
          value: 'Huizhou2',
          label: '惠州2'
        }]
      },
      {
        id: 4,
        label: '城市名3',
        checked: false,
        options: [{
          value: 'Chengdu3',
          label: '成都3'
        }, {
          value: 'Shenzhen3',
          label: '深圳3'
        }, {
          value: 'Guangzhou3',
          label: '广州3'
        }, {
          value: 'Dalian3',
          label: '大连3'
        },
        {
          value: 'Huizhou3',
          label: '惠州3'
        }]
      }]

接下来就是处理的代码了

 <template>
  <div>
    <el-select v-model="value" placeholder="请选择" @change="changeSelect" style="width: 400px"  clearable multiple collapse-tags
               popper-class="productGroupSelector">
      <el-option-group class="productGroupSelector-group"
                       v-for="group in options"
                       :key="group.label"
                       :label="group.label">
        <div style="display: flex; align-items: start;">
          <div style="display: flex;align-items: center; padding-top: 8px; padding-left: 5px">
            <el-checkbox v-model="group.checked" @change='selectAll($event,group.id)'></el-checkbox>
          </div>
          <div style="width: 400px">
            <el-option class="productGroupSelector-option"
                       v-for="item in group.options"
                       :key="item.value"
                       :label="item.label"
                       :value="group.label + '-' + item.value">
            </el-option>
          </div>
        </div>
      </el-option-group>
    </el-select>
  </div>
</template>
<script>
export default {
  name: 'SelectorComponent',
  data () {
    return {
      options: [{
        id: 1,
        label: '热门城市',
        checked: false,
        options: [{
          value: 'Shanghai',
          label: '上海'
        }, {
          value: 'Beijing',
          label: '北京'
        }]
      }, {
        id: 2,
        label: '城市名',
        checked: false,
        options: [{
          value: 'Chengdu',
          label: '成都'
        }, {
          value: 'Shenzhen',
          label: '深圳'
        }, {
          value: 'Guangzhou',
          label: '广州'
        }, {
          value: 'Dalian',
          label: '大连'
        },
        {
          value: 'Huizhou',
          label: '惠州'
        }]
      },
      {
        id: 3,
        label: '城市名2',
        checked: false,
        options: [{
          value: 'Chengdu2',
          label: '成都2'
        }, {
          value: 'Shenzhen2',
          label: '深圳2'
        }, {
          value: 'Guangzhou2',
          label: '广州2'
        }, {
          value: 'Dalian2',
          label: '大连2'
        },
        {
          value: 'Huizhou2',
          label: '惠州2'
        }]
      },
      {
        id: 4,
        label: '城市名3',
        checked: false,
        options: [{
          value: 'Chengdu3',
          label: '成都3'
        }, {
          value: 'Shenzhen3',
          label: '深圳3'
        }, {
          value: 'Guangzhou3',
          label: '广州3'
        }, {
          value: 'Dalian3',
          label: '大连3'
        },
        {
          value: 'Huizhou3',
          label: '惠州3'
        }]
      }],
      value: ''
    }
  },
  methods: {
    onChange (value) {
      console.log(value)
      console.log(this.value)
    },
    selectAll (val, id) {
      const selectOption = this.options.find(f => f.id === id)
      const arr = selectOption.options.map(m => selectOption.label + '-' + m.value)
      if (val) {
        arr.forEach(item => {
          if (!this.value.includes(item)) {
            this.value.push(item)
          }
        })
      } else {
        this.value.forEach((item, index) => {
          if (arr.includes(item)) {
            this.value.splice(index, 1, '')
          }
        })
      }
      this.value = this.value.filter(f => f !== '')
    },
    changeSelect (val) {
      this.options.forEach(item => {
        const arr = item.options.map(m => item.label + '-' + m.value)
        item.checked = arr.every((v) => {
          return val.some(s => s === v)
        })
      })
    }
  }
}
</script>

然后分组和全选的功能就完成了。

在这里插入图片描述

Select 选择器的样式修改问题

接下来就是比较头疼的样式修改问题。

element-ui文档中给出了两个有用的属性,能帮助我们修改样式。

在这里插入图片描述

样式总是修改不成功的问题主要是因为:通过Dom元素可以发现,官方Select选择器下拉框是直接插入body标签中,与最外级div同级,所以在Vue中无论怎么写css都无法覆盖到下拉框样式。

所以解决方法主要有两个了。

  1. 设置:popper-append-to-body="false",之后我们就可以从Dom元素中可以看出,下拉框元素被直接插入到 el-select 标签内,可以编写css或者用深度选择器去覆盖样式了。
  2. 使用全局样式去覆盖样式

我这里选择了第二种,然后使用popper-class属性给select 下拉框设置了一个class, 然后用css选择器给下拉框元素设置样式。

下面是全部的代码和样式:

<template>
  <div>
    <el-select v-model="value" placeholder="请选择" @change="changeSelect" style="width: 400px"  clearable multiple collapse-tags
               popper-class="productGroupSelector">
      <el-option-group class="productGroupSelector-group"
                       v-for="group in options"
                       :key="group.label"
                       :label="group.label">
        <div style="display: flex; align-items: start;">
          <div style="display: flex;align-items: center; padding-top: 8px; padding-left: 5px">
            <el-checkbox v-model="group.checked" @change='selectAll($event,group.id)'></el-checkbox>
          </div>
          <div style="width: 400px">
            <el-option class="productGroupSelector-option"
                       v-for="item in group.options"
                       :key="item.value"
                       :label="item.label"
                       :value="group.label + '-' + item.value">
            </el-option>
          </div>
        </div>
      </el-option-group>
    </el-select>
  </div>
</template>
<script>
export default {
  name: 'SelectorComponent',
  data () {
    return {
      options: [{
        id: 1,
        label: '热门城市',
        checked: false,
        options: [{
          value: 'Shanghai',
          label: '上海'
        }, {
          value: 'Beijing',
          label: '北京'
        }]
      }, {
        id: 2,
        label: '城市名',
        checked: false,
        options: [{
          value: 'Chengdu',
          label: '成都'
        }, {
          value: 'Shenzhen',
          label: '深圳'
        }, {
          value: 'Guangzhou',
          label: '广州'
        }, {
          value: 'Dalian',
          label: '大连'
        },
        {
          value: 'Huizhou',
          label: '惠州'
        }]
      },
      {
        id: 3,
        label: '城市名2',
        checked: false,
        options: [{
          value: 'Chengdu2',
          label: '成都2'
        }, {
          value: 'Shenzhen2',
          label: '深圳2'
        }, {
          value: 'Guangzhou2',
          label: '广州2'
        }, {
          value: 'Dalian2',
          label: '大连2'
        },
        {
          value: 'Huizhou2',
          label: '惠州2'
        }]
      },
      {
        id: 4,
        label: '城市名3',
        checked: false,
        options: [{
          value: 'Chengdu3',
          label: '成都3'
        }, {
          value: 'Shenzhen3',
          label: '深圳3'
        }, {
          value: 'Guangzhou3',
          label: '广州3'
        }, {
          value: 'Dalian3',
          label: '大连3'
        },
        {
          value: 'Huizhou3',
          label: '惠州3'
        }]
      }],
      value: ''
    }
  },
  methods: {
    onChange (value) {
      console.log(value)
      console.log(this.value)
    },
    selectAll (val, id) {
      const selectOption = this.options.find(f => f.id === id)
      const arr = selectOption.options.map(m => selectOption.label + '-' + m.value)
      if (val) {
        arr.forEach(item => {
          if (!this.value.includes(item)) {
            this.value.push(item)
          }
        })
      } else {
        this.value.forEach((item, index) => {
          if (arr.includes(item)) {
            this.value.splice(index, 1, '')
          }
        })
      }
      this.value = this.value.filter(f => f !== '')
    },
    changeSelect (val) {
      this.options.forEach(item => {
        const arr = item.options.map(m => item.label + '-' + m.value)
        item.checked = arr.every((v) => {
          return val.some(s => s === v)
        })
      })
    }
  }
}
</script>

<style>
.productGroupSelector .el-scrollbar .el-select-dropdown__wrap.el-scrollbar__wrap .el-scrollbar__view.el-select-dropdown__list {
  width: 800px;
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: center;
  padding-top: 0;
  overflow-x: hidden;
}

</style>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.productGroupSelector-group /deep/ .el-select-group {
  width: 400px;
  display: flex;
  flex-wrap: wrap;
  justify-content: start;
}
.productGroupSelector-group /deep/ .el-select-group__title {
 padding-left: 40px;
  font-size: 16px;
}

.productGroupSelector-group:first-child {
  padding-top: 0;
  margin-top: 0;
}

.productGroupSelector-group {
  padding-bottom: 0;
  padding-top: 32px;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  width: 400px
}

.productGroupSelector-option{
  display: inline-flex;
  align-items: center;
  flex-wrap: wrap;
}

.productGroupSelector-group::after {
  display: none;
}

/*/deep/ .el-scrollbar__view.el-select-dropdown__list {*/
/*  width: 600px;*/
/*  display: flex;*/
/*  flex-wrap: wrap;*/
/*  justify-content: start;*/
/*  align-items: center;*/
/*  padding-top: 0;*/
/*}*/

</style>

最终效果

在这里插入图片描述


更新于2022年3月30日

又修改了一下样式和做了一些功能的调整,最终代码如下:

<template>
  <div>
    <el-select v-model="value" placeholder="请选择" @change="changeSelect" style="width: 250px"  clearable multiple collapse-tags size="small"
               popper-class="productGroupSelector">
      <el-option-group class="productGroupSelector-group"
                       v-for="group in options"
                       :key="group.label"
                       :label="group.label">
        <div style="display: flex; align-items: start;">
          <div style="display: flex;align-items: center; padding-top: 8px; padding-left: 5px">
            <el-checkbox v-model="group.checked" @change='selectAll($event,group.id)' :indeterminate="group.isIndeterminate"></el-checkbox>
          </div>
          <div style="width: 400px">
            <el-option class="productGroupSelector-option"
                       v-for="item in group.options"
                       :key="item.value"
                       :label="item.label"
                       :value="group.label + '-' + item.value">
            </el-option>
          </div>
        </div>
      </el-option-group>
    </el-select>
  </div>
</template>
<script>
export default {
  name: 'SelectorComponent',
  data () {
    return {
      options: [{
        id: 1,
        label: '热门城市',
        checked: false,
        isIndeterminate: false,
        options: [{
          value: 'Shanghai',
          label: '上海'
        }, {
          value: 'Beijing',
          label: '北京'
        }]
      }, {
        id: 2,
        label: '城市名',
        checked: false,
        isIndeterminate: false,
        options: [{
          value: 'Chengdu',
          label: '成都'
        }, {
          value: 'Shenzhen',
          label: '深圳'
        }, {
          value: 'Guangzhou',
          label: '广州'
        }, {
          value: 'Dalian',
          label: '大连'
        },
        {
          value: 'Huizhou',
          label: '惠州'
        }]
      },
      {
        id: 3,
        label: '城市名2',
        checked: false,
        isIndeterminate: false,
        options: [{
          value: 'Chengdu2',
          label: '成都2'
        }, {
          value: 'Shenzhen2',
          label: '深圳2'
        }, {
          value: 'Guangzhou2',
          label: '广州2'
        }, {
          value: 'Dalian2',
          label: '大连2'
        },
        {
          value: 'Huizhou2',
          label: '惠州2'
        }]
      },
      {
        id: 4,
        label: '城市名3',
        checked: false,
        isIndeterminate: false,
        options: [{
          value: 'Chengdu3',
          label: '成都3'
        }, {
          value: 'Shenzhen3',
          label: '深圳3'
        }, {
          value: 'Guangzhou3',
          label: '广州3'
        }, {
          value: 'Dalian3',
          label: '大连3'
        },
        {
          value: 'Huizhou3',
          label: '惠州3'
        }]
      }],
      value: ''
    }
  },
  methods: {
    onChange (value) {
      console.log(value)
      console.log(this.value)
    },
    selectAll (val, id) {
      const selectOption = this.options.find(f => f.id === id)
      const arr = selectOption.options.map(m => selectOption.label + '-' + m.value)
      if (val) {
        arr.forEach(item => {
          if (!this.value.includes(item)) {
            this.value.push(item)
          }
        })
      } else {
        this.value.forEach((item, index) => {
          if (arr.includes(item)) {
            this.value.splice(index, 1, '')
          }
        })
      }
      this.value = this.value.filter(f => f !== '')
      if (selectOption.checked) {
        selectOption.isIndeterminate = false
      }
    },
    changeSelect (val) {
      this.options.forEach(item => {
        const arr = item.options.map(m => item.label + '-' + m.value)
        item.isIndeterminate = arr.some((v) => {
          return val.some(s => s === v)
        })
        item.checked = arr.every((v) => {
          return val.some(s => s === v)
        })
        if (item.checked) {
          item.isIndeterminate = false
        }
      })
    }
  }
}
</script>

<style scoped>
.productGroupSelector-group /deep/ .el-select-group {
  width: 400px;
  display: flex;
  flex-wrap: wrap;
  justify-content: start;
}
.productGroupSelector-group /deep/ .el-select-group__title {
  padding-left: 40px;
  font-size: 16px;
}

.productGroupSelector-group {
  padding-top: 10px;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  width: 400px;
  padding-bottom: 10px;
}

.productGroupSelector-option{
  display: inline-flex;
  align-items: center;
  flex-wrap: wrap;
}

.productGroupSelector-group::after {
  display: none;
}

</style>

<style lang="less">
.productGroupSelector {
  .el-scrollbar__view.el-select-dropdown__list {
    width: 800px;
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    align-items: baseline;
    padding-top: 0;
    overflow-x: hidden;
  }
}
.productGroupSelector {
  .el-select-dropdown__wrap.el-scrollbar__wrap {
    max-height: 350px
  }
}

</style>

最终的展示效果(应该能看出区别…):

在这里插入图片描述

最后附上demo地址。

参考

element el-select 分组加全选

ELEMENTUI SELECT选择器下拉框样式修改

修改el-select组件 el-select-dropdown__wrap样式不生效问题及解决

源代码

https://gitee.com/cckevincyh/vue-elementui

;