import { taskinfo, uploadavatar, uploadrecord, checkavatar, checkvoice, saveHumanAssets } from "../../../utils/request" const waveBars = [ { id: 'b1', cls: 'bar-22' }, { id: 'b2', cls: 'bar-34' }, { id: 'b3', cls: 'bar-48' }, { id: 'b4', cls: 'bar-34' }, { id: 'b5', cls: 'bar-62' }, { id: 'b6', cls: 'bar-42' }, { id: 'b7', cls: 'bar-70' }, { id: 'b8', cls: 'bar-52' }, { id: 'b9', cls: 'bar-38' }, { id: 'b10', cls: 'bar-58' }, { id: 'b11', cls: 'bar-46' }, { id: 'b12', cls: 'bar-30' }, ] const miniBars = [ { id: 'm1', cls: 'mini-bar-18' }, { id: 'm2', cls: 'mini-bar-30' }, { id: 'm3', cls: 'mini-bar-46' }, { id: 'm4', cls: 'mini-bar-28' }, { id: 'm5', cls: 'mini-bar-56' }, { id: 'm6', cls: 'mini-bar-36' }, ] function formatRecordTime(totalSeconds: number) { const minutes = Math.floor(totalSeconds / 60) const seconds = totalSeconds % 60 return `${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}` } Page({ recordTimer: 0, voiceCheckTimer: 0, data: { isRecording: false, recordingClass: 'recording-active', elapsedSeconds: 0, recordTime: '00:00', waveBars, miniBars, taskinfo: null as any, avatar: '/static/images/my/image.png', avatarUrl: '', avatarChecked: false, recordUrl: '', recordDurationMs: 0, voiceAsrTaskId: '', voiceChecked: false, voiceText: '', voiceStatusText: '' }, onLoad(ops: any) { taskinfo(ops.taskid).then((res: any) => { if (!res.code) { this.setData({ taskinfo: res.data }); } }) }, onShow() { if (this.data.isRecording) { this.startRecordTimer() } }, onHide() { this.clearRecordTimer() this.clearVoiceCheckTimer() }, onUnload() { this.clearRecordTimer() this.clearVoiceCheckTimer() }, handleBack() { wx.navigateBack({ fail: () => wx.navigateTo({ url: '/pages/aiPackage/preview/index' }), }) }, handleUploadPhoto() { const that = this; wx.chooseImage({ count: 1, success(res: any) { const file = res.tempFilePaths[0]; wx.showLoading({ mask: true, title: '上传照片中' }); uploadavatar(file, {}, '/api/upload/avatar').then((uploadRes: any) => { if (uploadRes.code) { wx.hideLoading(); wx.showModal({ title: '温馨提示', content: uploadRes.message || '照片上传失败', showCancel: false }) return; } wx.showLoading({ mask: true, title: '人脸检测中' }); checkavatar(uploadRes.data.url).then((checkRes: any) => { wx.hideLoading(); if (!checkRes.code) { that.setData({ avatar: uploadRes.data.url, avatarUrl: uploadRes.data.url, avatarChecked: true }) wx.showToast({ title: '照片检测通过', icon: 'success' }) } else { that.setData({ avatarUrl: '', avatarChecked: false }) wx.showModal({ title: '温馨提示', content: checkRes.message || '未检测到人脸', showCancel: false }) } }).catch(() => { wx.hideLoading(); that.setData({ avatarUrl: '', avatarChecked: false }) wx.showModal({ title: '温馨提示', content: '人脸检测失败,请稍后再试', showCancel: false }) }) }).catch(() => { wx.hideLoading(); wx.showModal({ title: '温馨提示', content: '网络异常,请稍后再试', showCancel: false }) }) } }) }, handleUseDefaultAvatar() { wx.showToast({ title: '暂未配置默认形象', icon: 'none' }) }, handleRecord() { if (this.data.isRecording) return if (this.recordTimer) { clearTimeout(this.recordTimer); clearInterval(this.recordTimer); } this.clearVoiceCheckTimer() this.recordTimer = 0; this.setData({ isRecording: true, recordTime: '00:00', elapsedSeconds: 0, recordingClass: 'recording-active', recordUrl: '', recordDurationMs: 0, voiceAsrTaskId: '', voiceChecked: false, voiceText: '', voiceStatusText: '' }) this.startRecordTimer() }, handleUseDefaultVoice() { this.clearRecordTimer() this.clearVoiceCheckTimer() this.setData({ isRecording: false, recordingClass: '' }) wx.showToast({ title: '暂未配置默认音色', icon: 'none' }) }, handleStopRecord() { this.clearRecordTimer() this.recordTimer = 0; this.setData({ isRecording: false, recordingClass: '' }) wx.getRecorderManager().stop() }, handleNext() { if (!this.data.taskinfo || !this.data.taskinfo.task_no) { wx.showModal({ title: '温馨提示', content: '创作记录不存在', showCancel: false }) return } if (!this.data.avatarChecked || !this.data.avatarUrl) { wx.showModal({ title: '温馨提示', content: '请先上传并通过数字人照片检测', showCancel: false }) return } if (!this.data.voiceChecked || !this.data.recordUrl || !this.data.voiceText) { wx.showModal({ title: '温馨提示', content: '请先录制并通过音色检测', showCancel: false }) return } wx.showLoading({ mask: true, title: '保存中' }) saveHumanAssets({ taskId: this.data.taskinfo.task_no, avatarUrl: this.data.avatarUrl, voiceUrl: this.data.recordUrl, voiceText: this.data.voiceText, durationMs: this.data.recordDurationMs, asrTaskId: this.data.voiceAsrTaskId }).then((res: any) => { wx.hideLoading() if (!res.code) { wx.redirectTo({ url: '/pages/aiPackage/bg/index?taskId=' + encodeURIComponent(this.data.taskinfo.task_no) }) } else { wx.showModal({ title: '温馨提示', content: res.message || '保存失败', showCancel: false }) } }).catch(() => { wx.hideLoading() wx.showModal({ title: '温馨提示', content: '网络异常,请稍后再试', showCancel: false }) }) }, startRecordTimer() { let that = this; that.clearRecordTimer() that.recordTimer = setInterval(() => { const elapsedSeconds = that.data.elapsedSeconds + 1 that.setData({ elapsedSeconds, recordTime: formatRecordTime(elapsedSeconds) }) }, 1000) as unknown as number let recorder = wx.getRecorderManager(); recorder.onError(that.onRecordError.bind(that)); recorder.onStop(that.onRecordStop.bind(that)) recorder.start({ duration: 10000, format: 'mp3' }); }, onRecordError(err: any) { console.log(err) this.clearRecordTimer() this.setData({ isRecording: false, recordingClass: '' }) wx.showModal({ title: '温馨提示', content: '录音失败,请重新录制', showCancel: false }) }, onRecordStop(resource: { duration: number, fileSize: number, tempFilePath: string }) { this.clearRecordTimer() this.recordTimer = 0; this.setData({ isRecording: false, recordingClass: '', voiceStatusText: '录音上传中' }) wx.showLoading({ mask: true, title: '上传录音中' }) uploadrecord(resource.tempFilePath).then((res: any) => { if (res.code) { wx.hideLoading() this.setData({ voiceStatusText: '', voiceChecked: false }) wx.showModal({ title: '温馨提示', content: res.message || '录音上传失败', showCancel: false }) return } this.setData({ recordUrl: res.data.url, recordDurationMs: res.data.durationMs || resource.duration || 0, voiceAsrTaskId: '', voiceChecked: false, voiceText: '', voiceStatusText: '录音识别中' }) this.pollVoiceCheck('') }).catch(() => { wx.hideLoading() this.setData({ voiceStatusText: '', voiceChecked: false }) wx.showModal({ title: '温馨提示', content: '网络异常,请稍后再试', showCancel: false }) }) }, pollVoiceCheck(asrTaskId: string) { checkvoice(this.data.recordUrl, asrTaskId).then((res: any) => { if (res.code === 200) { wx.hideLoading() const nextTaskId = res.data && res.data.asrTaskId ? res.data.asrTaskId : asrTaskId this.setData({ voiceAsrTaskId: nextTaskId, voiceStatusText: '录音识别中' }) this.clearVoiceCheckTimer() this.voiceCheckTimer = setTimeout(() => { this.pollVoiceCheck(nextTaskId) }, 1500) as unknown as number return } wx.hideLoading() if (!res.code) { this.clearVoiceCheckTimer() this.setData({ voiceAsrTaskId: res.data.asrTaskId, voiceText: res.data.recognizedText, voiceChecked: true, voiceStatusText: '录音检测通过' }) wx.showToast({ title: '音色检测通过', icon: 'success' }) } else { this.clearVoiceCheckTimer() this.setData({ voiceChecked: false, voiceStatusText: '' }) wx.showModal({ title: '温馨提示', content: res.message || '录音识别失败,请重新录制', showCancel: false }) } }).catch(() => { wx.hideLoading() this.clearVoiceCheckTimer() this.setData({ voiceChecked: false, voiceStatusText: '' }) wx.showModal({ title: '温馨提示', content: '录音识别失败,请稍后再试', showCancel: false }) }) }, clearRecordTimer() { if (!this.recordTimer) return clearInterval(this.recordTimer) this.recordTimer = 0 }, clearVoiceCheckTimer() { if (!this.voiceCheckTimer) return clearTimeout(this.voiceCheckTimer) this.voiceCheckTimer = 0 }, })