父组件:
<view class="padding" style="height: calc(100vh - 150px);" >
<drag-list
:itemHeight="itemHeight"
:list="navList"
@change="sortChange">
<template slot-scope="{ item }">
<view class="drag-item">
<view style="text-align: center;" >
{{ item.name }}
</view>
</view>
</template>
</drag-list>
</view>
<script>
import dragList from '../../components/gzz-drag/drag-list.vue';
export default {
components: {
dragList
},
data() {
return {
itemHeight: 96,
scrollHeight: 0,
startY: 0,
currentIndex: -1
}
},
onLoad(options) {
if(options.navs) {
this.navList = JSON.parse(decodeURIComponent(options.navs));
}
},
methods: {
sortChange(navList){
this.navList = navList.map((item, index) => {
item.name = item.title
item.index = index
return item
});
},
async saveSort() {
uni.$emit('navSortUpdated', this.navList)
// todo 调用接口,保存navList
// uni.showToast({
// title: '排序已保存',
// icon: 'success',
// duration: 1500
// });
setTimeout(() => {
uni.navigateBack({
delta: 1,
success: () => {
console.log('返回成功');
}
});
}, 1600);
}
}
}
</script>
子组件:
<template>
<view class="drag-box">
<view
v-for="(item,index) in dataList"
:key="index"
:style="{
top: item.top +'px',
height: (itemHeight - 1)+'rpx',
marginBottom: '16px'
}"
class="drag-item"
:class="{'drag-active': item.isActive}"
@longtap="longtap(item)"
@touchstart="touchstart"
@touchmove.stop.prevent="touchmove"
@touchend="touchend(item)">
<slot :item="item"></slot>
</view>
</view>
</template>
<script>
export default {
props: {
list: {
type: Array,
default: () => ([])
},
itemHeight: {
type: [Number],
default: 70
},
gap: {
type: Number,
default: 16
}
},
data() {
return {
activeItem: null,
isDrag: false,
dragTargetY: 0,
dataList: [],
sortIndexList: [],
}
},
watch: {
list: {
immediate: true,
deep: true,
handler(list) {
this.setList(list)
}
}
},
methods: {
touchstart(e) {
this.dragTargetY = e.touches[0].pageY;
},
longtap(item) {
this.activeItem = item;
this.isDrag = true;
item.isActive = true;
},
touchmove(e) {
if(!this.isDrag) return;
const newY = e.touches[0].pageY;
const d = newY - this.dragTargetY;
this.activeItem.top += d;
const currentIndex = this.sortIndexList[this.activeItem.index];
const prevItem = this.getItemByIndex(currentIndex - 1);
const nextItem = this.getItemByIndex(currentIndex + 1);
if (prevItem && d < 0) {
const threshold = prevItem.top + this.rowHeight + this.gap;
if (this.activeItem.top < threshold) {
this.swapItems(prevItem);
}
} else if (nextItem && d > 0) {
const threshold = nextItem.top - this.rowHeight - this.gap;
if (this.activeItem.top > threshold) {
this.swapItems(nextItem);
}
}
this.dragTargetY = newY;
},
touchend(item) {
if(!this.isDrag) return;
this.isDrag = false;
item.isActive = false;
this.activeItem.top = this.sortIndexList[item.index] * (this.rowHeight + this.gap);
const sortedList = this.dataList
.sort((a, b) => this.sortIndexList[a.index] - this.sortIndexList[b.index])
.map(item => {
const { isActive, top, index, ...rest } = item;
return rest;
});
this.$emit('change', sortedList);
},
getItemByIndex(index) {
const target = this.dataList.find(
item => this.sortIndexList[item.index] === index
);
return target || null;
},
swapItems(targetItem) {
const currentIndex = this.sortIndexList[this.activeItem.index];
const targetIndex = this.sortIndexList[targetItem.index];
this.sortIndexList[this.activeItem.index] = targetIndex;
this.sortIndexList[targetItem.index] = currentIndex;
targetItem.top = currentIndex * (this.rowHeight + this.gap);
},
setList(list) {
this.dataList = list.map((item, index) => ({
...item,
isActive: false,
top: index * (this.rowHeight + this.gap),
index: index
}));
this.sortIndexList = list.map((_, index) => index);
}
},
computed: {
rowHeight() {
const res = uni.getSystemInfoSync();
return this.itemHeight * res.screenWidth / 750;
}
}
}
</script>
<style lang="less">
.drag-box {
position: relative;
width: 100%;
height: 100%;
.drag-item {
position: absolute;
width: 100%;
line-height: 48px;
text-align: center;
background-color: #fff;
border-radius: 12px;
box-shadow: 0 4px 16px 0 rgba(94, 139, 255, 0.08);
border-bottom: 1rpx solid #F5F5F5;
transition: all 0.3s ease;
box-sizing: border-box;
z-index: 1;
&.drag-active {
z-index: 9;
transform: scale(1.05);
box-shadow: 0 8px 20px 0 #e6e6e6;
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
}
}
</style>