import { request, AUTH_EXPIRED_CODES } from '../../utils/request' import { ClosetShareDetail } from '../../types/models' import { ShareStatus } from '../../utils/config' import { formatDateTime } from '../../utils/format' import { logger } from '../../utils/logger' // 日志模块名 const LOG = 'closet-share-detail' Page({ data: { uuid: '', detail: null as ClosetShareDetail | null, expireText: '', loading: true, invalid: false, invalidTitle: '该分享已过期或已被取消', invalidDesc: '分享链接已失效', needLogin: false, }, onLoad(query: Record) { const uuid = query.uuid || '' logger.debug(LOG, 'onLoad, uuid=', uuid) this.setData({ uuid }) if (!uuid) { // uuid 缺失时若不兜底,loading 会永久为 true,页面白屏且无法退出 logger.warn(LOG, 'onLoad 缺少 uuid 参数') this.setInvalid('链接不完整', '缺少分享标识,无法打开') return } this.fetchDetail() }, fetchDetail() { request({ url: `/closet-share/${this.data.uuid}`, showError: false }) .then((detail) => { // 后端可能惰性过期:本地再校验一次状态与有效期,避免展示已失效内容 logger.debug(LOG, '详情加载成功, status=', detail && detail.status) if (this.checkInvalid(detail)) return const expireText = detail.expire_time === 0 ? '长期有效' : `有效期至 ${formatDateTime(detail.expire_time)}` this.setData({ detail, expireText, loading: false }) }) .catch((e: any) => { const code = e && e.code logger.warn(LOG, '详情加载失败, code=', code) if (AUTH_EXPIRED_CODES.indexOf(code) >= 0) { // 查看分享需登录:给「去登录」出口,登录成功后回到本页 this.setData({ loading: false, invalid: true, needLogin: true, invalidTitle: '请先登录', invalidDesc: '登录后即可查看好友分享的衣柜', }) return } if (code === 90101) { this.setInvalid('分享已过期', '该分享链接已超过有效期') } else if (code === 90102) { this.setInvalid('分享已取消', '该分享已被创建者取消') } else if (code === 90100) { this.setInvalid('分享不存在', '该分享链接无效') } else { // 兜底:任何未知错误都不能停在空白页 this.setInvalid('加载失败', '请稍后重试') } }) }, // 本地状态/有效期校验,返回 true 表示已置为失效态 checkInvalid(detail: ClosetShareDetail): boolean { if (detail.status === ShareStatus.CANCELLED) { this.setInvalid('分享已取消', '该分享已被创建者取消') return true } if (detail.status === ShareStatus.BANNED) { this.setInvalid('分享已封禁', '该分享内容违规,已停止展示') return true } const now = Math.floor(Date.now() / 1000) if (detail.status === ShareStatus.EXPIRED || (detail.expire_time > 0 && detail.expire_time < now)) { this.setInvalid('分享已过期', '该分享链接已超过有效期') return true } return false }, setInvalid(title: string, desc: string) { this.setData({ loading: false, invalid: true, needLogin: false, invalidTitle: title, invalidDesc: desc, }) }, onCancel() { wx.showModal({ title: '取消分享', content: '取消后该分享链接将失效,且不返还衣柜分享额度', confirmText: '取消分享', confirmColor: '#FF3B30', success: (res) => { if (!res.confirm) return request({ url: `/closet-share/${this.data.uuid}/cancel`, method: 'POST' }) .then(() => { logger.info(LOG, '取消分享成功') wx.showToast({ title: '已取消', icon: 'success' }) ;(this as any).backTimer = setTimeout( () => wx.navigateBack({ fail: (e: any) => logger.warn(LOG, 'navigateBack 失败, err=', e && e.errMsg), }), 500, ) }) .catch((e: any) => logger.warn(LOG, '取消分享失败, code=', e && e.code)) }, fail: (e: any) => logger.warn(LOG, '取消分享弹窗失败, err=', e && e.errMsg), }) }, onBackHome() { wx.switchTab({ url: '/pages/wardrobe/wardrobe', fail: (e: any) => logger.warn(LOG, 'switchTab 衣橱失败, err=', e && e.errMsg), }) }, onGoLogin() { const redirect = `/pages/closet-share-detail/closet-share-detail?uuid=${this.data.uuid}` const loginUrl = `/pages/login/login?redirect=${encodeURIComponent(redirect)}` wx.navigateTo({ url: loginUrl, fail: (e) => { // 页面栈满(10 层)时 navigateTo 失败:改用 redirectTo 仍跳登录页, // 且保留 redirect 参数(原先跳 profile 会丢失来源,登录后回不到分享页) logger.warn(LOG, 'navigateTo 登录页失败,改用 redirectTo, err=', e && e.errMsg) wx.redirectTo({ url: loginUrl, fail: (e2) => { logger.warn(LOG, 'redirectTo 登录页也失败,退回首页, err=', e2 && e2.errMsg) wx.switchTab({ url: '/pages/profile/profile', fail: (e3: any) => logger.warn(LOG, 'switchTab 我的失败, err=', e3 && e3.errMsg), }) }, }) }, }) }, onShareAppMessage() { const name = (this.data.detail && this.data.detail.owner_name) || '好友' logger.debug(LOG, '转发分享, uuid=', this.data.uuid) return { title: `${name} 向你分享了 TA 的衣柜`, path: `/pages/closet-share-detail/closet-share-detail?uuid=${this.data.uuid}`, } }, onUnload() { const timer = (this as any).backTimer if (timer) { clearTimeout(timer) ;(this as any).backTimer = null } }, })