Vue项目中实现拖拽排序效果-demo

news2024/12/26 21:28:00

在Vue3中实现拖拽排序,可以借助一些浏览器自带的API,以及一些Vue3的特性:

使用<template>标签中的v-for指令渲染出一个列表,每个列表项绑定一个draggable属性,使其能够被拖拽。

<template>
  <ul>
    <li v-for="(item, index) in list" :key="item.id" :draggable="true" @dragstart="dragStart(index)">
      {{ item.title }}
    </li>
  </ul>
</template>

<script>标签中,定义一个list数组,用于存储待排序的数据。同时,定义一个dragIndex变量,记录当前正在拖拽的元素的索引位置。

<script>
import { reactive } from 'vue';

export default {
  setup() {
    const state = reactive({
      list: [
        { id: 1, title: 'Item 1' },
        { id: 2, title: 'Item 2' },
        { id: 3, title: 'Item 3' },
        { id: 4, title: 'Item 4' },
        { id: 5, title: 'Item 5' }
      ],
      dragIndex: null
    });

    const dragStart = (index) => {
      state.dragIndex = index;
    }

    return { state, dragStart };
  }
}
</script>

使用@dragenter@dragover事件处理函数,阻止默认行为,避免无法放置拖拽元素。

<template>
  <ul>
    <li v-for="(item, index) in state.list" :key="item.id" :draggable="true" @dragstart="dragStart(index)" @dragenter.prevent @dragover.prevent>
      {{ item.title }}
    </li>
  </ul>
</template>

@dragenter事件处理函数中,获取当前拖拽元素的索引位置,以及目标元素的索引位置。根据两个索引位置的大小关系,判断拖拽元素是否需要往前移动或往后移动,同时更新list数组的顺序。

const dragEnter = (index) => {
  if (state.dragIndex !== null && state.dragIndex !== index) {
    // 计算拖拽元素和目标元素的位置关系
    const dragItem = state.list[state.dragIndex];
    const targetItem = state.list[index];
    const isAfter = state.dragIndex < index;

    // 更新列表顺序
    state.list.splice(state.dragIndex, 1);
    state.list.splice(isAfter ? index - 1 : index, 0, dragItem);
    state.dragIndex = isAfter ? index : index - 1;
  }
};

<template>标签中,绑定@dragenter事件,同时定义一个方法,将当前目标元素的索引位置作为参数传递给该方法。

<template>
  <ul>
    <li v-for="(item, index) in state.list" :key="item.id" :draggable="true" @dragstart="dragStart(index)" @dragenter.prevent @dragover.prevent @dragenter="dragEnter(index)">
      {{ item.title }}
    </li>
  </ul>
</template>

这样,就可以实现Vue3的拖拽排序功能了。完整代码如下:

<template>
  <ul>
    <li v-for="(item, index) in state.list" :key="item.id" :draggable="true" @dragstart="dragStart(index)" @dragenter.prevent @dragover.prevent @dragenter="dragEnter(index)">
      {{ item.title }}
    </li>
  </ul>
</template>

<script>
import { reactive } from 'vue';

export default {
  setup() {
    const state = reactive({
      list: [
        { id: 1, title: 'Item 1' },
        { id: 2, title: 'Item 2' },
        { id: 3, title: 'Item 3' },
        { id: 4, title: 'Item 4' },
        { id: 5, title: 'Item 5' }
      ],
      dragIndex: null
    });

    const dragStart = (index) => {
      state.dragIndex = index;
    }

    const dragEnter = (index) => {
      if (state.dragIndex !== null && state.dragIndex !== index) {
        const dragItem = state.list[state.dragIndex];
        const targetItem = state.list[index];
        const isAfter = state.dragIndex < index;
        state.list.splice(state.dragIndex, 1);
        state.list.splice(isAfter ? index - 1 : index, 0, dragItem);
        state.dragIndex = isAfter ? index : index - 1;
      }
    };

    return { state, dragStart, dragEnter };
  }
}
</script>

