105 lines
3.8 KiB
JavaScript
105 lines
3.8 KiB
JavaScript
/**
|
||
* 商品瀑布流通用 mixin
|
||
*
|
||
* product.vue / search.vue 的瀑布流高度同构:data 字段、按列数拆列 computed、
|
||
* 触底加载更多、下拉刷新这些样板代码两边一模一样。这里抽公共,避免改一处忘改另一处。
|
||
*
|
||
* 约定(引入本 mixin 的页面必须遵守):
|
||
* - 页面自己实现 getProductList():负责调接口、按 this.page 把结果赋值/追加到 this.products、
|
||
* 维护 this.noMoreData 与 this.loading / this.loadingMore。
|
||
* - getProductList 应返回 Promise(供 refreshProductList 关闭下拉动画)。
|
||
* - 页面的 onReachBottom / onPullDownRefresh 不用再写,mixin 已接管。
|
||
*
|
||
* 故意只抽 data + computed + 两个生命周期,不把 getProductList 收进来,
|
||
* 因为两个页面的请求参数不同(product 带 category_id,search 只带 title),硬塞进来反而更乱。
|
||
*/
|
||
export default {
|
||
data() {
|
||
return {
|
||
// 当前页码,从 1 开始
|
||
page: 1,
|
||
// 每页条数,与后端约定 10
|
||
pageSize: 10,
|
||
// 商品列表(扁平数组),computed 按 pcGoodsCols 拆成 N 列
|
||
products: [],
|
||
// 首屏 loading(控制骨架屏显隐)
|
||
loading: false,
|
||
// 触底加载更多 loading
|
||
loadingMore: false,
|
||
// 是否没有更多数据
|
||
noMoreData: false,
|
||
// 后端总页数(部分接口会带 last_page)
|
||
totalPages: 1,
|
||
}
|
||
},
|
||
computed: {
|
||
/**
|
||
* 按当前窗口列数把商品轮流分进各列,宽屏一行更多而不是两列撑满。
|
||
* 依赖 theme mixin 的 pcGoodsCols;两个列表页都混了 theme。
|
||
*/
|
||
productColumns() {
|
||
const n = Math.max(2, Number(this.pcGoodsCols) || 2)
|
||
const cols = Array.from({ length: n }, () => [])
|
||
this.products.forEach((item, index) => {
|
||
cols[index % n].push(item)
|
||
})
|
||
return cols
|
||
},
|
||
},
|
||
onPullDownRefresh() {
|
||
this.refreshProductList()
|
||
},
|
||
onReachBottom() {
|
||
this.loadMoreProducts()
|
||
},
|
||
methods: {
|
||
/**
|
||
* 下拉刷新:回到第一页重新拉,结束后关闭下拉动画 + toast
|
||
* 依赖页面 getProductList 返回 Promise
|
||
*/
|
||
refreshProductList() {
|
||
this.page = 1
|
||
this.products = []
|
||
this.noMoreData = false
|
||
const p = this.getProductList()
|
||
Promise.resolve(p)
|
||
.then(() => {
|
||
uni.stopPullDownRefresh()
|
||
uni.showToast({
|
||
title: '刷新成功',
|
||
icon: 'success',
|
||
duration: 1500,
|
||
})
|
||
})
|
||
.catch(() => {
|
||
uni.stopPullDownRefresh()
|
||
uni.showToast({
|
||
title: '刷新失败',
|
||
icon: 'none',
|
||
duration: 1500,
|
||
})
|
||
})
|
||
},
|
||
/**
|
||
* 触底加载更多:加锁 + 页码 +1 后交页面 getProductList 处理
|
||
* 失败时回退页码,避免下次再触底跳页
|
||
*/
|
||
loadMoreProducts() {
|
||
if (this.loadingMore || this.noMoreData) return
|
||
this.page++
|
||
this.loadingMore = true
|
||
Promise.resolve(this.getProductList())
|
||
.catch(() => {
|
||
this.page--
|
||
uni.showToast({
|
||
title: '加载失败',
|
||
icon: 'none',
|
||
})
|
||
})
|
||
.finally(() => {
|
||
this.loadingMore = false
|
||
})
|
||
},
|
||
},
|
||
}
|