vue3中使用vuedraggable实现拖拽el-tree数据进分组

看效果:

        可以实现单个拖拽、双击添加、按住ctrl键实现多个添加,或者按住shift键实现范围添加,添加到框中的数据,还能拖拽排序

先安装 vuedraggable 

这是他的官网 vue.draggable中文文档 - itxst.com

npm i vuedraggable -S

 直接粘贴代码即可: 

index.vue 

<template>
  <div class="w-full h-full">
    <!-- 页面拖拽组件 -->
    <div class="leftPart">
      <ElTree
        class="w100%"
        :data="$.treeData"
        ref="treeTableListRef"
        :props="$.defaultProps"
        highlight-current
        :expand-on-click-node="false"
        key="id"
        :default-expand-all="true"
        @node-click="(data, node) => $.tableFieldsNodeClick(data, node, treeTableListRef)"
      >
        <template #default="{ data }">
          <Draggable
            :list="[data]"
            ghost-class="ghost"
            chosen-class="chosenClass"
            animation="300"
            @start="onStart"
            @end="onEnd"
            group="group1"
            v-tooltip="`Tips:按住Ctrl或Shift进行批量选择`"
          >
            <template #item="{ element }">
              <div @dblclick="dbAddData(element)" style="user-select: none" class="item">
                {{ element.name }}
              </div>
            </template>
          </Draggable>
        </template>
      </ElTree>
    </div>
    <!-- 右侧内容 -->
    <div class="rightPart">
      <div class="flex">
        <div
          @mouseover="divMouseOver"
          @mouseleave="divMouselease"
          class="w-full rightContent"
          style="border: 1px solid #ccc"
        >
          <Draggable
            :list="state.list"
            ghost-class="ghost"
            group="group1"
            chosen-class="chosenClass"
            animation="300"
            @start="onStart"
            @end="onEnd"
            @add="addData"
            class="w-full dragArea"
          >
            <template #item="{ element }">
              <div class="item">
                {{ element.name }}
              </div>
            </template>
          </Draggable>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import Draggable from "vuedraggable";
import { useData } from "./hooks/drag";
const treeTableListRef = ref();
let { $data: $ } = useData();
const state = reactive<any>({
  //需要拖拽的数据,拖拽后数据的顺序也会变化
  list: [],
});
//拖拽开始的事件
const onStart = () => {
  console.log("开始拖拽");
};
const divMouseOver = (e: any) => {};
const divMouselease = (e: any) => {};
//拖拽结束的事件
const onEnd = () => {
  console.log("结束拖拽");
};
// 双击添加
const dbAddData = (data: any) => {
  let i = state.list.findIndex((a: any) => a.id == data.id);
  if (data.children) return; //父级节点不添加
  if (i == -1) state.list.push(data);
};
// 批量添加
const addData = () => {
  // 拿到所有nodes节点数组
  const nodes = treeTableListRef.value.store._getAllNodes();
  nodes.map((a: any) => {
    if ($.selectNodes.includes(a.id)) {
      state.list.push(a.data);
    }
    // 排除父级,只添加子级
    state.list = state.list.filter((a: any) => !a.children);
    // 去重
    state.list = [...new Set(state.list)];
  });
};
onMounted(() => {});
onBeforeMount(() => {
  window.addEventListener("keydown", handleKeyDown);
  window.addEventListener("keyup", handleKeyUp);
});
// 按下为true
const handleKeyDown = (event: any) => {
  // 代表按下的是ctrl键
  if (event.key == "Control") {
    $.ctrlKeyPressed = true;
  }
  // 代表按下的是shift键
  if (event.key == "Shift") {
    $.shiftKeyPressed = true;
  }
};
// 释放为false
const handleKeyUp = (event: any) => {
  // 代表按下的是ctrl键
  if (event.key == "Control") {
    $.ctrlKeyPressed = false;
  }
  // 代表按下的是shift键
  if (event.key == "Shift") {
    $.shiftKeyPressed = false;
  }
};
</script>

