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>

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

相关推荐
海上彼尚2 小时前
秒删node_modules[无废话版]
vue.js·react.js
不爱吃饭爱吃菜4 小时前
uniapp微信小程序-长按按钮百度语音识别回显文字
前端·javascript·vue.js·百度·微信小程序·uni-app·语音识别
GoodStudyAndDayDayUp6 小时前
gitlab+portainer 实现Ruoyi Vue前端CI/CD
前端·vue.js·gitlab
前端 贾公子6 小时前
uniapp -- 验证码倒计时按钮组件
前端·vue.js·uni-app
LuckyLay6 小时前
Vue百日学习计划Day4-8——Gemini版
前端·vue.js·学习
&白帝&16 小时前
vue右键显示菜单
前端·javascript·vue.js
Wannaer16 小时前
从 Vue3 回望 Vue2:事件总线的前世今生
前端·javascript·vue.js
光影少年16 小时前
vue中,created和mounted两个钩子之间调用时差值受什么影响
前端·javascript·vue.js
cdcdhj17 小时前
vue用通过npm的webpack打包编译,这样更适合灵活配置的项目
vue.js·webpack·npm