element-ui的el-cascader增加全选按钮实现(附源码)

最近遇到了在级联选择器上添加全选框的需求 ,但是项目使用的是Vue2 + Element UI的架构,而我们都知道Element UI提供的级联选择器el-cascader是不支持全选框的,而我又没有在网上找到适合我项目的实现,索性自己实现一个组件(源码附在文末,需要自取)

实现效果:

主要功能实现:

1. 全选按钮可选

通过属性 showAllNode 控制在多选模式下全选按钮的添加与否(注意,单选模式下没有全选按钮):

props: {
    showAllNode: {
      type: Boolean,
      default: false
    }
},
methods: {
async fetchDictTree() {

      let children = this.data.data[0].children || []
      // 添加全选节点
      this.treeData = this.showAllNode ? [this.allNode, ...children] : children

      // 获取所有节点值(一维数组)
      this.allNodeValues = this.getAllNodeValues()

      // 修改初始化逻辑
      if (this.showAllNode) {
        // 选中所有节点(包括全选节点)
        this.selectedValue = ['0', ...this.allNodeValues]
        this.lastSelectedValue = [...this.selectedValue]
      } else {
        this.selectedValue = []
        this.lastSelectedValue = []
      }

      this.$emit('input', this.selectedValue)
      this.$emit('ready', true)
    },
}
2. 全选按钮与其他节点的联动效果:
  • 点击全选按钮所有节点都被选中,非全选状态下全选按钮不被选中
    if (this.showAllNode) {
        // 检查是否包含全选节点
        const hasAll = value.includes('0')
        const prevHasAll = this.lastSelectedValue.includes('0')

        if (hasAll && !prevHasAll) {
          // 选中全选,同时选中所有节点
          const allValues = ['0', ...this.allNodeValues]
          this.selectedValue = allValues
          this.lastSelectedValue = [...allValues]
          // 只返回 ['0'] 表示全选
          this.$emit('input', ['0'])
          this.$emit('change', ['0'])
          return
        } else if (!hasAll && prevHasAll) {
          // 取消全选,清空所有选中
          setTimeout(() => {
            this.selectedValue = []
            this.lastSelectedValue = []
            this.$emit('input', [])
            this.$emit('change', [])
          }, 0)
          return
        }

        // 检查是否所有节点都被选中
        const allSelected = this.allNodeValues.every(nodeValue =>
          value.includes(nodeValue)
        )

        // 如果所有节点都被选中,但全选节点没有被选中,则添加全选节点
        if (allSelected && !hasAll) {
          const newValue = ['0', ...value]
          this.selectedValue = newValue
          this.lastSelectedValue = [...newValue]
          // 只返回 ['0'] 表示全选
          this.$emit('input', ['0'])
          this.$emit('change', ['0'])
          return
        }

        // 如果不是所有节点都被选中,但全选节点被选中了,则移除全选节点
        if (!allSelected && hasAll) {
          const newValue = value.filter(v => v !== '0')
          this.selectedValue = newValue
          this.lastSelectedValue = [...newValue]
          this.$emit('input', newValue)
          this.$emit('change', newValue)
          return
        }
      }
3. 全选按钮选中时传回的节点数据(dictKey)的值为0而非所有节点key值
watch: {
    value: {
      handler(val) {
        // 如果传入的值是 ['0'],表示选中全选节点
        if (Array.isArray(val) && val.length === 1 && val[0] === '0' && this.showAllNode) {
          // 内部选中所有节点
          this.selectedValue = ['0', ...this.allNodeValues]
        } else if (!this.multiple && Array.isArray(val) && val.length === 1) {
          // 单选模式下,如果传入的是数组,取第一个元素
          this.selectedValue = val[0]
        } else {
          this.selectedValue = val
        }
      },
      immediate: true
    },
  },

组件使用:

<template>
  <div>
      <classify-cascader v-model="classify" placeholder="请选择试卷分类" multiple
      checkStrictly show-all-node width="200px" />
  </div>
</template>

<script>
import classifyCascader from '@/components/classify-cascader/index.vue'

export default {
  name: 'indexVue',
  components: {
    classifyCascader
  },
  data() {
    return {
      classify: []
    }
  }
}
</script>
  • 笔者已经分装好了一个组件,可以直接使用,如果下拉框的数据是从后端获取的话,改一下fetchDictTree() 方法中 children 的数据赋值代码就可以,类似于:
const { data } = await APIgetDictTree(params)
let children = data.data[0].children || []

组件实现:

<template>
  <el-cascader v-model="selectedValue" :options="treeData" :props="cascaderProps" :placeholder="placeholder"
    :disabled="disabled" :clearable="clearable" :style="{ width: width }" collapse-tags @change="handleChange" />
</template>

<script>

