vue+elementUI实现树形穿梭框

news2024/11/15 11:06:12

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) 
          ) {
            treeDataArr[index].selected = type;
          }
          if (item.children && item.children.length) {
            setTree(item.children);
            // 判断半选框是否需要移动
            if (this.halfCheckedKeys.includes(item.code)) {
              if (type) {
                treeDataArr[index].selected = type;
              } else {
                const target = treeDataArr[index].children.find((it) => {
                  return it.selected;
                });
                if (!target) {
                  treeDataArr[index].selected = type;
                }
              }
            }
          }
        });
      };
      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>

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

TOP、CCF、IEEE-Trans系列SCI,均2个月左右录用!进展超顺!

能源工程类SCIE&#xff08;中科院TOP&#xff0c;周期短&#xff09; 【期刊简介】IF&#xff1a;11.0-12.0&#xff0c;JCR1区&#xff0c;中科院1区TOP 【出版社】ELSEVIER出版社 【版面情况】正刊&#xff0c;2024.03.31截稿 【预警情况】2020-2024年无预警记录 【检索…

机器学习(27)

文章目录 文献阅读1. 题目2. abstract3. 网络架构3.1 Theoretical Results 4. 文献解读4.1 Introduction4.2 创新点4.3 实验过程4.3.1 数据集4.3.2 参数设置 4.4 结论 三、实现GAN1. 任务要求2. 实验结果3.实验代码3.1数据准备3.2 模型构建3.3 展示函数3.4 训练过程 小结本周内…

在线一问一答网页版源码系统 源码全开源可二次开发 带完整的安装代码包以及系统搭建教程

在信息化社会&#xff0c;知识共享和互动交流成为了人们日常生活和工作中不可或缺的一部分。在线问答系统作为连接知识提供者和需求者的桥梁&#xff0c;具有广阔的市场前景和巨大的社会价值。然而&#xff0c;传统的问答系统往往存在功能单一、定制性差、开发成本高等问题&…

数据库读写分离分案

环境&#xff1a;MySQL主从数据库。如需要搭建可参考上一篇文章&#xff1a;MySQL主从数据库简单搭建 数据库使用主从可确保数据一致性&#xff0c;示例是基于一个完整的项目之上做了一些修改&#xff0c;为测试效果直接连接了两个非主从配置的数据库&#xff0c;其中只有测试…

Linux cp、mv命令显示进度条

1.advcpmv 平常使用cp 拷贝大文件时&#xff0c;看不到多久可以完成&#xff0c;虽然加上-v参数也只能看到正在拷贝文件&#xff0c;那就使用以下方法实现 git clone https://github.com/jarun/advcpmv.git cd advcpmv/ bash install.shmv ./advcp /usr/local/bin/ mv ./advmv …

Vite+Vue3+TS+Vue-Router+Axios+Pinia开发模板

一、模板介绍 VUE3开发全家桶模板&#xff0c;安装了ts,router,axios,pinia并提供了简单示例并提供了它们的官网链接。 对axios进行了简单封装。 二、下载地址 https://github.com/yigedayouzi/ViteTemplateOne 三、快速开始 1、git clone gitgithub.com:yigedayouzi/Vite…

备忘录导出的HTML文档转换MarkDown尝试记录

备忘录导出的HTML文档转换MarkDown尝试记录 1. pandoc命令行2. HTML转换MARKDOWN3. MD导入CSDN记录过长报错及压缩尝试参考 本地备忘录写了些旅游攻略&#xff0c;想做个纪念&#xff0c;导出为长图片ok&#xff0c;导出为HTML&#xff0c;也可以。但是导出图片是base64格式的&…

VMware Live Site Recovery 9.0 - 数据中心灾难恢复 (DR)

VMware Live Site Recovery 9.0 - 数据中心灾难恢复 (DR) VMware Site Recovery Manager, VMware SRM 现已更名为 VMware Live Site Recovery 请访问原文链接&#xff1a;https://sysin.org/blog/vmware-live-site-recovery-9/&#xff0c;查看最新版。原创作品&#xff0c;转…

控价其实是对品牌市场的保护

品牌发展过程中&#xff0c;如果有越来越多的经销商加入&#xff0c;必然要做好控价&#xff0c;否则渠道的混乱&#xff0c;会使得品牌价值受损&#xff0c;比如低价的出现&#xff0c;会影响正规经销商的出货&#xff0c;使其竞争力增加&#xff0c;同时价格的不稳定会连带产…

【框架】说一说 Fork/Join?

