import { request } from '../../utils/request' import { ClothesItem } from '../../types/models' import { ProcessStatus } from '../../utils/config' const ATTRS = [ { key: 'category_main', label: '大类', type: 'text' }, { key: 'category_sub', label: '小类', type: 'text' }, { key: 'style', label: '风格', type: 'text' }, { key: 'color_main', label: '主色', type: 'color' }, { key: 'color_sub', label: '副色', type: 'text' }, { key: 'thickness', label: '厚度', type: 'text' }, { key: 'version', label: '版型', type: 'text' }, { key: 'material', label: '材质', type: 'text' }, { key: 'pattern', label: '花纹', type: 'text' }, { key: 'sleeve_type', label: '袖型', type: 'text' }, { key: 'clothes_type', label: '衣长', type: 'text' }, { key: 'neckline_type', label: '领型', type: 'text' }, { key: 'formality', label: '正式程度', type: 'score' }, { key: 'occasions', label: '适用场合', type: 'text' }, { key: 'visual_impact', label: '视觉冲击力', type: 'score' }, ] // 将后端 image_process_status 映射为前端展示步骤:0=排队中 1=AI 抠图 2=智能分类 function statusToStep(status: number): number { if (status === ProcessStatus.QUEUED) return 0 if (status === ProcessStatus.MATTING) return 1 if (status === ProcessStatus.CLASSIFYING) return 2 return 2 } Page({ data: { uuid: '', attrs: ATTRS, detail: {} as ClothesItem, formData: {} as ClothesItem, imageStatus: 0, step: 0, failReason: '', loading: true, editMode: false, notFound: false, }, onLoad(query: Record) { const uuid = query.uuid || '' this.setData({ uuid }) if (!uuid) { // uuid 缺失时不请求不轮询,否则会停在空白页且无法退出 this.setData({ loading: false, notFound: true }) return } this.fetchDetail() this.pollStatus() }, onBackWardrobe() { wx.switchTab({ url: '/pages/wardrobe/wardrobe' }) }, fetchDetail(): Promise { return request({ url: `/clothes/${this.data.uuid}` }) .then((detail) => this.setData({ detail, loading: false })) .catch(() => this.setData({ loading: false })) }, pollStatus() { let count = 0 ;(this as any).pollTimer = setInterval(() => { count++ request<{ image_process_status: number; fail_reason?: string }>({ url: `/clothes/${this.data.uuid}/status`, showError: false, }) .then((res) => { const status = res.image_process_status if (status === ProcessStatus.SUCCESS) { // 成功:先拉最新详情,再切换为详情视图,避免空属性闪烁 this.clearTimer() this.fetchDetail().then(() => this.setData({ imageStatus: status, step: 2 })) } else if (status === ProcessStatus.FAILED) { this.clearTimer() this.setData({ imageStatus: status, failReason: res.fail_reason || '识别失败' }) } else { this.setData({ imageStatus: status, step: statusToStep(status) }) } }) .catch(() => {}) if (count >= 120) { this.clearTimer() } }, 2000) }, clearTimer() { const t = (this as any).pollTimer if (t) { clearInterval(t) ;(this as any).pollTimer = null } }, onUnload() { this.clearTimer() }, onToggleEdit() { if (this.data.editMode) { this.setData({ editMode: false }) } else { this.setData({ editMode: true, formData: { ...this.data.detail } }) } }, onNameInput(e: WechatMiniprogram.Input) { this.setData({ 'formData.name': e.detail.value }) }, onRemarkInput(e: WechatMiniprogram.Input) { this.setData({ 'formData.remark': e.detail.value }) }, onAttrTap(e: WechatMiniprogram.TouchEvent) { if (!this.data.editMode) return const key = e.currentTarget.dataset.key as string const attr = ATTRS.find((a) => a.key === key) if (!attr) return const current = (this.data.formData as any)[key] || '' if (attr.type === 'score') { wx.showActionSheet({ itemList: ['1', '2', '3', '4', '5'], success: (res) => { this.setData({ [`formData.${key}`]: String(res.tapIndex + 1) }) }, }) return } wx.showModal({ title: `编辑${attr.label}`, editable: true, placeholderText: `请输入${attr.label}`, content: current, success: (res) => { if (res.confirm) { this.setData({ [`formData.${key}`]: res.content }) } }, }) }, onSave() { const fd = this.data.formData as any const body: Record = {} const fields: Array<[string, string]> = [ ['name', 'name'], ['remark', 'remark'], ['category_main', 'category_main'], ['category_sub', 'category_sub'], ['style', 'style'], ['color_main', 'color'], // 主色请求字段名是 color ['color_sub', 'color_sub'], ['thickness', 'thickness'], ['version', 'version'], ['material', 'material'], ['pattern', 'pattern'], ['sleeve_type', 'sleeve_type'], ['clothes_type', 'clothes_type'], ['neckline_type', 'neckline_type'], ['formality', 'formality'], ['occasions', 'occasions'], ['visual_impact', 'visual_impact'], ] fields.forEach(([src, dest]) => { if (fd[src]) body[dest] = fd[src] }) request({ url: `/clothes/${this.data.uuid}`, method: 'PUT', data: body }) .then(() => { wx.showToast({ title: '已保存', icon: 'success' }) this.setData({ editMode: false }) this.fetchDetail() }) .catch(() => {}) }, onDelete() { const canRefund = this.data.imageStatus !== ProcessStatus.FAILED const content = canRefund ? '删除后将退回 1 次上传额度,确认删除?' : '确认删除?' wx.showModal({ title: '删除这件衣物', content, confirmText: '确认删除', confirmColor: '#FF3B30', success: (res) => { if (!res.confirm) return request({ url: `/clothes/${this.data.uuid}`, method: 'DELETE' }) .then(() => { wx.showToast({ title: '已删除', icon: 'success' }) setTimeout(() => wx.navigateBack(), 500) }) .catch(() => {}) }, }) }, })