Files
ai-human/miniprogram/pages/aiPackage/record/index.ts
T

302 lines
9.4 KiB
TypeScript
Raw Normal View History

2026-07-10 16:14:31 +08:00
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
},
})