Skip to content

n-draggable 拖拽排序 ​

平台兼容性 ​

App-Android App-iOS App-Harmony Web 微信小程序
✓ ✓ ✓ ✓ ✓

使用说明 ​

列表与网格的拖拽排序:长按(默认)或触摸进入拖拽,拖拽项实时跟手,其余项按插入位置平滑让位,松手落位并回发重排后的新数组。支持单列列表与多列网格(columns)、方向限定(direction)、手柄触发(#handle 插槽)、逐项禁用(数据项 disabled 字段)与可关闭的九宫格编辑(closeable)。

html
<template>
	<n-draggable v-model="apps" :columns="2" :long-press="false">
		<template #item="{ item }">
			<view class="grid-item">
				<text class="grid-text">{{ item['text'] }}</text>
			</view>
		</template>
	</n-draggable>
</template>

<script setup lang="uts">
	import { ref } from "vue";
	import { type NDraggableItem } from "@/uni_modules/new-ui/components/n-draggable/props";

	const apps = ref<NDraggableItem[]>([
		{ id: 1, text: "微信" },
		{ id: 2, text: "支付宝", disabled: true },
		{ id: 3, text: "浏览器" },
		{ id: 4, text: "相册" }
	]);
</script>

拖拽触发两种方式:

  • 长按(默认 long-press 为 true):按住约 350ms 进入拖拽,适合页面可滚动的列表场景,不会与滚动冲突。
  • 触摸即拖(long-press 为 false):手指按下移动即拖拽,适合无滚动的九宫格编辑区。

数据项只读:排序与移除都在组件内部的数组副本上完成,传入的数组与项对象不会被就地修改;完成时通过 update:modelValue / change 回发重排后的新数组,直接赋回即可。

数据项可用字段(组件按约定读取):

字段 类型 说明
disabled boolean 为 true 时该项不可拖拽、呈禁用视觉

手柄触发:提供 #handle 插槽后,只有手柄区域可以触发拖拽(长按/触摸模式同样受 long-press 控制),适合行内容需要保留点击、滑动等交互的列表。

html
<n-draggable v-model="cities" direction="vertical">
	<template #item="{ item }">
		<view class="row-item">
			<text class="row-text">{{ item['name'] }}</text>
		</view>
	</template>
	<template #handle>
		<view class="row-handle">
			<n-icon name="drag" :size="16" color="#9ca3af" />
		</view>
	</template>
</n-draggable>

实现说明(机制对照) ​

机制 参考做法 本库做法 分歧理由
值通道 一参考 modelValue v-model,另一参考 list 单向属性 + change modelValue v-model + change 同参同发 v-model 是库内统一规则;排序完成整体回发新数组
数据项改写 参考直接向使用者数据项注入内部 uid 字段 数据项只读,排序在内部数组副本完成 根治参考改写使用者数据对象的副作用
行高来源 一参考逐项实测几何;另一参考固定 itemHeight(默认 60)近似 拖拽启动时逐项实测,让位位移取「新旧位置几何差值」 实测 + 差值在异构行高下同样正确,固定行高近似无存在价值,不收录 itemHeight
防误触 中心移出原项区域才启动排序预览 同左(sortingStarted 门槛) 轻触/微动不触发重排,两参考一致
插入判定 单列按纵向最近中心、网格按欧氏最近中心 同左;direction 显式限定时用对应轴 并集保留方向限定,并让它参与判定轴选择
松手落位 数组重排 + 位移清零,落位瞬间与内容重排同步完成 同左 让位过渡已提供过程动画,落位跳变与重排同帧发生,感知为「就位」
触摸事件绑定 参考将 touchmove 直接绑在项上 非微信端绑在项上、拖拽中才调用 preventDefault 阻断滚动;微信端「触摸即拖」变体编译为 catch 阻断、「长按」变体不阻断 列表项是页面滚动的常规起点,未拖拽时必须放行滚动;微信端运行时 preventDefault 不生效,只能编译期 catch,「长按 + 页面滚动」组合在微信端为已知限制(拖拽功能不受影响)
长按默认值 一参考默认 true、另一参考默认 false 默认 true 列表场景防误触优先;无滚动的九宫格由使用者显式传 false
项 key 小程序用下标、其他端用注入的 uid 三端统一用下标 样式与位移全部按下标声明式推导,下标 key 下节点内容随重排整体更新,行为确定且无注入字段
关闭钮 参考提供 closeable 显示关闭钮 + close 事件 点击后组件自行移除该项并回发新数组,同时发 close(index) 按参考演示行为落地,外部无需再操作数据
触觉反馈 参考经宿主全局事件触发震动 进入拖拽时调用 uni.vibrateShort 官方振动 API,三端可用;宿主自定义事件机制不通用

API ​

属性 ​

属性 类型 默认值 说明
model-value NDraggableItem[] [] 数据数组,供 v-model 使用;排序完成时组件回发重排后的新数组(原数组不被就地修改)
columns number 1 列数:1 为单列纵向列表,大于 1 时为网格布局(每项宽度 = 100/列数)
direction NDraggableDirection "all" 拖拽方向限定:all 自由、vertical 仅纵向、horizontal 仅横向;同时决定插入位置判定轴
long-press boolean true 是否长按触发拖拽;false 为触摸即拖
disabled boolean false 组件级禁用:整组不可拖拽、项呈禁用视觉
closeable boolean false 可关闭模式:每项右上角显示关闭钮,点击后该项从列表移除并回发新数组
custom-class string "" 组件根节点类名
custom-style string "" 组件根节点行内样式

事件 ​

事件 参数 说明
update:model-value (list: NDraggableItem[]) 排序完成或移除项时回发重排后的新数组,供 v-model 使用
change (list: NDraggableItem[]) 排序完成时触发,参数为重排后的新数组
start (index: number) 进入拖拽时触发,参数为拖拽项下标
end (index: number) 拖拽结束时触发,参数为落位下标(未换位时为原下标)
close (index: number) 点击项关闭钮时触发,参数为被移除项的原下标

插槽 ​

名称 参数 说明
item 项内容;dragging 为 true 且 index === dragIndex 时该项正在被拖拽
handle — 拖拽手柄;提供后仅手柄区域可触发拖拽
close — 关闭钮内容,覆盖默认的红底白叉圆钮

记录 · 思考 · 创作