基于 uni-app + <movable-view>拖拽实现的标签排序-适用于微信小程序、H5等多端

在实际业务中,我们经常遇到「标签排序」或「菜单调整」的场景。微信小程序原生的 movable-view 为我们提供了一个简单、高效的拖拽能力,结合 Vue3 + uni-app 的组合,我们可以实现一个体验良好的标签管理界面

核心组件<movable-view><movable-area>

1.每项数据结构加入 y 坐标

javascript 复制代码
const itemHeight = uni.upx2px(110); // 每项高度,决定拖动步长

function initDragg(list) {
  return list.map((item, index) => ({
    ...item,
    y: index * itemHeight, // 初始位置
  }));
}

2. 拖动状态管理变量

javascript 复制代码
const canDrag = ref(false);          // 当前是否允许拖动(长按才激活)
const draggingIndex = ref(-1);       // 当前拖动的 item 的 index

3.用户手指按下某项

javascript 复制代码
<image
  @touchstart="canDrag = true"
  :src="getSpecImgUrl('sort_set')"
/>

4.进入拖动状态

javascript 复制代码
function onTouchStart(index) {
  if (!canDrag.value) return;
  draggingIndex.value = index;
}

5.拖动过程中(自动触发)

javascript 复制代码
function onChange(e, index) {
  const dy = e.detail.y;
  const targetIndex = Math.round(dy / itemHeight);
  
  if (targetIndex !== index && targetIndex >= 0 && targetIndex < groupList.value.length) {
    // 拖动项移到新位置
    const moved = groupList.value.splice(index, 1)[0];
    groupList.value.splice(targetIndex, 0, moved);
    draggingIndex.value = targetIndex;
  } else {
    // 仅移动视觉,不换位置
    groupList.value[index].y = dy;
  }
}

6.拖动结束

javascript 复制代码
function onTouchEnd() {
  if (!canDrag.value) return;
  draggingIndex.value = -1;

  // 所有项重置为标准位置
  groupList.value.forEach((item, i) => {
    item.y = i * itemHeight;
  });

  // 同步顺序到后端
  sortLabelList(groupList.value);
  canDrag.value = false;
}

完整代码测试代码

javascript 复制代码
<template>
  <scroll-view scroll-y style="height: 100vh">
    <movable-area style="height: 1000rpx; width: 100vw; position: relative">
      <block v-for="(item, index) in list" :key="item.id">
        <movable-view
          direction="vertical"
          damping="50"
          inertia
          :y="item.y"
          :style="getStyle(index)"
          @touchstart="onTouchStart(index)"
          @touchend="onTouchEnd"
          @change="onChange($event, index)"
        >
          <view class="item">
            {{ item.name }}
          </view>
        </movable-view>
      </block>
    </movable-area>
  </scroll-view>
</template>

<script setup>
  import { ref, reactive, onMounted } from 'vue';
  import { debounce } from '@/utils/util';

  const itemHeight = 100; // 每项高度,单位 rpx(需配合实际样式调整)

  const list = reactive([
    { id: 1, name: '任务一', y: 0 },
    { id: 2, name: '任务二', y: 100 },
    { id: 3, name: '任务三', y: 200 },
    { id: 4, name: '任务四', y: 300 },
  ]);

  let draggingIndex = ref(-1);

  function onTouchStart(index) {
    draggingIndex.value = index;
  }

  function onTouchEnd(e, index) {
    console.log(444);
    draggingIndex.value = -1;
    // 重置 Y 防止漂移
    list.forEach((item, i) => {
      item.y = i * itemHeight;
    });
  }

  // 拖拽过程中计算是否需要交换
  function onChange(e, index) {
    // const bounce = debounce(foo,500);
    // bounce()
    const dy = e.detail.y;
    const targetIndex = Math.round(dy / itemHeight);
    console.log(2222,e,index,targetIndex);

    if (
      targetIndex !== index &&
      targetIndex >= 0 &&
      targetIndex < list.length
    ) {
      console.log(3333);
      // 交换数据
      const moved = list.splice(index, 1)[0];
      list.splice(targetIndex, 0, moved);

      // 重新设置 y 值
      // list.forEach((item, i) => {
      //   item.y = i * itemHeight;
      // });

      draggingIndex.value = targetIndex;
    } else {
      list[index].y = dy;
    }
  }

  function getStyle(index) {
    return `position: absolute; left: 0; width: 100%; height: ${itemHeight}rpx; z-index: ${
      draggingIndex.value === index ? 10 : 1
    };`;
  }
</script>

<style scoped>
  .item {
    background-color: #f1f1f1;
    margin: 10rpx;
    border-radius: 10rpx;
    text-align: center;
    line-height: 100rpx;
    box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.1);
  }
</style>
相关推荐
知识分享小能手8 小时前
微信小程序入门学习教程,从入门到精通,WXML(WeiXin Markup Language)语法基础(8)
前端·学习·react.js·微信小程序·小程序·vue·个人开发
王佳斌8 小时前
sass变量默认
uni-app
小咕聊编程8 小时前
【含文档+PPT+源码】基于微信小程序的在线考试与选课教学辅助系统
微信小程序·小程序·毕业设计·教学辅助系统
一匹电信狗9 小时前
【MySQL】数据库基础
linux·运维·服务器·数据库·mysql·ubuntu·小程序
知识分享小能手10 小时前
微信小程序入门学习教程,从入门到精通,微信小程序开发进阶(7)
前端·javascript·学习·程序人生·微信小程序·小程序·vue3
二饭11 小时前
uniapp与webview通信
uni-app
2501_9159090612 小时前
iOS 抓包工具有哪些?实战对比、场景分工与开发者排查流程
android·开发语言·ios·小程序·uni-app·php·iphone
软希网分享源码21 小时前
WIFI大师小程序4.1.9独立版源码(源码下载)
小程序·wifi大师小程序
说私域21 小时前
互联网新热土视角下开源AI大模型与S2B2C商城小程序的县域市场渗透策略研究
人工智能·小程序·开源
小咕聊编程21 小时前
【含文档+PPT+源码】基于微信小程序的关爱老年人在线能力评估系统
微信小程序·小程序·毕业设计·老人能力评估