element-ui —Cascader 级联选择器(选中方式处理)

·  阅读 1569

目前Vue Element的 el-cascader 级联选择器,多选或者选择任意一级,需要点击左侧的checkbox才能选中。

目标:点击label选中,已选中状态再次点击label取消选中

有两种方式实现

  • 通过添加点击事件
  • 通过css样式控制

方法一:通过点击事件

<!-- 改造前 -->

<template>
  <div>
    <el-cascader
        v-model="cascaderValue"
        :options="options"
        :props="{ multiple: true,checkStrictly:true }"
        clearable>
    </el-cascader>
    {{cascaderValue}}
  </div>
</template>

复制代码

改造点

  • 可以通过 scoped slot 对级联选择器的备选项的节点内容进行自定义
  • 添加点击事件 @click="onClick(node)" 将选中的数据进行处理
  • 区分单选/多选

单选:

  1. 将选中的数据赋值到v-model

多选

  1. 将之前选择的数据保存
  2. 处理:未选勾选选中/已选取消选中
  3. 将选中的数据赋值到v-model
<!-- 改造后 -->

<template>
  <div>
    <el-cascader
        v-model="cascaderValue"
        :options="options"
        :props="{ multiple: true,checkStrictly:true }"
        clearable>
          <!-- 重点:第一步,增加slot-scope -->
          <template slot-scope="{ node, data }">
            <span style="display:block" @click="onClick(node)">{{ data.label }}</span>
          </template>
    </el-cascader>
    {{cascaderValue}}
  </div>
</template>

<script>

export default {
  name: 'Test',
  data () {
    return {
      cascaderValue: [],
      options: [
        {
          value: 1,
          label: '东南',
          disabled: true,
          children: [
            {
              value: 2,
              label: '上海',
              disabled: true,
              children: [
                { value: 3, label: '普陀' },
                { value: 4, label: '黄埔' },
                { value: 5, label: '徐汇' }
              ]
            }
          ]
        },
        {
          value: 6,
          label: '西北',
          disabled: true,
          children: [
            { value: 7, label: '陕西' },
            { value: 8, label: '新疆' }
          ]
        },
        {
          value: 9,
          label: '东北'
        }
      ]
    }
  },
  methods: {
    // 单选的情况
    // onClick (node) {
    //   if (!node.isDisabled) {
    //     this.cascaderValue = node.path
    //   }
    // },
    // 多选的情况
    onClick (node) {
      if (node.isDisabled) {
        return
      }

      // 定义一个数组,用于处理数据
      const data = []

      // 将之前选择的数据保存
      if (this.cascaderValue.length) {
        this.cascaderValue.forEach(n => {
          data.push(n)
          console.log('之前的值', data)
        })
      }

      // 处理:
      // 未选状态:将当前点击的node.path(是一个数组[1,2,3]),添加到data中
      // 已选状态:已选中的从data数组中查找node.path删除
      if (!node.checked) {
        data.push(node.path)
        console.log('未选->选中', data)
      } else {
        if (data.length) {
          // 将[ [ 1, 2, 3 ], [ 1, 2, 4 ], [ 1, 2, 5 ] ]数据转为["1,2,3", "1,2,4", "1,2,5"],因为数组不能比较
          const map = data.map(n => n.join(','))
          // 找出node.path删除
          const idx = map.findIndex(q => q === node.path.join(','))
          if (idx > -1) {
            data.splice(idx, 1)
          }
          console.log('已选->取消选中', data)
        }
      }

      // 最后双向绑定v-model的数据
      this.cascaderValue = data
    }
  }
}
</script>
复制代码

多选存在问题:当使用el-cascader属性 clearable 的时候,会出现删除错误的问题,因为级联选择器绑定的数据是按照指定的格式添加。

因此需要在 “数据赋值到v-model” 以前对数据进行处理

  1. 将之前选择的数据保存
  2. 处理:未选勾选选中/已选取消选中
  3. 将options树形数据转为数组数据
  4. 将满足条件的数据过滤出来
  5. 将选中的数据赋值到v-model
