diff --git a/TUICallKit-Vue3/training/pages/components/pose-intro.vue b/TUICallKit-Vue3/training/pages/components/pose-intro.vue index 4404feb65..a5c3f3d1c 100644 --- a/TUICallKit-Vue3/training/pages/components/pose-intro.vue +++ b/TUICallKit-Vue3/training/pages/components/pose-intro.vue @@ -104,10 +104,7 @@ const minutes = computed(() => { function change(dir: 1 | -1) { const [lo, hi] = props.exercise.repRange const next = Math.min(hi, Math.max(lo, props.target + dir * props.exercise.repStep)) - if (next !== props.target) { - emit('update:target', next) - uni.vibrateShort({ type: 'light' } as any) - } + if (next !== props.target) emit('update:target', next) } diff --git a/TUICallKit-Vue3/training/pages/pose-coach.vue b/TUICallKit-Vue3/training/pages/pose-coach.vue index 125b02066..5ebdbb13c 100644 --- a/TUICallKit-Vue3/training/pages/pose-coach.vue +++ b/TUICallKit-Vue3/training/pages/pose-coach.vue @@ -479,12 +479,19 @@ function startStage(m: 'vision' | 'sim' | 'guided') { function onCameraReady() { if (mode.value !== 'vision') return camera.start() - setTimeout(() => { - const s = camera.status.value - if (s === 'unsupported' || s === 'error') fallbackToGuided('当前手机暂不支持动作识别,已切换为领练模式') - }, 1500) } +// VisionKit 启动、取帧失败是异步回调,可能晚于任何固定等待时间;随时失败随时切到领练,不会卡在站位。 +// 同步触发:再来一次时状态会在同一轮里 unsupported → starting → unsupported,默认异步合并后看不到变化 +watch( + () => camera.status.value, + (s) => { + if (stage.value !== 'camera' || mode.value !== 'vision') return + if (s === 'unsupported' || s === 'error') fallbackToGuided('当前手机暂不支持动作识别,已切换为领练模式') + }, + { flush: 'sync' }, +) + function onCameraError() { camera.onCameraError(null) fallbackToGuided('摄像头不可用,已切换为领练模式') @@ -541,18 +548,15 @@ function onEvent(e: SessionEvent) { break case 'countdown': voice.speak(String(e.n), 'high') - uni.vibrateShort({ type: 'light' } as any) break case 'rep': { if (mode.value === 'guided') { - // 领练模式不评分:只报数和轻震动 - uni.vibrateShort({ type: 'light' } as any) + // 领练模式不评分:只报数 if (shouldSpeakCount(e.count)) voice.speak(String(e.count), 'count') break } const text = e.grade === 'perfect' ? '完美' : e.grade === 'good' ? '不错' : '加油' Object.assign(pop, { key: pop.key + 1, text, score: e.score, grade: e.grade }) - uni.vibrateShort({ type: e.grade === 'perfect' ? 'medium' : 'light' } as any) if (e.tip) { showTip(e.tip, e.joints ?? []) voice.speak(e.tip, 'high') @@ -648,11 +652,14 @@ function onPause() { function onResume() { if (!session) return handle(session.resume(Date.now())) + // 倒计时中暂停,恢复后会回到站位;领练模式没有识别、站位永远过不去,直接重新倒计时 + if (mode.value === 'guided' && session.phase === 'framing') startGuided() } function finish() { if (!session || stage.value !== 'camera') return - handle(session.finish()) + // 只改状态、不派发 done 事件:done 的处理就是调用 finish,派发会重入,同一次训练记两条历史 + session.finish() const s = summarize(session, ex.value.netMet) if (mode.value === 'guided') { Object.assign(s, {