Files
zt-closet-wxmini-frontend/miniprogram/pages/upload/upload.ts
T
2026-09-13 02:28:46 +08:00

171 lines
5.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { request, uploadFile, NOT_LOGIN_CODE } from '../../utils/request'
import { ProcessStatus } from '../../utils/config'
import { logger } from '../../utils/logger'
// 日志模块名
const LOG = 'upload'
Page({
data: {
state: 'idle' as 'idle' | 'processing' | 'failed',
imagePath: '',
clothesUuid: '',
failReason: '',
step: 0,
},
onChoose() {
wx.showActionSheet({
itemList: ['拍照', '从相册选择'],
success: (res) => {
const sourceType: Array<'camera' | 'album'> = res.tapIndex === 0 ? ['camera'] : ['album']
this.chooseImage(sourceType)
},
fail: (e) => logger.debug(LOG, '取消选择图片, err=', e && e.errMsg),
})
},
chooseImage(sourceType: Array<'camera' | 'album'>) {
wx.chooseMedia({
count: 1,
mediaType: ['image'],
sourceType,
success: (res) => {
const file = res.tempFiles[0]
logger.debug(LOG, '已选择图片, 来源=', sourceType.join(','))
this.startUpload(file.tempFilePath)
},
fail: (e) => logger.warn(LOG, '选择图片失败, err=', e && e.errMsg),
})
},
startUpload(filePath: string) {
this.setData({ state: 'processing', imagePath: filePath, step: 0, failReason: '' })
uploadFile<{ clothes_uuid: string; image_process_status: number }>(filePath)
.then((res) => {
logger.info(LOG, '衣物上传成功, uuid=', res.clothes_uuid)
this.setData({ clothesUuid: res.clothes_uuid })
this.askNextAction()
})
.catch((e: any) => {
this.setData({ state: 'idle' })
logger.warn(LOG, '衣物上传失败, code=', e && e.code)
if (e && e.code === 80000) {
this.showQuotaModal()
return
}
// uploadFile 已统一 toast 业务错误与网络错误;未登录时请求并未发出,需补一次提示
if (e && e.code === NOT_LOGIN_CODE) {
wx.showToast({ title: '请先登录', icon: 'none' })
}
})
},
askNextAction() {
// 后台继续轮询,仅用于更新步骤进度(不再自动跳详情)
this.pollStatus(this.data.clothesUuid)
// 立即弹窗让用户选择下一步,避免干等
wx.showModal({
title: '衣物已上传',
content: 'AI 正在识别中,可在衣橱查看进度',
confirmText: '继续上传',
cancelText: '回到衣橱',
success: (res) => {
this.clearTimer()
if (res.confirm) {
logger.debug(LOG, '选择继续上传,重置状态')
this.setData({ state: 'idle', imagePath: '', clothesUuid: '', step: 0, failReason: '' })
} else {
wx.navigateBack({
delta: 1,
fail: (e) => {
logger.warn(LOG, 'navigateBack 失败,改跳衣橱, err=', e && e.errMsg)
wx.switchTab({
url: '/pages/wardrobe/wardrobe',
fail: (e2) => logger.warn(LOG, 'switchTab 衣橱失败, err=', e2 && e2.errMsg),
})
},
})
}
},
fail: (e) => logger.warn(LOG, '上传完成弹窗失败, err=', e && e.errMsg),
})
},
pollStatus(uuid: string) {
let count = 0
;(this as any).pollTimer = setInterval(() => {
count++
request<{ image_process_status: number; fail_reason: string }>({
url: `/clothes/${uuid}/status`,
showError: false,
})
.then((res) => {
const status = res.image_process_status
if (status === ProcessStatus.MATTING) {
this.setData({ step: 1 })
} else if (status === ProcessStatus.CLASSIFYING) {
this.setData({ step: 2 })
} else if (status === ProcessStatus.SUCCESS) {
// 处理成功不再自动跳转,由用户自行从衣橱进入详情
this.clearTimer()
} else if (status === ProcessStatus.FAILED) {
this.clearTimer()
logger.warn(LOG, 'AI 识别失败, 原因=', res.fail_reason)
this.setData({ state: 'failed', failReason: res.fail_reason || '识别失败' })
}
})
.catch((e: any) => logger.warn(LOG, '状态轮询失败, code=', e && e.code))
if (count >= 60) {
this.clearTimer()
logger.warn(LOG, 'AI 处理超时(90s)')
this.setData({ state: 'failed', failReason: '处理超时,请重试' })
}
}, 1500)
},
clearTimer() {
const t = (this as any).pollTimer
if (t) {
clearInterval(t)
;(this as any).pollTimer = null
}
},
onUnload() {
this.clearTimer()
},
showQuotaModal() {
wx.showModal({
title: '上传额度不足',
content: '当前剩余 0 次,购买额度包后可继续上传',
confirmText: '去购买',
cancelText: '取消',
success: (res) => {
if (res.confirm) {
wx.navigateTo({
url: '/pages/mall/mall',
fail: (e) => logger.warn(LOG, 'navigateTo 商城失败, err=', e && e.errMsg),
})
}
},
fail: (e) => logger.warn(LOG, '额度弹窗失败, err=', e && e.errMsg),
})
},
onRetry() {
this.setData({ state: 'idle', imagePath: '', clothesUuid: '', failReason: '' })
},
onManualEdit() {
if (!this.data.clothesUuid) return
wx.redirectTo({
url: `/pages/clothes-detail/clothes-detail?uuid=${this.data.clothesUuid}`,
fail: (e) => logger.warn(LOG, 'redirectTo 衣物详情失败, err=', e && e.errMsg),
})
},
})