更新记录
1.0.0(2026-06-02)
v1.0.0 2026-06-02 初始版本,支持两列瀑布流、一列横向布局、下拉刷新、上拉加载
v1.1.0 2026-06-02 新增列表项点击事件(itemclick)
v1.2.0 2026-06-02 新增自定义操作按钮 slot(#action),支持在列表项底部添加自定义按钮
v1.3.0 2026-06-02 优化布局,操作按钮与价格/销量在同一行左右排列
v1.4.0 2026-06-02 修复空图片路径导致的 404 错误,完善文档
平台兼容性
uni-app(3.7.8)
| Vue2 |
Vue3 |
Chrome |
Safari |
app-vue |
app-nvue |
Android |
iOS |
鸿蒙 |
| √ |
√ |
√ |
√ |
√ |
- |
√ |
√ |
√ |
| 微信小程序 |
支付宝小程序 |
抖音小程序 |
百度小程序 |
快手小程序 |
京东小程序 |
鸿蒙元服务 |
QQ小程序 |
飞书小程序 |
小红书小程序 |
快应用-华为 |
快应用-联盟 |
| √ |
√ |
√ |
√ |
√ |
√ |
√ |
√ |
√ |
√ |
- |
- |
其他
WaterfallFlow 瀑布流组件
一个基于 uni-app 的瀑布流组件,支持横向/竖向布局切换、上拉加载、下拉刷新、点击事件和自定义操作按钮。
特性
- ✅ 两列竖向瀑布流布局(默认)
- ✅ 一列横向滚动布局
- ✅ 支持布局切换
- ✅ 下拉刷新功能
- ✅ 上拉加载更多
- ✅ 图片自适应高度
- ✅ 支持 easycom 自动引入
- ✅ 列表项点击事件
- ✅ 自定义操作按钮(slot)
组件属性
| 属性名 |
类型 |
默认值 |
说明 |
| columnCount |
Number |
2 |
布局模式:1=横向布局,2=竖向瀑布流 |
| refreshEnabled |
Boolean |
true |
是否开启下拉刷新 |
| loadingEnabled |
Boolean |
true |
是否开启上拉加载 |
| threshold |
Number |
200 |
加载更多触发阈值(距离底部距离) |
组件事件
| 事件名 |
说明 |
参数 |
| refresh |
下拉刷新触发 |
- |
| loadmore |
上拉加载触发 |
- |
| scroll |
滚动时触发 |
滚动事件对象 |
| itemclick |
点击列表项触发 |
item - 当前点击的列表项数据 |
组件方法
| 方法名 |
说明 |
参数 |
| setData |
设置数据(覆盖原有数据) |
items: Array - 数据数组 |
| appendItems |
追加数据 |
items: Array - 新数据数组 |
| finishRefresh |
结束刷新状态 |
- |
| finishLoadmore |
结束加载状态 |
hasMore: Boolean - 是否还有更多数据 |
| clear |
清空数据 |
- |
组件 Slot
| Slot 名 |
说明 |
作用域 |
| action |
自定义操作按钮区域 |
item - 当前列表项数据 |
数据格式
{
id: Number, // 唯一标识(必填)
image: String, // 图片地址(可选,为空时显示占位符)
title: String, // 标题(必填)
desc: String, // 描述(可选)
price: String, // 价格(可选,如:¥9999)
sales: String // 销量(可选,如:月销 1200)
}
使用示例
基础用法
<template>
<WaterfallFlow
ref="waterfallRef"
:column-count="columnCount"
@refresh="onRefresh"
@loadmore="onLoadMore"
@itemclick="onItemClick"
>
<template #action="{ item }">
<view class="action-btn" @tap.stop="onActionClick(item)">
<text class="btn-text">立即购买</text>
</view>
</template>
</WaterfallFlow>
</template>
<script>
export default {
data() {
return {
columnCount: 2,
page: 1,
totalPage: 10
}
},
methods: {
onRefresh() {
// 刷新数据
this.page = 1
const newData = this.generateMockData(this.page)
this.$refs.waterfallRef.setData(newData)
this.$refs.waterfallRef.finishRefresh()
},
onLoadMore() {
// 加载更多
if (this.page >= this.totalPage) {
this.$refs.waterfallRef.finishLoadmore(false)
return
}
this.page++
const newData = this.generateMockData(this.page)
this.$refs.waterfallRef.appendItems(newData)
this.$refs.waterfallRef.finishLoadmore(this.page < this.totalPage)
},
onItemClick(item) {
// 点击列表项
uni.showToast({
title: `点击了:${item.title}`,
icon: 'none'
})
},
onActionClick(item) {
// 点击操作按钮
uni.showToast({
title: `购买:${item.title}`,
icon: 'none'
})
},
generateMockData(page) {
// 生成模拟数据
const mockProducts = [
{
id: 1,
title: 'Apple iPhone X 256GB 深空灰',
desc: '全面屏设计,面容ID解锁,A11芯片',
price: '¥9999',
sales: '月销 640',
image: 'https://picsum.photos/300/280'
},
{
id: 2,
title: 'HUAWEI P40 Pro 5G 麒麟990',
desc: '5000万超感知主摄,40W快充',
price: '¥6499',
sales: '月销 1200',
image: 'https://picsum.photos/300/350'
}
]
return mockProducts.map((item, index) => ({
...item,
id: (page - 1) * 2 + index + 1,
image: item.image ? `${item.image}?random=${(page - 1) * 2 + index}` : ''
}))
}
}
}
</script>
<style lang="scss" scoped>
.action-btn {
padding: 12rpx 24rpx;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 24rpx;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
.btn-text {
font-size: 24rpx;
color: #ffffff;
font-weight: 500;
}
}
</style>
easycom 配置
在 pages.json 中配置 easycom:
{
"easycom": {
"autoscan": true,
"custom": {
"WaterfallFlow": "@/components/WaterfallFlow/WaterfallFlow.vue"
}
}
}
布局切换
通过修改 columnCount 属性可以切换布局:
// 切换为横向布局(一列)
this.columnCount = 1
// 切换为竖向瀑布流(两列)
this.columnCount = 2
布局说明
| 布局模式 |
说明 |
| columnCount=1(横向布局) |
图片横向排列,容器水平滚动,每张卡片宽度固定 |
| columnCount=2(竖向瀑布流) |
左右两列布局,容器垂直滚动,高度自适应,价格/销量与操作按钮左右排列 |
自定义操作按钮说明
通过 #action slot 可以自定义列表项底部的操作按钮,slot 会暴露当前列表项的 item 数据,方便进行个性化操作。
注意事项:
- 操作按钮事件建议使用
@tap.stop 阻止事件冒泡,避免触发列表项的点击事件
- 按钮样式需要在父组件中自行定义
- 若不需要操作按钮,可不传入
#action slot
注意事项
- 图片地址建议使用完整的 HTTP/HTTPS 链接
- 数据列表必须包含
id 字段作为唯一标识
- 建议对
image 字段做空值判断,避免无效图片请求
- 下拉刷新和上拉加载完成后,需调用对应的
finishRefresh() 和 finishLoadmore() 方法结束状态