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

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

基于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>
<!-- 添加或修改制冷设备服务处理记录对话框 --> <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body> <el-form ref="form" :model="form" :rules="rules" label-width="90px"> <el-form-item label="服务单号" prop="seviceRecno"> <el-input v-model="form.seviceRecno" placeholder="请输入服务单号" @blur="fetchServiceInfo" clearable/> </el-form-item> <el-form-item label="服务请求ID" prop="serviceRequestId"> <el-input v-model="form.serviceRequestId" placeholder="获取服务请求ID" disabled/> </el-form-item> <el-form-item label="工程师ID" prop="engineerId"> <el-input v-model="form.engineerId" placeholder="获取工程师ID" disabled/> </el-form-item> <el-form-item label="操作类型" prop="actionType"> <el-input v-model="form.actionType" type="textarea" placeholder="请输入内容" /> </el-form-item> <el-form-item label="操作描述" prop="actionDescription"> <el-input v-model="form.actionDescription" type="textarea" placeholder="请输入内容" /> </el-form-item> <el-form-item label="使用零件" prop="partsUsed"> <el-input v-model="form.partsUsed" type="textarea" placeholder="请输入内容" /> </el-form-item> <el-form-item label="耗时(小时)" prop="hoursSpent"> <el-input v-model="form.hoursSpent" placeholder="请输入耗时(小时)" /> </el-form-item> <el-form-item label="下一步计划" prop="nextStep"> <el-input v-model="form.nextStep" type="textarea" placeholder="请输入内容" /> </el-form-item> <el-form-item label="经验教训" prop="lessonLearn"> <el-input v-model="form.lessonLearn" type="textarea" placeholder="请输入经验教训" /> </el-form-item> <el-form-item label="记录时间" prop="recordDate"> <el-date-picker clearable v-model="form.recordDate" type="datetime" value-format="yyyy-MM-dd HH:mm:ss" placeholder="请择记录时间"> </el-date-picker> </el-form-item> <el-form-item label="图片记录"> <div v-for="(field, index) in fields" :key="index" class="upload-group"> <label>{{ field.label }}</label> <el-upload :action="''" :show-file-list="false" :before-upload="(file) => beforeUpload(file, field.name)" :auto-upload="true"> <el-button size="small" type="primary">择图片</el-button> </el-upload> <img v-if="form[field.name]" :src="form[field.name]" class="preview-img"> </div> </el-form-item> <el-form-item label="纸质记录" prop="paperRecord"> <el-input v-model="form.paperRecord" placeholder="请输入纸质记录,这里填写上传文件名,上传后双击可以打开" /> </el-form-item> <el-form-item label="记录上传服务器地址" prop="recordAdrees"> <el-input v-model="form.recordAdrees" placeholder="请输入记录上传服务器地址," /> </el-form-item> </el-form> <div slot="footer" class="dialog-footer"> <el-button type="primary" @click="submitForm">确 定</el-button> <el-button @click="cancel">取 消</el-button> </div> </el-dialog>this.form = { id: null, seviceRecno: null, serviceRequestId: null, engineerId: null, actionType: null, actionDescription: null, partsUsed: null, hoursSpent: null, nextStep: null, lessonLearn: null, recordDate: null, createBy: null, createTime: null, loadPotoa: null, loadPotob: null, loadPotoc: null, loadPotod: null, loadPotoe: null, loadPotof: null,如何实现一起存储
最新发布
10-11
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值