vue实现自定义树形穿梭框功能

需求:

我们在开发过程中,会遇到需要将一个数据选择做成穿梭框,但是要求穿梭框左侧为树形结构、右侧为无层级结构的数据展示,ElementUI自身无法在穿梭框中添加树形结构,网上搜到了大佬封装的插件但是对于右侧的无树形结构一点还是不太满足。以下是我们简单的封装写的组件可以实现此功能

在这里插入图片描述

1,封装的treeTransfetr组件如下:
<template>
    <div class="treeTransfer">
      <!-- 左边 -->
      <div class="leftTree">
          <div class="list">
              <div class="left_lowline">
                  <div class="leftcheck_con">
                      <el-checkbox v-model="checkedLeft" :disabled="leftTreeData.length < 1" label="" size="large"
                      style="margin-right: 12px" @change="leftAllCheck" />
                      <p class="left_title">{{ props.title[0] }}</p>
                  </div>
                  <div>
                      {{ leftOperation.length }}/{{ leftAllselectIdarry.length }}
                  </div>
              </div>
              <el-tree 
                  ref="leftTreeRef" 
                  :data="leftTreeData" 
                  show-checkbox
                  node-key="id" 
                  :props="props.defaultProps" 
                  v-slot="{ node, data }" 
                  accordion
                  :check-strictly="true"
                  @check="onCheckLeft" 
                  default-expand-all>
                  <div>
                      {{ data.label }}
                  </div>
              </el-tree>
          </div>
      </div>
      <!-- 中间按钮 -->
      <div class="btnDiv">
          <div class="mg10">
              <el-button @click="toRight()" icon="ele-Right" type="primary"  circle :disabled="leftOperation.length < 1"/>
          </div>
          <div class="mg10">
              <el-button @click="toLeft()" icon="ele-Back" type="primary" circle :disabled="rightOperation.length < 1"/>
          </div>
      </div>
      <!-- 右边 -->
      <div class="rightTree">
          <div class="list">
              <div class="left_lowline">
                  <div class="leftcheck_con">
                      <el-checkbox v-model="checkedRight" :disabled="rightTreeData.length < 1" label="" size="large"
                      style="margin-right: 12px" @change="rightAllCheck" />
                      <p class="left_title">{{ props.title[1] }}</p>
                  </div>
                  <div>
                      {{ rightOperation.length }}/{{ rightAllselectIdarry.length }}
                  </div>
              </div>
              <el-tree ref="rightTreeRef" 
                  :data="rightTreeData" 
                  show-checkbox 
                  node-key="id"
                  :props="props.defaultProps" 
                  v-slot="{ node, data }" 
                  accordion 
                  :check-strictly="true"
                  @check="onCheckRight" 
                  default-expand-all>
                  <div>
                      {{ data.label }}
                  </div>
              </el-tree>
          </div>
      </div>
    </div>
  </template>
  
  <script setup lang="ts">
  import { ref, onMounted, watch, nextTick } from 'vue';
  import lodash from 'lodash-es'
  const props = defineProps(['fromData', 'toData', 'defaultProps', 'title', 'visible']);
   
  const checkedLeft = ref(false)
  const checkedRight = ref(false)
  
  const leftOperation = ref<any[]>([])
  const rightOperation = ref<any[]>([])
   
  // 定义emit
  const emits = defineEmits(['addStaffchange']);
  const leftTreeRef = ref();
  const rightTreeRef = ref();
   
  // 左侧数据
  const leftTreeData = ref([] as any);
  // 右侧数据
  const rightTreeData = ref([] as any);
  
  // 左侧可以选中id集合
  const leftAllselectIdarry = ref([] as any)
  
  // 右侧可以选中id集合
  const rightAllselectIdarry = ref([] as any)
   
  watch(
      props,
      newVal => {
          leftTreeData.value = lodash.cloneDeep(newVal.fromData)
          rightTreeData.value = lodash.cloneDeep(newVal.toData)
          // 获取左侧的全选中的id
          leftAllselectIdarry.value = getAllIds(leftTreeData.value, [])
          if (newVal.visible) {
              checkedLeft.value = false
              checkedRight.value = false
              leftOperation.value = []
              rightOperation.value = []
              nextTick(() => {
                  leftTreeRef?.value.setCheckedKeys([])
              })
          }
      },
      { immediate: true }
  )
   
  defineExpose({
      leftTreeData,
      rightTreeData
  })
   
  onMounted(() => {
  })
  
  // 去右边
  const toRight = async () => {
      const leftTree = leftTreeRef.value;
      if (!leftTree) {
          return
      }
      const leftNodes = leftTree.getCheckedNodes(false, false)
      const checkedKeys = leftTree.getCheckedKeys(false)
      const rightTree = rightTreeRef.value
      const newArr = rightTreeData.value.concat(leftNodes)
      
      let obj = {};
      let peon = newArr.reduce((cur,next) => {
          obj[next['id']] ? "" : obj[next['id']] = true && cur.push(next);
          return cur;
      },[]) 
      //设置cur默认类型为数组,并且初始值为空的数组
      const getnewleftArry = peon.map(item => {
         return {
          id: item.id,
          label: item.label,
          pid: item.pid,
          children: [],
         }
      })
      rightTreeData.value = getnewleftArry
      leftOperation.value = leftTreeRef.value?.getCheckedKeys(false)
      emits('addStaffchange', checkedKeys)
      setTimeout(() => {
          rightTree?.setCheckedNodes(leftNodes);
          rightOperation.value = rightTreeRef.value?.getCheckedKeys(false)
          rightAllcheckChange()
      }, 500)
  };
  
  // 去左边
  const toLeft = async () => {
      const rightTree = rightTreeRef.value
      if (!rightTree) {
          return
      }
      const checkedKeys = rightTree.getCheckedKeys(false)
      for(var i=0; i<rightTreeData.value.length;i++){
        if(checkedKeys.includes(rightTreeData.value[i].id)){
          rightTreeData.value.splice(i,1)
          i-=1
        }
      }
      rightOperation.value = rightTree?.getCheckedKeys(false)
      if (rightTreeData.value && rightTreeData.value.length === 0) {
          checkedRight.value = false
      }
      emits('addStaffchange', getAllIds(rightTreeData.value, []))
  };
  
   
  //左侧选中
  const onCheckLeft = () => {
      leftOperation.value = leftTreeRef.value?.getCheckedKeys(false)
      if (leftOperation.value.length === leftAllselectIdarry.value.length) {
          checkedLeft.value = true
      } else {
          checkedLeft.value = false
      }
  }
  
  // 右侧选中
  const onCheckRight = () => {
      rightOperation.value = rightTreeRef.value?.getCheckedKeys(false)
      rightAllselectIdarry.value.length = getAllIds(rightTreeData.value, []).length
      rightAllcheckChange()
  }
  
  // 右侧是否全选获取
  function rightAllcheckChange () {
      rightAllselectIdarry.value.length = getAllIds(rightTreeData.value, []).length
      if (rightOperation.value.length === rightAllselectIdarry.value.length) {
          checkedRight.value = true
      } else {
          checkedRight.value = false
      }
      return checkedRight.value
  }
  
   
  // 左侧全选中值 
  const leftAllCheck = () => {
      if (checkedLeft.value) {
          leftTreeRef.value.setCheckedKeys(getAllIds(leftTreeData.value, []))
          checkedLeft.value = true;
      } else {
          leftTreeRef.value.setCheckedKeys([])
          checkedLeft.value = false
      }
      leftOperation.value = leftTreeRef.value?.getCheckedKeys(false)
  }
   
  // 右侧全选中值 
  const rightAllCheck = () => {
      if (checkedRight.value) {
          rightTreeRef.value.setCheckedKeys(getAllIds(rightTreeData.value, []))
          checkedRight.value = true
      } else {
          rightTreeRef.value.setCheckedKeys([])
          checkedRight.value = false
      }
      rightOperation.value = rightTreeRef.value?.getCheckedKeys(false)
  }
  
  
  // 递归获取所有id数据
  function getAllIds(tree, result) {
    //遍历树获取id数组
    for (const i in tree) {
      if (!tree[i].disabled) {
          result.push(tree[i].id); // 遍历项目满足条件后的操作
      }
      if (tree[i].children) {
        // 存在子节点就递归
        getAllIds(tree[i].children, result);
      }
    }
    return result;
  }
  </script>
  
  <style scoped lang="scss">
  .treeTransfer {
      display: flex;
      justify-content: center;
      .el-tree {
          overflow: auto;
          max-height: 360px;
      }
      .leftTree {
          border: 1px solid #ebeef5;
          width: 40%;
          height: calc(100% - 60px);
          overflow: auto;
      }
  
      .left_lowline {
          display: flex;
          align-items: center;
          justify-content: space-between;
          background: #f5f7fa;
          padding: 0 23px 0 10px;
          .leftcheck_con {
              display: flex;
              align-items: center;
          }
      }
      .btnDiv {
          width: 20%;
          height: calc(100% - 60px);
          text-align: center;
          margin: auto 0;
          line-height: 40px;
          position: relative;
      }
      .rightTree {
          width: 40%;
          height: calc(100% - 60px);
      }
  }
  </style>