export default {
  name: 'ClassifyCascader',
  props: {
    value: {
      type: [String, Array],
      default: () => []
    },
    checkStrictly: {
      type: Boolean,
      default: false
    },
    multiple: {
      type: Boolean,
      default: false
    },
    showAllNode: {
      type: Boolean,
      default: false
    },
    disabled: {
      type: Boolean,
      default: false
    },
    placeholder: {
      type: String,
      default: '请选择'
    },
    clearable: {
      type: Boolean,
      default: true
    },
    width: {
      type: String,
      default: '200px'
    }
  },
  data() {
    return {
      treeData: [],
      selectedValue: this.value,
      cascaderProps: {
        value: 'dictKey',
        label: 'dictValue',
        children: 'children',
        checkStrictly: this.checkStrictly,
        multiple: this.multiple,
        emitPath: false
      },
      allNode: {
        dictKey: '0',
        dictValue: '全选'
      },
      lastSelectedValue: [],
      allNodeValues: [], // 存储所有节点的值(一维数组)
      data: {
        data: [
          {
            children: [
              {
                dictKey: '1',
                dictValue: '分类1'
              },
              {
                dictKey: '2',
                dictValue: '分类2',
                children: [
                  {
                    dictKey: '2-1',
                    dictValue: '子分类2-1',
                    children: []
                  },
                  {
                    dictKey: '2-2',
                    dictValue: '子分类2-2',
                    children: []
                  }
                ]
              }
            ]
          }
        ]
      }
    }
  },
  watch: {
    value: {
      handler(val) {
        // 如果传入的值是 ['0'],表示选中全选节点
        if (Array.isArray(val) && val.length === 1 && val[0] === '0' && this.showAllNode) {
          // 内部选中所有节点
          this.selectedValue = ['0', ...this.allNodeValues]
        } else if (!this.multiple && Array.isArray(val) && val.length === 1) {
          // 单选模式下,如果传入的是数组,取第一个元素
          this.selectedValue = val[0]
        } else {
          this.selectedValue = val
        }
      },
      immediate: true
    },
  },
  created() {
    this.fetchDictTree()
  },
  methods: {
    // 刷新数据
    refreshData() {
      return this.fetchDictTree()
    },

    async fetchDictTree() {

      let children = this.data.data[0].children || []
      // 添加全选节点
      this.treeData = this.showAllNode ? [this.allNode, ...children] : children

      // 获取所有节点值(一维数组)
      this.allNodeValues = this.getAllNodeValues()

      // 修改初始化逻辑
      if (this.showAllNode) {
        // 选中所有节点(包括全选节点)
        this.selectedValue = ['0', ...this.allNodeValues]
        this.lastSelectedValue = [...this.selectedValue]
      } else {
        this.selectedValue = []
        this.lastSelectedValue = []
      }

      this.$emit('input', this.selectedValue)
      this.$emit('ready', true)
    },



    handleChange(value) {
      if (this.showAllNode) {
        // 检查是否包含全选节点
        const hasAll = value.includes('0')
        const prevHasAll = this.lastSelectedValue.includes('0')

        if (hasAll && !prevHasAll) {
          // 选中全选,同时选中所有节点
          const allValues = ['0', ...this.allNodeValues]
          this.selectedValue = allValues
          this.lastSelectedValue = [...allValues]
          // 只返回 ['0'] 表示全选
          this.$emit('input', ['0'])
          this.$emit('change', ['0'])
          return
        } else if (!hasAll && prevHasAll) {
          // 取消全选,清空所有选中
          setTimeout(() => {
            this.selectedValue = []
            this.lastSelectedValue = []
            this.$emit('input', [])
            this.$emit('change', [])
          }, 0)
          return
        }

        // 检查是否所有节点都被选中
        const allSelected = this.allNodeValues.every(nodeValue =>
          value.includes(nodeValue)
        )

        // 如果所有节点都被选中,但全选节点没有被选中,则添加全选节点
        if (allSelected && !hasAll) {
          const newValue = ['0', ...value]
          this.selectedValue = newValue
          this.lastSelectedValue = [...newValue]
          // 只返回 ['0'] 表示全选
          this.$emit('input', ['0'])
          this.$emit('change', ['0'])
          return
        }

        // 如果不是所有节点都被选中,但全选节点被选中了,则移除全选节点
        if (!allSelected && hasAll) {
          const newValue = value.filter(v => v !== '0')
          this.selectedValue = newValue
          this.lastSelectedValue = [...newValue]
          this.$emit('input', newValue)
          this.$emit('change', newValue)
          return
        }
      }
      // 正常情况下的处理
      this.lastSelectedValue = [...value]
      const outputValue = Array.isArray(value) ? value : [value]
      this.$emit('input', outputValue)
      this.$emit('change', outputValue)
    },

    // 获取所有节点的值(一维数组)
    getAllNodeValues() {
      const allValues = []
      const traverse = (nodes) => {
        nodes.forEach(node => {
          if (node.dictKey === '0') return
          allValues.push(node.dictKey)
          if (node.children && node.children.length > 0) {
            traverse(node.children)
          }
        })
      }
      traverse(this.treeData)
      return allValues
    }
  }
}
</script>

大家有什么问题可以评论区交流一下,我看到了也会回复的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值