HTML5 新增的可拖拽属性 

draggable 属性是 HTML5 新增的可拖拽属性

HTML 中,除了图像、链接和选择的文本默认可拖拽外,其他元素默认是不可拖拽的。如果想让其他元素变成可拖拽的,首先需要把 draggable 属性设置为 true

<p draggable="true"> 可拖拽draggable</p>

拖拽元素的事件

事件触发时机
dragstart开始拖拽时执行 1 次
drag拖拽开始后多次触发
dragend拖动结束后触发 1 次

可释放目标的事件

事件触发时机
dragenter拖拽元素进入可释放目标时执行 1 次
dragover拖拽元素进入可释放目标时触发多次(100毫秒触发一次)
drop拖拽元素进入可释放目标内释放时(设置了dragover此事件才会生效)

可放置目标

dragenter 或 dragover事件可用于表示有效的放置目标,也就是被拖拽元素可能放置的地方。

设置允许被被放置还需要阻止 dragenter 和 dragover 事件的默认处理。

<div ondragenter="event.preventDefault()">
  1. 创建一个列表,遍历渲染到页面
  2. 列表项添加 draggable="true"
  3. 列表项添加事件 dragstart dragenter dragend dragover
  4. dragenter 事件中,需要传入列表项的下标,实时进行元素的排序。排序的核心逻辑也是在 dragenter
  5. 代码执行的逻辑是:列表项拖拽到可放置目标时,将该拖拽的元素从原位置删除,再将拖拽的元素插入到当前可放置目标的位置

 

<template>
  <div>
    <TransitionGroup name="list" tag="div" class="container">
      <div
        class="item"
        v-for="(item, i) in drag.list"
        :key="item.id"
        :draggable="true"
        @dragstart="dragstart($event, i)"
        @dragenter="dragenter($event, i)"
        @dragend="dragend"
        @dragover="dragover"
      >
        {{ item.name }}
      </div>
    </TransitionGroup>
  </div>
</template>
<script setup>
import { reactive } from 'vue';
const drag = reactive({
  list: [
    { name: 'a', id: 1 },
    { name: 'b', id: 2 },
    { name: 'c', id: 3 },
    { name: 'd', id: 4 },
    { name: 'e', id: 5 }
  ]
});

let dragIndex = 0;

function dragstart(e, index) {
  e.stopPropagation();
  dragIndex = index;
  setTimeout(() => {
    e.target.classList.add('moveing');
  }, 0);
}
function dragenter(e, index) {
  e.preventDefault();
  // 拖拽到原位置时不触发
  if (dragIndex !== index) {
    const source = drag.list[dragIndex];
    drag.list.splice(dragIndex, 1);
    drag.list.splice(index, 0, source);

    // 更新节点位置
    dragIndex = index;
  }
}
function dragover(e) {
  e.preventDefault();
  e.dataTransfer.dropEffect = 'move';
}
function dragend(e) {
  e.target.classList.remove('moveing');
}
</script>

<style lang="scss" scoped>
.item {
  width: 200px;
  height: 40px;
  line-height: 40px;
  // background-color: #f5f6f8;
  background-color: skyblue;
  text-align: center;
  margin: 10px;
  color: #fff;
  font-size: 18px;
}

.container {
  position: relative;
  padding: 0;
}

.moveing {
  opacity: 0;
}

.list-move, /* 对移动中的元素应用的过渡 */
    .list-enter-active,
    .list-leave-active {
  transition: all 0.2s ease;
}
</style>

sortable.js-配置文档

Element Plus 组件库中使用 sortable.js 进行表格排序

npm i sortablejs -S
<template>
<div>
        <el-table :data="tableData" id="dragTable" border style="width: 800px;">
                <el-table-column prop="date" label="Date" width="180" />
                <el-table-column prop="name" label="Name" width="180" />
                <el-table-column prop="address" label="Address" />
        </el-table>
