import { request } from '../../utils/request' import { ClothesItem, PagedResult, UserInfo } from '../../types/models' import { STORAGE_KEYS } from '../../utils/config' import { normalizePaged, hasMorePaged } from '../../utils/paged' import { navScroll } from '../../utils/nav' import { logger } from '../../utils/logger' // 日志模块名 const LOG = 'closet-share-create' const CATEGORIES = [ { name: '全部', value: '' }, { name: '上装', value: '上装' }, { name: '下装', value: '下装' }, { name: '连衣裙', value: '连衣裙' }, { name: '外套', value: '外套' }, { name: '鞋', value: '鞋' }, { name: '配饰', value: '配饰' }, ] const EXPIRE_OPTIONS = [ { label: '1 天', days: 1 }, { label: '7 天', days: 7 }, { label: '30 天', days: 30 }, { label: '长期有效', days: 0 }, ] const PAGE_SIZE = 50 Page({ data: { view: 'config' as 'config' | 'select' | 'success', shareType: 1, expireDays: 7, expireOptions: EXPIRE_OPTIONS, selectedUuids: [] as string[], categories: CATEGORIES, activeCategory: 0, allClothes: [] as ClothesItem[], selectLoading: true, loadingMore: false, hasMore: true, page: 1, submitting: false, shareUuid: '', nickname: '微信用户', avatar: '', selectedCount: 0, // 滚动收缩:由 utils/nav.ts 的共享 handler 驱动 navScrolled: false, // 额度不足引导:v2 玻璃弹窗 quotaVisible: false, quotaTitle: '衣柜分享额度不足', quotaText: '购买额度包后可创建衣柜分享', quotaButtons: [ { text: '取消', value: '' }, { text: '去购买', value: 'ok', type: 'main' }, ], }, // scroll-view 滚动 → 导航收缩为玻璃小标题 onNavScroll: navScroll, onLoad() { this.loadUserInfo() }, loadUserInfo() { const cached = wx.getStorageSync(STORAGE_KEYS.USER_INFO) as Record if (cached) { this.setData({ nickname: cached.username || '微信用户', avatar: cached.avatar || '' }) } // 头像为带时间戳的签名 URL,缓存可能过期,实时刷新并回写缓存。 request({ url: '/user/info', showError: false }) .then((user) => { logger.debug(LOG, '用户信息加载成功') wx.setStorageSync(STORAGE_KEYS.USER_INFO, user) this.setData({ nickname: user.username || '微信用户', avatar: user.avatar || '' }) }) .catch((e: any) => logger.warn(LOG, '用户信息加载失败(用缓存兜底), code=', e && e.code)) }, selectPartial() { this.setData({ shareType: 1 }) }, selectFull() { this.setData({ shareType: 2 }) }, selectExpire(e: WechatMiniprogram.TouchEvent) { const days = Number(e.currentTarget.dataset.days) this.setData({ expireDays: days }) }, openSelect() { this.setData({ view: 'select', selectLoading: true, page: 1, hasMore: true }) this.fetchAllClothes(1) }, fetchAllClothes(page = 1) { const cat = CATEGORIES[this.data.activeCategory].value const params: Record = { page, page_size: PAGE_SIZE } 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 }) 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, _selected: this.data.selectedUuids.indexOf(it.clothes_uuid) >= 0, })) const merged = page === 1 ? list : this.data.allClothes.concat(list) this.setData({ allClothes: merged, selectLoading: false, loadingMore: false, page, hasMore: hasMorePaged(merged, total, PAGE_SIZE), }) }) .catch((e: any) => { if ((this as any).reqSeq !== seq) return logger.warn(LOG, '衣物列表加载失败, page=', page, 'code=', e && e.code) this.setData({ selectLoading: false, loadingMore: false }) }) }, onCategoryTap(e: WechatMiniprogram.TouchEvent) { const index = Number(e.currentTarget.dataset.index) if (index === this.data.activeCategory) return this.setData({ activeCategory: index, selectLoading: true, page: 1, hasMore: true }) this.fetchAllClothes(1) }, onReachBottom() { if (this.data.view !== 'select') return if (!this.data.hasMore || this.data.selectLoading || this.data.loadingMore) return this.fetchAllClothes(this.data.page + 1) }, onToggleSelect(e: WechatMiniprogram.CustomEvent) { const item = e.detail.item as ClothesItem const uuids = this.data.selectedUuids const idx = uuids.indexOf(item.clothes_uuid) if (idx >= 0) { this.syncSelect(uuids.filter((u) => u !== item.clothes_uuid)) } else { this.syncSelect([...uuids, item.clothes_uuid]) } }, syncSelect(uuids: string[]) { this.setData({ selectedUuids: uuids, allClothes: this.data.allClothes.map((it) => ({ ...it, _selected: uuids.indexOf(it.clothes_uuid) >= 0, })), }) }, confirmSelect() { this.setData({ view: 'config', selectedCount: this.data.selectedUuids.length }) }, backToConfig() { this.setData({ view: 'config' }) }, onConfirm() { if (this.data.submitting) return const body: Record = { share_type: this.data.shareType } if (this.data.shareType === 1) { if (!this.data.selectedUuids.length) { wx.showToast({ title: '请选择要分享的衣物', icon: 'none' }) return } body.clothes_uuids = this.data.selectedUuids } const days = this.data.expireDays body.expire_time = days > 0 ? Math.floor(Date.now() / 1000) + days * 86400 : 0 // 提交加锁:连点会重复创建分享、重复扣额度 this.setData({ submitting: true }) request<{ share_uuid: string }>({ url: '/closet-share', method: 'POST', data: body }) .then((res) => { logger.info(LOG, '分享创建成功, uuid=', res.share_uuid) this.setData({ shareUuid: res.share_uuid, view: 'success', submitting: false }) }) .catch((e: any) => { logger.warn(LOG, '分享创建失败, code=', e && e.code) this.setData({ submitting: false }) // 衣柜分享额度不足:弹窗引导购买 if (e && e.code === 80000) { this.setData({ quotaVisible: true }) } }) }, onViewShare() { wx.redirectTo({ url: `/pages/closet-share-detail/closet-share-detail?uuid=${this.data.shareUuid}`, fail: (e) => logger.warn(LOG, 'redirectTo 分享详情失败, err=', e && e.errMsg), }) }, onDone() { wx.navigateBack({ fail: (e) => logger.warn(LOG, 'navigateBack 失败, err=', e && e.errMsg), }) }, onQuotaCancel() { this.setData({ quotaVisible: false }) }, onQuotaConfirm(e: WechatMiniprogram.CustomEvent) { this.setData({ quotaVisible: false }) if (e.detail.value !== 'ok') return wx.navigateTo({ url: '/pages/mall/mall', fail: (err) => logger.warn(LOG, 'navigateTo 商城失败, err=', err && err.errMsg), }) }, })