基于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
的事件。
-
在
<el-select>
的@change
的事件处理:当分组下的全部选项勾选的时候,全选框需要对应勾选,当分组下的全部选项取消勾选的时候,全选框需要取消对应勾选 -
<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都无法覆盖到下拉框样式。
所以解决方法主要有两个了。
- 设置
:popper-append-to-body="false"
,之后我们就可以从Dom元素中可以看出,下拉框元素被直接插入到 el-select 标签内,可以编写css或者用深度选择器去覆盖样式了。 - 使用全局样式去覆盖样式
我这里选择了第二种,然后使用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地址。
参考
修改el-select组件 el-select-dropdown__wrap样式不生效问题及解决