diff --git a/TUICallKit-Vue3/components/dev-training-entry/index.vue b/TUICallKit-Vue3/components/dev-training-entry/index.vue
index f5a82248..b24ef9e4 100644
--- a/TUICallKit-Vue3/components/dev-training-entry/index.vue
+++ b/TUICallKit-Vue3/components/dev-training-entry/index.vue
@@ -16,7 +16,7 @@
-
-
-
- {{ tip }}
+
+
+
+
+
+
+ 观看动作示范
+ 视频教学 · 学会正确握法
+
+ ›
+
+
+
+
+
+
+
+
+ 关键要领
+
+
+ {{ tip }}
+
+
@@ -210,6 +243,9 @@ const PRESET_ICONS: Record = {
const TIPS = ['握紧停 1 秒', '完全放松', '左右手交替'] as const
+// 视频教学
+const TUTORIAL_VIDEO_URL = 'https://gz-1349751149.cos.ap-guangzhou.myqcloud.com/uploads/video/20260528/20260528173911bf6f93386.mp4'
+
interface TargetOption {
minutes: 0 | 5 | 10 | 15
label: string
@@ -291,6 +327,7 @@ const targetMinutes = ref<0 | 5 | 10 | 15>(0) // 训练目标(0=自由)
const gripCanvasRef = ref(null)
const showRewardPopup = ref(false)
const showSummary = ref(false)
+const showTutorial = ref(false) // 视频教学弹层
const currentReward = ref(REWARDS[0])
const crushAudioCtx = ref(null)
const bgmAudioCtx = ref(null)
@@ -550,6 +587,20 @@ function dismissSummary() {
showSummary.value = false
}
+// 打开视频教学(避免与 BGM 音轨冲突)
+function openTutorial() {
+ stopBgm()
+ showTutorial.value = true
+}
+
+function closeTutorial() {
+ showTutorial.value = false
+ // 训练中且 BGM 开关开着 → 恢复 BGM
+ if (isPlaying.value && bgmEnabled.value) {
+ startBgm()
+ }
+}
+
function onContinue() {
showSummary.value = false
startTrainingInternal()
@@ -618,22 +669,22 @@ onShareTimeline(() => ({
}))
onHide(() => {
- if (isPlaying.value) {
- metronome.stop()
- stopBgm()
- stopTimer()
- uni.setKeepScreenOn({ keepScreenOn: false })
+ // 切出页面 → 自动暂停训练 + 强制停掉所有音频
+ // 不依赖 isPlaying 判断: 暂停/结算态也可能有未释放的音频实例
+ // requiredBackgroundModes:['audio'] 下,InnerAudioContext.pause 不一定立即生效,这里用 stop+destroy 兜底
+ try { metronome.stop() } catch (_) {}
+ stopTimer()
+ uni.setKeepScreenOn({ keepScreenOn: false })
+
+ if (bgmAudioCtx.value) {
+ try { bgmAudioCtx.value.stop() } catch (_) {}
+ try { bgmAudioCtx.value.destroy() } catch (_) {}
+ bgmAudioCtx.value = null
}
if (crushAudioCtx.value) {
- try {
- crushAudioCtx.value.destroy()
- } catch (_) {}
- }
- if (bgmAudioCtx.value) {
- try {
- bgmAudioCtx.value.destroy()
- } catch (_) {}
- bgmAudioCtx.value = null
+ try { crushAudioCtx.value.stop() } catch (_) {}
+ try { crushAudioCtx.value.destroy() } catch (_) {}
+ crushAudioCtx.value = null
}
})
@@ -1139,26 +1190,190 @@ $shadow-sm: 0 4rpx 12rpx rgba(15, 23, 42, 0.04);
}
/* ============================================================
- * 动作要领条
+ * 视频教学入口卡片
* ============================================================ */
-.tips-strip {
+.tutorial-card {
display: flex;
- flex-wrap: wrap;
- gap: 10rpx;
+ align-items: center;
+ gap: 16rpx;
+ padding: 18rpx 24rpx 18rpx 18rpx;
+ background: linear-gradient(135deg, #ffffff 0%, #f0fdfa 100%);
+ border-radius: 24rpx;
+ border: 2rpx solid rgba(20, 184, 166, 0.18);
+ box-shadow:
+ 0 4rpx 16rpx rgba(15, 23, 42, 0.04),
+ 0 1rpx 0 rgba(255, 255, 255, 0.9) inset;
+ transition: transform 0.15s;
+
+ &:active {
+ transform: scale(0.98);
+ background: linear-gradient(135deg, #f0fdfa 0%, #ecfeff 100%);
+ }
+}
+
+.tutorial-icon {
+ width: 72rpx;
+ height: 72rpx;
+ border-radius: 50%;
+ background: linear-gradient(140deg, $brand 0%, $brand-deep 100%);
+ display: flex;
+ align-items: center;
justify-content: center;
+ flex-shrink: 0;
+ box-shadow: 0 4rpx 12rpx rgba(20, 184, 166, 0.35);
}
-.tip-chip {
- padding: 10rpx 20rpx;
- background: rgba(255, 255, 255, 0.7);
- border-radius: 999rpx;
- border: 2rpx solid $card-border;
+.tutorial-play-triangle {
+ width: 0;
+ height: 0;
+ border-left: 22rpx solid #fff;
+ border-top: 14rpx solid transparent;
+ border-bottom: 14rpx solid transparent;
+ margin-left: 6rpx;
}
-.tip-text {
+.tutorial-info {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ gap: 4rpx;
+ min-width: 0;
+}
+
+.tutorial-title {
+ font-size: 28rpx;
+ font-weight: 700;
+ color: $text-1;
+ letter-spacing: 1rpx;
+}
+
+.tutorial-sub {
font-size: 22rpx;
+ color: $text-3;
+ letter-spacing: 0.5rpx;
+}
+
+.tutorial-arrow {
+ font-size: 36rpx;
+ color: #cbd5e1;
+ font-weight: 300;
+ line-height: 1;
+ flex-shrink: 0;
+}
+
+/* ============================================================
+ * 视频教学弹层
+ * ============================================================ */
+.tutorial-mask {
+ position: fixed;
+ inset: 0;
+ background: rgba(15, 23, 42, 0.65);
+ backdrop-filter: blur(8rpx);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ z-index: 950;
+ padding: 0 24rpx;
+ animation: fade-in 0.2s ease;
+}
+
+.tutorial-modal {
+ width: 100%;
+ max-width: 700rpx;
+ background: #fff;
+ border-radius: 28rpx;
+ overflow: hidden;
+ display: flex;
+ flex-direction: column;
+ animation: tutorial-pop 0.25s cubic-bezier(0.34, 1.56, 0.64, 1);
+}
+
+@keyframes tutorial-pop {
+ from {
+ transform: scale(0.92);
+ opacity: 0;
+ }
+ to {
+ transform: scale(1);
+ opacity: 1;
+ }
+}
+
+.tutorial-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 24rpx 28rpx 16rpx;
+}
+
+.tutorial-modal-title {
+ font-size: 32rpx;
+ font-weight: 700;
+ color: $text-1;
+ letter-spacing: 1rpx;
+}
+
+.tutorial-close {
+ width: 56rpx;
+ height: 56rpx;
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background: #f1f5f9;
+
+ &:active {
+ background: #e2e8f0;
+ }
+}
+
+.tutorial-close-icon {
+ font-size: 40rpx;
color: $text-2;
- line-height: 1.3;
+ line-height: 1;
+ font-weight: 300;
+}
+
+.tutorial-video {
+ width: 100%;
+ // 视频源 720×720 方形, 容器同比贴合避免左右黑边
+ // modal max-width = 700rpx, 这里也用 700rpx 完美 1:1
+ height: 700rpx;
+ background: #000;
+}
+
+.tutorial-tips {
+ padding: 24rpx 28rpx 32rpx;
+ display: flex;
+ flex-direction: column;
+ gap: 12rpx;
+}
+
+.tutorial-tips-title {
+ font-size: 24rpx;
+ color: $text-3;
+ letter-spacing: 1rpx;
+ margin-bottom: 4rpx;
+}
+
+.tutorial-tip-item {
+ display: flex;
+ align-items: center;
+ gap: 12rpx;
+}
+
+.tip-dot {
+ width: 8rpx;
+ height: 8rpx;
+ border-radius: 50%;
+ background: $brand;
+ flex-shrink: 0;
+}
+
+.tip-content {
+ font-size: 26rpx;
+ color: $text-2;
+ line-height: 1.5;
}
/* ============================================================