当前位置: 首页 > news >正文

使用Vue3+elementPlus的Tree组件实现一个拖拽文件夹管理

文章目录

  • 1、前言
  • 2、分析
  • 3、实现
  • 4、踩坑
    • 4.1、拖拽辅助线的坑
    • 4.2、数据的坑
    • 4.3、限制拖拽
    • 4.4、样式调整

1、前言

最近在做一个文件夹管理的功能,要实现一个树状的文件夹面板。里面包含两种元素,文件夹以及文件。交互要求如下:

  1. 创建、删除,重命名文件夹和文件
  2. 可以拖拽,拖拽文件到文件夹中,或着拖拽文件夹到文件夹中
  3. 文件夹可展开,显示里面全部文件
  4. 拖拽的时候要有辅助线显示

文件夹管理交互表现

2、分析

根据这个要求,我先找到了vue.draggable.next这个库,结合elementPlus的Collapse折叠面板,以及Vue 3的递归组件封装了一个组件drag-folder,结果测试发现,这个库太久没维护了,很多事件不支持,导致功能很难实现。比如,拖动的时候拿不到拖动对象所选中的目标、没有辅助线、Collapse折叠面板关闭后无法拖入等问题。

就在头疼之时,我不小心看到了elementPlus的Tree组件,发现它也支持拖拽,而且有辅助线,有丰富的回调事件,于是,我准备魔改一下。

3、实现

Tree组件只需要准备一个树状数据,然后根据数据渲染出Tree组件即可,可以自定义子节点的键名,也可以使用插槽自定义内容,于是一番操作后,我完成了第二版的drag-folder组件:

<template><div class="drag_folder_box"><el-treedraggablenode-key="uid":default-expanded-keys="defaultExpanded":data="interiorList":allow-drop="handleDragBehavior":allow-drag="handleAllowDrag"@node-drag-start="handleDragStart"@node-drag-enter="handleDragEnter"@node-drag-leave="handleDragLeave"@node-drag-over="handleDragOver"@node-drag-end="handleDragEnd"@node-drop="handleDrop"@node-click="handleSwitchBillboard"><template #default="{ data }"><div class="tree_item"><div class="item_title">{{ data.label }}</div><div class="item_operate"><div class="operate_item" title="编辑" @click.stop="editBillboard(data)"><el-icon :size="16"><ele-Edit /></el-icon></div><div class="operate_item" title="删除" @click.stop="deleteBillboard(data)"><el-icon :size="16"><ele-Delete /></el-icon></div></div></div></template></el-tree></div>
</template><script setup lang="ts">
import { ref, onMounted, watch } from 'vue'
import type Node from 'element-plus/es/components/tree/src/model/node'
import type { DragEvents } from 'element-plus/es/components/tree/src/model/useDragNode'
import type { AllowDropType, NodeDropType } from 'element-plus/es/components/tree/src/tree.type'
import type { IGetBillboardListTreeItem } from '@/types/data-billboard'// #region 组件逻辑
interface IProps {list?: Array<IGetBillboardListTreeItem>
}const props = withDefaults(defineProps<IProps>(), {list: () => []
})const emit = defineEmits(['edit', 'delete', 'switch', 'change'])const interiorList = ref<Array<IGetBillboardListTreeItem>>([])
// #endregion// #region 拖拽逻辑
watch(() => props.list,(newValue) => {interiorList.value = newValue},{deep: true,immediate: true}
)
// 节点开始拖拽时
const handleDragStart = (node: Node, ev: DragEvents) => {}// 拖拽进入其他节点时
const handleDragEnter = (draggingNode: Node, dropNode: Node, ev: DragEvents) => {}// 拖拽离开某个节点时
const handleDragLeave = (draggingNode: Node, dropNode: Node, ev: DragEvents) => {}// 在拖拽节点时
const handleDragOver = (draggingNode: Node, dropNode: Node, ev: DragEvents) => {}// 拖拽结束时
const handleDragEnd = (draggingNode: Node, dropNode: Node, dropType: NodeDropType, ev: DragEvents) => {}// 拖拽成功时
const handleDrop = (draggingNode: Node, dropNode: Node, dropType: NodeDropType, ev: DragEvents) => {emit('change', interiorList.value)
}
// 是否允许拖拽
const handleAllowDrag = (draggingNode: Node) => {return true
}
// 拖拽行为判断
const handleDragBehavior = (draggingNode: Node, dropNode: Node, type: AllowDropType) => {// 如果选中的节点不是看板 则不允许拖动到内部,只能是 'prev' 或 'next'if (dropNode.data.type === 'billboard') {return type !== 'inner'}return true
}// 编辑看板/文件夹
const editBillboard = (data: IGetBillboardListTreeItem) => {emit('edit', data)
}// 删除看板/文件夹
const deleteBillboard = (data: IGetBillboardListTreeItem) => {emit('delete', data)
}// 选择看板
const handleSwitchBillboard = (data: IGetBillboardListTreeItem) => {if (data.type === 'dir') returnif (data.id === props.billboardId) returnemit('switch', data)
}
// #endregion// #region 生命周期
onMounted(() => {interiorList.value = props?.list || []
})
// #endregion
</script>

