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

vue3: vuedraggable 的使用方法(正常数据的基本使用与树结构数据递归使用)

目录

效果展示

第一章 准备

 第二章 源代码

2.1 基本使用

2.2  树结构数据递归使用


效果展示

  • 正常数据拖拽

  • 树结构数据拖拽 ——说明:自定义拖拽树,添加数据展开收起拖拽时非同层级不可拖拽,使用灰色区分,跨层拖拽不生效,并使用递归组件实现。

第一章 准备

官方文档:vuedraggable - npm

安装:

yarn add vuedraggable@4.1.0
yarn add vuedraggable-es@4.1.0

 第二章 源代码

(为了方便大家,小编直接上源码,里面附带讲解,大家复制即可使用)

2.1 基本使用

  • 封装组件:

js—— 

<script setup name="NormalDraggable">
import draggable from "vuedraggable";
import { reactive } from "vue";const props = defineProps({originData: {type: Array,default: () => {return [];},},
});const emit = defineEmits(["handleChange"]);const state = reactive({list: props.originData.map((item) => {return {...item,};}),
});//拖拽开始的事件
const onStart = () => {console.log("开始拖拽");
};//拖拽结束的事件
const onEnd = () => {// 拖拽结束后重新处理数据,参数:原数据,变化的数据emit("handleChange", state.list);
};
</script>

 html——

<template><draggable:list="state.list"itemKey="id"animation="0"@start="onStart"@end="onEnd"><template #item="{ element }"><div><div class="draggable_item"><div class="draggable_item-left"><span>{{ element.fieldName }}</span></div><div class="draggable_item-right"><div class="example"><span>{{ element.exampleValue }}</span></div></div></div></div></template></draggable>
</template>

css——

<style lang="less" scoped>
.draggable_item {width: 100%;padding: 0px 12px;min-height: 40px;background: #ffffff;display: flex;justify-content: space-between;align-items: center;border: 1px solid #e0e6eb;box-shadow: 0px 2px 0px 0px rgba(0, 0, 0, 0.02);border-radius: 2px;margin-bottom: 4px;.draggable_item-left {display: flex;align-items: center;max-width: 260px;.icon {margin-right: 10px;width: 14px;}}.draggable_item-right {display: flex;align-items: center;.example {color: rgba(0, 0, 0, 0.25);margin-right: 10px;max-width: 184px;}}
}
</style>
  • 使用:
<script setup name="NormalDraggable">
import NormalDraggable from "./NormalDraggable/index.vue";const originDataList = ref([{exampleValue: "1",fieldName: "1层1号",id: "41cf48fb5c7546758b8ea679c7fc40b3",parentId: "",},{exampleValue: "123",fieldName: "1层2号",id: "57e6b0b1f0f14dff86deeb9ca2937772",parentId: "",},{exampleValue: '{a:"123"}',fieldName: "1层3号",id: "57e6b0b1f0f14dff86deeb9ca2937773",parentId: "",},{exampleValue: "1",fieldName: "1层4号",id: "b83e93dd599d40e6b58b24c0f3cf1cc8",parentId: "",},{exampleValue: "1",fieldName: "1层5号",id: "b83e93dd599d40e6b58b24c0f3cf1cd8",parentId: "",},{exampleValue: "1",fieldName: "1层6号",id: "b83e93dd599d40e6b58b24c0f3cf1cf8",parentId: "",},
]);const handleChangeList = (data) => {console.log("data", data);
};
</script><NormalDraggable:originData="originDataList"@handleChange="handleChangeList"
/>

2.2  树结构数据递归使用

  • 封装拖拽组件:

js——