</div>
</template>

<script setup>
const tableData = [
	{
		date: '2016-05-03',
		name: 'Tom',
		address: 'No. 189, Grove St, Los Angeles',
	},
	{
		date: '2016-05-02',
		name: 'Cilly',
		address: 'No. 189, Grove St, Los Angeles',
	},
	{
		date: '2016-05-04',
		name: 'Linda',
		address: 'No. 189, Grove St, Los Angeles',
	},
	{
		date: '2016-05-01',
		name: 'John',
		address: 'No. 189, Grove St, Los Angeles',
	},
]
</script>

导入 sortable.js 

<script setup>
import Sortable from 'sortablejs'
import { onMounted } from 'vue'

function setSort() {
    const el = document.querySelector('#dragTable table tbody')
    new Sortable(el, {
    sort: true,
		ghostClass: 'sortable-ghost',
		onEnd: (e) => {
			const targetRow = tableData.splice(e.oldIndex, 1)[0]
			tableData.splice(e.newIndex, 0, targetRow)
			console.log(tableData)
		},
	})
}
onMounted(() => {
	setSort()
})

const tableData = [
 // ...
]
</script>

在 onMounted 中,也就是组件挂载完成之后,实例化 Sortable(),传入要进行拖拽排序的节点 el 和其它一些配置参数。现在可以进行拖拽了。

可能需要按住拖动图标才可以进行拖动,需要添加 handle 配置,并指定对应的样式名

<el-table :data="tableData" id="dragTable" border style="width: 600px; margin: 20px">
      <!-- ...... 省略代码 -->
      <el-table-column label="操作" width="100">
        <template #default>
          <div class="handle-drag">
            <el-icon>
              <Sort />
            </el-icon>
          </div>
        </template>
      </el-table-column>
    </el-table>

上面代码将表格添加了一个操作列,并将操作列的图标设置一个样式类。下面的配置表示只有包含.handle-drag 样式的元素才可以被拖动。其他位置不能被拖动

new Sortable(el, {
    // ...
    handle: '.handle-drag',
    // ...
  })


vuedraggable-配置文档-推荐

vue.draggable.next 是 Vue3 的拖拽组件,是基于 Sortable.js 实现的。可以用于拖拽列表、菜单、工作台、选项卡等常见的场景。

npm i -S vuedraggable@next

属性 

参数说明类型默认值
value用于实现拖拽的list,通常和内部v-for循环的数组为同一数组Arraynull
list效果同value的。和v-model不能共用Arraynull
tagdraggable 标签在渲染后展现出来的标签类型Stringdiv
optionsdraggable 列表配置项Objectnull
emptyInsertThreshold拖动时,鼠标必须与空的可排序对象之间的距离Number5
clone返回值为true时克隆,可以理解为正常的拖拽变成了复制。当pull:'clone时的拖拽的回调函数’Function无处理
move如果不为空,这个函数将以类似于Sortable onMove回调的方式调用。返回false将取消拖动操作。Functionnull
componentData用来结合UI组件的,可以理解为代理了UI组件的定制信息Objectnull

注意:vuedraggable新版本废弃了options属性,建议使用v-bind属性作为配置项

options配置项