2,具体使用如下
<treeTransfetr 
          ref="treeTransferRef" 
          :fromData="fromData"
          :toData="toData"
          :visible="visible"
          :defaultProps="transferProps" 
          @addStaffchange="addStaffchange" 
          :title="['筛选结果', '添加人员']"
        >
        </treeTransfetr>

let treeTransferRef = ref(); // 树形穿梭框
let fromData = ref([
  {
    id: "1",
    pid: 0,    //自定义pid的参数名,默认为"pid" 必填:false
    label: "张三-D1-DM",
    disabled: false,
    children: [
      {
        id: "1-1",
        pid: "1",
        label: "李四-D1-TL",
        disabled: false,
        children: []
      },
      {
        id: "1-2",
        pid: "1",
        label: "王五-D2-TL",
        disabled: false,
        children: [
          {
            id: "1-2-1",
            pid: "1-2",
            children: [],
            label: "赵六-D3-TL",
            disabled: true,
          },
          {
            id: "1-2-2",
            pid: "1-2",
            children: [],
            label: "李明-D4-TL",
            disabled: false,
          },
          {
            id: "1-2-3",
            pid: "1-2",
            children: [],
            label: "王三明-D5-TL",
            disabled: false,
          }
        ]
      }
    ]
  }
]); // 树形数据
let toData = ref([]); // 选中的ids数据
const transferProps = ref({
  label: 'label',
  children: 'children',
  disabled: 'disabled',
});

