更新记录
1.0.1(2025-09-26)
- 添加拖拽开始事件拦截功能
- 添加item-click事件处理
- 为默认网格项添加删除按钮样式配置
1.0.0(2025-09-26)
v1.0.0
平台兼容性
uni-app(4.66)
Vue2 |
Vue3 |
Chrome |
Safari |
app-vue |
app-nvue |
Android |
iOS |
鸿蒙 |
× |
- |
√ |
√ |
√ |
- |
√ |
√ |
√ |
微信小程序 |
支付宝小程序 |
抖音小程序 |
百度小程序 |
快手小程序 |
京东小程序 |
鸿蒙元服务 |
QQ小程序 |
飞书小程序 |
快应用-华为 |
快应用-联盟 |
√ |
√ |
√ |
√ |
√ |
√ |
√ |
√ |
√ |
√ |
√ |
liaction-grid-drag
基于Vue 3的可拖拽网格组件,提供灵活的拖拽排序、项目管理功能,适用于UniApp项目。
特性
- 支持两种拖拽模式:移位(
shift
)和交换(swap
)
- 可自定义网格布局(列数、间距、内边距等)
- 支持固定项目(不可拖拽)
- 内置删除功能,支持确认对话框
- 支持添加项目功能
- 丰富的样式自定义选项
- 响应式设计,自适应窗口大小
- 提供完整的事件回调机制
- 支持项目级别的样式覆盖
基本用法
<template>
<view class="container">
<liaction-grid-drag
:items="gridItems"
:columns="3"
:draggable="true"
:drag-mode="'shift'"
:show-delete-button="true"
:show-add-slot="true"
:useCustomDeleteModal="useCustomDeleteModal"
@sortEnd="onSortEnd"
@add-item="onAddItem"
@delete-item="onDeleteItem"
@item-click="onItemClick"
@show-delete-modal="onShowDeleteModal"
>
<template #item="{ item, index }">
<view style="width: 100%; height: 100%; display: flex; align-items: center; justify-content: center;">
<image :src="item.icon" mode="aspectFit" style="width: 50%; height: 50%;" />
<text>{{ item.name }}</text>
</view>
</template>
</liaction-grid-drag>
</view>
</template>
<script>
export default {
data() {
return {
gridItems: [
{ id: '1', name: '项目1', icon: '/static/icons/1.png' },
{ id: '2', name: '项目2', icon: '/static/icons/2.png' },
{ id: '3', name: '项目3', icon: '/static/icons/3.png' },
{ id: '4', name: '项目4', icon: '/static/icons/4.png' },
{ id: '5', name: '项目5', icon: '/static/icons/5.png' }
],
useCustomDeleteModal: false
}
},
methods: {
onSortEnd(items) {
this.gridItems = items;
console.log('排序结果:', items);
},
onAddItem() {
const newItem = {
id: Date.now().toString(),
name: `新项目${this.gridItems.length + 1}`,
icon: `/static/icons/${Math.floor(Math.random() * 9) + 1}.png`
};
this.gridItems.push(newItem);
},
onDeleteItem({ item, index }) {
this.gridItems.splice(index, 1);
},
onItemClick({ item, index }) {
console.log('点击了项目:', item, '索引:', index);
},
onShowDeleteModal({ item, index, confirm, cancel }) {
// 显示自定义删除确认弹窗
// 用户确认后调用 confirm()
// 用户取消后调用 cancel()
}
}
}
</script>
<style>
.container {
padding: 20px;
}
</style>
Props
属性名 |
类型 |
默认值 |
说明 |
columns |
Number |
3 |
网格列数,必须为正整数 |
items |
Array |
[] |
网格项目数据数组 |
gap |
Number |
10 |
项目间距(像素),必须为非负数 |
padding |
Number |
10 |
容器内边距(像素),必须为非负数 |
dragMode |
String |
'shift' |
拖拽模式,可选值:'shift'(移位)、'swap'(交换) |
draggable |
Boolean |
false |
是否允许拖拽 |
aspectRatio |
Number |
1 |
项目宽高比,必须大于0 |
showDeleteButton |
Boolean |
false |
是否显示删除按钮 |
showAddSlot |
Boolean |
false |
是否显示添加按钮 |
maxItemCount |
Number |
Infinity |
最大项目数量限制 |
maxHeight |
Number |
Infinity |
容器最大高度(像素) |
minHeight |
Number |
100 |
容器最小高度(像素) |
useDefaultItems |
Boolean |
false |
是否使用默认项目数据 |
defaultItemCount |
Number |
9 |
默认项目数量 |
defaultItemsDraggable |
Boolean |
true |
默认项目是否可拖拽 |
defaultItemsDeletable |
Boolean |
true |
默认项目是否可删除 |
useCustomDeleteModal |
Boolean |
false |
是否使用自定义删除弹窗 |
样式相关属性
属性名 |
类型 |
默认值 |
说明 |
itemBgColor |
String |
'#409eff' |
项目背景色 |
fixedItemBgColor |
String |
'#909399' |
固定项目背景色 |
addSlotBgColor |
String |
'#f0f2f5' |
添加按钮背景色 |
itemTextColor |
String |
'white' |
项目文本颜色 |
addSlotTextColor |
String |
'#606266' |
添加按钮文本颜色 |
itemBorderRadius |
Number |
8 |
项目圆角(像素) |
itemTextSize |
Number |
14 |
项目文本大小(像素) |
itemTextWeight |
String/Number |
'normal' |
项目文本粗细 |
itemTextFontFamily |
String |
'sans-serif' |
项目文本字体 |
itemTextStyle |
Object |
{} |
项目文本额外样式 |
删除按钮相关属性
属性名 |
类型 |
默认值 |
说明 |
deleteBtnSize |
Number |
24 |
删除按钮大小(像素) |
deleteBtnBgColor |
String |
'#ff0000' |
删除按钮背景色 |
deleteBtnTextColor |
String |
'#fff' |
删除按钮文本颜色 |
deleteBtnFontSize |
Number |
12 |
删除按钮字体大小 |
deleteBtnText |
String |
'×' |
删除按钮文本 |
deleteBtnMaxDigitsLength |
Number |
2 |
数字类型删除按钮文本最大长度 |
deleteBtnMaxLength |
Number |
1 |
非数字类型删除按钮文本最大长度 |
删除确认对话框属性
属性名 |
类型 |
默认值 |
说明 |
deleteConfirmTitle |
String |
'提示' |
确认对话框标题 |
deleteConfirmMessage |
String |
'确定要删除这个项目吗?' |
确认对话框消息 |
deleteConfirmConfirmText |
String |
'确定' |
确认按钮文本 |
deleteConfirmCancelText |
String |
'取消' |
取消按钮文本 |
滚动条相关属性
属性名 |
类型 |
默认值 |
说明 |
scrollbarWidth |
Number |
6 |
滚动条宽度(像素),必须大于等于2 |
scrollbarTrackColor |
String |
'#f5f5f5' |
滚动条轨道颜色 |
scrollbarThumbColor |
String |
'#ccc' |
滚动条滑块颜色 |
scrollbarThumbHoverColor |
String |
'#aaa' |
滚动条滑块悬停颜色 |
scrollbarRadius |
Number |
3 |
滚动条圆角(像素) |
Item 数据结构
每个网格项目可以包含以下属性:
属性名 |
类型 |
说明 |
id |
String/Number |
项目唯一标识 |
name |
String |
项目名称(默认显示文本) |
draggable |
Boolean |
该项是否可拖拽(覆盖全局设置) |
deletable |
Boolean |
该项是否可删除 |
fixed |
Boolean |
该项是否固定(固定项不可拖拽) |
deleteBtnSize |
Number |
该项删除按钮大小(覆盖全局设置) |
deleteBtnBgColor |
String |
该项删除按钮背景色(覆盖全局设置) |
deleteBtnTextColor |
String |
该项删除按钮文本颜色(覆盖全局设置) |
deleteBtnFontSize |
Number |
该项删除按钮字体大小(覆盖全局设置) |
deleteBtnText |
String |
该项删除按钮文本(覆盖全局设置) |
deleteBtnMaxDigitsLength |
Number |
该项数字类型删除按钮文本最大长度(覆盖全局设置) |
deleteBtnMaxLength |
Number |
该项非数字类型删除按钮文本最大长度(覆盖全局设置) |
其他自定义属性 |
任意 |
自定义数据,可在插槽中使用 |
事件
事件名 |
说明 |
回调参数 |
sortEnd |
排序完成时触发 |
items: 排序后的项目数组 |
sort-complete |
排序完成时触发(与sortEnd相同,兼容性别名) |
items: 排序后的项目数组 |
update:items |
项目数组更新时触发 |
items: 更新后的项目数组 |
add-item |
点击添加按钮时触发 |
无 |
delete-item |
确认删除项目时触发 |
{ item: 删除的项目, index: 删除的索引 } |
delete-confirmed |
确认删除项目时触发(与delete-item相同,用于明确区分确认状态) |
{ item: 删除的项目, index: 删除的索引 } |
delete-canceled |
取消删除项目时触发 |
{ item: 取消删除的项目, index: 项目索引 } |
drag-start |
开始拖拽时触发 |
{ item: 拖拽的项目, index: 项目索引 } |
drag-end |
结束拖拽时触发 |
{ item: 拖拽的项目, index: 项目索引 } |
item-click |
点击项目时触发 |
{ item: 点击的项目, index: 项目索引 } |
show-delete-modal |
使用自定义删除弹窗时触发(需要设置useCustomDeleteModal为true) |
{ item: 要删除的项目, index: 项目索引, confirm: 确认回调, cancel: 取消回调 } |
插槽
插槽名 |
说明 |
插槽参数 |
item |
自定义项目内容 |
{ item: 项目数据, index: 项目索引, isDragging: 是否正在拖拽, isDraggable: 是否可拖拽 } |
delete-button |
自定义删除按钮 |
{ item: 项目数据, index: 项目索引 } |
addSlot |
自定义添加按钮 |
无 |
部分示例
基本用法
<liaction-grid-drag
:useDefaultItems="true"
/>