这个组件,使用起来很简单,只需要引入组件,然后绑定list就行了,下面我讲一下这里面的一些坑。

4、踩坑

这里面有几个坑,但是基本都解决了。

4.1、拖拽辅助线的坑

Tree组件没有itemSize属性,它的辅助线,默认是26px,而我的每一项,是36px的高度,所以就会导致辅助线不能对准。
辅助线

最开始我想着修改样式,给height和line-height,发现不起作用。于是我去翻源码,发现源码:node_modules\element-plus\lib\components\tree\src\model\useDragNode.js里,treeNodeDragOver方法是给辅助线设置top的,这个top是根据前面的iconPosition的高度来的,所以我设置了icon的height和line-height,一顿操纵如下:

.el-tree-node__expand-icon {line-height: 36px !important;height: 36px !important;padding: 0px 2px !important;
}

改完发现,面板折叠起来是正常的,但是打开后就还是不正常,审查元素发现,这个icon会旋转,打开面板后会添加一个expanded的类名,该类名添加了transform: rotate(90deg)的属性,导致高度不对了,于是我又一顿操作:

.el-tree-node__expand-icon {line-height: 36px !important;height: 36px !important;padding: 0px 2px !important;
}
.el-tree-node__expand-icon.expanded {transform: rotate(0deg);& svg {transform: rotate(90deg);}
}

4.2、数据的坑

这个是我们后端设计的锅。文件夹和文件的ID,会出现一样的,没有唯一ID,没办法,谁让我们前端就是这么善解人意,写个递归函数,拼接一个唯一ID出来咯。