<style scoped lang="scss">
.leftPart {
  width: 20%;
  height: 100%;
  float: left;
  border-right: 1px dashed #ccc;
}
.rightPart {
  padding: 20px;
  width: 60%;
  height: 100%;
  float: left;
}
.list_drap {
  min-width: 120px;
  max-height: 86px;
  min-height: 22px;
  overflow-y: auto;
  height: auto;
}
.rightContent {
  border-radius: 4px;
  min-height: 30px;
  display: flex;
}
.dragArea {
  padding: 10px 5px;
  flex-grow: 1;
  .item {
    float: left;
    min-width: 50px;
    display: inline;
    margin: 0 3px 2px 3px;
    background-color: rgb(235, 241, 255);
    color: #3370ff;
    font-size: 12px;
    cursor: all-scroll;
    user-select: none;
    height: 20px;
    line-height: 20px;
    padding-left: 9px;
    padding-right: 9px;
    background: #ececfd;
    color: #333333;
    font-size: 12px;
  }
}
</style>

drag.ts

export function useData() {
  const $data: any = reactive({
    ctrlKeyPressed: false,
    shiftKeyPressed: false,
    shiftKeyFelid: [],
    defaultProps: {
      children: "children",
      label: "name",
    },
    treeData: [
      {
        name: "一级1",
        id: 1,
        children: [
          {
            name: "二级1",
            id: 2,
            children: [
              {
                name: "三级1",
                id: 2,
              },
              {
                name: "三级2",
                id: 4,
              },
              {
                name: "三级3",
                id: 5,
              },
              {
                name: "三级4",
                id: 6,
              },
              {
                name: "三级5",
                id: 7,
              },
            ],
          },
          {
            name: "二级2",
            id: 8,
          },
          {
            name: "二级3",
            id: 9,
          },
          {
            name: "二级4",
            id: 10,
          },
          {
            name: "二级5",
            id: 11,
          },
        ],
      },
      {
        name: "一级2",
        id: 12,
        children: [
          {
            name: "二级1",
            id: 13,
          },
          {
            name: "二级2",
            id: 14,
          },
          {
            name: "二级3",
            id: 15,
          },
          {
            name: "二级4",
            id: 16,
          },
          {
            name: "二级5",
            id: 17,
          },
        ],
      },
    ],
    selectNodes: [],
    treeTableListRef: null,
  });
  // 节点选中事件
  $data.tableFieldsNodeClick = (nodeData: any, node: any, treeTableListRef: any) => {
    const nodes = treeTableListRef.store._getAllNodes(); //所有node节点
    const ishas = $data.selectNodes.includes(node.id);
    // 递归遍历节点数组进行ID存放
    function addSelectId(arr: any) {
      for (const item of arr) {
        $data.selectNodes.push(item.id);
        if (Array.isArray(item.childNodes) && item.childNodes.length) {
          addSelectId(item.childNodes);
        }
      }
    }
    // 递归遍历删除节点id
    function delSelectId(arr: any) {
      for (const item of arr) {
        const index = $data.selectNodes.findIndex((x: any) => x == item.id);
        $data.selectNodes.splice(index, 1);

        if (Array.isArray(item.children) && item.children.length) {
          delSelectId(item.children);
        }
      }
    }
    // 按住了ctrl键,可以进行单个多选
    if ($data.ctrlKeyPressed) {
      // 如果为true代表当前选中的节点已存在
      if (ishas) {
        // 查找当前选中的节点的索引
        const index = $data.selectNodes.findIndex((x: any) => x == node.id);
        // 删除父节点
        $data.selectNodes.splice(index, 1);
        // 删除子节点
        if (Array.isArray(node.childNodes) && node.childNodes.length) {
          delSelectId(node.childNodes);
        }
      } else {
        // 否则当前选中的节点不存在,就加入到已选节点数组序列
        $data.selectNodes.push(node.id);
        // 防止选中的是父节点,就需要递归将子节点加入
        if (Array.isArray(node.childNodes) && node.childNodes.length) {
          addSelectId(node.childNodes);
        }
      }
      node.isCurrent = !node.isCurrent;
      // 按下了shift键,可以进行范围多选
    } else if ($data.shiftKeyPressed) {
      // 先清空
      $data.selectNodes = [];
      // 将当前节点放入
      $data.selectNodes.push(node.id);
      $data.shiftKeyFelid.push(node.id);
      if ($data.shiftKeyFelid.length > 1) {
        // 首索引
        const sIndex = nodes.findIndex((x: any) => x.id == $data.shiftKeyFelid[0]);
        // 尾索引
        const eIndex = nodes.findIndex((x: any) => x.id == $data.shiftKeyFelid[$data.shiftKeyFelid.length - 1]);
        // 根据首尾索引,存入中间节点
        const s = sIndex < eIndex ? sIndex : eIndex; //取小值当开头索引
        const e = sIndex < eIndex ? eIndex : sIndex; //取大值当结尾索引
        for (let i = s; i < e; i++) {
          $data.selectNodes.push(nodes[i].id);
        }
      }
    } else {
      // 否则就是单机选择
      $data.shiftKeyFelid = [];
      $data.selectNodes = [];
      $data.selectNodes = [node.id];
    }
    // 下面是对已选中的节点,进行高亮展示
    // 通过控制elementui中节点上的isCurrent属性
    // isCurrent为true是高亮,否则取消高亮
    for (const item of nodes) {
      if ($data.selectNodes.includes(item.id)) {
        item.isCurrent = true;
      } else {
        item.isCurrent = false;
      }
    }
  };
  return {
    $data: $data,
  };
}

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

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

