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

news2025/2/24 8:17:05

看效果:

        可以实现单个拖拽、双击添加、按住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.coloradmin.cn/o/1465791.html

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

相关文章

Kubernetes安装nginx-controller作为统一网关

nginx-controller是什么呢? 它是一个能调度nginx的一个kubernetes operator,它能监听用户创建,更新,删除NginxConf对象,来调度本地的nginx实现配置的动态更新。如添加新的代理(http,https,tcp,udp),缓存(浏览器缓存,本地缓存),ssl证书(配置本身,ConfigMap,Secret),更新,删除等…

Js的 Promise的 then catch 笔记240222

Js的 Promise的 then catch 笔记240222 基本用法 new Promise(f>{setTimeout(ev>{f("一秒后输出控制台");},1000); }).then(f的参数>{console.log(f的参数); }); // 控制台输出: 一秒后输出控制台上面代码中, f 的标准名叫做 resolve , 所以应该写成 new …

开发Chrome插件,background.js中log打印未出现在控制台

不同于内容脚本&#xff08;通常命名content.js&#xff09;&#xff0c;在后台脚本&#xff08;通常命名background.js或service-worker.js&#xff09;中console.log并不会在控制台中直接显示。 要查看后台脚本上下文的正确控制台&#xff0c;执行如下步骤&#xff1a; 访问…

【Python笔记-设计模式】原型模式

一、说明 原型模式是一种创建型设计模式&#xff0c; 用于创建重复的对象&#xff0c;同时又能保证性能。 使一个原型实例指定了要创建的对象的种类&#xff0c;并且通过拷贝这个原型来创建新的对象。 (一) 解决问题 主要解决了对象的创建与复制过程中的性能问题。主要针对…

有哪些适合程序员的副业?

如果你经常玩知乎、看公众号&#xff08;软件、工具、互联网这几类的&#xff09;你就会发现&#xff0c;好多资源连接都变成了夸克网盘、迅雷网盘的资源链接。 例如&#xff1a;天涯神贴&#xff0c;基本上全是夸克、UC、迅雷网盘的资源链接。 有资源的前提下&#xff0c;迅雷…

QT_day4

1.思维导图 2. 输入闹钟时间格式是小时:分钟 widget.cpp #include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this);id startTimer(1000);flag1;speecher new QTextT…

基础数据结构和算法《》

递归 1.递归应该一种比较常见的实现一些特殊代码逻辑时需要做的&#xff0c;但常常也是最绕的一种方式&#xff0c;在解释递归 之前&#xff0c;我们用循环和递归来做个比较1.1.如果你打开一扇门后&#xff0c;同样发现前方也有一扇们&#xff0c;紧接着你又打开下一扇门...直…

应用回归分析:泊松回归

泊松回归是一种广泛用于计数数据的回归分析方法。它适用于响应变量是非负整数的情况&#xff0c;特别是当这些计数呈现出明显的离散分布时。泊松回归通过泊松分布的概率分布函数来建模计数数据&#xff0c;使其成为处理计数数据的自然选择。本文将介绍泊松回归的基本概念、应用…

石头剪刀布游戏(C语言)

题目描述 石头剪刀布游戏有 3 种出拳形状&#xff1a;石头、剪刀、布。分别用字母 A , B , C 表示。 游戏规则: 出拳形状之间的胜负规则如下&#xff1a; A > B&#xff1b;B > C&#xff1b;C > A&#xff1b;">"左边一个字母&#xff0c;表示相对优…

进程线程通信-day6

1、将信号和消息队列的课堂代码敲一遍 //发送端 #include<myhead.h>//定义一个消息结构类型 struct msgbuf {long mtype;char mtext[1024]; }; //定义一个宏&#xff0c;表示消息正文大小 #define MSGSIZE sizeof(struct msgbuf)-sizeof(long)int main(int argc, const…

PBM模型学习

