vue+elementUI实现树形穿梭框

1.实现效果
在这里插入图片描述
在这里插入图片描述
2.整体思路
将左侧选中的节点移动到右侧,还要保持树结构,意味着移动子节点,需要把该子节点对应的父节点甚至父节点的父节点一并移到右侧形成一个新的树结构,树结构的层级和原来的树保持一致,只是右侧展示的子节点为选中的子节点,
思路一:一开始准备左侧删除节点,右侧构建新的节点,生成一棵新的树,但是在节点组装时需要依次找到外层节点进行树结构组装,非常麻烦以及性能也不是很好;
思路二:利用elementUI的filter API对选中节点进行筛选,左侧筛选出未选中的,右侧筛选出选中的,用的还是同一棵树,用一个属性来区分是否选择,好处是子节点选中,父节点会跟随保存,不用重新构建树结构

左侧
<el-tree
            ref="treeLeft"
            :data="treeDataArr"
            :props="props"
            node-key="id"
            show-checkbox
            :filter-node-method="filterNodeLeft"
            @check-change="handleCheckChange('left')"
          />
 右侧
 <el-tree
            ref="treeRight"
            :data="treeDataArr"
            :props="props"
            node-key="id"
            show-checkbox
            :filter-node-method="filterNodeRight"
            @check-change="handleCheckChange('right')"
          />
筛选函数
      this.$refs.treeLeft.filter();
      this.$refs.treeRight.filter();
      filterNodeLeft(value, data) {
            return !data.selected;
      },
      filterNodeRight(value, data) {
           return data.selected;
      },

filter API用法
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.完整代码

<template>
  <el-dialog
    title="编辑"
    :visible="isVisible"
    width="50%"
    custom-class="pw-edit"
    :close-on-press-escape="false"
    :close-on-click-modal="false"
    :before-close="closeEditDialog"
  >
    <div class="per_container">
      <div class="per_con_left">
        <div class="per_con_title">未选</div>
        <div class="check_all">
          <el-checkbox
            :indeterminate="config.left.isIndeterminate"
            v-model="config.left.checkAll"
            @change="handleCheckAll($event, 'left')"
            >全选/全不选</el-checkbox
          >
        </div>
        <div class="tree">
          <el-tree
            ref="treeLeft"
            :data="treeDataArr"
            :props="props"
            node-key="id"
            show-checkbox
            :filter-node-method="filterNodeLeft"
            @check-change="handleCheckChange('left')"
          />
        </div>
      </div>
      <div class="operation">
        <el-button type="primary" @click="toRight()"
          >移入
          <i class="el-icon-d-arrow-right"></i>
        </el-button>
        <el-button type="primary" icon="el-icon-d-arrow-left" @click="toLeft()"
          >移除</el-button
        >
      </div>
      <div class="per_con_right">
        <div class="per_con_title">已选</div>
        <div class="check_all">
          <el-checkbox
            :indeterminate="config.right.isIndeterminate"
            v-model="config.right.checkAll"
            @change="handleCheckAll($event, 'right')"
            >全选/全不选</el-checkbox
          >
        </div>
        <div class="tree">
          <el-tree
            ref="treeRight"
            :data="treeDataArr"
            :props="props"
            node-key="id"
            show-checkbox
            :filter-node-method="filterNodeRight"
            @check-change="handleCheckChange('right')"
          />
        </div>
      </div>
    </div>
    <span slot="footer" class="dialog-footer">
      <el-button @click="closeEditDialog" size="small">取 消</el-button>
      <el-button type="primary" size="small" @click="submitEdit"
        >确 定</el-button
      >
    </span>
  </el-dialog>
