基于 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>
相关推荐
2501_933907211 天前
宁波小程序开发服务与技术团队专业支持
科技·微信小程序·小程序
sheji34161 天前
【开题答辩全过程】以 基于微信小程序的少儿编程学习平台为例,包含答辩的问题和答案
学习·微信小程序·小程序
const_qiu1 天前
微信小程序自动化测试100%通过率实践
微信小程序·小程序
Greg_Zhong2 天前
小程序中登出/切换/重置页面状态的使用
小程序
你的眼睛會笑2 天前
uni-app 实战:使用 lime-painter 实现页面内容一键生成海报并下载
uni-app
一字白首2 天前
微信小程序进阶实战:从 UI 组件库到全局状态管理全解DAY05
ui·微信小程序·小程序
一只程序熊2 天前
uniapp 高德地图 打开选择地址报错,也没有展示出附近的位置
android·uni-app
海石2 天前
微信小程序开发02:原始人也能看懂的着色器与视频处理
前端·微信小程序·视频编码
li9056632802 天前
hanzi-writer-miniprogram Path2D问题以及Bug修复
微信小程序·bug
土土哥V_araolin2 天前
双迪大健康新零售系统开发要点
小程序·个人开发·零售