相关文章

Python-pdfplumber读取PDF内容

文章目录 前言一、pdfplumber模块1.1 pdfplumber的特点1.2 pdfplumber.PDF类1.3pdfplumber.Page类 二 pdfplumber的使用2.1 加载PDF2.2 pdfplumber.PDF 类2.3 pdfplumber.Page 类2.4 读取PDF2.5 读取PDF文档信息2.6 查看总页数2.7 查看总页数读取第一页的宽度&#xff0c;页高等…

chatGPT 使用随想

一年前 chatGPT 刚出的时候&#xff0c;我就火速注册试用了。 因为自己就是 AI 行业的&#xff0c;所以想看看国际上最牛的 AI 到底发展到什么程度了. 自从一年前 chatGPT 火出圈之后&#xff0c;国际上的 AI 就一直被 OpenAI 这家公司引领潮流&#xff0c;一直到现在&#x…

【安卓基础4】Activity(二)

&#x1f3c6;作者简介&#xff1a;|康有为| &#xff0c;大四在读&#xff0c;目前在小米安卓实习&#xff0c;毕业入职 &#x1f3c6;安卓学习资料推荐&#xff1a; 视频&#xff1a;b站搜动脑学院 视频链接 &#xff08;他们的视频后面一部分没再更新&#xff0c;看看前面也…

VSCode The preLaunchTask ‘C/C++: clang++ 生成活动文件‘ terminated with exit code -1

更改tasks.json文件里面的type为shell 选择g 选择g&#xff0c;然后点回到text.c&#xff0c;按下F5. 得到结果。 文中内容参考: 从零开始手把手教你配置属于你的VS Code_哔哩哔哩_bilibili https://blog.csdn.net/qq_63872647/article/details/128006861