</template>
<script>
export default {
  props: {
    isVisible: {
      type: Boolean,
      default: false,
    },
    treeData: [],
  },
  watch: {
    isVisible(val) {
      if (val) {
        this.$nextTick(() => {
          this.setTreeFilter();
          this.$refs.treeLeft.setCheckedKeys([]);
          this.$refs.treeRight.setCheckedKeys([]);
        });
      }
    },
    treeData: {
      handler(val) {
        this.treeDataArr = val;
        this.allParentKeys = this.treeDataArr.map((item) => {
          return item.id;
        });
        if (this.$refs.treeLeft && this.$refs.treeRight) {
          this.$nextTick(() => {
            this.setTreeFilter();
          });
        }
      },
      deep: true,
    },
  },
  data() {
    return {
      props: {
        label: "name",
      },
      isIndeterminateL: false,
      isIndeterminateR: false,
      checkAllLeft: false,
      checkAllRight: false,
      treeDataArr: [],
      checkedKeys: [],
      halfCheckedKeys: [],
      checkedNodes: [],
      config: {
        left: {
          isIndeterminate: false,
          checkAll: false,
          ref: "treeLeft",
        },
        right: {
          isIndeterminate: false,
          checkAll: false,
          ref: "treeRight",
        },
      },
    };
  },

  methods: {
    closeEditDialog() {
      this.$emit("closePerEdit");
    },
    setTreeFilter() {
      this.$refs.treeLeft.filter();
      this.$refs.treeRight.filter();
    },
    toLeft() {
      this.checkedKeys = this.$refs.treeRight.getCheckedKeys();
      this.halfCheckedKeys = this.$refs.treeRight.getHalfCheckedKeys();
      this.settreeDataArr(this.treeDataArr, false);
      this.setTreeFilter();
      this.$refs.treeLeft.setCheckedKeys(this.checkedKeys);
      this.$refs.treeRight.setCheckedKeys([]);
    },
    toRight() {
      this.checkedKeys = this.$refs.treeLeft.getCheckedKeys();
      this.halfCheckedKeys = this.$refs.treeLeft.getHalfCheckedKeys();
      this.settreeDataArr(this.treeDataArr, true);
      this.setTreeFilter();
      this.$refs.treeRight.setCheckedKeys(this.checkedKeys);
      this.$refs.treeLeft.setCheckedKeys([]);
    },

    filterNodeLeft(value, data) {
      return !data.selected;
    },
    filterNodeRight(value, data) {
      return data.selected;
    },

    // 递归设置数据选中状态
    settreeDataArr(tree, type) {
      const setTree = (treeDataArr) => {
        treeDataArr.forEach((item, index) => {
          if (
            this.checkedKeys.includes(item.id) ||
            this.halfCheckedKeys.includes(item.id)
          ) {
            treeDataArr[index].selected = type;
          }
          if (item.children && item.children.length) {
            setTree(item.children);
          }
        });
      };
      setTree(tree);
    },

    submitEdit() {
      this.$emit("permissionData", this.treeDataArr);
    },
    // 勾选树结构时判断是否勾选上面的全选
    handleCheckChange(type) {
      this.checkedNodes = this.$refs[this.config[type].ref].getCheckedNodes();
      const pIds = this.checkedNodes.filter((item) => {
        return !item.pId;
      });
      if (!pIds.length) {
        this.config[type].checkAll = false;
        this.config[type].isIndeterminate = false;
        return;
      }
      if (pIds.length === this.allParentKeys.length) {
        this.config[type].checkAll = true;
        this.config[type].isIndeterminate = false;
      } else {
        this.config[type].isIndeterminate = true;
        this.config[type].checkAll = false;
      }
    },
    // 全选
    handleCheckAll(value, type) {
      const keys = value
        ? this.treeDataArr.map((item) => {
            return item.id;
          })
        : [];
      this.$refs[this.config[type].ref].setCheckedKeys(keys, false);
    },
  },
};
</script>
<style lang="less" scoped>
/deep/.per_container {
  display: flex;
  height: 500px;
  justify-content: space-between;
  align-items: center;
  .per_con_left,
  .per_con_right {
    width: 45%;
    height: 100%;
  }
  .operation {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 20px;
    .el-button {
      margin-left: 0;
      margin-bottom: 10px;
    }
  }
  .per_con_title {
    height: 42px;
    line-height: 26px;
    border-radius: 8px 8px 0 0;
    padding: 8px;
    align-self: stretch;
    background: #f2f6f9;
    font-size: 16px;
    box-sizing: border-box;
    border: 1px solid #d8d8d8;
    font-weight: 700;
    text-align: left;
  }
  .check_all {
    height: 42px;
    line-height: 42px;
    padding: 0 5px;
    border: 1px solid #d8d8d8;
    border-top: none;
    text-align: left;
  }
  .tree {
    height: calc(100% - 82px);
    border: 1px solid #d8d8d8;
    border-top: none;
    overflow: auto;
  }
}
</style>

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

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