<template>
  <div>
    <!-- 多选 -->
    <el-cascader
        v-model="cascaderValue"
        :options="options"
        :props="{ multiple: true,checkStrictly:true }"
        clearable>
          <!-- 重点:增加slot-scope -->
          <template slot-scope="{ node, data }">
            <span style="display:block" @click="onClick(node)">{{ data.label }}</span>
          </template>
    </el-cascader>
    {{cascaderValue}}
  </div>
</template>

<script>

export default {
  name: 'Test',
  data () {
    return {
      cascaderValue: [],
      options: [
        {
          value: 1,
          label: '东南',
          disabled: true,
          children: [
            {
              value: 2,
              label: '上海',
              disabled: true,
              children: [
                { value: 3, label: '普陀' },
                { value: 4, label: '黄埔' },
                { value: 5, label: '徐汇' }
              ]
            }
          ]
        },
        {
          value: 6,
          label: '西北',
          disabled: true,
          children: [
            { value: 7, label: '陕西' },
            { value: 8, label: '新疆' }
          ]
        },
        {
          value: 9,
          label: '东北'
        }
      ]
    }
  },
  computed: {
    // 1.第一步,将树形数据转数组数据
    // ["1,2,3", "1,2,4", "1,2,5", "6,7", "6,8", "9"]
    gradeLabelToList () {
      return this.treeToList(this.options)
    }
  },
  methods: {
    // 单选的情况
    // onClick (node) {
    //   if (!node.isDisabled) {
    //     this.cascaderValue = node.path
    //   }
    // },
    // 多选的情况
    onClick (node) {
      if (node.isDisabled) {
        return
      }

      // 定义一个数组,用于处理数据
      const data = []

      // 将之前选择的数据保存
      if (this.cascaderValue.length) {
        this.cascaderValue.forEach(n => {
          data.push(n)
          console.log('之前的值', data)
        })
      }

      // 处理:
      // 未选状态:将当前点击的node.path(是一个数组[1,2,3]),添加到data中
      // 已选状态:已选中的从data数组中查找node.path删除
      if (!node.checked) {
        data.push(node.path)
        console.log('未选->选中', data)
      } else {
        if (data.length) {
          // 将[ [ 1, 2, 3 ], [ 1, 2, 4 ], [ 1, 2, 5 ] ]数据转为["1,2,3", "1,2,4", "1,2,5"],因为数组不能比较
          const map = data.map(n => n.join(','))
          // 找出node.path删除
          const idx = map.findIndex(q => q === node.path.join(','))
          if (idx > -1) {
            data.splice(idx, 1)
          }
          console.log('已选->取消选中', data)
        }
      }

      // 如果不添加以下代码,当使用组件的属性 clearable 的时候,会出现删除错误的问题,因为数据是有顺序的
      const joinData = data.map(n => n.join(','))
      // 将满足条件的数据过滤出来
      const tempData = this.gradeLabelToList.filter(n => joinData.find(m => m === n))
      let retData = []
      // 再将数据数据构造回[ [ 1, 2, 3 ], [ 1, 2, 4 ], [ 1, 2, 5 ] ]
      retData = tempData.map(n => n.split(',').map(Number))

      // 最后双向绑定v-model的数据
      this.cascaderValue = retData
    },
    treeToList (tree, out, pid) {
      if (out === undefined) {
        out = []
      }
      tree.forEach(n => {
        const path = pid ? `${pid},` : ''
        const data = `${path}${n.value}`
        if (n.children) {
          this.treeToList(n.children, out, data)
        } else {
          out.push(data)
        }
      })
      return out
    }
  }
}
</script>

复制代码

方法二:通过CSS样式

还有另一种方法,仅根据css样式,扩大radio或者checkbox的区域达到选中效果

存在问题:无法展开下一级

// css样式
<style lang="less">
  .el-cascader-panel .el-radio,
  .el-checkbox {
    width: 100%;
    height: 100%;
    z-index: 10;
    position: absolute;
    top: 10px;
    right: 10px;
  }

  .el-cascader-panel .el-radio__input,
  .el-checkbox__input {
    visibility: hidden;
  }
</style>

复制代码
分类:
前端
标签:
分类:
前端
标签:
收藏成功!
已添加到「」, 点击更改