SueWakeup 个人主页&#xff1a;SueWakeup 系列专栏&#xff1a;学习Java框架 个性签名&#xff1a;人生乏味啊&#xff0c;我欲令之光怪陆离 本文封面由 凯楠&#x1f4f7; 友情赞助 目录 前言 什么是 Fork&#xff1f; 什么是 Join&#xff1f; Fork/Join 的核心组件 F…

用友 GRP-U8 sqcxIndex SQL 注入漏洞

产品简介 用友GRP-U8R10行政事业内控管理软件是用友公司专注于国家电子政务事业&#xff0c;基于云计算技术所推出的新一代产品&#xff0c;是我国行政事业财务领域最专业的政府财务管理软件。 漏洞描述 用友GRP-U8 sqcxIndex.jsp接口存在SQL注入漏洞&#xff0c;未经身份认…

AWTK T9 输入法实现原理

1. T9 输入法的中文字典数据 网上可以找到 T9 输入法的中文字典数据&#xff0c;但是通常有两个问题&#xff1a; 采用 GPL 协议&#xff0c;不太适合加入 AWTK。 只支持单个汉字的输入&#xff0c;不支持词组的输入。 经过考虑之后&#xff0c;决定自己生成 T9 输入法的中…

2024,淘天六大升级,电商人都准备好了吗?|淘天商品数据采集商品订单接口

淘天各类商品API接口 许多商家一直在问&#xff0c;到了2024年&#xff0c;淘天到底有哪些新改变&#xff1f;商家们又该如何做优化、调整&#xff1f; 我们总结来看&#xff0c;主要在六个方面进行了升级。 第一个是流量获取的逻辑变了。 从曾经的单一的流量入口&#xff0c;…

Leetcode——560. 和为 K 的子数组

560. 和为 K 的子数组 - 力扣&#xff08;LeetCode&#xff09;https://leetcode.cn/problems/subarray-sum-equals-k/description/ 题目描述&#xff1a; 给你一个整数数组 nums 和一个整数 k &#xff0c;请你统计并返回该数组中和为 k 的子数组的个数 。子数组是数组中元素…

Ubuntu学习笔记之Shell与APT下载工具

基本都是摘抄正点原子的文章&#xff1a;<领航者 ZYNQ 之嵌入式Linux 开发指南 V3.2.pdf&#xff0c;因初次学习&#xff0c;仅作学习摘录之用&#xff0c;有不懂之处后续会继续更新~ 一、Ubuntu Shell操作 简单的说Shell 就是敲命令。国内把 Linux 下通过命令行输入命令叫…

福昕阅读器 PDF 文档基本操作

福昕阅读器 PDF 文档基本操作 References 转至 PDF 顶部 快捷键&#xff1a;Home. 转至 PDF 顶部 快捷键&#xff1a;End. 打开超链接 文本选择工具 -> 手形工具 (Hand Tool) -> 点击超链接 福昕阅读器 同时在多个窗口中打开多个文件 文件 -> 偏好设置 -> 文…

Autosar-WdgM配置详解(免费)-2

1.4生成Wdg模块以及代码 按以下步骤生成WdgM模块代码&#xff1a; 工具如果成功生成的话&#xff0c;会在COmponnets下面创建一个WdgM的SWC&#xff0c;如下图: 1.5创建SWC模块 WdgM已经提供了deadline的port&#xff0c;接下来就是需要SWC使用WdgM的port。 1.5.1创建Master…

目标检测中的mAP计算原理和源码实现

简介 在目标检测任务中&#xff0c;mAP&#xff08;mean Average Precision&#xff0c;平均精度均值&#xff09;是一个非常重要的评价指标&#xff0c;用于衡量模型在多个类别上的平均性能。它综合考虑了模型在不同召回率下的精确率&#xff0c;能够全面反映模型在检测任务中…

Python从0到100(七):Python列表介绍及运用

一、 列表概述 问题描述&#xff1a; 假设一个班有100个学生&#xff0c;如果每个变量存放一个学生的姓名&#xff0c;是不是很麻烦&#xff1f;如果有一千个学生甚至更多&#xff0c;那该怎么办呢&#xff1f; 列表是Python中的一种数据结构&#xff0c;它可以存储不同类型的…

冰蓄冷空调的工作原理介绍

冰蓄冷空调(Ice Storage Air Conditioning System)是一种利用夜间电力低谷时段储存冷量,白天用电高峰时段释放冷量的空调技术。 这种技术通过在电网负荷低谷时(如深夜)运行制冷设备,将电能转化为冷量储存在冰块或者冷冻水中,然后在白天电网负荷高峰时,将储存的冷量释放…