本专栏着重讲解PBM学习所得&#xff0c;学习笔记、心得&#xff0c;并附有视频素材资料&#xff0c;视频详细目录如下&#xff1a; PBM相关参数解释1 PBM相关参数解释2 PBM相关案例实践1 PBM相关案例实践2 PBM相关案例实践2 PBM相关案例实践3 PBM多相流中次相界面设置1 PBM多相…

Kotlin 进阶 学习 委托

1.接口委托 package com.jmj.jetpackcomposecompositionlocal.byStudy/*** 接口委托*/ interface HomeDao{fun getAllData():List<String> }interface ADao{fun getById(id:Int):String }class HomeDaoImpl:HomeDao{override fun getAllData(): List<String> {ret…

MATLAB中的稀疏矩阵和密集矩阵

在MATLAB中&#xff0c;矩阵可以表示为密集或稀疏格式。通常&#xff0c;矩阵默认以密集格式存储&#xff0c;这意味着每个元素都明确地存储在内存中&#xff0c;无论它的值是多少。然而&#xff0c;当矩阵含有大量的零元素时&#xff0c;这种存储方式就会变得非常低效。为了更…

iOS整理 - 关于直播 - 搭建服务端

前言 其实本人一直都想自己简单做一套直播&#xff08;包括移动端和服务端&#xff09;的开发测试&#xff0c;但是之前一直做得比较迷茫。最近偶然间在来了灵感&#xff0c;瞬间解除了我很多疑惑。我会分享出来&#xff0c;希望大家一起研究下。稍后&#xff0c;我完整做好了…

ZS Associates致盛咨询是什么公司?排名怎么样?

随着商业化时代的加速演进&#xff0c;咨询公司在企业发展中的“智囊团”角色愈发突显。对于医药企业来说&#xff0c;一个优秀的咨询团队不仅可以帮助推动整体战略转型及内部改革&#xff0c;还对药品研发、营销起到优化促进作用。 那什么样的咨询企业可称之为优秀的咨询企业…

关于msvcr120.dll丢失怎样修复的详细解决步骤方法分享,msvcr120.dll文件的相关内容

在电脑使用过程中&#xff0c;我们经常遇到各种系统错误&#xff0c;其中msvcr120.dll丢失是一个常见问题。msvcr120.dll文件是Visual C Redistributable for Visual Studio 2015/2017的一个组件&#xff0c;主要用于支持某些应用程序的正常运行。当电脑出现msvcr120.dll丢失情…

SpringBoot项目启动报java.nio.charset.MalformedInputException Input length = 1解决方案

报错详情 SpringBoot启动报错java.nio.charset.MalformedInputException: Input length 1 报错原因 出现这个的原因&#xff0c;就是解析yml文件时&#xff0c;中文字符集不是utf-8的原因&#xff0c;这是maven在项目编译时&#xff0c;默认字符集编码是GBK。 解决方式 检…

多数pythoneer只知有列表list却不知道python也有array数组

数组和列表 Python中数组和列表是不同的&#xff0c;我敢断言大多数的pythoneer只知道有列表list&#xff0c;却不知道python也有array数组。列表是一个包含不同数据类型的元素集合&#xff0c;而数组是一个只能含相同数据类型的元素集合。 Python的array库是一个提供数组操作…

ORA-02062: distributed recovery received DBID 9ad10df5, expected 38cc1cd5

今晚做重启维护&#xff0c;发现节点二上报错如下 Fri Feb 23 21:47:43 2024 Errors in file /u01/app/oracle/diag/rdbms/orcl/orcl2/trace/orcl2_reco_58540.trc: ORA-02062: distributed recovery received DBID 9ad10df5, expected 38cc1cd5 Errors in file /u01/app/oracl…

DTV的LCN功能介绍

文章目录 LCN简介LCN获取LCN Conflict LCN简介 Logical Channel Number&#xff08;LCN&#xff09;是数字电视系统中用于标识和组织频道的逻辑编号。LCN的目的是为了方便用户浏览和选择频道&#xff0c;使得数字电视接收设备能够根据这些逻辑编号对频道进行排序和显示。 LCN…