如果我们想要用插件实现,推荐使用el-tree-transfer

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/419221.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Go字符串实战操作大全!

目录 1. 引言文章结构概览 2. Go字符串基础字符串的定义与特性什么是字符串&#xff1f;Go字符串的不可变性原则 字符串的数据结构Go字符串的内部表达byte和rune的简介 3. 字符串操作与应用3.1 操作与应用字符串连接字符串切片字符串查找字符串比较字符串的替换字符串的大小写转…

androidapp开发工具,Android MVP模式详解

**工欲善其事必先利其器&#xff0c;要想拿到满意的offer&#xff0c;必须有一定的准备。**以下列出来的东西是笔者认为应该准备的东西 简历中提到的&#xff0c;一定要有准备&#xff0c;别给自己挖坑Java准备&#xff0c;Java基础&#xff0c;有的公司会扣的很细&#xff0c…

自动驾驶技术详解

&#x1f3ac;个人简介&#xff1a;一个全栈工程师的升级之路&#xff01; &#x1f4cb;个人专栏&#xff1a;自动驾驶技术 &#x1f380;CSDN主页 发狂的小花 &#x1f304;人生秘诀&#xff1a;学习的本质就是极致重复! 目录 一 自动驾驶视觉感知算法 1目标检测 1.1 两阶…

挑战30天学完Python:Day28 数据库Mysql

&#x1f389; 本系列为Python基础学习&#xff0c;原稿来源于 30-Days-Of-Python 英文项目&#xff0c;大奇主要是对其本地化翻译、逐条验证和补充&#xff0c;想通过30天完成正儿八经的系统化实践。此系列适合零基础同学&#xff0c;或仅了解Python一点知识&#xff0c;但又没…

Dsco Dropship EDI需求分析

供应商要想从Dsco处通过EDI获取订单&#xff0c;需要部署自己的EDI系统&#xff0c;与Dsco的EDI供应商CommerceHub 建立连接&#xff0c;分为两个方向&#xff1a; 1.从CommerceHub 的 Dsco 平台获取 EDI 850 采购订单 2.向Dsco发送库存&#xff08;846&#xff09;、订单状态…

中仕公考:2024年安徽省直事业单位发布公告

2024年度安徽省直事业单位统一招聘发布公告&#xff0c;具体考试时间如下&#xff1a; 报名时间&#xff1a;2024年3月5日9.00-3月11日18.00 缴费时间&#xff1a;2024年3月13日18.00前 打印准考证时间&#xff1a;2024年3月27日-3月29日 笔试时间&#xff1a;2024年3月30日…

实现前端开发几个常用技巧

如何知道iframe下载完成 定时器轮询监听readyState的状态&#xff0c;如果是 complete 或者 interactive 说明文件加载完成。 常用的全屏居中 JS 函数 JS实现deepCopy 生成星级评分 JS数组扁平化之简单方法实现 toString 优点&#xff1a;简单&#xff0c;方便&#xff0c;对…

大势智慧黄先锋:现实世界数字重建 拥抱AI 擘画自主可控的三维画卷

来源&#xff1a;中国地理信息产业协会 实景三维涉及到大面积、高精度的地理空间信息数据&#xff0c;然而早期国内99%以上的实景三维数据制作测绘单位都基于国外软件进行三维重建&#xff0c;如此重要的工作大量使用国外软件&#xff0c;如何确保国家地理空间信息的安全&#…

MAC M1 安装mongodb7.0.5 版本

