基于el- cascader二次封装,实现点击label选中和双击选中所有子级

本文介绍如何基于El-Cascader组件实现自定义交互功能,包括单击选中节点和双击选中所有子节点。通过修改组件的HTML结构和绑定事件处理函数,实现了这些定制化的交互行为。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

基于el- cascader二次封装,实现单击label选中和双击选中子级

最终实现效果

最近接到需求需要级联选择器父子互不关联且

  1. 点击label可以选中
  2. 双击选中所有子级

一、探索思路

  1. 定制性这么强的需求第一步当然是使用自定义节点内容
    在这里插入图片描述
  2. 文档中没有说明对应功能的函数,只能通过查看源码寻找解决方案

源码中并没有el-tree中的setChecked方法,但是在cascader文件570行发现了handleSuggestionKeyDown函数

该函数中通过触发click事件完成选择操作
在这里插入图片描述
到这里大致思路就清晰了,可以使用自定义内容和操作DOM完成以上需求

二、实现方案

1.自定义节点内容

 <el-cascader
    v-model="value"
    v-bind="$attrs"
    popper-class="z-cascader"
    @change="$emit('change', value)"
  >
    <template slot-scope="{ node }">
      <div
        class="z-cascader-event"
        @click.stop="handleChoice($event)"
        @dblclick.stop="handleMoreChoice(node)"
      ></div>
      <span :id="node.value" class="z-cascader-label">{{ node.label }}</span>
    </template>
  </el-cascader>
  1. 操作DOM
 methods: {
 	//单击
    handleChoice(e) {
      clearTimeout(this.clickSetTimeout);
      this.clickSetTimeout = setTimeout(() => {
        e.target.parentElement.previousElementSibling.click();
      }, 300);
    },
    //双击
    handleMoreChoice(node) {
      clearTimeout(this.clickSetTimeout);
      const nodeList = getNodeChildren(node);
      const value = JSON.parse(JSON.stringify(this.value));
      //取消选中
      if (node.checked) {
        const cancel = [];
        nodeList.forEach((item) => {
          for (let index = 0; index < value.length; index++) {
            if (String(item.path) === String(value[index])) {
              cancel.push(item.value);
              value.splice(index, 1);
              break;
            }
          }
        });
        cancel.forEach((id) => {
          document.getElementById(id).parentElement.previousElementSibling.click();
        });
      } else {
      //选中
        value.forEach((item) => {
          for (let index = 0; index < nodeList.length; index++) {
            if (String(item) === String(nodeList[index].path)) {
              nodeList.splice(index, 1);
              break;
            }
          }
        });
        nodeList.forEach(({ value }) => {
          document.getElementById(value).parentElement.previousElementSibling.click();
        });
      }
      //获取所有子级
      function getNodeChildren(data) {
        const children = [];
        _getNodeChildren(data);
        return children;
        function _getNodeChildren(_data) {
          children.push({ path: _data.path, value: _data.value });
          if (_data.children.length) {
            for (let index = 0; index < _data.children.length; index++) {
              _getNodeChildren(_data.children[index]);
            }
          }
        }
      }
    },
  }

三、完整代码

初版方案,请结合实际情况修改优化使用,欢迎评论区留言讨论。

//由于占用label点击事件故次级菜单的展开方式只能使用hover
expandTrigger:'hover'
//只支持严格的遵守父子节点不互相关联的情况下使用
checkStrictly:true
<template>
  <el-cascader
    v-model="value"
    v-bind="$attrs"
    popper-class="z-cascader"
    @change="$emit('change', value)"
  >
    <template slot-scope="{ node }">
      <div
        class="z-cascader-event"
        @click.stop="handleChoice($event)"
        @dblclick.stop="handleMoreChoice(node)"
      ></div>
      <span :id="node.value" class="z-cascader-label">{{ node.label }}</span>
    </template>
  </el-cascader>
</template>
<script>
export default {
  inheritAttrs: false,
  model: {
    prop: "v",
    event: "change",
  },
  props: {
    v: {
      type: [Array || String],
      default: "",
    },
  },
  data() {
    return {
      value: "",
      clickSetTimeout: null,
    };
  },
  created() {
    console.log(this.$attrs);
    this.value = this.v;
  },
  methods: {
    handleChoice(e) {
      clearTimeout(this.clickSetTimeout);
      this.clickSetTimeout = setTimeout(() => {
        e.target.parentElement.previousElementSibling.click();
      }, 300);
    },
    handleMoreChoice(node) {
      clearTimeout(this.clickSetTimeout);
      const nodeList = getNodeChildren(node);
      const value = JSON.parse(JSON.stringify(this.value));
      if (node.checked) {
        const cancel = [];
        nodeList.forEach((item) => {
          for (let index = 0; index < value.length; index++) {
            if (String(item.path) === String(value[index])) {
              cancel.push(item.value);
              value.splice(index, 1);
              break;
            }
          }
        });
        cancel.forEach((id) => {
          document.getElementById(id).parentElement.previousElementSibling.click();
        });
      } else {
        value.forEach((item) => {
          for (let index = 0; index < nodeList.length; index++) {
            if (String(item) === String(nodeList[index].path)) {
              nodeList.splice(index, 1);
              break;
            }
          }
        });

        nodeList.forEach(({ value }) => {
          document.getElementById(value).parentElement.previousElementSibling.click();
        });
      }
      function getNodeChildren(data) {
        const children = [];
        _getNodeChildren(data);
        return children;
        function _getNodeChildren(_data) {
          children.push({ path: _data.path, value: _data.value });
          if (_data.children.length) {
            for (let index = 0; index < _data.children.length; index++) {
              _getNodeChildren(_data.children[index]);
            }
          }
        }
      }
    },
  },
};
</script>
<style>
	.z-cascader .el-cascader-node {
	  height: auto;
	  line-height: 16px;
	  padding: 8px 30px 8px 20px;
	}
	.z-cascader .el-checkbox{
	  pointer-events: none;
	}
	.z-cascader .z-cascader-event {
	  width: 100%;
	  height: 20px;
	  position: absolute;
	  left: 0;
	  top: 50%;
	  transform: translateY(-50%);
	}
	.z-cascader .z-cascader-label {
	  display: inline-block;
	  width: 100%;
	  max-width: 200px;
	  white-space: pre-wrap;
	  word-break: break-all;
	}
</style>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值