<script> 
// 由于是递归组件,小编的demo中配置不是特别完全,所以需要将该组件名字向外暴露,但是需要区分,setup语法糖
export default {name: "TreeDraggable",
};
</script>
<script setup name="TreeDraggable">
import {CaretRightOutlined,CaretDownOutlined,DragOutlined,
} from "@ant-design/icons-vue"; // 小编使用的时antd小图标需要额外引入import draggable from "vuedraggable"; // 引入拖拽组件const props = defineProps({// 整个数据对象originData: {type: Array,default: () => {return [];},},// 数据对象中的子节点childrenData: {type: Array,default: () => {return [];},},
});const emit = defineEmits(["handleChange","startHandleChange","initHandleChange",
]);//拖拽开始的事件
const onStart = (event) => {const { element } = event.item.__draggable_context;// 拖拽开始时处理数据(向父组件发送消息)emit("startHandleChange", element.level);
};// 递归组件中介函数
const startHandleChange = (level) => {emit("startHandleChange", level);
};//拖拽结束的事件
const onEnd = () => {// 拖拽结束后重新处理数据,参数:原数据,变化的数据(向父组件发送消息)emit("handleChange", props.originData, props.childrenData);
};// 递归组件中介函数
const handleChange = (data1, data2) => {emit("handleChange", data1, data2);
};// 侧边小图标点击事件
const showTreeInfo = (ele) => {if (ele.children) {ele.open = !ele.open;}
};
</script>

html——

<template><draggable:list="childrenData" // 传入的拖拽数据itemKey="id" // 需要传入id防止会跨层拖拽animation="0"@start="onStart" // 拖拽开始事件@end="onEnd" // 拖拽结束事件>// 下面是拖拽组件的基本使用结构<template #item="{ element }"> // 单条拖拽元素数据,即draggable组件的插槽<div> //自定义每一条拖拽数据的展示结构<divclass="draggable_item":style="{backgroundColor: element.isdraggable // 通过isdraggable区分是否能拖拽的颜色? '#ffffff': 'rgba(0, 0, 0, 0.04)',color: element.isdraggable? 'rgba(0, 0, 0, 0.88)': 'rgba(0, 0, 0, 0.25)',}"><divclass="draggable_item-left":style="{ marginLeft: `${(element.level - 1) * 16}px` }" // 构造一下树结构><div class="icon" @click="showTreeInfo(element)"> // 通过点击小图标控制数据的展开与隐藏<div v-if="element.children.length"> // 如果没子级则不展示小图标<CaretRightOutlined v-if="!element.open" /> // open控制小图标<CaretDownOutlined v-else /> </div></div><div class="name"><span>{{ element.fieldName }}</span></div></div><divclass="draggable_item-right":style="{color: element.isdraggable? 'rgba(0, 0, 0, 0.45)': 'rgba(0, 0, 0, 0.25)',}"><div class="example"><span>{{ element.exampleValue }}</span></div><div><DragOutlined /></div></div></div><TreeDraggable // 递归组件传入对应的方法以及参数:originData="originData":childrenData="element.children"@handleChange="handleChange"@startHandleChange="startHandleChange"v-if="element.children && element.open"/></div></template></draggable>
</template>