1、进入官网 Download MongoDB Community Server | MongoDBDownload MongoDB Community Server non-relational database to take your next big project to a higher level!https://www.mongodb.com/try/download/community 2、选择版本 3、下载后解压 放到 /usr/local 并修改…

C语言的数据存储详解

C语言数据存储 文章目录 C语言数据存储类型的基本归类类型的意义 数据在内存中的存储整形在内存中的存储大小端整形提升和截断 浮点型在内存中的存储浮点型的存储规则E的不同情况 运用 类型的基本归类 有无符号的意义&#xff1a;生活中有写数据是没有符号之分的&#xff0c;将…

DolphinScheduler——蔚来汽车数据治理开发平台的应用改造

目录 一、业务痛点 二、应用现状 三、技术改造 3.1 稳定性 3.1.1 滚动重启黑名单机制精准路由 3.2 易用性 依赖节点优化 补数任务优化 多 SQL 执行 原文大佬的这篇基于调度系统的数据治理案例有借鉴意义&#xff0c;这里摘抄下来用作学习和知识沉淀。 一、业务痛点 蔚…

lazada、速卖通、亚马逊店铺需要补单来稳定出单率吗?

亚马逊、速卖通、Lazada、shoppe、速卖通、敦煌网、Temu、shein、阿里国际、卖家如何保证店铺出单稳定?在竞争激烈的平台上&#xff0c;保持店铺的稳定出单是每个卖家都追求的目标。为了实现这一目标&#xff0c;卖家需要综合考虑产品、运营、客户服务等多个方面的因素&#x…

刘志雄:新产品市场+新智造模式,构建“声音+”产业创新生态 | 演讲嘉宾公布

随着科技的飞速发展&#xff0c;新技术、新的应用场景不断涌现&#xff0c;也影响着“声音”产业未来的发展方向。如何应对市场变化&#xff0c;满足市场的多样化需求&#xff1f;如何应用新产品市场、智造新模式去构造“声音”产业创新生态呢&#xff1f;请到GAS2024一探究竟。…

ElasticSearch之Completion Suggester

写在前面 通过completion suggester可以实现如下的效果&#xff1a; 其实就是做的like xxx%这种。通过FST这种数据结构来存储&#xff0c;实现快速的前缀匹配&#xff0c;并且可以将es所有的数据加载到内存中所以速度completion的查询速度非常快。 需要注意&#xff0c;如果…

2024最新版,Android开发教程入门

真正最能锻炼能力的便是直接去阅读源码&#xff0c;不仅限于阅读Android系统源码&#xff0c;还包括各种优秀的开源库。 由于整个文档比较全面&#xff0c;内容比较多&#xff0c;篇幅不允许&#xff0c;下面以截图方式展示 。 深入解析微信 MMKV 源码 初始化获取修改删除读取…

利用Quartz实现复杂的任务调度

第一章&#xff1a;引言 大家好&#xff0c;我是小黑&#xff0c;任务调度&#xff0c;简而言之&#xff0c;就是按照预定计划自动执行任务的过程。不管是数据库备份、报表生成还是发送定时邮件&#xff0c;它们都需要一个可靠的任务调度系统来保证按时完成。 那么&#xff0…

【学习笔记】深度学习实战 | LeNet

简要声明 学习相关网址 [双语字幕]吴恩达深度学习deeplearning.aiPapers With CodeDatasets 深度学习网络基于PyTorch学习架构&#xff0c;代码测试可跑。本学习笔记单纯是为了能对学到的内容有更深入的理解&#xff0c;如果有错误的地方&#xff0c;恳请包容和指正。 参考文献…

基于ssm网络办公系统论文

摘 要 计算机网络发展到现在已经好几十年了&#xff0c;在理论上面已经有了很丰富的基础&#xff0c;并且在现实生活中也到处都在使用&#xff0c;可以说&#xff0c;经过几十年的发展&#xff0c;互联网技术已经把地域信息的隔阂给消除了&#xff0c;让整个世界都可以即时通话…

【Linux】Linux安装

Linux安装&#xff08;保姆级教程&#xff09; 准备工具下载链接 Linux镜像系统官网&#xff08;Centos版本&#xff09;&#xff1a;https://www.centos.org/ 虚拟机下载官网&#xff1a;https://www.vmware.com 注&#xff1a;Linux是一种系统统称&#xff0c;就像Windows…

小米科技分享:深入解析阿里巴巴面试题之SQL查询

大家好,我是小米,今天要和大家分享的是在阿里巴巴面试中常见的SQL查询题目。SQL查询是数据库领域中的基础,但也是一个非常重要的技能,无论是在面试中还是实际工作中,都有着举足轻重的地位。让我们一起深入了解一下吧! SQL语句的执行过程 首先,我们来了解一下SQL语句的执…