基于springboot+vue的桂林旅游景点导游平台(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战&#xff0c;欢迎高校老师\讲师\同行交流合作 ​主要内容&#xff1a;毕业设计(Javaweb项目|小程序|Pyt…

从扩散模型基础到DIT

Diffusion model 扩散模型如何工作&#xff1f; 输入随机噪声和文本内容&#xff0c;通过多次预测并去除图片中的噪声后&#xff0c;最终生成清晰的图像。 以上左边这张图&#xff0c;刚开始是随机噪声&#xff0c;999为时间序列。 为什么不直接预测下一张图片呢&#xff1f;…

java 面向对象-上

1.构造器&#xff08;或构造方法&#xff09;&#xff1a; Constructor 构造器的作用&#xff1a; 1.创建对象 2.初始化对象的信息 2.使用说明&#xff1a; * 1.如果没显式的定义类的构造器的话&#xff0c;则系统默认提供一个空参的构造器 * 2.定义构造器的格式&#xff1a…

python OpenCV:seamlessClone泊松融合

一、seamlessClone函数的用法 翻译 https://www.learnopencv.com/seamless-cloning-using-opencv-python-cpp/ def seamlessClone(src, dst, mask, p, flags, blendNone): # real signature unknown; restored from __doc__"""seamlessClone(src, dst, mask, …

RM电控讲义【定时器篇】

HAL库最显著的特点是基于结构体从而利用句柄进而简化代码。 定时器初始化&#xff1a; TIM6_DAC_IRQn是STM32G431单片机中定时器TIM6的中断请求名。TIM6是STM32G431的一个基本定时器&#xff0c;具有基本的定时功能。当累加的时钟脉冲数超过预定值时&#xff0c;TIM6能触发中断…

C语言-指针初学速成

1.指针是什么 C语言指针是一种特殊的变量&#xff0c;用于存储内存地址。它可以指向其他变量或者其他数据结构&#xff0c;通过指针可以直接访问或修改存储在指定地址的值。指针可以帮助我们在程序中动态地分配和释放内存&#xff0c;以及进行复杂的数据操作。在C语言中&#…

如何快速卸载windows电脑的一些软件?

本系列是一些电脑常规操作的普及&#xff0c;有需要借鉴即可 注&#xff1a;每个电脑都会有差异&#xff0c;参考即可。 其实大部分软件你删除桌面上的图标不等于删除&#xff0c;因为桌面上的那个图标就是一个简单的快捷方式而已。 在这里插入图片描述 那如何正确的卸载软件呢…

数据安全:超越威胁搜寻,监控数据流和用户行为

网络安全曾经是建立在严格协议和反应措施之上的堡垒&#xff0c;现在正在经历变革。随着数字环境变得更加复杂和数据驱动&#xff0c;对保护数字资产采取细致入微的方法的需求比以往任何时候都更加明显。这种演变标志着与传统威胁检测的背离&#xff0c;转向强调上下文并抢占用…

windows下快速安装nginx 并配置开机自启动

1、下载地址&#xff1a;http://nginx.org/en/download.html 2、启动nginx 注意⚠️ 不要直接双击nginx.exe&#xff0c;这样会导致修改配置后重启、停止nginx无效&#xff0c;需要手动关闭任务管理器内的所有nginx进程。 在nginx.exe目录&#xff0c;打开命令行工具&#xf…

缓存篇—缓存击穿

在很多场景下&#xff0c;我们的业务通常会有几个数据会被频繁地访问&#xff0c;比如秒杀活动&#xff0c;这类被频地访问的数据被称为热点数据。 如果缓存中的某个热点数据过期了&#xff0c;此时大量的请求访问了该热点数据&#xff0c;就无法从缓存中读取&#xff0c;直接…

AD24-蛇形走线

一、单端蛇形走线 1、公差参数 2、布线-网络等长调节 3、参数说明 ①手工输入绕线的长度 ②参照个网络的长度绕线 ③按照自身设置的规绕线&#xff08;一般选用) 4、调节 5、最后 二、差分蛇形走线 1、布线-差分对网络等长调节 2、如在选中的时候出现问题&#xff0c;按CtrlD…

安卓游戏开发之音频技术优劣分析

一、引言 在安卓游戏开发中&#xff0c;音频处理技术扮演着至关重要的角色&#xff0c;它不仅能够增强游戏的沉浸感和玩家体验&#xff0c;还能通过声音效果传达关键的游戏信息。以下将对几种常见的安卓游戏音频处理技术进行优劣分析&#xff0c;并结合应用场景来阐述其特点。 …

自学Python第十八天-自动化测试框架(二):DrissionPage、appium

自学Python第十八天-自动化测试框架&#xff08;二&#xff09;&#xff1a;DrissionPage、appium DrissionPage环境和安装配置准备工作简单的使用示例控制浏览器收发数据包模式切换 浏览器模式创建浏览器对象访问页面加载模式none 模式技巧 获取页面信息页面交互查找元素ele()…

C 嵌入式系统设计模式 09:硬件适配器模式

本书的原著为&#xff1a;《Design Patterns for Embedded Systems in C ——An Embedded Software Engineering Toolkit 》&#xff0c;讲解的是嵌入式系统设计模式&#xff0c;是一本不可多得的好书。 本系列描述我对书中内容的理解。本文章描述访问硬件的设计模式之二&…

【C语言】程序编译链接详解

目录 一、程序的翻译环境和执行环境 二、编译链接过程 2.1、程序编译过程 2.2、程序编译链接的阶段 2.2.1、预处理 2.2.2、编译 2.2.3、汇编 2.2.4、链接 2.2.5、整体过程 三、运行环境 一、程序的翻译环境和执行环境 在ANSI C的任何一种实现中&#xff0c;存在两个不…

odoo16-API(Controller)带有验证访问的接口

odoo16-API&#xff08;Controller&#xff09;带有验证访问的接口 目前我使用odoo原生的登录token来验证登陆的有效性 废话不多说直接上代码 # 测试获取session_id import requests class GetOdooData(http.Controller):def getOdooToken(self):# http://localhost:8123访问…
最新文章