Appearance
列表
版本说明
时间 | 修改人 | 备注 |
---|---|---|
2025-04-24 | YG | 补充小程序scroll-view 用法 |
列表需要可以下拉刷新
、上拉加载
。
示例项目:
svn://39.107.13.238:12580/base_digi
/_uniapp/src/pages/demo/list/list.vue
一、基础规范
列表样式
- 列表项高度应保持一致,避免高度不一致导致的视觉跳动
- 列表项之间应有明确的分割线或间距,提高可读性
- 列表项内容应对齐,保持视觉上的整齐
- 列表项点击态应明确,提供用户反馈
- 列表为空时应显示空状态提示,而非空白页面
列表性能
- 列表项应避免过度嵌套,减少渲染层级
- 列表图片应懒加载,减少初始加载时间
- 列表数据应分页加载,避免一次性请求大量数据
二、交互规范
下拉刷新
- 需要有下拉即可刷新、释放即可刷新、加载中、加载成功的提示
- 只有在滚动到最顶部才能下拉刷新
- 需要有过渡动画
- 刷新时应显示加载指示器,并禁用其他操作
- 刷新完成后应有成功提示,并自动消失
- 刷新失败时应有错误提示,并提供重试选项
上拉加载
- 需要有上拉即可加载、释放即可加载、加载中、加载成功的提示
- 只有在滚动到最底部才能上拉加载
- 需要有过渡动画
- 加载完成后应无缝衔接新内容,避免视觉跳动
- 所有数据加载完毕时,应显示"没有更多了"提示
- 加载失败时应有错误提示,并提供重试选项
三、实现方式
1. h5实现
vue
import List from "@/components/list/index.vue";
<List ref="list" :openLoad="openLoad" @refresh="loadTop" @load="loadBottom">
/*列表内容*/
</List>
说明
参数openLoad
控制是否开启上拉加载,当列表没有全部加载,openLoad 置为 true,滑动到底部时上拉触发上拉加载;当列表全部加载,openLoad 置为 false,上拉时会显示没有更多了
; 事件refresh
,下拉刷新触发; 事件load
,上拉加载触发。
注: 请求完数据需要调用this.$refs.list.endUpate()
结束加载(刷新)动画。
Props
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
openLoad | 是否开启上拉加载 | boolean | false |
maxRemoving | 可拉动的最大的距离,单位:rpx | number | 200 |
allLoadHint | 全部加载完,上拉时显示的文案 | string | 没有更多了 |
refreshTime | 刷新时动画时间,为 0 永久存在,可通过触发 endUpate()关闭 | number | 0 |
loadTime | 加载时动画时间,为 0 永久存在,可通过触发 endUpate()关闭 | number | 0 |
Events
事件名 | 说明 | 回调参数 |
---|---|---|
refresh | 下拉刷新触发 | - |
load | 上拉加载触发 | - |
方法
方法名 | 说明 | 参数 | 返回值 |
---|---|---|---|
endUpate | 结束加载(刷新)动画 | - | - |
- 注:列表的宽高设置的 100%,由父元素大小决定。加载和刷新也是在父元素里。
2. UniApp原生实现
vue
<template>
<view class="list-container">
<scroll-view
class="scroll-view"
scroll-y
refresher-enabled
:refresher-triggered="isRefreshing"
@refresherrefresh="onRefresh"
@scrolltolower="onLoadMore"
>
<!-- 列表内容 -->
<view class="list-item" v-for="(item, index) in list" :key="index">
<text>{{ item.title }}</text>
</view>
<!-- 加载更多状态 -->
<view class="loading-more" v-if="list.length > 0">
<text v-if="isLoadingMore">加载中...</text>
<text v-else-if="hasMore">上拉加载更多</text>
<text v-else>已加载全部</text>
</view>
<!-- 空状态 -->
<n-empty-data v-if="!isLoading && list.length === 0" />
</scroll-view>
</view>
</template>
<script setup>
import { ref, onMounted } from 'vue';
// 列表数据
const list = ref([]);
// 状态控制
const isLoading = ref(true);
const isRefreshing = ref(false);
const isLoadingMore = ref(false);
const hasMore = ref(true);
const page = ref(1);
const pageSize = ref(10);
// 初始加载
onMounted(() => {
loadData();
});
// 加载数据
const loadData = async () => {
try {
const res = await fetchData(page.value, pageSize.value);
list.value = res.data;
hasMore.value = res.hasMore;
} catch (error) {
uni.showToast({
title: '加载失败',
icon: 'none'
});
} finally {
isLoading.value = false;
isRefreshing.value = false;
}
};
// 下拉刷新
const onRefresh = async () => {
isRefreshing.value = true;
page.value = 1;
await loadData();
uni.showToast({
title: '刷新成功',
icon: 'success',
duration: 1000
});
};
// 上拉加载更多
const onLoadMore = async () => {
if (isLoadingMore.value || !hasMore.value) return;
isLoadingMore.value = true;
page.value += 1;
try {
const res = await fetchData(page.value, pageSize.value);
list.value = [...list.value, ...res.data];
hasMore.value = res.hasMore;
} catch (error) {
page.value -= 1;
uni.showToast({
title: '加载失败',
icon: 'none'
});
} finally {
isLoadingMore.value = false;
}
};
// 模拟数据请求
const fetchData = (page, pageSize) => {
return new Promise((resolve) => {
setTimeout(() => {
const total = 35;
const start = (page - 1) * pageSize;
const end = Math.min(start + pageSize, total);
const data = [];
for (let i = start; i < end; i++) {
data.push({
id: i + 1,
title: `列表项 ${i + 1}`
});
}
resolve({
data,
hasMore: end < total
});
}, 1000);
});
};
</script>
<style lang="scss" scoped>
.list-container {
position: relative;
height: 100vh;
.scroll-view {
height: 100%;
}
.list-item {
padding: 30rpx;
border-bottom: 1rpx solid #eee;
}
.loading-more {
padding: 20rpx 0;
text-align: center;
color: #999;
font-size: 24rpx;
}
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 100rpx 0;
image {
width: 200rpx;
height: 200rpx;
margin-bottom: 20rpx;
}
text {
color: #999;
font-size: 28rpx;
}
}
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
</style>
注意事项:
- 要注意不能一开始就展示缺省页,要先展示加载的loading,如果没有数据在展示缺省页。