相关文章

全国媒体公关服务资源分析,媒体邀约资源包括哪些?-51媒体网

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 全国媒体公关服务资源分析是一个涵盖多方面的复杂议题&#xff0c;主要涉及到不同媒体类型、传播渠道、以及公关策略等多个维度。在当前媒体环境下&#xff0c;媒体公关服务资源主要包括…

机器人路径规划:基于斑翠鸟优化算法(Pied Kingfisher Optimizer ,PKO)的机器人路径规划(提供MATLAB代码)

一、机器人路径规划介绍 移动机器人&#xff08;Mobile robot&#xff0c;MR&#xff09;的路径规划是 移动机器人研究的重要分支之&#xff0c;是对其进行控制的基础。根据环境信息的已知程度不同&#xff0c;路径规划分为基于环境信息已知的全局路径规划和基于环境信息未知或…

【论文阅读】通过组件对齐评估和改进 text-to-SQL 的组合泛化

Measuring and Improving Compositional Generalization in Text-to-SQL via Component Alignment NAACL 2022| CCF B Abstract 在 text-to-SQL 任务中&#xff0c;正如在许多 NLP 中一样&#xff0c;组合泛化是一个重大挑战&#xff1a;神经网络在训练和测试分布不同的情况…

Python 深度学习第二版(GPT 重译)(二)

四、入门神经网络&#xff1a;分类和回归 本章涵盖 您的第一个真实世界机器学习工作流示例 处理矢量数据上的分类问题 处理矢量数据上的连续回归问题 本章旨在帮助您开始使用神经网络解决实际问题。您将巩固从第二章和第三章中获得的知识&#xff0c;并将所学应用于三个新…

数据之王国:解析Facebook的大数据应用

引言 作为全球最大的社交媒体平台之一&#xff0c;Facebook拥有庞大的用户群体和海量的数据资源。这些数据不仅包括用户的个人信息和社交行为&#xff0c;还涵盖了广告点击、浏览记录等多方面内容。Facebook通过巧妙地利用这些数据&#xff0c;构建了强大的大数据应用系统&…

T470 双电池机制

ThinkPad系列电脑牛黑科技双电池管理体系技术,你知道吗&#xff1f; - 北京正方康特联想电脑代理商 上文的地址 在放电情况下&#xff1a;优先让外置电池放电&#xff0c;当放到一定电量后开始让内置电池放电。 在充电情况下&#xff1a;优先给内置电池充电&#xff0c;当充…

uboot - pinctrl - FPGA回片前测试阶段 - 设置GPIO引脚复用失败

问题描述 pinctrl设置引脚复用失败&#xff0c;没有调用到controller中的set_groups_function函数。 问题定位 pinctrl如何注册dm节点如何进行设备树中各个设备节点下的复用配置为什么没调用到控制器实现的set_groups_function函数 &gpio0 {status "okay";p…

web自动化3-pytest前后夹具

一、pytest前后置&#xff08;夹具&#xff09;-fixture 夹具的作用&#xff1a;在用例执行之前和之后&#xff0c;需要做的准备工作之前和收尾工作。 用于固定测试环境&#xff0c;以及清理回收资源。 举个例子&#xff1a;访问一个被测页面-登录页面&#xff0c;执行测试用…

阿里云镜像仓库服务--推送docker image到远程仓库

一、背景 阿里云对于镜像仓库服务的使用文档已比较完善&#xff0c;结合它给的示例。 本文是站在小白用户的视角&#xff0c;梳理整个的使用过程以及遇到的问题。 二、使用步骤 阿里云镜像仓库服务和harbor、nexus等私有仓库等并没有什么大差不差之处&#xff0c;仍旧是四步走…

Java设计模式 | 工厂方法模式

工厂方法模式 针对简单工厂模式案例中的缺点&#xff0c;使用工厂方法模式就可以完美的解决&#xff0c;完全遵循开闭原则。简单工厂模式只有一个工厂类&#xff0c;负责创建所有产品&#xff0c;如果要添加新的产品&#xff0c;就需要修改工厂类的代码。而工厂方法模式引入了…

