Element的Select分组全选模式

目录

思路

思路一实现

 搭建基本结构

实现OptionGroup

思路二实现

实现OptionGroup

主逻辑 

使用组件


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"
        :disabled="!group.options.length"
        :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)
       // 判断options为空的情况
        if (!item.options.length) {
          item.checked = false
        } else {
          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, // 必须存在
            checked: false, // 必须存在
            options: [
              {
                value: 'Shanghai',
                label: '上海'
              },
              {
                value: 'Beijing',
                label: '北京'
              }
            ]
          },
          {
            label: '城市名',
            id: 2,
            checked: false,
            options: [
              {
                value: 'Chengdu',
                label: '成都'
              },
              {
                value: 'Shenzhen',
                label: '深圳'
              },
              {
                value: 'Guangzhou',
                label: '广州'
              },
              {
                value: 'Dalian',
                label: '大连'
              }]
          }],
        value: []
      }
    }
  }
}
</script>

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值