如何在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>
关键细节说明
- 状态统一管理:
expandedBottleId变量确保同一时间只有一个酒瓶项处于展开状态,避免多展开的混乱。 - 滑动逻辑控制:滑动时判断方向和距离,仅在符合条件时切换展开状态,同时自动关闭其他已展开项。
- 事件隔离处理:删除按钮使用
@click.stop阻止事件冒泡,避免触发列表项的点击收起逻辑;点击非展开项时自动收起所有删除按钮。 - 删除后状态清理:删除酒瓶后立即重置
expandedBottleId,防止状态残留导致后续交互异常。
关于useSwipe钩子的补充
如果你的useSwipe是自定义钩子,需确保它能返回滑动方向(left/right)和滑动距离,核心逻辑是监听touchstart/mousedown记录起始位置,touchmove/mousemove计算位移,touchend/mouseup判断最终滑动方向和距离。
内容的提问来源于stack exchange,提问作者Disdroido
相关产品推荐
相关产品推荐