参数说明类型
group用于分组,同一组的不同list可以相互拖动String/Array
sort定义是否可以拖拽Boolean
delay定义鼠标选中列表单元可以开始拖动的延迟时间Number
disabled定义是否此sortable对象是否可用Boolean
animation动画时间 单位:msNumber
handle使列表单元中符合选择器的元素成为拖动的手柄,只有按住拖动手柄才能使列表单元进行拖动Selector
filter定义哪些列表单元不能进行拖放,可设置为多个选择器,中间用“,”分隔Selector
preventOnFilter当拖动filter时是否触发event.preventDefault() 默认触发Boolean
draggable定义哪些列表单元可以进行拖放Selector
ghostClass当拖动列表单元时会生成一个副本作为影子单元来模拟被拖动单元排序的情况,此配置项就是来给这个影子单元添加一个classSelector
chosenClass目标被选中时添加Selector
dragClass目标拖动过程中添加Selector
forceFallback如果设置为true时,将不使用原生的html5的拖放,可以修改一些拖放中元素的样式等Boolean
fallbackClass:当forceFallback设置为true时,拖放过程中鼠标附着单元的样式String
dataIdAttrdata-idSelector
scroll当排序的容器是个可滚动的区域,拖放可以引起区域滚动Boolean
scrollFn用于自定义滚动条的适配Function(offsetX, offsetY, originalEvent, touchEvt, hoverTargetEl)
ScrollSensitivity就是鼠标靠近边缘多远开始滚动默认30Number
scrollSpeed滚动速度Number

事件

参数说明回调参数
start开始拖动时的回调函数function({to,from,item,clone,oldIndex,newIndex})
add添加单元时的回调函数function({to,from,item,clone,oldIndex,newIndex})
remove单元被移动到另一个列表时的回调函数function({to,from,item,clone,oldIndex,newIndex})
update排序发生变化时的回调函数function({to,from,item,clone,oldIndex,newIndex})
end拖动结束时的回调函数function({to,from,item,clone,oldIndex,newIndex})
choose选择单元时的回调函数function({to,from,item,clone,oldIndex,newIndex})
sort排序发生变化时的回调函数function({to,from,item,clone,oldIndex,newIndex})
filter尝试选择一个被filter过滤的单元的回调函数function({to,from,item,clone,oldIndex,newIndex})
cloneclone时的回调函数function({to,from,item,clone,oldIndex,newIndex})

插槽

页眉或页脚插槽都不能与 tarnstion-group 一起使用。

Header
使用标题插槽在vuedraggable组件中添加不可拖动的元素。它应该与draggable选项一起使用来标记draggable元素。请注意,无论标题槽在模板中的位置如何,它总是被添加到默认槽之前。

<draggable v-model="myArray" draggable=".item">
    <div v-for="element in myArray" :key="element.id" class="item">
        {{element.name}}
    </div>
    <button slot="header" @click="addPeople">Add</button>
</draggable>

Footer
使用页脚槽在vuedraggable组件中添加不可拖动的元素。它应该与draggable选项一起使用,以标记draggable元素。请注意,无论页脚槽在模板中的位置如何,它都将始终添加到默认槽之后。

<draggable v-model="myArray" draggable=".item">
    <div v-for="element in myArray" :key="element.id" class="item">
        {{element.name}}
    </div>
    <button slot="footer" @click="addPeople">Add</button>
</draggable>

使用代码

<script setup>
import draggable from 'vuedraggable'
import { reactive } from 'vue'

const state = reactive({
	list1: [1, 2, 3, 4],
	list2: ['a', 'b', 'c', 'd'],
})

function onStart() {}

function onEnd() {
	console.log(state)
}

</script>

导入 draggable并定义一些基础数据

<template>
	<div style="margin-left: 30px;">
		<draggable
			:list="state.list1"
			:force-fallback="true"
			chosen-class="chosen"
			animation="300"
			@start="onStart"
			@end="onEnd"
		>
			<template #item="{ element }">
				<div class="item">
					{{ element }}
				</div>
			</template>
		</draggable>
	</div>
</template>

其中 @start 和 @end 为拖拽开始和结束时的事件。chosen-class 为拖拽时的样式

为组件设置相同的 group 属性,可以实现在不同的块之间拖拽

<draggable group="group" :list="state.list1" >
			<template #item="{ element }">
				<div class="item bck1">
					{{ element }}
				</div>
			</template>
		</draggable>

		<draggable group="group" :list="state.list2" >
			<template #item="{ element }">
				<div class="item bck2">
					{{ element }}
				</div>
			</template>
		</draggable>

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

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

相关文章

一次I/O操作的过程