鸿蒙Harmony应用开发—ArkTS-转场动画(组件内隐式共享元素转场)

geometryTransition用于组件内隐式共享元素转场&#xff0c;在组件显示切换过程中提供平滑过渡效果。通用transition机制提供了opacity、scale等转场动效&#xff0c;geometryTransition通过id绑定in/out组件(in指入场组件、out指出场组件)&#xff0c;使得组件原本独立的trans…

IOS/Android App备案(uniapp)

IOS/App备案 IOS备案Android备案 IOS备案 准备好p12证书即可 链接: https://aitoolnav.caichuangkeji.com/#/AppMd5 Android备案 上DCLOUD开发者中心&#xff0c;找到相关应用后&#xff0c;直接查看证书即可获取到MD5 公钥&#xff1a;先根据上述页面下载证书&#xff0c;…

Windows10无盘母盘制作-以云更新为例

Windows10无盘母盘制作-以云更新为例 缘起环境准备创建虚拟机安装系统导出系统 缘起 网吧客户端在实际环境中&#xff0c;经常要面对形形色色对无盘系统&#xff0c;五花八门对无盘镜像&#xff0c; 为了方便确认不同无盘环境对客户的对影响&#xff0c;决定自己制作一个无盘母…

Linux/Monitored

Enumeration nmap 用 nmap 扫描了常见的端口&#xff0c;发现对外开放了 22,80,389,443,5667 端口&#xff0c;端口详细信息如下 ┌──(kali㉿kali)-[~/vegetable/HTB/Monitored] └─$ nmap -sC -sV -p 22,80,389,443,5667 10.10.11.248 Starting Nmap 7.93 ( https://nm…

Git和本地仓库托管到gitee

Git作用&#xff1a;记录代码内容&#xff0c;切换代码版本&#xff0c;实现多人开发 Git安装&#xff1a; 打开bash端 命令&#xff1a;git-v(查看版本&#xff09; 配置用户信息 git config --global user.name “用户名” git config --global user.email "邮箱名…

云蜜罐技术(德迅猎鹰)诞生

数字化程度高且高价值信息密集的行业&#xff0c;如金融、能源、互联网、政府、教育、医疗、军工等行业&#xff0c;面对日益规模化、专业化的网络攻击&#xff0c;渐渐不再满足于一味的防守加固。除了巩固防线之外&#xff0c;他们愈发看重主动出击、感知更大范围内的攻击&…

Windows下IntelliJ IDEA远程连接服务器中Hadoop运行WordCount(详细版)

使用IDEA直接运行Hadoop项目&#xff0c;有两种方式&#xff0c;分别是本地式&#xff1a;本地安装HadoopIDEA&#xff1b;远程式&#xff1a;远程部署Hadoop&#xff0c;本地安装IDEA并连接&#xff0c; 本文介绍第二种。 一、安装配置Hadoop (1)虚拟机伪分布式 见上才艺&a…

Java二阶知识点总结(七)SVN和Git

SVN 1、SVN和Git的区别 SVN是集中式的&#xff0c;也就是会有一个服务器保存所有代码&#xff0c;拉取代码的时候只能从这个服务器上拉取&#xff1b;Git是分布式的&#xff0c;也就是说每个人都保存有所有代码&#xff0c;如果要获取代码&#xff0c;可以从其他人手上获取SV…

实用工具推荐:适用于 TypeScript 网络爬取的常用爬虫框架与库

随着互联网的迅猛发展&#xff0c;网络爬虫在信息收集、数据分析等领域扮演着重要角色。而在当前的技术环境下&#xff0c;使用TypeScript编写网络爬虫程序成为越来越流行的选择。TypeScript作为JavaScript的超集&#xff0c;通过类型检查和面向对象的特性&#xff0c;提高了代…

LeetCode 面试经典150题 罗马数字转整数

题目&#xff1a; 罗马数字包含以下七种字符: I&#xff0c; V&#xff0c; X&#xff0c; L&#xff0c;C&#xff0c;D 和 M。 字符 数值 I 1 V 5 X 10 L 50 C 100 D 500 M …