固定项目示例
<template>
<liaction-grid-drag
:items="gridItems"
:columns="4"
:draggable="true"
:show-delete-button="true"
/>
</template>
<script>
export default {
data() {
return {
gridItems: [
{ id: '1', name: '可拖拽项目', draggable: true },
{ id: '2', name: '固定项目', fixed: true }, // 通过fixed属性固定项目
{ id: '3', name: '可拖拽项目2', draggable: true },
{ id: '4', name: '不可删除', deletable: false }
]
}
}
}
</script>

自定义项目样式
<template>
<liaction-grid-drag :items="gridItems" :columns="3" :gap="15" itemBgColor="#67c23a" itemTextColor="#fff"
itemBorderRadius="12">
<template #item="{ item, isDragging }">
<view class="custom-item" :class="{ 'dragging': isDragging }">
<image :src="item.image" mode="aspectFit" class="item-image" />
<text class="item-title">{{ item.title }}</text>
<text class="item-desc">{{ item.description }}</text>
</view>
</template>
</liaction-grid-drag>
</template>
<script>
export default {
data() {
return {
gridItems: [
{
image: '/static/icons/1.png',
title: 'Item 1',
description: 'Description 1'
},
{
image: '/static/icons/2.png',
title: 'Item 2',
description: 'Description 2'
},
{
image: '/static/icons/3.png',
title: 'Item 3',
description: 'Description 3'
},
{
image: '/static/icons/4.png',
title: 'Item 4',
description: 'Description 4'
},
{
image: '/static/icons/5.png',
title: 'Item 5',
description: 'Description 5'
}
],
}
}
}
</script>
<style scoped>
.custom-item {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 10px;
box-sizing: border-box;
}
.item-image {
width: 60px;
height: 60px;
margin-bottom: 8px;
}
.item-title {
font-weight: bold;
margin-bottom: 4px;
}
.item-desc {
font-size: 12px;
text-align: center;
}
.dragging {
opacity: 0.8;
}
</style>

