mirror of
https://gitee.com/myxzgzs/boyue_jnpf.git
synced 2025-08-09 07:42:43 +08:00
254 lines
5.5 KiB
Vue
254 lines
5.5 KiB
Vue
![]() |
<template>
|
||
|
<view class="swipe-item-v">
|
||
|
<view v-for="(item, index) in listData" :key="index" class="list-item" @click="onItemClick(item)"
|
||
|
:style="{'margin-bottom':marginB+'rpx'}">
|
||
|
<view class="swipe-container" :style="{'border-radius':borderR+'rpx'}">
|
||
|
<view class="content-wrapper" :style="{ transform: `translateX(${item.offsetX}px)` }"
|
||
|
@touchstart="!item.swipeAction && handleTouchStart($event, index)"
|
||
|
@touchmove="!item.swipeAction && handleTouchMove($event, index)"
|
||
|
@touchend="!item.swipeAction && handleTouchEnd(index)">
|
||
|
<!-- 使用插槽渲染自定义内容 -->
|
||
|
<slot :item="item" :index="index"></slot>
|
||
|
</view>
|
||
|
<view class="action-buttons" :style="{ width: `${actionWidth}px` }">
|
||
|
<view v-for="(btn, btnIndex) in buttonsData" :key="btnIndex" class="btn" :style="{
|
||
|
background: btn.style.backgroundColor,
|
||
|
opacity: (item.swipeAction) ? 0.5 : 1
|
||
|
}" @tap="!item.swipeAction && handleButtonClick(btn.value, item, index,btn)">
|
||
|
<text class="btn-text">{{ btn.text }}</text>
|
||
|
<text v-if="btn.icon" class="iconfont" :class="btn.icon"></text>
|
||
|
</view>
|
||
|
</view>
|
||
|
</view>
|
||
|
</view>
|
||
|
</view>
|
||
|
</template>
|
||
|
|
||
|
<script>
|
||
|
/*
|
||
|
列表数据
|
||
|
[{
|
||
|
id: 1,
|
||
|
title: '列表项 1',
|
||
|
offsetX: 0,
|
||
|
startX: 0,
|
||
|
startY: 0,
|
||
|
isHorizontal: false,
|
||
|
swipeAction: false
|
||
|
}]
|
||
|
滑动操作按钮数据
|
||
|
[{
|
||
|
text: '编辑',
|
||
|
icon: 'icon-edit',
|
||
|
value: 'edit',
|
||
|
style:{
|
||
|
backgroundColor: '#007aff' // 添加背景色配置
|
||
|
}
|
||
|
}]
|
||
|
*/
|
||
|
export default {
|
||
|
name: 'SwipeAction',
|
||
|
props: {
|
||
|
// 列表数据
|
||
|
list: {
|
||
|
type: Array,
|
||
|
default: () => []
|
||
|
},
|
||
|
// 按钮配置
|
||
|
buttons: {
|
||
|
type: Array,
|
||
|
default: () => []
|
||
|
},
|
||
|
// 唯一标识字段
|
||
|
rowKey: {
|
||
|
type: String,
|
||
|
default: 'id'
|
||
|
},
|
||
|
marginB: {
|
||
|
type: [String, Number],
|
||
|
default: '0'
|
||
|
},
|
||
|
borderR: {
|
||
|
type: [String, Number],
|
||
|
default: '0'
|
||
|
}
|
||
|
},
|
||
|
|
||
|
data() {
|
||
|
return {
|
||
|
listData: [], // 处理后的列表数据
|
||
|
buttonsData: [], // 处理后的按钮数据
|
||
|
currentOpenIndex: -1, // 当前打开的item索引
|
||
|
actionWidth: 0, // 操作按钮总宽度
|
||
|
buttonWidth: 160
|
||
|
}
|
||
|
},
|
||
|
|
||
|
watch: {
|
||
|
list: {
|
||
|
handler(newVal) {
|
||
|
// 为列表数据添加必要的属性
|
||
|
this.listData = newVal.map(item => ({
|
||
|
...item,
|
||
|
offsetX: 0,
|
||
|
startX: 0,
|
||
|
startY: 0,
|
||
|
isHorizontal: false
|
||
|
}));
|
||
|
},
|
||
|
immediate: true
|
||
|
},
|
||
|
buttons: {
|
||
|
handler(newVal) {
|
||
|
this.buttonsData = newVal;
|
||
|
this.calculateActionWidth();
|
||
|
},
|
||
|
immediate: true
|
||
|
}
|
||
|
},
|
||
|
|
||
|
methods: {
|
||
|
// 计算操作按钮总宽度
|
||
|
calculateActionWidth() {
|
||
|
this.actionWidth = this.buttonsData.length * this.buttonWidth / 2;
|
||
|
},
|
||
|
|
||
|
// 点击整行
|
||
|
onItemClick(item) {
|
||
|
this.$emit('click', item);
|
||
|
},
|
||
|
|
||
|
// 按钮点击事件
|
||
|
handleButtonClick(value, item, index, btn) {
|
||
|
this.$emit('action', {
|
||
|
value,
|
||
|
item,
|
||
|
index,
|
||
|
btn
|
||
|
});
|
||
|
// 操作完成后收起
|
||
|
this.closeSwipe(index);
|
||
|
},
|
||
|
|
||
|
// 关闭滑动
|
||
|
closeSwipe(index) {
|
||
|
if (index === undefined) {
|
||
|
// 关闭所有
|
||
|
this.listData.forEach(item => item.offsetX = 0);
|
||
|
this.currentOpenIndex = -1;
|
||
|
} else {
|
||
|
// 关闭指定项
|
||
|
this.listData[index].offsetX = 0;
|
||
|
this.currentOpenIndex = -1;
|
||
|
}
|
||
|
},
|
||
|
|
||
|
// 触摸开始
|
||
|
handleTouchStart(e, index) {
|
||
|
const item = this.listData[index];
|
||
|
item.startX = e.touches[0].clientX;
|
||
|
item.startY = e.touches[0].clientY;
|
||
|
item.isHorizontal = false;
|
||
|
|
||
|
// 关闭其他打开的项
|
||
|
if (this.currentOpenIndex !== -1 && this.currentOpenIndex !== index) {
|
||
|
this.closeSwipe(this.currentOpenIndex);
|
||
|
}
|
||
|
},
|
||
|
|
||
|
// 触摸移动
|
||
|
handleTouchMove(e, index) {
|
||
|
const item = this.listData[index];
|
||
|
const deltaX = e.touches[0].clientX - item.startX;
|
||
|
const deltaY = Math.abs(e.touches[0].clientY - item.startY);
|
||
|
if (!item.isHorizontal) {
|
||
|
if (deltaY > 5) return;
|
||
|
if (Math.abs(deltaX) > 5) item.isHorizontal = true;
|
||
|
}
|
||
|
if (item.isHorizontal) {
|
||
|
const validDeltaX = Math.min(0, deltaX);
|
||
|
const maxOffset = -this.actionWidth;
|
||
|
item.offsetX = Math.max(maxOffset, validDeltaX);
|
||
|
e.preventDefault();
|
||
|
}
|
||
|
},
|
||
|
|
||
|
// 触摸结束
|
||
|
handleTouchEnd(index) {
|
||
|
const item = this.listData[index];
|
||
|
const threshold = this.actionWidth * 0.3;
|
||
|
if (item.offsetX <= -threshold) {
|
||
|
item.offsetX = -this.actionWidth;
|
||
|
this.currentOpenIndex = index;
|
||
|
} else {
|
||
|
item.offsetX = 0;
|
||
|
this.currentOpenIndex = -1;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
</script>
|
||
|
|
||
|
<style scoped lang="scss">
|
||
|
.swipe-item-v {
|
||
|
.list-item {
|
||
|
|
||
|
.swipe-container {
|
||
|
position: relative;
|
||
|
overflow: hidden;
|
||
|
background: #fff;
|
||
|
|
||
|
.content-wrapper {
|
||
|
position: relative;
|
||
|
z-index: 2;
|
||
|
background: inherit;
|
||
|
transition: transform 0.3s ease;
|
||
|
|
||
|
.item-content {
|
||
|
padding: 40rpx;
|
||
|
background: #fff;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
.action-buttons {
|
||
|
position: absolute;
|
||
|
top: 0;
|
||
|
right: 0;
|
||
|
bottom: 0;
|
||
|
display: flex;
|
||
|
/* 增加宽度 */
|
||
|
z-index: 1;
|
||
|
|
||
|
.btn {
|
||
|
flex: 1;
|
||
|
display: flex;
|
||
|
align-items: center;
|
||
|
justify-content: center;
|
||
|
padding: 0 20rpx;
|
||
|
gap: 10rpx;
|
||
|
transition: opacity 0.3s ease;
|
||
|
|
||
|
&.disabled {
|
||
|
opacity: 0.5;
|
||
|
cursor: not-allowed;
|
||
|
}
|
||
|
|
||
|
.btn-text {
|
||
|
color: #fff;
|
||
|
font-size: 28rpx;
|
||
|
text-align: center;
|
||
|
word-break: break-all;
|
||
|
display: -webkit-box;
|
||
|
line-height: 1.3;
|
||
|
}
|
||
|
|
||
|
.iconfont {
|
||
|
color: #fff;
|
||
|
font-size: 32rpx;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
</style>
|