Select 选择器选择器的分组,如上图所示,我们希望做到的效果是,点击“热门城市”或“城市名”的时候全选分组的options。
思路
思路一:目前的Select 选择器分组OptionGroup的Title只是一个文本DOM,没用其他东西,我们需要给这个文本DOM添加一个点击事件,实现全选的功能。
<template>
<ul class="el-select-group__wrap" v-show="visible">
// option-group中只显示文本
<li class="el-select-group__title">{{ label }}</li>
<li>
<ul class="el-select-group">
<slot></slot>
</ul>
</li>
</ul>
</template>
思路二:直接在<el-option-group>中添加一个<el-checkbox>然后隐藏分组的标题,用多选框中的标题替换分组标题,如下图所示
思路一实现
搭建基本结构
直接到官网粘贴复制:Element - The world's most popular Vue UI framework
这里的<option-group>,就是重写的组件
<template>
<div>
<el-select v-model="value" placeholder="请选择" multiple style="width: 400px;" clearable @change="changeSelect">
<option-group
v-for="group in options"
:key="group.label"
:label="group.label"
@click="handleClick"
>
<el-option
v-for="item in group.options"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</option-group>
</el-select>
</div>
</template>
<script>
import optionGroup from '../components/selectGroup'
export default {
name: 'ruleInput',
components: { optionGroup },
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: []
}
},
methods: {
// 点击事件,实现全选
handleClick (e) {
const options = this.options.filter(item => e === item.label)[0].options
const newMap = options.filter(item => {
return !this.value.includes(item.value)
})
const result = newMap.length === 0 ? options : newMap
result.forEach(item => {
const index = this.value.indexOf(item.value)
if (index > -1) {
this.value.splice(index, 1)
} else {
this.value.push(item.value)
}
})
},
changeSelect () {
// console.log(this.value)
}
}
}
</script>
实现OptionGroup
通过vue的extends(继承),Element的OptionGroup,在monuted生命周期方法中,绑定一个点击事件。
新建一个selectGroup.vue文件,去掉<template>,只留下<script>和<style>的部分。
<script>
import { OptionGroup } from 'element-ui'
export default {
extends: OptionGroup,
name: 'SelectGroup',
mounted () {
const select = this.$el
const _this = this
// 绑定一个点击事件
select.addEventListener('click', function (e) {
if (e.target.matches('li.el-select-group__title')) {
// 获取当前点击的DOM文本
_this.$emit('click', e.target.textContent)
}
})
}
}
</script>
<style scoped lang="scss">
.el-select-group__title{
cursor: pointer;
}
</style>
思路二实现
实现OptionGroup
新建一个自己的option-group.vue,
<script>
import { OptionGroup } from 'element-ui'
export default {
extends: OptionGroup,
name: 'SelectGroup',
props: {
// showTitle为true显示标题
showTitle: {
type: Boolean,
default: false
}
},
mounted () {
const select = this.$el
const _this = this
// 这里兼容一下思路一的思想
if (this.showTitle) {
const group = select.querySelector('.el-select-group')
group.style.marginLeft = '10px'
select.addEventListener('click', function (e) {
if (e.target.matches('li.el-select-group__title')) {
_this.$emit('click', e.target.textContent)
}
})
return
}
// 思路二的实现,隐藏文本
const titleDom = select.querySelector('.el-select-group__title')
titleDom.style.display = 'none'
}
}
</script>
<style scoped lang="scss">
.el-select-group__title{
cursor: pointer;
font-size: 15px;
color: #606266;
font-weight: bold;
&:hover{
color: #409eff;
}
}
</style>
主逻辑
在新建一个select.vue,编写主要逻辑
<template>
<el-select v-model="model.value" placeholder="请选择" multiple style="width: 700px;" clearable @change="changeSelect">
<option-group
:show-title="showTitle"
v-for="group in model.options"
:key="group.id"
:label="group.label"
@click="handleClick"
>
<i>{{title}}</i>
<el-checkbox
v-if="!showTitle"
v-model="group.checked"
class="group-check"
:indeterminate="isIndeterminate || groupsJudge(group.options,model.value)"
@change='selectAll($event,group.id)'>
{{group.label}}
</el-checkbox>
<el-option
v-for="item in group.options"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</option-group>
</el-select>
</template>
<script>
import optionGroup from './option-group'
export default {
components: { optionGroup },
name: 'index',
props: {
value: {
type: Object,
default: () => {}
},
// showTitle为true显示标题
showTitle: {
type: Boolean,
default: false
},
title: {
type: String,
default: ''
}
},
computed: {
// 利用Proxy,监听整个对象变化,优化父子组件传值
model: {
get () {
const that = this
return new Proxy(this.value, {
set (obj, name, val) {
that.$emit('update:value', {
...obj,
[name]: val
})
return true
}
})
},
set (val) {
this.$emit('update:value', val)
}
}
},
data () {
return {
isIndeterminate: false
}
},
methods: {
// 点击全选框时
selectAll (val, id) {
const arr = this.model.options.find(f => f.id === id).options.map(m => m.value)
if (val) {
arr.forEach(item => {
if (!this.model.value.includes(item)) {
this.model.value.push(item)
}
})
} else {
this.model.value.forEach((item, index) => {
if (arr.includes(item)) {
this.model.value.splice(index, 1, '')
}
})
}
this.model.value = this.model.value.filter(f => f !== '')
this.isIndeterminate = false
},
// 选项发生变化时
changeSelect (val) {
this.model.options.forEach(item => {
const arr = item.options.map(m => m.value)
item.checked = arr.every((v) => {
return val.some(s => s === v)
})
})
},
// 多选框indeterminate的状态判定
groupsJudge (options, item) {
const groups = options.map(v => v.value)
if (groups.length === 0 || groups.every(e => item.includes(e))) {
return false
}
return groups.some(e => item.includes(e))
},
// 【思路一的实现】点击事件,实现全选
handleClick (e) {
const options = this.model.options.filter(item => e === item.label)[0].options
const newMap = options.filter(item => {
return !this.model.value.includes(item.value)
})
const result = newMap.length === 0 ? options : newMap
result.forEach(item => {
const index = this.model.value.indexOf(item.value)
if (index > -1) {
this.model.value.splice(index, 1)
} else {
this.model.value.push(item.value)
}
})
}
}
}
</script>
<style scoped lang="scss">
.group-check{
font-size: 15px;
font-weight: bold;
margin-left: 20px;
}
</style>
使用组件
<template>
<selectGroup :value.sync="optionsData" style="margin: 40px"></selectGroup>
</template>
<script>
import selectGroup from '../components/selectGroup/index'
export default {
name: 'ruleInput',
components: { selectGroup },
data () {
return {
optionsData: {
options: [
// id和check必须要有
{
label: '热门城市',
id: 1, // 必须存在
check: false, // 必须存在
options: [
{
value: 'Shanghai',
label: '上海'
},
{
value: 'Beijing',
label: '北京'
}
]
},
{
label: '城市名',
id: 2,
check: false,
options: [
{
value: 'Chengdu',
label: '成都'
},
{
value: 'Shenzhen',
label: '深圳'
},
{
value: 'Guangzhou',
label: '广州'
},
{
value: 'Dalian',
label: '大连'
}]
}],
value: []
}
}
}
}
</script>