import { request } from '../../utils/request' import { AiRecommendResult, ClothesItem } from '../../types/models' import { navScroll } from '../../utils/nav' import { logger } from '../../utils/logger' // 日志模块名 const LOG = 'ai-recommend' Page({ data: { state: 'scene' as 'scene' | 'generating' | 'result' | 'empty', showSceneSheet: true, sceneType: 1, scene: '', result: null as AiRecommendResult | null, clothes: [] as ClothesItem[], generating: false, // 滚动收缩:由 utils/nav.ts 的共享 handler 驱动 navScrolled: false, // v2 玻璃弹层 confirmVisible: false, confirmButtons: [ { text: '取消', value: '' }, { text: '开始生成', value: 'ok', type: 'main' }, ], limitVisible: false, limitButtons: [ { text: '取消', value: '' }, { text: '去购买', value: 'ok', type: 'main' }, ], }, // scroll-view 滚动 → 导航收缩为玻璃小标题 onNavScroll: navScroll, noop() {}, selectDaily() { this.setData({ sceneType: 1 }) }, selectCustom() { this.setData({ sceneType: 2 }) }, onSceneInput(e: WechatMiniprogram.Input) { this.setData({ scene: e.detail.value }) }, onSkipScene() { this.setData({ showSceneSheet: false }) this.startGenerate(1, '') }, onStartGenerate() { this.setData({ showSceneSheet: false }) this.startGenerate(this.data.sceneType, this.data.scene) }, startGenerate(sceneType: number, scene: string) { if (this.data.generating) return // 待生成的场景参数暂存到实例,确认后再发起 ;(this as any).pendingScene = { sceneType, scene } this.setData({ confirmVisible: true }) }, onConfirmCancel() { this.setData({ confirmVisible: false }) }, onConfirmGenerate(e: WechatMiniprogram.CustomEvent) { this.setData({ confirmVisible: false }) if (e.detail.value !== 'ok') return const pending = (this as any).pendingScene || { sceneType: 1, scene: '' } this.doGenerate(pending.sceneType, pending.scene) }, doGenerate(sceneType: number, scene: string) { if (this.data.generating) return this.setData({ state: 'generating', generating: true }) const body: Record = { scene_type: sceneType } if (sceneType === 2 && scene) body.scene = scene request({ url: '/ai-recommend', method: 'POST', data: body, showError: false }) .then(async (res) => { logger.info(LOG, 'AI 推荐生成成功, 单品数=', res.outfit_item && res.outfit_item.length) const clothes = await Promise.all( res.outfit_item.map(async (it) => { try { return await request({ url: `/clothes/${it.clothes_uuid}`, showError: false }) } catch (e: any) { logger.warn(LOG, '单品详情拉取失败, uuid=', it.clothes_uuid, 'code=', e && e.code) return { clothes_uuid: it.clothes_uuid } as ClothesItem } }) ) this.setData({ result: res, clothes, state: 'result', generating: false }) }) .catch((e: any) => { this.setData({ generating: false }) const code = e && e.code logger.warn(LOG, 'AI 推荐生成失败, code=', code) if (code === 90400) { this.setData({ state: 'empty' }) } else if (code === 90401 || code === 80000) { // 90401=AI 推荐次数上限;80000=额度不足兜底,统一弹窗引导购买 this.setData({ state: 'scene', showSceneSheet: true }) this.showLimitModal() } else if (code === 10003) { wx.showToast({ title: '正在生成中,请稍候', icon: 'none' }) this.setData({ state: 'scene', showSceneSheet: true }) } else { this.setData({ state: 'scene', showSceneSheet: true }) } }) }, showLimitModal() { this.setData({ limitVisible: true }) }, onLimitCancel() { this.setData({ limitVisible: false }) }, onLimitConfirm(e: WechatMiniprogram.CustomEvent) { this.setData({ limitVisible: false }) if (e.detail.value !== 'ok') return wx.navigateTo({ url: '/pages/mall/mall', fail: (err) => logger.warn(LOG, 'navigateTo 商城失败, err=', err && err.errMsg), }) }, onSave() { if (!this.data.result) return request({ url: '/ai-recommend/save', method: 'POST', data: { outfit_uuid: this.data.result.outfit_uuid }, }) .then(() => { logger.info(LOG, 'AI 穿搭保存成功') wx.showToast({ title: '已保存到我的穿搭', icon: 'success' }) setTimeout(() => { wx.redirectTo({ url: `/pages/outfit-detail/outfit-detail?uuid=${this.data.result!.outfit_uuid}&owner=1`, fail: (e) => logger.warn(LOG, 'redirectTo 穿搭详情失败, err=', e && e.errMsg), }) }, 500) }) .catch((e: any) => logger.warn(LOG, 'AI 穿搭保存失败, code=', e && e.code)) }, onReroll() { this.startGenerate(this.data.sceneType, this.data.scene) }, onGoUpload() { wx.navigateTo({ url: '/pages/upload/upload', fail: (e) => logger.warn(LOG, 'navigateTo 上传页失败, err=', e && e.errMsg), }) }, })