常见问题与解决方案
问题:拖拽时项目位置不正确
解决方法:确保没有在样式中覆盖组件内部的定位样式,特别是movable-view元素的定位属性。
问题:固定项目被意外移动
解决方法:检查项目数据中fixed属性是否正确设置,确保固定项目的draggable属性为false。
问题:滚动条样式不符合预期
解决方法:通过自定义滚动条相关props来调整样式,或者在全局样式中定义相应的滚动条样式覆盖。
问题:在小屏幕设备上拖拽不流畅
解决方法:可以适当增加DRAG_THRESHOLD值来减少计算频率,或者减少同时显示的项目数量。
注意事项
- 组件使用了Vue 3的Composition API,确保您的项目支持Vue 3
- 组件依赖UniApp的movable-area和movable-view组件,确保在非UniApp环境中适当适配
- 对于大量数据(如超过50项),可能需要优化性能或考虑虚拟列表
- 拖拽操作可能在某些低性能设备上有延迟,建议根据实际场景调整参数
- 在自定义样式时,避免直接修改组件内部的类名样式,建议使用插槽和自定义类
- 当设置
fixed: true
属性时,组件会自动将该项的draggable
属性设置为false
- 自定义删除弹窗功能需要同时设置
useCustomDeleteModal: true
并监听show-delete-modal
事件
- 组件内部使用
_uniqueKey
作为项目的唯一标识,在事件回调中会自动过滤掉该属性