import { request, redirectToLogin } from '../../utils/request' import * as auth from '../../utils/auth' import { PagedResult, ClothesItem, QuotaItem } from '../../types/models' import { QuotaType } from '../../utils/config' import { normalizePaged, mergePaged, hasMorePaged } from '../../utils/paged' import { setTabBarSelected } from '../../utils/tabbar' import { logger } from '../../utils/logger' // 日志模块名 const LOG = 'wardrobe' const CATEGORIES = [ { name: '全部', value: '' }, { name: '上装', value: '上装' }, { name: '下装', value: '下装' }, { name: '连衣裙', value: '连衣裙' }, { name: '外套', value: '外套' }, { name: '鞋', value: '鞋' }, { name: '配饰', value: '配饰' }, ] Page({ data: { categories: CATEGORIES, activeCategory: 0, list: [] as ClothesItem[], total: 0, uploadRemain: 0, loading: true, refreshing: false, loadingMore: false, // 上拉加载独立闸门,避免与首次 loading 互相干扰 page: 1, pageSize: 20, hasMore: true, }, onShow() { this.syncTabBar() // 未登录直接回登录页(幂等跳转),不再发出必然 401 的请求 if (!auth.isLoggedIn()) { redirectToLogin() return } this.refresh() }, syncTabBar() { setTabBarSelected(0) }, refresh() { this.setData({ page: 1, hasMore: true, loading: this.data.list.length === 0 }) this.fetchList(1) this.fetchQuota() }, onRefresh() { this.setData({ refreshing: true }) Promise.all([this.fetchList(1), this.fetchQuota()]).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 { const cat = CATEGORIES[this.data.activeCategory].value const params: Record = { page, page_size: this.data.pageSize } if (cat) params.category_main = cat // 请求序号:快速切分类/下拉刷新时旧响应可能后到,序号不匹配的响应直接丢弃 const seq = ((this as any).reqSeq || 0) + 1 ;(this as any).reqSeq = seq if (page > 1) this.setData({ loadingMore: true }) return request>({ url: '/clothes', data: params }) .then((res) => { if ((this as any).reqSeq !== seq) return const { list: raw, total } = normalizePaged(res) logger.debug(LOG, '列表加载成功, 分类=', cat || '全部', 'page=', page, '条数=', raw && raw.length) const list = raw.map((it) => ({ ...it, _statusText: !it.category_main && !it.processed_image ? 'AI 处理中' : '', _statusType: 'blue', })) const merged = mergePaged(this.data.list, list, page === 1) this.setData({ list: merged, total, 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, '列表加载失败, page=', page, 'code=', e && e.code) this.setData({ loading: false, loadingMore: false }) }) }, fetchQuota(): Promise { return request<{ quotas: QuotaItem[] }>({ url: '/quota/info', showError: false, silentAuth: true }) .then((res) => { const upload = res.quotas.find((q) => q.quota_type === QuotaType.UPLOAD) if (upload) this.setData({ uploadRemain: upload.remain }) logger.debug(LOG, '额度加载成功, 上传剩余=', upload ? upload.remain : 0) }) .catch((e: any) => logger.warn(LOG, '额度加载失败(静默降级), code=', e && e.code)) }, onCategoryTap(e: WechatMiniprogram.TouchEvent) { const index = Number(e.currentTarget.dataset.index) if (index === this.data.activeCategory) return this.setData({ activeCategory: index, list: [], loading: true }) this.refresh() }, onClothesTap(e: WechatMiniprogram.CustomEvent) { const item = e.detail.item as ClothesItem logger.debug(LOG, '点击衣物, uuid=', item && item.clothes_uuid) wx.navigateTo({ url: `/pages/clothes-detail/clothes-detail?uuid=${item.clothes_uuid}`, fail: (err) => logger.warn(LOG, 'navigateTo 衣物详情失败, err=', err && err.errMsg), }) }, onUpload() { wx.navigateTo({ url: '/pages/upload/upload', fail: (e) => logger.warn(LOG, 'navigateTo 上传页失败, err=', e && e.errMsg), }) }, })