feat(todo页面): 优化待办列表滚动加载体验

1. 新增scroll-view高度动态计算适配不同设备
2. 将原scrolltolower触发改为滑动手势触发加载更多
3. 添加加载冷却期防止重复触发
4. 新增底部滑动提示动画
5. 重构加载更多逻辑并添加调试日志
This commit is contained in:
王文昊
2026-05-23 17:49:12 +08:00
parent eeff149c17
commit 07848446aa

View File

@@ -30,13 +30,15 @@
/> />
<!-- 列表内容 --> <!-- 列表内容 -->
<scroll-view <scroll-view
scroll-y scroll-y
class="list-container" class="list-container"
:style="{ height: scrollViewHeight + 'px' }"
:refresher-enabled="true" :refresher-enabled="true"
:refresher-triggered="refreshing" :refresher-triggered="refreshing"
@refresherrefresh="onRefresh" @refresherrefresh="onRefresh"
@scrolltolower="onLoadMore" @scroll="onScroll"
lower-threshold="100"
> >
<!-- 待贴标签列表 --> <!-- 待贴标签列表 -->
<view v-if="activeTab === 'label'" class="coil-list"> <view v-if="activeTab === 'label'" class="coil-list">
@@ -67,12 +69,17 @@
<uni-load-more status="loading" /> <uni-load-more status="loading" />
</view> </view>
<!-- 加载更多 --> <!-- 加载更多提示 -->
<view v-if="activeTab === 'label' && list.length > 0" class="load-more-wrapper" @click="onLoadMore"> <view v-if="activeTab === 'label' && list.length > 0 && list.length < total" class="load-more-wrapper">
<uni-load-more <view v-if="isAtBottom && canLoadMore" class="bottom-hint">
:status="loadMoreStatus" <text class="hint-text"> 向上滑动加载更多</text>
:content-text="{ contentdown: '点击加载更多', contentrefresh: '加载中...', contentnomore: '没有更多了' }" </view>
/> <view v-else @click="onLoadMore">
<uni-load-more
:status="loadMoreStatus"
:content-text="{ contentdown: '点击加载更多', contentrefresh: '加载中...', contentnomore: '没有更多了' }"
/>
</view>
</view> </view>
</scroll-view> </scroll-view>
@@ -101,6 +108,7 @@ export default {
data() { data() {
return { return {
statusBarHeight: 0, statusBarHeight: 0,
scrollViewHeight: 0, // scroll-view 计算高度
tabs: [ tabs: [
{ key: 'label', label: '待贴标签', badge: 0 }, { key: 'label', label: '待贴标签', badge: 0 },
{ key: 'inspect', label: '检验任务', badge: 0 }, { key: 'inspect', label: '检验任务', badge: 0 },
@@ -111,6 +119,10 @@ export default {
list: [], list: [],
loading: false, loading: false,
refreshing: false, refreshing: false,
loadMoreCooldown: false, // 加载更多冷却期标志
isAtBottom: false, // 是否滑动到底部
canLoadMore: false, // 是否可以加载更多(需要再次向上滑动)
lastScrollTop: 0, // 上次滚动位置
query: { query: {
pageNum: 1, pageNum: 1,
pageSize: 10, pageSize: 10,
@@ -129,10 +141,18 @@ export default {
// 获取状态栏高度 // 获取状态栏高度
const systemInfo = uni.getSystemInfoSync() const systemInfo = uni.getSystemInfoSync()
this.statusBarHeight = systemInfo.statusBarHeight || 0 this.statusBarHeight = systemInfo.statusBarHeight || 0
// 计算 scroll-view 高度
this.calcScrollViewHeight()
},
onReady() {
// 页面就绪后再次计算高度(确保布局完成)
this.$nextTick(() => {
this.calcScrollViewHeight()
})
}, },
computed: { computed: {
loadMoreStatus() { loadMoreStatus() {
if (this.loading) return 'loading' if (this.loading || this.loadMoreCooldown) return 'loading'
if (this.list.length >= this.total) return 'noMore' if (this.list.length >= this.total) return 'noMore'
return 'more' return 'more'
} }
@@ -141,6 +161,36 @@ export default {
this.fetchList() this.fetchList()
}, },
methods: { methods: {
// 计算 scroll-view 高度
calcScrollViewHeight() {
const systemInfo = uni.getSystemInfoSync()
const windowHeight = systemInfo.windowHeight
const statusBarHeight = systemInfo.statusBarHeight || 0
// 各区域高度单位px
const navBarHeight = 44 // 导航栏高度
const tabBarHeight = 50 // Tab栏高度
const filterBarHeight = 60 // 筛选栏高度(留有余量)
const bottomSafeArea = systemInfo.safeAreaInsets?.bottom || 0 // 底部安全区域
const tabBarOffset = 50 // 底部TabBar高度
// 计算 scroll-view 可用高度确保最小高度为200px
let calcHeight = windowHeight - statusBarHeight - navBarHeight - tabBarHeight - filterBarHeight - bottomSafeArea - tabBarOffset
this.scrollViewHeight = Math.max(calcHeight, 200)
console.log('scroll-view 高度计算:', {
windowHeight,
statusBarHeight,
navBarHeight,
tabBarHeight,
filterBarHeight,
bottomSafeArea,
tabBarOffset,
calcHeight,
scrollViewHeight: this.scrollViewHeight
})
},
// 切换Tab // 切换Tab
handleTabChange(key) { handleTabChange(key) {
this.activeTab = key this.activeTab = key
@@ -212,6 +262,11 @@ export default {
} finally { } finally {
this.loading = false this.loading = false
this.refreshing = false this.refreshing = false
// 延迟解除冷却期,防止立即再次触发加载
setTimeout(() => {
this.loadMoreCooldown = false
console.log('加载冷却期结束')
}, 500)
} }
}, },
@@ -248,21 +303,97 @@ export default {
this.fetchList() this.fetchList()
}, },
// 加载更多 // 滚动事件处理
onLoadMore() { onScroll(e) {
console.log('触发加载更多', { const scrollTop = e.detail.scrollTop
const scrollHeight = e.detail.scrollHeight
const clientHeight = this.scrollViewHeight
// 防止无效值
if (!scrollHeight || !clientHeight) return
// 判断是否滑动到底部(距离底部小于 150px
const distanceToBottom = scrollHeight - scrollTop - clientHeight
const isNearBottom = distanceToBottom < 150
// 判断滑动方向(增加最小变化阈值,防止微小抖动)
const scrollDelta = scrollTop - this.lastScrollTop
const isScrollingDown = scrollDelta > 3
const isScrollingUp = scrollDelta < -3
console.log('滚动事件:', {
scrollTop,
scrollHeight,
clientHeight,
distanceToBottom,
isNearBottom,
isScrollingDown,
isScrollingUp,
isAtBottom: this.isAtBottom,
canLoadMore: this.canLoadMore
})
// 如果滑动到底部,标记状态但不加载
if (isNearBottom && !this.isAtBottom) {
console.log('滑动到底部,等待再次向上滑动触发加载')
this.isAtBottom = true
this.canLoadMore = true
}
// 如果已经到底部,并且用户开始向上滑动,触发加载
if (this.isAtBottom && this.canLoadMore && isScrollingUp) {
console.log('底部后向上滑动,触发加载更多')
this.canLoadMore = false
this.doLoadMore()
}
// 如果离开底部区域,重置状态
if (!isNearBottom && this.isAtBottom) {
console.log('离开底部区域')
this.isAtBottom = false
this.canLoadMore = false
}
// 只有滑动方向明确时才更新 lastScrollTop
if (Math.abs(scrollDelta) > 3) {
this.lastScrollTop = scrollTop
}
},
// 实际执行加载更多
doLoadMore() {
console.log('执行加载更多', {
listLength: this.list.length, listLength: this.list.length,
total: this.total, total: this.total,
loading: this.loading, loading: this.loading,
loadMoreCooldown: this.loadMoreCooldown,
pageNum: this.query.pageNum pageNum: this.query.pageNum
}) })
if (this.list.length >= this.total || this.loading) {
console.log('加载更多被阻止:已到最后一页或正在加载中') // 防抖保护:如果正在加载、冷却期或已到最后一页,直接返回
if (this.list.length >= this.total || this.loading || this.loadMoreCooldown) {
console.log('加载更多被阻止:已到最后一页、正在加载中或冷却期内')
return return
} }
this.query.pageNum++
console.log('加载第', this.query.pageNum, '页') // 设置冷却期,防止连续触发
this.fetchList(true) this.loadMoreCooldown = true
// 增加短暂延迟,防止 scrolltolower 立即重复触发
if (this._loadMoreTimer) {
clearTimeout(this._loadMoreTimer)
}
this._loadMoreTimer = setTimeout(() => {
this.query.pageNum++
console.log('加载第', this.query.pageNum, '页')
this.fetchList(true)
}, 150)
},
// 加载更多(点击按钮时调用)
onLoadMore() {
this.doLoadMore()
}, },
// 重贴标签 // 重贴标签
@@ -397,9 +528,10 @@ export default {
} }
.list-container { .list-container {
flex: 1;
padding: 20rpx; padding: 20rpx;
box-sizing: border-box; box-sizing: border-box;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
.coil-list { .coil-list {
padding-bottom: 40rpx; padding-bottom: 40rpx;
@@ -410,12 +542,37 @@ export default {
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
&:active { &:active {
opacity: 0.7; opacity: 0.7;
} }
} }
.bottom-hint {
padding: 30rpx 0 50rpx;
display: flex;
justify-content: center;
align-items: center;
.hint-text {
font-size: 28rpx;
color: #1a73e8;
font-weight: 500;
animation: pulse 1.5s ease-in-out infinite;
}
}
@keyframes pulse {
0%, 100% {
opacity: 0.5;
transform: translateY(0);
}
50% {
opacity: 1;
transform: translateY(-3rpx);
}
}
.placeholder-page { .placeholder-page {
display: flex; display: flex;
flex-direction: column; flex-direction: column;