diff --git a/.gitignore b/.gitignore index f7dd36f3..87f8ee1d 100644 --- a/.gitignore +++ b/.gitignore @@ -33,3 +33,4 @@ bin-release/ /server/.spool /server/.claude /.spool +TUICallKit-Vue3/.env diff --git a/TUICallKit-Vue3/App.vue b/TUICallKit-Vue3/App.vue index c58b9fcc..b88a68b7 100644 --- a/TUICallKit-Vue3/App.vue +++ b/TUICallKit-Vue3/App.vue @@ -15,6 +15,19 @@ const globalData = { let avatarUrl = ref(""); // 声明为响应式变量 uni.CallManager = new CallManager(); onLaunch(() => { + // iOS 静音键模式下 InnerAudioContext 默认不发声, + // 必须在 onLaunch 全局调用此 API,单实例 obeyMuteSwitch 在 iOS 不可靠 + // #ifdef MP-WEIXIN + try { + uni.setInnerAudioOption({ + obeyMuteSwitch: false, + mixWithOther: true, + }) + } catch (e) { + console.warn('setInnerAudioOption failed:', e) + } + // #endif + uni.login({ provider: 'weixin', success: function (loginRes) { diff --git a/TUICallKit-Vue3/components/dev-training-entry/index.vue b/TUICallKit-Vue3/components/dev-training-entry/index.vue new file mode 100644 index 00000000..cb3217b7 --- /dev/null +++ b/TUICallKit-Vue3/components/dev-training-entry/index.vue @@ -0,0 +1,309 @@ + + + + + diff --git a/TUICallKit-Vue3/pages.json b/TUICallKit-Vue3/pages.json index e19aca2a..8fe89074 100644 --- a/TUICallKit-Vue3/pages.json +++ b/TUICallKit-Vue3/pages.json @@ -105,6 +105,27 @@ } } ] + }, + { + "root": "training", + "pages": [ + { + "path": "pages/index", + "style": { + "navigationBarTitleText": "练一练" + } + }, + { + "path": "pages/metronome", + "style": { + "navigationBarTitleText": "节拍器", + "navigationBarBackgroundColor": "#f8fafc", + "navigationBarTextStyle": "black", + "backgroundColor": "#f8fafc", + "disableScroll": true + } + } + ] } ], "tabBar": { diff --git a/TUICallKit-Vue3/pages/user/user.vue b/TUICallKit-Vue3/pages/user/user.vue index 4cc3c0dc..24c8a1a7 100644 --- a/TUICallKit-Vue3/pages/user/user.vue +++ b/TUICallKit-Vue3/pages/user/user.vue @@ -88,13 +88,18 @@ + + + + + diff --git a/TUICallKit-Vue3/training/pages/exercises.ts b/TUICallKit-Vue3/training/pages/exercises.ts new file mode 100644 index 00000000..b72f9c0f --- /dev/null +++ b/TUICallKit-Vue3/training/pages/exercises.ts @@ -0,0 +1,116 @@ +export type AnimationType = + | 'curl' + | 'press' + | 'sidefly' + | 'grip' + | 'crunch' + +export interface ExercisePreset { + id: string + name: string + equipment: '哑铃' | '握力环' | '健身环' | '徒手' + icon: string + animationType: AnimationType + description: string + tips: string[] + contraindication?: string + + defaultBpm: number + bpmMin: number + bpmMax: number + defaultSets: number + defaultReps: number + defaultRestSec: number + + beatsPerRep: number +} + +export const EXERCISE_PRESETS: ExercisePreset[] = [ + { + id: 'dumbbell-curl', + name: '哑铃弯举', + equipment: '哑铃', + icon: '🏋', + animationType: 'curl', + description: '锻炼肱二头肌,注意肘部固定不动', + tips: ['肘部贴紧身体', '上举吸气,下落呼气', '动作放慢,控制重量'], + contraindication: '肘关节炎症急性期不宜', + defaultBpm: 60, + bpmMin: 40, + bpmMax: 100, + defaultSets: 3, + defaultReps: 12, + defaultRestSec: 60, + beatsPerRep: 2, + }, + { + id: 'dumbbell-press', + name: '哑铃推举', + equipment: '哑铃', + icon: '🏋', + animationType: 'press', + description: '锻炼肩部三角肌,核心保持收紧', + tips: ['不要含胸驼背', '推举时呼气', '不要锁死肘关节'], + contraindication: '肩袖损伤者请咨询医生', + defaultBpm: 60, + bpmMin: 40, + bpmMax: 90, + defaultSets: 3, + defaultReps: 10, + defaultRestSec: 90, + beatsPerRep: 2, + }, + { + id: 'dumbbell-sidefly', + name: '哑铃侧平举', + equipment: '哑铃', + icon: '🏋', + animationType: 'sidefly', + description: '锻炼三角肌中束,重量宜轻不宜重', + tips: ['手肘微屈', '抬至与肩平齐即可', '想象用肩膀发力,不是手臂'], + defaultBpm: 60, + bpmMin: 40, + bpmMax: 80, + defaultSets: 3, + defaultReps: 12, + defaultRestSec: 60, + beatsPerRep: 2, + }, + { + id: 'grip-ring', + name: '握力环训练', + equipment: '握力环', + icon: '🟢', + animationType: 'grip', + description: '锻炼前臂屈肌群,改善握力', + tips: ['完全握紧停顿 1 秒', '完全张开放松', '左右手交替'], + defaultBpm: 80, + bpmMin: 60, + bpmMax: 120, + defaultSets: 4, + defaultReps: 20, + defaultRestSec: 45, + beatsPerRep: 2, + }, + { + id: 'crunch', + name: '集中机(仰卧卷腹)', + equipment: '健身环', + icon: '💪', + animationType: 'crunch', + description: '锻炼腹直肌,借助健身环增加阻力', + tips: ['下背贴地', '用腹部发力,不是脖子', '上抬呼气,下落吸气'], + contraindication: '腰椎间盘突出急性期不宜', + defaultBpm: 50, + bpmMin: 40, + bpmMax: 80, + defaultSets: 3, + defaultReps: 15, + defaultRestSec: 60, + beatsPerRep: 2, + }, +] + +export function getPresetById(id: string): ExercisePreset | undefined { + return EXERCISE_PRESETS.find((e) => e.id === id) +} diff --git a/TUICallKit-Vue3/training/pages/index.vue b/TUICallKit-Vue3/training/pages/index.vue new file mode 100644 index 00000000..6552e37b --- /dev/null +++ b/TUICallKit-Vue3/training/pages/index.vue @@ -0,0 +1,610 @@ + + + + + diff --git a/TUICallKit-Vue3/training/pages/metronome.vue b/TUICallKit-Vue3/training/pages/metronome.vue new file mode 100644 index 00000000..023797ee --- /dev/null +++ b/TUICallKit-Vue3/training/pages/metronome.vue @@ -0,0 +1,625 @@ + + + + + diff --git a/TUICallKit-Vue3/training/static/README.md b/TUICallKit-Vue3/training/static/README.md new file mode 100644 index 00000000..1c5442f4 --- /dev/null +++ b/TUICallKit-Vue3/training/static/README.md @@ -0,0 +1,105 @@ +# 训练模块静态资源 + +本目录用于存放"练一练"功能的所有音频素材。 + +## 目录结构 + +``` +training/static/ +├── audio/ +│ └── click.mp3 # 节拍器主音(短促金属敲击声,约 50ms) +├── voice/ +│ ├── numbers/ +│ │ └── 1.mp3 ~ 30.mp3 # 数字报数 +│ └── prompts/ +│ ├── start.mp3 +│ ├── ready.mp3 +│ ├── rest.mp3 +│ ├── next-set.mp3 +│ ├── last-rep.mp3 +│ ├── keep-it-up.mp3 +│ ├── good-job.mp3 +│ ├── breathe-in.mp3 +│ └── breathe-out.mp3 +└── bgm/ + ├── train-light.mp3 # 训练时背景乐(轻快有节奏感) + └── rest-meditation.mp3 # 休息时背景乐(舒缓冥想) +``` + +## 准备步骤 + +### 1. 语音素材(小米 MiMo TTS 自动生成) + +需要 Node 18+(用内置 `fetch`)。先准备小米 MiMo API Key: + +- 文档: https://platform.xiaomimimo.com/ +- API Key 形如 `sk-xxxxxxxx` + +```bash +export MIMO_API_KEY=sk-your-key-here + +cd uniapp +node scripts/generate-voice.mjs +``` + +会自动调用 `mimo-v2.5-tts` 模型生成 39 个 mp3(30 个数字 + 9 个口令)到 `voice/` 目录。 + +**可选参数**: + +```bash +# 换音色(默认 冰糖;可选:冰糖/茉莉/苏打/白桦/Mia/Chloe/Milo/Dean) +node scripts/generate-voice.mjs --voice 茉莉 + +# 换格式(默认 mp3,需要跟 hooks/useVoiceCoach.ts 里的 .mp3 后缀对应) +node scripts/generate-voice.mjs --format wav + +# 强制重新生成(默认存在则跳过) +node scripts/generate-voice.mjs --force +``` + +**音色推荐**(中文女声更适合健身教练): +- `冰糖`:温柔甜美,亲和力强(默认) +- `茉莉`:清爽利落,有"运动博主"感 +- `苏打`:年轻男声,有力量感 +- `白桦`:成熟男声,沉稳 + +### 2. 节拍器 click 音 + +**已内置 3 种音色**(来自 [chrono-bump](https://github.com/johnnysn/chrono-bump) MIT 协议项目): + +| 文件 | 用途 | 大小 | +|------|------|------| +| `audio/click.mp3` | 默认 click(标准节拍器音) | 3.4 KB | +| `audio/click-soft.mp3` | 柔和音色 | 4.2 KB | +| `audio/click-wood.mp3` | 木鱼/原木质感(推荐用于重音 accent) | 4.6 KB | + +如果想换更好听的,可以去: +- [freesound.org](https://freesound.org) 搜 "click" / "tick"(注意 CC 协议) +- [pixabay.com/sound-effects](https://pixabay.com/sound-effects/) 搜 "metronome"(免费可商用) + +替换时保持文件名不变即可,无需改代码。 + +### 3. 背景音乐 + +每首 30 秒~2 分钟即可(loop 后听不出接缝)。 + +推荐来源(免费可商用): +- [pixabay.com/music](https://pixabay.com/music) 搜 "calm" / "meditation" / "lofi" +- [freemusicarchive.org](https://freemusicarchive.org) +- [bensound.com](https://bensound.com)(含署名) + +文件大小建议 < 1MB(mp3 128kbps 单声道即可)。 + +## 在代码里被引用的位置 + +- `training/hooks/useMetronome.ts` → `audio/click.mp3` +- `training/hooks/useVoiceCoach.ts` → `voice/numbers/*.mp3`、`voice/prompts/*.mp3` +- `training/hooks/useTrainingBgm.ts` → `bgm/*.mp3` + +如需修改路径,修改对应 hook 文件里的常量即可。 + +## 注意事项 + +- 微信小程序对单个文件大小有 10MB 上限,本目录所有文件加起来建议控制在 5MB 以内。 +- 小程序整包大小限制(主包 2MB / 总包 20MB),如果资源较大建议放 CDN 而非本地 static。 + - 把 `useVoiceCoach.ts` 里的 `VOICE_BASE` 改成 CDN URL 即可。 diff --git a/TUICallKit-Vue3/training/static/audio/click-wood.mp3 b/TUICallKit-Vue3/training/static/audio/click-wood.mp3 new file mode 100644 index 00000000..fb3fff34 Binary files /dev/null and b/TUICallKit-Vue3/training/static/audio/click-wood.mp3 differ diff --git a/TUICallKit-Vue3/training/static/audio/click.mp3 b/TUICallKit-Vue3/training/static/audio/click.mp3 new file mode 100644 index 00000000..faa1693a Binary files /dev/null and b/TUICallKit-Vue3/training/static/audio/click.mp3 differ diff --git a/TUICallKit-Vue3/training/static/footprint.svg b/TUICallKit-Vue3/training/static/footprint.svg new file mode 100644 index 00000000..83290b90 --- /dev/null +++ b/TUICallKit-Vue3/training/static/footprint.svg @@ -0,0 +1,11 @@ + + + + + + + + + + +