什么是IO呢&#xff1f; IO&#xff0c;英文全称是Input/Output&#xff0c;翻译过来就是输入/输出。平时我们听得挺多&#xff0c;就是什么磁盘IO&#xff0c;网络IO。那IO到底是什么呢&#xff1f;是不是有种懵懵懂懂的感觉呀&#xff0c;好像大概知道它是什么&#xff0c;又…

ARIMA模型

1、简介 ARIMA模型(Autoregressive Integrated Moving Average model)&#xff0c;差分整合移动平均自回归模型&#xff0c;又称整合移动平均自回归模型&#xff0c;时间序列预测分析方法之一。ARIMA(p,d,q)中&#xff0c;AR是"自回归"&#xff0c;p为自回归项数;MA…

二叉树题目:最大层内元素和

文章目录 题目标题和出处难度题目描述要求示例数据范围 解法一思路和算法代码复杂度分析 解法二思路和算法代码复杂度分析 题目 标题和出处 标题&#xff1a;最大层内元素和 出处&#xff1a;1161. 最大层内元素和 难度 4 级 题目描述 要求 给定一个二叉树的根结点 ro…

zookeeper可视化工具ZooInspector用法

最近在做银行的项目&#xff0c;用到了thrift&#xff0c;rpc和zookeeper&#xff0c;所有应用都是注册到zookeeper上的&#xff0c;想知道哪些应用注册上了&#xff0c;就用到ZooInspector这个可视化的工具。 1&#xff0c;下载 链接&#xff1a;https://issues.apache.org/…

flex弹性盒模型与阿里图标的使用

华子目录 flex布局flex布局原理flex使用三要素 阿里图标&#xff08;字体&#xff09; flex布局 相关学习网站&#xff1a;http://c.biancheng.net/css3/flex.html 1.flex是当前最主流的布局方式&#xff1a;用它布局起来更方便&#xff0c;取代了浮动的作用。 2.浮动布局有缺…

BUG:阿里巴巴图标库引入链接后,icon有时候会不显示的话svg下载到本地使用

忽然icon图标就不显示&#xff0c;但是代码、icon链接地址都没有发生变化 解决办法&#xff1a;将icon图标下载到本地&#xff0c;记住前后引用本地的名字要保持一致

学习jQuery库的第一天

简介 什么是 jQuery &#xff1f; jQuery 是一个广泛使用的 JavaScript 库。它简化了网页开发中常见的许多任务&#xff0c;例如 HTML 文档遍历、操作 HTML 元素、处理事件、动画效果、Ajax 网络请求等。通过使用 jQuery&#xff0c;开发人员可以更加高效地编写跨浏览器兼容的…

如何制作一个成功的超市购物小程序

随着互联网的普及和移动支付的便捷性&#xff0c;越来越多的消费者选择在网上购物&#xff0c;这也促使越来越多的商家开始搭建自己的小程序商城。对于超市便利店来说&#xff0c;拥有一个便捷、易用的小程序商城能够吸引更多的消费者&#xff0c;提高销售效率。那么如何快速搭…

MyBatis初级

文章目录 一、mybatis1、概念2、JDBC缺点2.1、之前jdbc操作2.2 、原始jdbc操作的分析 3、mybatis的使用3.1、导入maven依赖3.2、新建表3.3、实体类3.4、编写mybatis的配置文件3.5、编写接口 和 映射文件3.6、编写测试类3.7、注意事项 4、代理方式开发5、mybatis和spring整合5.1…

数学实验-最佳分数近似值(Mathematica实现)

一、实验名称&#xff1a;最佳分数近似值 二、实验环境&#xff1a;Mathematica 10.3软件 三、实验目的&#xff1a;研究怎样用分数近似值去对给定的无理数作最佳逼近&#xff0c;“最佳”就是既要误差小&#xff0c;又要分母小。我们首先需要对“最佳”定出具体而明确的标准…

