You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在v-for循环中使用VueUse/Nuxt useSwipe实现单条滑动删除

解决方案

核心思路

问题根源在于没有统一管理「展开状态」的全局变量,导致点击其他列表项时状态混乱,误触删除操作。我们需要用一个变量跟踪当前唯一展开的酒瓶ID,配合滑动逻辑和点击事件来精准控制删除按钮的显示/隐藏。

完整代码实现

模板部分

<template>
  <div class="bottle-list">
    <div 
      v-for="(bottle, index) in filteredBottles" 
      :key="bottle.bottleId"
      class="bottle-item"
      @click="handleItemClick(bottle.bottleId)"
      v-swipe="(direction, distance) => handleSwipe(bottle.bottleId, direction, distance)"
    >
      <div class="bottle-info">
        {{ bottle.bottle_name }}
      </div>
      <button 
        class="delete-btn"
        v-show="expandedBottleId === bottle.bottleId"
        @click.stop="deleteBottle(bottle.bottleId)"
      >
        Delete Bottle
      </button>
    </div>
  </div>
</template>

脚本部分(Vue 3 Composition API)

<script setup>
import { ref } from 'vue';
import useSwipe from './useSwipe'; // 替换为你的滑动钩子实际路径

// 模拟酒瓶数据(实际项目中替换为接口返回数据)
const filteredBottles = ref([
  { bottleId: 1, bottle_name: '波尔多赤霞珠' },
  { bottleId: 2, bottle_name: '勃艮第霞多丽' },
  { bottleId: 3, bottle_name: '香槟产区起泡酒' }
]);

// 跟踪当前展开的酒瓶ID,初始为null表示无展开项
const expandedBottleId = ref(null);

// 滑动事件处理
const handleSwipe = (bottleId, direction, distance) => {
  // 向右滑动超过50px时触发展开/收起逻辑
  if (direction === 'right' && distance > 50) {
    // 若已有其他展开项,先收起
    if (expandedBottleId.value && expandedBottleId.value !== bottleId) {
      expandedBottleId.value = null;
    }
    // 切换当前项的展开状态
    expandedBottleId.value = expandedBottleId.value === bottleId ? null : bottleId;
  } 
  // 向左滑动超过50px时强制收起所有
  else if (direction === 'left' && distance > 50) {
    expandedBottleId.value = null;
  }
};

// 点击列表项处理:点击非展开项时收起所有删除按钮
const handleItemClick = (bottleId) => {
  if (expandedBottleId.value !== bottleId) {
    expandedBottleId.value = null;
  }
};

// 删除酒瓶逻辑
const deleteBottle = (bottleId) => {
  // 从数组中移除对应酒瓶
  filteredBottles.value = filteredBottles.value.filter(b => b.bottleId !== bottleId);
  // 删除后重置展开状态,避免残留
  expandedBottleId.value = null;
};
</script>

样式参考(可选)

<style scoped>
.bottle-list {
  width: 100%;
  max-width: 600px;
  margin: 0 auto;
}
.bottle-item {
  position: relative;
  display: flex;
  align-items: center;
  padding: 1rem;
  border-bottom: 1px solid #eee;
  cursor: pointer;
  overflow: hidden;
}
.bottle-info {
  flex: 1;
}
.delete-btn {
  position: absolute;
  right: 0;
  top: 0;
  height: 100%;
  padding: 0 1.5rem;
  background-color: #ff4444;
  color: white;
  border: none;
  cursor: pointer;
  transition: transform 0.3s ease;
  /* 默认隐藏在容器右侧 */
  transform: translateX(100%);
}
/* 展开时显示按钮 */
.bottle-item:has(.delete-btn[style*="display: block"]) .delete-btn {
  transform: translateX(0);
}
</style>

关键细节说明

  1. 状态统一管理:expandedBottleId变量确保同一时间只有一个酒瓶项处于展开状态,避免多展开的混乱。
  2. 滑动逻辑控制:滑动时判断方向和距离,仅在符合条件时切换展开状态,同时自动关闭其他已展开项。
  3. 事件隔离处理:删除按钮使用@click.stop阻止事件冒泡,避免触发列表项的点击收起逻辑;点击非展开项时自动收起所有删除按钮。
  4. 删除后状态清理:删除酒瓶后立即重置expandedBottleId,防止状态残留导致后续交互异常。

关于useSwipe钩子的补充

如果你的useSwipe是自定义钩子,需确保它能返回滑动方向(left/right)和滑动距离,核心逻辑是监听touchstart/mousedown记录起始位置,touchmove/mousemove计算位移,touchend/mouseup判断最终滑动方向和距离。

内容的提问来源于stack exchange,提问作者Disdroido

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 11:00:21