基于 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>
相关推荐
00后程序员张1 小时前
iOS软件性能监控实战指南 开发到上线的完整流程解析
android·ios·小程序·https·uni-app·iphone·webview
weixin_lynhgworld4 小时前
剧本杀小程序系统开发:构建数字化剧本杀生态圈
大数据·小程序·剧本杀
icebreaker4 小时前
weapp-tailwindcss 已支持 uni-app x 多端构建
前端·javascript·uni-app
落雪小轩韩16 小时前
微信小程序性能优化与内存管理
微信小程序
徐礼昭|商派软件市场负责人1 天前
从“多、老、旧”到“4i焕新”:品牌官方商城(小程序/官网/APP···)的范式跃迁与增长再想象
小程序·商城系统·零售
胡西风_foxww2 天前
微信小程序转Vue2组件智能提示词
微信小程序·小程序·提示词·智能体·vue2组件
七七软件开发2 天前
一对一交友小程序 / APP 系统架构分析
java·python·小程序·系统架构·php
2501_916007472 天前
iPhone查看App日志和系统崩溃日志的完整实用指南
android·ios·小程序·https·uni-app·iphone·webview
说私域2 天前
基于开源链动2+1模式AI智能名片S2B2C商城小程序的私域流量拉新策略研究
人工智能·小程序·开源
2501_915918412 天前
iOS 抓不到包怎么办?全流程排查思路与替代引导
android·ios·小程序·https·uni-app·iphone·webview