const formatBillboardList = (list: Array<IBillboardTreeItem>, pid: number): Array<IBillboardTreeItem> => {return list.map((item, index) => {// 唯一IDconst uid = `${item.type}_${item.id}`// 父IDconst parentId = pid === 0 ? item.id : pid// 子层const children = Array.isArray(item.children) ? formatBillboardList(item.children, item.id) : []return {...item,uid,order: index,parentId,children}})
}const list = formatBillboardList(res.data, 0)

4.3、限制拖拽

文件夹和文件,都是可以拖拽的,但是文件可以拖拽到文件夹上,文件夹不能拖拽到文件里。这里我们用到了Tree组件的allow-drop处理函数,它又三个参数,分别是拖拽对象,目标对象,拖拽类型。

const handleDragBehavior = (draggingNode: Node, dropNode: Node, type: AllowDropType) => {// 如果选中的节点不是看板 则不允许拖动到内部,只能是 'prev' 或 'next'if (dropNode.data.type === 'billboard') {return type !== 'inner'}return true
}

4.4、样式调整

  • 文件夹和文件的样式不一样,要区分,这里我们用Tree组件的props属性,定制class实现
const treeOption = {class: (data: IGetBillboardListTreeItem, node: Node) => {if (data.id === props.billboardId && data.type === 'billboard') {return 'on_tree_item'} else if (data.type === 'dir') {return 'folder'} else {return 'billboard'}}
}
  • 每一层,要有不同的缩进,比如第一层缩进10,第二层20,以此类推,这里我们用Tree组件的indent属性实现,直接绑定即可。
<el-tree :indent="10"></el-tree>
  • 修改hover和focus时候的背景色
.drag_folder_box {&:deep(.el-tree-node__content) {width: 100%;height: auto;border-bottom: 1px solid #c1c1c1;&:hover {background-color: #e4f2ff !important;}&:focus {background-color: #e4f2ff !important;}}
}

如上,基本的样式和功能就全部完成了。


本次分享就到这儿啦,我是鹏多多,如果您看了觉得有帮助,欢迎评论,关注,点赞,转发,我们下次见~

往期文章

  • Vue2全家桶+Element搭建的PC端在线音乐网站
  • vue3+element-plus配置cdn
  • 助你上手Vue3全家桶之Vue3教程
  • 助你上手Vue3全家桶之VueX4教程
  • 助你上手Vue3全家桶之Vue-Router4教程
  • 超详细!Vue的九种通信方式
  • 超详细!Vuex手把手教程
  • 使用nvm管理node.js版本以及更换npm淘宝镜像源
  • vue中利用.env文件存储全局环境变量,以及配置vue启动和打包命令
  • 超详细!Vue-Router手把手教程

个人主页

  • CSDN
  • GitHub
  • 简书
  • 博客园
  • 掘金

相关文章:

使用Vue3+elementPlus的Tree组件实现一个拖拽文件夹管理

文章目录 1、前言2、分析3、实现4、踩坑4.1、拖拽辅助线的坑4.2、数据的坑4.3、限制拖拽4.4、样式调整 1、前言 最近在做一个文件夹管理的功能&#xff0c;要实现一个树状的文件夹面板。里面包含两种元素&#xff0c;文件夹以及文件。交互要求如下&#xff1a; 创建、删除&am…...

小谈设计模式(7)—装饰模式

小谈设计模式&#xff08;7&#xff09;—装饰模式 专栏介绍专栏地址专栏介绍 装饰模式装饰模式角色Component&#xff08;抽象组件&#xff09;ConcreteComponent&#xff08;具体组件&#xff09;Decorator&#xff08;抽象装饰器&#xff09;ConcreteDecorator&#xff08;具…...

nginx 多层代理 + k8s ingress 后端服务获取客户真实ip 配置

1.nginx http 七层代理 修改命令空间&#xff1a; namespace: nginx-ingress : configmap&#xff1a;nginx-configuration kubectl get cm nginx-configuration -n ingress-nginx -o yaml添加如上配置 compute-full-forwarded-for: “true” forwarded-for-header: X-Forwa…...

6种最常用的3D点云语义分割AI模型对比

由于增强现实/虚拟现实的发展及其在计算机视觉、自动驾驶和机器人领域的广泛应用&#xff0c;点云学习最近引起了人们的关注。 深度学习已成功用于解决 2D 视觉问题&#xff0c;然而&#xff0c;由于其处理面临独特的挑战&#xff0c;深度学习技术在点云上的使用仍处于起步阶段…...

UG NX二次开发(C#)-获取UI中选择对象的handle值

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 1、前言2、设计一个简单的UI界面3、创建工程项目4、测试结果1、前言 我在哔哩哔哩的视频中看到有人问我如何获取UI选择对象的Handle,本来想把Tag、Taggedobject、Handle三者的关系讲一下,然后看…...

win10,WSL的Ubuntu配python3.7手记

1.装linux 先在windows上安装WSL版本的Ubuntu Windows10系统安装Ubuntu子系统_哔哩哔哩_bilibili &#xff08;WSL2什么的一直没搞清楚&#xff09; 图形界面会出一些问题&#xff0c;注意勾选ccsm出的界面设置 win10安装Ubuntu16.04子系统&#xff0c;并开启桌面环境_win…...

02-Zookeeper实战

上一篇&#xff1a;01-Zookeeper特性与节点数据类型详解 1. zookeeper安装 Step1&#xff1a; 配置JAVA环境&#xff0c;检验环境&#xff1a; java -versionStep2: 下载解压 zookeeper wget https://mirror.bit.edu.cn/apache/zookeeper/zookeeper-3.5.8/apache-zookeepe…...

【C语言深入理解指针(1)】

1.内存和地址 1.1内存 在讲内存和地址之前&#xff0c;我们想有个⽣活中的案例&#xff1a; 假设有⼀栋宿舍楼&#xff0c;把你放在楼⾥&#xff0c;楼上有100个房间&#xff0c;但是房间没有编号&#xff0c;你的⼀个朋友来找你玩&#xff0c;如果想找到你&#xff0c;就得挨…...

模拟实现简单的通讯录

前言&#xff1a;生活中处处都会看到或是用到通讯录&#xff0c;今天我们就通过C语言来简单的模拟实现一下通讯录。 鸡汤&#xff1a;跨越山海&#xff0c;终见曙光&#xff01; 链接:gitee仓库&#xff1a;代码链接 目录 主函数声明部分初始化通讯录实现扩容的函数增加通讯录所…...

rabbitMQ死信队列快速编写记录

文章目录 1.介绍1.1 什么是死信队列1.2 死信队列有什么用 2. 如何编码2.1 架构分析2.2 maven坐标2.3 工具类编写2.4 consumer1编写2.5 consumer2编写2.6 producer编写 3.整合springboot3.1 架构图3.2 maven坐标3.3 构建配置类&#xff0c;创建exchange&#xff0c;queue&#x…...

数位dp,338. 计数问题

338. 计数问题 - AcWing题库 给定两个整数 a 和 b&#xff0c;求 a 和 b 之间的所有数字中 0∼90∼9 的出现次数。 例如&#xff0c;a1024&#xff0c;b1032&#xff0c;则 a 和 b 之间共有 9 个数如下&#xff1a; 1024 1025 1026 1027 1028 1029 1030 1031 1032 其中 0 出…...

如何解决git clone http/https仓库失败(403错误)

本来不打算写这篇文章&#xff0c;但是后来又遇到这个问题忘了之前是怎么解决的了。 一般情况下&#xff0c;个人使用 GitHub 等平台时是使用 SSH 协议的&#xff0c;这样不光方便管理可访问用户&#xff0c;也保证了安全性。但是 GitHub 上仓库的 SSH 地址是要登陆才能看到&a…...

华为云云耀云服务器L实例评测 | 实例评测使用之硬件性能评测:华为云云耀云服务器下的硬件运行评测

华为云云耀云服务器L实例评测 &#xff5c; 实例评测使用之硬件性能评测&#xff1a;华为云云耀云服务器下的硬件运行评测 介绍华为云云耀云服务器 华为云云耀云服务器 &#xff08;目前已经全新升级为 华为云云耀云服务器L实例&#xff09; 华为云云耀云服务器是什么华为云云耀…...

Elasticsearch:使用 Elasticsearch 进行语义搜索

在数字时代&#xff0c;搜索引擎在通过浏览互联网上的大量可用信息来检索数据方面发挥着重要作用。 此方法涉及用户在搜索栏中输入特定术语或短语&#xff0c;期望搜索引擎返回与这些确切关键字匹配的结果。 虽然关键字搜索对于简化信息检索非常有价值&#xff0c;但它也有其局…...

JVM的主要组成及其作用

jvm主要组成部分有: 类加载器、运行时数据区 (内存结构)、执行引擎、本地接口库、垃圾回收机制 Java程序运行的时候&#xff0c;首先会通过类加载器把Java 代码转换成字节码。然后运行时数据区再将字节码加载到内存中&#xff0c;但字节码文件只是JVM 的一套指令集规范&#xf…...

会议AISTATS(Artificial Intelligence and Statistics) Latex模板参考文献引用问题

前言 在看AISTATS2024模板的时候&#xff0c;发现模板里面根本没有教怎么引用&#xff0c;要被气死了。 如下&#xff0c;引用(Cheesman, 1985)的时候&#xff0c;模板是自己手打上去的&#xff1f;而且模板提供的那三个引用&#xff0c;根本也没有Cheesman这个人&#xff0c…...

2023最新外贸建站:WordPress搭建外贸独立站零基础小白保姆级教程

想从零开始建立一个外贸自建站&#xff0c;那么你来对地方了。 如果你还在找外贸建站或者是WordPress建站教程&#xff0c;不妨看看这篇文章&#xff0c;本教程涵盖了2023最新的外贸建站教程&#xff0c;你将学会使用WordPress自建外贸独立站&#xff0c;步骤包括购买域名主机…...

HTTP请求交互基础(基于GPT3.5,持续更新)

HTTP交互基础 目的HTTP定义详解HTTP协议&#xff08;规范&#xff09;1. 主要组成部分1.1 请求行&#xff08;Request Line&#xff09;&#xff1a;包含请求方法、请求URI&#xff08;Uniform Resource Identifier&#xff09;和HTTP协议版本。1.2 请求头部&#xff08;Reques…...

小谈设计模式(6)—依赖倒转原则

小谈设计模式&#xff08;6&#xff09;—依赖倒转原则 专栏介绍专栏地址专栏介绍 依赖倒转原则核心思想关键点分析abc 优缺点分析优点降低模块间的耦合度提高代码的可扩展性便于进行单元测试 缺点增加代码的复杂性需要额外的设计和开发工作 Java代码实现示例分析 总结 专栏介绍…...

JetBrains常用插件

Codota AI Autocomplete Java and JavaScript&#xff1a;自动补全插件 Background Image plus&#xff1a;背景图片设置 rainbow brackets&#xff1a;彩虹括号&#xff0c;便于识别 CodeGlance2&#xff1a; 类似于 Sublime 中的代码缩略图&#xff08;代码小地图&#xff…...

【C++哈希应用】位图、布隆过滤器

【C哈希应用】位图、布隆过滤器 目录 【C哈希应用】位图、布隆过滤器位图概念位图的实现位图改造位图应用总结布隆过滤器布隆过滤器的提出布隆过滤器的概念布隆过滤器的查找布隆过滤器删除布隆过滤器优点布隆过滤器缺陷 作者&#xff1a;爱写代码的刚子 时间&#xff1a;2023.9…...

Qt 编译纯c的C99的项目, error: undefined reference to `f()‘

把Cpp的后缀该为C是什么样的 尝试引用一个奇门排盘的c程序&#xff0c;在git上找到的叫cqm&#xff0c; 然后总是报错 error: undefined reference to f() 很是郁闷 于是新建了个项目试验一下&#xff0c;终于摸清了需要命名空间。 后来这么写就可以了 a.h namespace XX …...

TensorFlow入门(五、指定GPU运算)

一般情况下,下载的TensorFlow版本如果是GPU版本,在运行过程中TensorFlow能自动检测。如果检测到GPU,TensorFlow会默认利用找到的第一个GPU来执行操作。如果机器上有超过一个可用的GPU,除第一个之外的其他GPU默认是不参与计算的。如果想让TensorFlow使用这些GPU执行操作,需要将运…...

Unity - 实践: Metallic流程贴图 转 Specular流程贴图

文章目录 目的Metallic Flow - SP - 输出输出的 MRA (MGA) 贴图 Metallic->Specular (根据教程一步一步实践)1. Base color Metallic -> Diffuse2. Base color Metallic -> Specular3. Roughness -> Glossiness输出贴图&#xff0c;在 unity 中展示&#xff1a;M…...

第三章:最新版零基础学习 PYTHON 教程(第四节 - Python 运算符—Python 逻辑运算符及示例)

运算符用于对值和变量执行操作。这些是执行算术和逻辑计算的特殊符号。运算符运算的值称为操作数。 表中的内容逻辑运算符 逻辑与运算符 逻辑或运算符 逻辑非运算符 逻辑运算符的求值顺序 逻辑运算符 在 Python 中,逻辑运算符用于条件语句(True 或 False)。它们执行逻辑 AN…...

如何做好测试?(三)功能测试 (Functional Testing, FT)

1. 功能测试的详细介绍&#xff1a; 功能测试 (Functional Testing, FT)&#xff0c;是一种软件测试方法&#xff0c;旨在验证系统的功能是否按照需求规格说明书或用户期望的方式正常工作。它关注系统的整体行为&#xff0c;以确保各个功能模块和组件之间的交互和集成正确。 …...

Ubuntu-Server-22.04安装桌面+VNC

前提&#xff1a;Ubuntu Server安装好后&#xff0c;ubantu其他版本是否适用这里未知&#xff0c;欢迎大佬们前来评论 一、默认没有图形界面&#xff0c;有时觉得用图形界面操作更简单直接&#xff0c;于是用如下命令安装&#xff1a; 1.更新本地环境 sudo apt-get update s…...

职业规划,什么是职业兴趣 - 我喜欢做什么?

能够在工作岗位上面做出成绩的人&#xff0c;都是结合自身兴趣&#xff0c;对职业进行合理规划的那一类。尤其是步入中年以后&#xff0c;能够创造出巨大价值的人&#xff0c;无一例外都是喜欢自己职业的人。没有将兴趣融入工作的人&#xff0c;只能够忍受默默无闻地活着&#…...

基于Java的高校学生党员发展流程管理系统设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作…...

【NLP的python库(03/4) 】: 全面概述

一、说明 Python 对自然语言处理库有丰富的支持。从文本处理、标记化文本并确定其引理开始&#xff0c;到句法分析、解析文本并分配句法角色&#xff0c;再到语义处理&#xff0c;例如识别命名实体、情感分析和文档分类&#xff0c;一切都由至少一个库提供。那么&#xff0c;你…...

网站建设预付款/网址怎么创建

1 LSA Introduction LSA(latent semantic analysis)潜在语义分析&#xff0c;也被称为LSI(latent semantic index)&#xff0c;是Scott Deerwester, Susan T. Dumais等人在1990年提出来的一种新的索引和检索方法。该方法和传统向量空间模型(vector space model)一样使用向量来表…...

沈阳室内设计公司排名/seo快速排名优化方式

Hadoop2.7.3 Writable 序列化之二1. Hadoop序列化背景或原因2. Hadoop Writable机制3. 实验内容3.1 思路3.2 编写HadoopPerson类3.3 编写测试类4. 实验结果4.1 调用ser()序列化后文件内容如下4.1 调用反序列化方法deSer()结果如下1. Hadoop序列化背景或原因 Java序列化会附带额…...

合肥网站建设制作价格/端口扫描站长工具

进程创建 fork vfork 进程等待 wait方法 waitpid方法 获取子进程status wait方式获取子进程status waitpid方式获取status 进程等待 waitpid进程的阻塞方式等待 waitpid进程的非阻塞等待方式 进程替换 Shell进程替换 Shell进程替换的本质 进程替换原理 实现一个minishell 进程创…...

永川建网站/东莞网站seo公司

这里用到的ide是netbeans ruby环境安装好后&#xff0c;下载netbeans&#xff0c;然后进行安装 netbeans ruby plugins download URL&#xff1a;http://jruby.org.s3.amazonaws.com/downloads/community-ruby/community-ruby_7_1_preview1.zip 解压 打开netbeans&#xff0c;工…...

沈阳网站优化怎么做/手机seo关键词优化

正则表达式 上下文无关by Christopher Diggins克里斯托弗迪金斯(Christopher Diggins) 正则表达式之外&#xff1a;解析上下文无关语法的简介 (Beyond regular expressions: An introduction to parsing context-free grammars) An important and useful tool that is already…...

政府网站网页设计/16888精品货源入口

基本环境&#xff1a;安装好pyCharm社区版使用 pip install Django安装好Django步骤1&#xff1a;使用pyCharm新建项目1. pyCharm新建项目示意图步骤2&#xff1a;新建Django项目命令&#xff1a;django-admin startproject project_name2-1. 使用命令行新建Django项目2-2. Dja…...