css(与2.1基本使用的样式一致

  • 使用
<script name="App" setup>
import TreeDraggable from "./TreeDraggable/index.vue"; // 引入主键
import { onMounted, ref } from "vue";
let originData = ref([ // 数据格式{children: [],exampleValue: "1",fieldName: "1层1号",id: "41cf48fb5c7546758b8ea679c7fc40b3",parentId: "",},{children: [{children: [{children: [],exampleValue: "示例值",fieldName: "2号2层1号",id: "7647910dc6d94a51ac5311392ba290d3",parentId: "7647910dc6d94a51ac5311382ba990d3",},{children: [],exampleValue: "示例值",fieldName: "2号2层2号",id: "7647910dc6d94a51ac5311382ba290d4",parentId: "7647910dc6d94a51ac5311382ba990d3",},],exampleValue: "示例值",fieldName: "2号1层1",id: "7647910dc6d94a51ac5311382ba990d3",parentId: "57e6b0b1f0f14dff86deeb9ca2937772",},],exampleValue: "123",fieldName: "1层2号",id: "57e6b0b1f0f14dff86deeb9ca2937772",parentId: "",},{children: [{children: [{children: [],exampleValue: "示例值",fieldName: "3号2层1号",id: "7647910dc6d94a51ac5311382ba294d3",parentId: "7647910dc6d94a51ac5311382ba291d3",},{children: [],exampleValue: "示例值",fieldName: "3号2层2号",id: "7647910dc6d94a51ac5311382ba290d4",parentId: "7647910dc6d94a51ac5311382ba291d3",},],exampleValue: "示例值",fieldName: "3号1层1",id: "7647910dc6d94a51ac5311382ba291d3",parentId: "57e6b0b1f0f14dff86deeb9ca2937773",},{children: [],exampleValue: "示例值",fieldName: "3号1层2",id: "7647910dc6d94a51ac5311382ba290d3",parentId: "57e6b0b1f0f14dff86deeb9ca2937773",},],exampleValue: '{a:"123"}',fieldName: "1层3号",id: "57e6b0b1f0f14dff86deeb9ca2937773",parentId: "",},{children: [],exampleValue: "1",fieldName: "1层4号",id: "b83e93dd599d40e6b58b24c0f3cf1cc8",parentId: "",},{children: [],exampleValue: "1",fieldName: "1层5号",id: "b83e93dd599d40e6b58b24c0f3cf1cd8",parentId: "",},{children: [],exampleValue: "1",fieldName: "1层6号",id: "b83e93dd599d40e6b58b24c0f3cf1cf8",parentId: "",},
]);//下面的方法基本上都是使用递归实现的
// 初始化数据,添加level(层级)、serial(序号)、isdraggable(是否可拖拽)、open(控制数据是否展开):要进行如下初始化的原因是正常开发时后端是不会这么给我们前端返回的,我们需要处理数据
const initTreeData = (nodes, level) => {for (let i = 0; i < nodes.length; i++) { // 遍历数据const node = nodes[i];node.serial = i + 1; // 额外添加以下字段node.level = level;node.isdraggable = true;node.open = false;// 如果节点有子节点,递归遍历子节点,并将层数加1if (node.children) {initTreeData(node.children, level + 1);}}return nodes;
};onMounted(() => {originData.value = initTreeData(originData.value, 1);
});// 部分数据重置(添加重置的方法原因是拖拽后已经展开的数据(open:true)不再收起设置为(open:false))
const restTreeData = (nodes, level) => {for (let i = 0; i < nodes.length; i++) {const node = nodes[i];node.serial = i + 1;node.level = level;node.isdraggable = true;// 如果节点有子节点,递归遍历子节点,并将层数加1if (node.children) {restTreeData(node.children, level + 1);}}return nodes;
};// 开始拖动时处理节点是否可拖拽,需要样式区分
const dealAddData = (nodes, level) => {for (let i = 0; i < nodes.length; i++) {const node = nodes[i];if (node.level === level) {node.isdraggable = true;} else {node.isdraggable = false;}// 如果节点有子节点,递归遍历子节点,并将层数加1if (node.children) {dealAddData(node.children, level);}}return nodes;
};// 拖动后处理变化的数据(拖拽后需要对拖拽的chilren节点进行初始化,这里有一点优化,由于只能同层级拖拽,所以该节点下的子节点数据是没有变化的,只需要处理当前层级的即可)
const dealChangeData = (nodes, childrenNode, parentId) => {if (parentId === "") {nodes = childrenNode;} else {for (let i = 0; i < nodes.length; i++) {const node = nodes[i];if (node.parentId === parentId) {node.children = childrenNode;}// 如果节点有子节点,递归遍历子节点,并将层数加1if (node.children) {dealAddData(node.children, childrenNode, parentId);}}}return nodes;
};// 结束拖动时处理数据
const handleChange = (data1, data2) => {const parentId = data2[0].parentId;const data = dealChangeData(data1, data2, parentId); // 处理数据originData.value = restTreeData(data, 1); // 初始化数据
};// 开始拖动时修改数据:添加字段控制样式
const startHandleChange = (level) => {originData.value = dealAddData(originData.value, level);
};
</script><template><div class="row"><div style="min-height: 500px"><TreeDraggable // 使用组件:originData="originData":childrenData="originData"@handleChange="handleChange"@startHandleChange="startHandleChange"/></div></div>
</template><style scoped></style>

如果数据格式中所有children都是[],也可以与基本使用的样式一样:

let originData = ref([{children: [],exampleValue: "1",fieldName: "1层1号",id: "41cf48fb5c7546758b8ea679c7fc40b3",parentId: "",},{children: [],exampleValue: "123",fieldName: "1层2号",id: "57e6b0b1f0f14dff86deeb9ca2937772",parentId: "",},{children: [],exampleValue: '{a:"123"}',fieldName: "1层3号",id: "57e6b0b1f0f14dff86deeb9ca2937773",parentId: "",},{children: [],exampleValue: "1",fieldName: "1层4号",id: "b83e93dd599d40e6b58b24c0f3cf1cc8",parentId: "",},{children: [],exampleValue: "1",fieldName: "1层5号",id: "b83e93dd599d40e6b58b24c0f3cf1cd8",parentId: "",},{children: [],exampleValue: "1",fieldName: "1层6号",id: "b83e93dd599d40e6b58b24c0f3cf1cf8",parentId: "",},
]);

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 【K8S】为什么需要Kubernetes?
  • 【Wireshark 抓 CAN 总线】Wireshark 抓取 CAN 总线数据的实现思路
  • STM32 | ADC+RS485编写代码,实现光敏电阻控制灯的亮度
  • C语言 | Leetcode C语言题解之第319题灯泡开关
  • CSS面试题
  • 学习笔记一
  • JVM—HotSpot虚拟机对象探秘
  • 2024年第五届华数杯全国大学生数学建模竞赛【ABC题】完整思路
  • Python从入门到精通(第十章——1 类和对象)
  • 基于JSP、java、Tomcat三者的项目实战--校园交易网(3)主页--添加商品功能
  • 【C++】数组案例 五只小猪称体重
  • C#中DataTable新增列、删除列、更改列名、交换列位置
  • 代码随想录算法训练营第37天|完全背包理论基础、518.零钱兑换II、377. 组合总和 Ⅳ、70. 爬楼梯(进阶版)
  • 【深度学习】深度学习基本概念、工作原理及实际应用案例
  • (STM32笔记)九、RCC时钟树与时钟 第一部分
  • 【140天】尚学堂高淇Java300集视频精华笔记(86-87)
  • Date型的使用
  • ES学习笔记(10)--ES6中的函数和数组补漏
  • HTTP传输编码增加了传输量,只为解决这一个问题 | 实用 HTTP
  • JAVA 学习IO流
  • JDK9: 集成 Jshell 和 Maven 项目.
  • MySQL的数据类型
  • Spark VS Hadoop:两大大数据分析系统深度解读
  • text-decoration与color属性
  • 记录:CentOS7.2配置LNMP环境记录
  • 罗辑思维在全链路压测方面的实践和工作笔记
  • 微信小程序设置上一页数据
  • 我的面试准备过程--容器(更新中)
  • 项目管理碎碎念系列之一:干系人管理
  • 云栖大讲堂Java基础入门(三)- 阿里巴巴Java开发手册介绍
  • JavaScript 新语法详解:Class 的私有属性与私有方法 ...
  • 格斗健身潮牌24KiCK获近千万Pre-A轮融资,用户留存高达9个月 ...
  • 机器人开始自主学习,是人类福祉,还是定时炸弹? ...
  • 专访Pony.ai 楼天城:自动驾驶已经走过了“从0到1”,“规模”是行业的分水岭| 自动驾驶这十年 ...
  • ​iOS实时查看App运行日志
  • # 服务治理中间件详解:Spring Cloud与Dubbo
  • #Datawhale X 李宏毅苹果书 AI夏令营#3.13.2局部极小值与鞍点批量和动量
  • #我与Java虚拟机的故事#连载12:一本书带我深入Java领域
  • (02)Unity使用在线AI大模型(调用Python)
  • (3)选择元素——(17)练习(Exercises)
  • (pojstep1.1.2)2654(直叙式模拟)
  • (笔记)Kotlin——Android封装ViewBinding之二 优化
  • (超详细)语音信号处理之特征提取
  • (附源码)php投票系统 毕业设计 121500
  • (求助)用傲游上csdn博客时标签栏和网址栏一直显示袁萌 的头像
  • (三)模仿学习-Action数据的模仿
  • (一)C语言之入门:使用Visual Studio Community 2022运行hello world
  • (原创)boost.property_tree解析xml的帮助类以及中文解析问题的解决
  • (转)【Hibernate总结系列】使用举例
  • (转载)PyTorch代码规范最佳实践和样式指南
  • *setTimeout实现text输入在用户停顿时才调用事件!*
  • .mkp勒索病毒解密方法|勒索病毒解决|勒索病毒恢复|数据库修复
  • .NET 4.0网络开发入门之旅-- 我在“网” 中央(下)
  • .NET Framework杂记
  • .Net IOC框架入门之一 Unity