Files
zt-closet-wxmini-frontend/miniprogram/pages/outfit/outfit.ts
T
2026-09-13 02:35:32 +08:00

154 lines
5.1 KiB
TypeScript

import { request, redirectToLogin } from '../../utils/request'
import * as auth from '../../utils/auth'
import { PagedResult, OutfitItem } from '../../types/models'
import { OutfitSource, IsPublic, STORAGE_KEYS } from '../../utils/config'
import { normalizePaged, mergePaged, hasMorePaged } from '../../utils/paged'
import { setTabBarSelected } from '../../utils/tabbar'
import { logger } from '../../utils/logger'
// 日志模块名
const LOG = 'outfit'
Page({
data: {
activeTab: 0,
list: [] as OutfitItem[],
loading: true,
refreshing: false,
loadingMore: false,
page: 1,
pageSize: 20,
hasMore: true,
},
onShow() {
this.syncTabBar()
// 未登录直接回登录页(幂等跳转),不再发出必然 401 的请求
if (!auth.isLoggedIn()) {
redirectToLogin()
return
}
const openCollect = wx.getStorageSync(STORAGE_KEYS.OPEN_COLLECT)
if (openCollect) {
logger.debug(LOG, '命中打开收藏夹标记,切换到收藏 tab')
wx.removeStorageSync(STORAGE_KEYS.OPEN_COLLECT)
if (this.data.activeTab !== 1) {
this.setData({ activeTab: 1, list: [], loading: true })
}
}
// 首帧渲染未结束就 setData 会触发「Expected updated data but get first rendering data」,
// 整个 webview 渲染失败,用户视觉上仍看到上一个页面。首次 onShow 的刷新延迟到下一帧
if (!(this as any).firstShown) {
;(this as any).firstShown = true
wx.nextTick(() => this.refresh())
return
}
this.refresh()
},
syncTabBar() {
setTabBarSelected(1)
},
onTabChange(e: WechatMiniprogram.TouchEvent) {
const index = Number(e.currentTarget.dataset.index)
if (index === this.data.activeTab) return
this.setData({ activeTab: index, list: [], loading: true })
this.refresh()
},
refresh() {
this.setData({ page: 1, hasMore: true, loading: this.data.list.length === 0 })
this.fetchList(1)
},
onRefresh() {
this.setData({ refreshing: true })
this.fetchList(1).finally(() => this.setData({ refreshing: false }))
},
onReachBottom() {
if (!this.data.hasMore || this.data.loading || this.data.loadingMore) return
this.fetchList(this.data.page + 1)
},
fetchList(page: number): Promise<void> {
const isCollect = this.data.activeTab === 1
const url = isCollect ? '/collect' : '/outfit'
const seq = ((this as any).reqSeq || 0) + 1
;(this as any).reqSeq = seq
if (page > 1) this.setData({ loadingMore: true })
return request<PagedResult<OutfitItem>>({ url, data: { page, page_size: this.data.pageSize } })
.then(async (res) => {
const { list: raw, total } = normalizePaged<OutfitItem>(res)
logger.debug(LOG, '列表加载成功, url=', url, 'page=', page, '条数=', raw && raw.length)
const items = await this.decorateList(raw)
// decorateList 是异步的,期间可能已切换 tab:序号不匹配则丢弃
if ((this as any).reqSeq !== seq) return
const merged = mergePaged(this.data.list, items, page === 1)
this.setData({
list: merged,
hasMore: hasMorePaged(merged, total, this.data.pageSize),
loading: false,
loadingMore: false,
page,
})
})
.catch((e: any) => {
if ((this as any).reqSeq !== seq) return
logger.warn(LOG, '列表加载失败, url=', url, 'page=', page, 'code=', e && e.code)
this.setData({ loading: false, loadingMore: false })
})
},
decorateList(list: OutfitItem[]): Promise<OutfitItem[]> {
return Promise.all(
list.map(async (it) => {
let likeCount = 0
try {
const like = await request<{ is_liked: boolean; like_count: number }>({
url: `/like/${it.outfit_uuid}`,
showError: false,
})
likeCount = like.like_count
} catch (e: any) {
logger.warn(LOG, '点赞数拉取失败, uuid=', it.outfit_uuid, 'code=', e && e.code)
}
return { ...it, _likeCount: likeCount, _status: this.getStatusText(it) }
})
)
},
getStatusText(it: OutfitItem): { text: string; type: string } {
if (it.source === OutfitSource.AI) return { text: 'AI 推荐', type: 'indigo' }
if (it.is_public === IsPublic.PUBLIC) return { text: '已公开', type: 'blue' }
return { text: '仅自己可见', type: 'gray' }
},
onCreate() {
wx.navigateTo({
url: '/pages/outfit-edit/outfit-edit',
fail: (e) => logger.warn(LOG, 'navigateTo 新建穿搭失败, err=', e && e.errMsg),
})
},
onAiRecommend() {
wx.navigateTo({
url: '/pages/ai-recommend/ai-recommend',
fail: (e) => logger.warn(LOG, 'navigateTo AI 推荐失败, err=', e && e.errMsg),
})
},
onOutfitTap(e: WechatMiniprogram.TouchEvent) {
const uuid = e.currentTarget.dataset.uuid as string
const owner = this.data.activeTab === 0 ? '1' : '0'
logger.debug(LOG, '点击穿搭, uuid=', uuid, 'owner=', owner)
wx.navigateTo({
url: `/pages/outfit-detail/outfit-detail?uuid=${uuid}&owner=${owner}`,
fail: (err) => logger.warn(LOG, 'navigateTo 穿搭详情失败, err=', err && err.errMsg),
})
},
})