动态IP代理是什么?一文看懂动态代理IP

一、什么是动态IP代理&#xff1f; 动态IP代理是一种代理服务&#xff0c;而动态IP是由ISP动态分配给用户的IP地址&#xff0c;这些IP地址会周期性地更改。每次链接互联网时&#xff0c;用户会被分配一个新的IP地址&#xff0c;因而也称为”轮换IP”。 IP地址轮换是一个过程&…

Roreg复现

一、roreg复现 [github链接][参考] 1.1 报错1 Traceback (most recent call last): File "setup.py", line 52, in <module> import torch File "/home/w/anaconda3/envs/pty_roreg/lib/python3.7/site-packages/torch/__init__.py", line …

公司中一个好的管理者应该是什么样的?

一个好的管理者&#xff0c;不仅在公司中发挥着至关重要的作用&#xff0c;而且能够对组织的成功和员工的幸福产生深远的影响。那么&#xff0c;一个好的管理者应该具备哪些特质和技能呢&#xff1f;以下是我根据现有研究和经验总结出来的一些观点。 强大的领导力&#xff1a;…

【干货超全】国内外常见的Ai大模型汇总!!!

国内外常见的Ai大模型汇总: 点击文字即可跳转 1&#xff09;国内 讯飞星火大模型 https://xinghuo.xfyun.cn 百度文心一言 https://yiyan.baidu.com 阿里通义千问 https://tongyi.aliyun.com 华为盘古大模型 https://pangu.huaweicloud.com/ 清华智谱清言链接&#xff1…

卖课不挣钱,为什么还要卖?背后原因你绝对想不到

#职场经验谈# 大家好&#xff0c;这里是程序员晚枫&#xff0c;全网同名。 这几年一直在维护一个开源项目&#xff1a;python-office&#xff0c;GitHub和gitee都能搜索到。 我今晚算了一下&#xff0c;在维护这个开源项目的过程中&#xff0c;通过技术答疑、制作课程、发布广…

观测云接入 NewRelic .NET 探针

背景 部分客户系统采用的是.NET 4.5 部署研发的、基于 IIS 进行发布的 Web 项目&#xff0c;需要接入到观测云进行链路信息展示&#xff0c;ddtrace 和 otel 对于低版本.NET 支持力度有限。 环境信息 IIS 4.0.NET core 4.0、4.5、4.6Window Server 2012 R2 域名配置 准备一…

释放数据价值这道难题,Smartbi V11有解

《未来简史》预言&#xff1a;数据将成为人们未来的信仰。 未来已来&#xff0c;将至已至。如今&#xff0c;数据所扮演的角色与作用超乎想象。从政府将数据要素列入生产要素之中&#xff0c;到数据驱动型业务场景涌现&#xff0c;企业与组织对于数据及其价值的认可度明显提升…

【皇帝的新装】从不一样的视角看OKR

OKR是“目标和关键成果”&#xff0c;是企业进行目标管理的一个简单有效的系统&#xff0c;能够将目标管理自上而下贯穿到基层&#xff1b;为什么公司和个人都要用OKR&#xff1f; 一、Why 我们分两点来谈“为什么要用OKR”&#xff0c;一个点是“我们公司&#xff08;老板&am…

【Linux】—— 在Linux上进行读写文件操作

前言&#xff1a; 在之前&#xff0c;我已经对进程的相关知识进行了详细的介绍。本期开始&#xff0c;我们将要学习的是关于 “基础I/O”的知识&#xff01;&#xff01;&#xff01; 目录 &#xff08;一&#xff09;C文件接口 &#xff08;二&#xff09;系统文件I/O 1、接…

git中无法使用方向键的问题

windows下使用git命令行执行react脚本安装&#xff0c;发现无法使用上下键来去选中选项。最后只能换成cmd命令执行&#xff0c;发现可以上下移动以选中需要的选项。 bash命令行&#xff1a;移动光标无法移动选项 cmd命令行