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

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

实现效果:

主要功能实现:

1. 全选按钮可选

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

javascript 复制代码
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. 全选按钮与其他节点的联动效果:
  • 点击全选按钮所有节点都被选中,非全选状态下全选按钮不被选中
javascript 复制代码
    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值
javascript 复制代码
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
    },
  },

组件使用:

html 复制代码
<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 的数据赋值代码就可以,类似于:
javascript 复制代码
const { data } = await APIgetDictTree(params)
let children = data.data[0].children || []

组件实现:

javascript 复制代码
<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>

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

相关推荐
猩兵哥哥6 小时前
前端面向对象设计原则运用 - 策略模式
前端·javascript·vue.js
EMT8 小时前
在 Vue 项目中使用 URL Query 保存和恢复搜索条件
javascript·vue.js
我是日安8 小时前
从零到一打造 Vue3 响应式系统 Day 9 - Effect:调度器实现与应用
前端·vue.js
鹏多多8 小时前
深入解析vue的keep-alive缓存机制
前端·javascript·vue.js
用户51681661458411 天前
Vue Router 路由懒加载引发的生产页面白屏问题
vue.js·vue-router
前端缘梦1 天前
Vue Keep-Alive 组件详解:优化性能与保留组件状态的终极指南
前端·vue.js·面试
Simon_He1 天前
这次来点狠的:用 Vue 3 把 AI 的“碎片 Markdown”渲染得又快又稳(Monaco 实时更新 + Mermaid 渐进绘图)
前端·vue.js·markdown
王同学QaQ1 天前
Vue3对接UE,通过MQTT完成通讯
javascript·vue.js
华仔啊1 天前
基于 RuoYi-Vue 轻松实现单用户登录功能,亲测有效
java·vue.js·后端
艾小码1 天前
告别Vue混入的坑!Composition API让我效率翻倍的3个秘密
前端·javascript·vue.js