update
This commit is contained in:
@@ -74,28 +74,11 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- ===== 控制面板:统计 + 档位 + 目标 ===== -->
|
||||
<!-- ===== 控制面板:训练强度(主) + 训练时长(折叠) ===== -->
|
||||
<view class="control-deck" :class="{ 'is-playing': isPlaying, 'is-paused': isPaused }">
|
||||
<view class="stats-row">
|
||||
<view class="stat-chip">
|
||||
<text class="stat-chip-label">次数</text>
|
||||
<text class="stat-chip-value" :class="{ muted: !hasStats }">{{ hasStats ? totalReps : '—' }}</text>
|
||||
</view>
|
||||
<view class="stat-chip">
|
||||
<text class="stat-chip-label">时长</text>
|
||||
<text class="stat-chip-value" :class="{ muted: !hasStats }">{{ hasStats ? formatTime(elapsedSeconds) : '—' }}</text>
|
||||
</view>
|
||||
<view class="stat-chip">
|
||||
<text class="stat-chip-label">糖分</text>
|
||||
<text class="stat-chip-value" :class="{ muted: !hasStats }">{{ hasStats ? `≈${totalSugar.toFixed(1)}g` : '—' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="deck-divider" />
|
||||
|
||||
<view class="presets-block">
|
||||
<view class="presets-header">
|
||||
<text class="presets-title">训练强度</text>
|
||||
<text class="presets-title">选择训练强度</text>
|
||||
<text v-if="isPlaying" class="presets-hint">训练中不可切换</text>
|
||||
</view>
|
||||
<view class="presets">
|
||||
@@ -106,27 +89,24 @@
|
||||
:class="{ active: currentPreset === p.id, disabled: isPlaying }"
|
||||
@click="onPresetTap(p.id)"
|
||||
>
|
||||
<text class="preset-icon">{{ PRESET_ICONS[p.id] }}</text>
|
||||
<text class="preset-name">{{ p.label }}</text>
|
||||
<text class="preset-bpm">{{ p.bpm }} BPM</text>
|
||||
<text class="preset-desc">{{ p.desc }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 当前强度预估消耗 -->
|
||||
<view class="intensity-estimate">
|
||||
<text class="estimate-icon">🔥</text>
|
||||
<text class="estimate-text">{{ estimateText }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="deck-divider" />
|
||||
|
||||
<!-- 训练时长(默认折叠,点击展开) -->
|
||||
<view class="target-block">
|
||||
<view class="target-header">
|
||||
<text class="target-title">训练目标</text>
|
||||
<text class="target-hint">到时间自动结束</text>
|
||||
<view class="target-header" @click="targetExpanded = !targetExpanded">
|
||||
<text class="target-title">训练时长</text>
|
||||
<view class="target-current">
|
||||
<text class="target-current-text">{{ currentTargetLabel }}</text>
|
||||
<text class="target-toggle">{{ targetExpanded ? '▲' : '▼' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="target-chips">
|
||||
<view v-if="targetExpanded" class="target-chips">
|
||||
<view
|
||||
v-for="t in TARGET_OPTIONS"
|
||||
:key="t.minutes"
|
||||
@@ -141,7 +121,7 @@
|
||||
|
||||
<view class="deck-divider" />
|
||||
|
||||
<!-- 训练功效介绍(可折叠) -->
|
||||
<!-- 训练功效介绍(可折叠,默认收起) -->
|
||||
<view class="benefit-block">
|
||||
<view class="benefit-header" @click="benefitExpanded = !benefitExpanded">
|
||||
<text class="benefit-title">训练功效</text>
|
||||
@@ -270,21 +250,6 @@ const GRIP_PRESETS: readonly GripPreset[] = [
|
||||
{ id: 'heavy', bpm: 100, met: 4.5, label: '重度', desc: '强化 · 挑战' },
|
||||
]
|
||||
|
||||
// 训练功效介绍(专业文案)
|
||||
const TRAINING_BENEFIT = {
|
||||
muscles: ['前臂屈肌', '手部小肌群', '握力'],
|
||||
effect: '增强握力与前臂肌肉耐力,促进手部血液循环,缓解手指与腕部僵硬,适合日常碎片化锻炼及手部功能康复。',
|
||||
}
|
||||
|
||||
// 强度预估参考时长(选择"自由"目标时按此估算)
|
||||
const ESTIMATE_REF_MINUTES = 10
|
||||
|
||||
const PRESET_ICONS: Record<PresetId, string> = {
|
||||
light: '🌱',
|
||||
medium: '💪',
|
||||
heavy: '🔥',
|
||||
}
|
||||
|
||||
// 画布指导视频(跟随训练状态播放)
|
||||
const TUTORIAL_VIDEO_URL = 'https://gz-1349751149.cos.ap-guangzhou.myqcloud.com/uploads/video/20260528/20260528173911bf6f93386.mp4'
|
||||
// 视频封面(未开始/暂停时显示的静态首帧)
|
||||
@@ -391,20 +356,25 @@ const timerInterval = ref<number | null>(null)
|
||||
const preset = computed(() => GRIP_PRESETS.find(p => p.id === currentPreset.value) || GRIP_PRESETS[1])
|
||||
const currentBpm = computed(() => preset.value.bpm)
|
||||
const currentMet = computed(() => preset.value.met)
|
||||
const benefitExpanded = ref(false)
|
||||
|
||||
// 当前强度预估消耗文案: 按选中目标时长(自由→10分钟参考)估算
|
||||
const estimateText = computed(() => {
|
||||
const mins = targetMinutes.value > 0 ? targetMinutes.value : ESTIMATE_REF_MINUTES
|
||||
const sugar = (currentMet.value * 60 * (mins / 60)) / 4
|
||||
const suffix = targetMinutes.value > 0 ? '' : '(按 10 分钟估算)'
|
||||
return `${preset.value.label}训练 ${mins} 分钟,约消耗 ${sugar.toFixed(1)}g 糖分${suffix}`
|
||||
// 训练时长折叠状态 + 当前时长文案
|
||||
const targetExpanded = ref(false)
|
||||
const currentTargetLabel = computed(() => {
|
||||
const opt = TARGET_OPTIONS.find(t => t.minutes === targetMinutes.value)
|
||||
return opt ? opt.label : '自由'
|
||||
})
|
||||
|
||||
// 训练功效介绍(专业文案) + 折叠状态
|
||||
const benefitExpanded = ref(false)
|
||||
const TRAINING_BENEFIT = {
|
||||
muscles: ['前臂屈肌', '手部小肌群', '握力'],
|
||||
effect: '增强握力与前臂肌肉耐力,促进手部血液循环,缓解手指与腕部僵硬,适合日常碎片化锻炼及手部功能康复。',
|
||||
}
|
||||
|
||||
const metronome = useMetronome({
|
||||
initialBpm: currentBpm.value,
|
||||
accentEvery: 2,
|
||||
volume: 0.35, // 节拍音音量调小,避免木鱼声太大
|
||||
silent: true, // 移除木鱼节奏音,仅保留节拍驱动(视觉缩放/次数统计)
|
||||
onBeat: onBeat,
|
||||
})
|
||||
const { isPlaying } = metronome
|
||||
@@ -905,7 +875,7 @@ $shadow-sm: 0 4rpx 12rpx rgba(15, 23, 42, 0.04);
|
||||
margin-left: -28rpx;
|
||||
margin-right: -28rpx;
|
||||
flex-shrink: 0;
|
||||
height: 520rpx;
|
||||
height: 740rpx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
@@ -938,8 +908,8 @@ $shadow-sm: 0 4rpx 12rpx rgba(15, 23, 42, 0.04);
|
||||
|
||||
.ring-container {
|
||||
position: relative;
|
||||
width: 560rpx;
|
||||
height: 560rpx;
|
||||
width: 720rpx;
|
||||
height: 720rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@@ -948,8 +918,8 @@ $shadow-sm: 0 4rpx 12rpx rgba(15, 23, 42, 0.04);
|
||||
|
||||
.beat-halo {
|
||||
position: absolute;
|
||||
width: 560rpx;
|
||||
height: 560rpx;
|
||||
width: 720rpx;
|
||||
height: 720rpx;
|
||||
border-radius: 50%;
|
||||
background: radial-gradient(circle,
|
||||
rgba(20, 184, 166, 0.45) 0%,
|
||||
@@ -978,8 +948,8 @@ $shadow-sm: 0 4rpx 12rpx rgba(15, 23, 42, 0.04);
|
||||
|
||||
.video-ring {
|
||||
position: relative;
|
||||
width: 460rpx;
|
||||
height: 460rpx;
|
||||
width: 640rpx;
|
||||
height: 640rpx;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
box-shadow:
|
||||
@@ -1043,8 +1013,8 @@ $shadow-sm: 0 4rpx 12rpx rgba(15, 23, 42, 0.04);
|
||||
}
|
||||
|
||||
.hint-icon {
|
||||
width: 116rpx;
|
||||
height: 116rpx;
|
||||
width: 140rpx;
|
||||
height: 140rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@@ -1060,10 +1030,10 @@ $shadow-sm: 0 4rpx 12rpx rgba(15, 23, 42, 0.04);
|
||||
.play-triangle {
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 34rpx solid #ffffff;
|
||||
border-top: 23rpx solid transparent;
|
||||
border-bottom: 23rpx solid transparent;
|
||||
margin-left: 8rpx;
|
||||
border-left: 44rpx solid #ffffff;
|
||||
border-top: 30rpx solid transparent;
|
||||
border-bottom: 30rpx solid transparent;
|
||||
margin-left: 10rpx;
|
||||
}
|
||||
|
||||
@keyframes hint-bounce {
|
||||
@@ -1076,7 +1046,7 @@ $shadow-sm: 0 4rpx 12rpx rgba(15, 23, 42, 0.04);
|
||||
}
|
||||
|
||||
.hint-text {
|
||||
font-size: 26rpx;
|
||||
font-size: 34rpx;
|
||||
color: #ffffff;
|
||||
font-weight: 700;
|
||||
letter-spacing: 3rpx;
|
||||
@@ -1261,8 +1231,8 @@ $shadow-sm: 0 4rpx 12rpx rgba(15, 23, 42, 0.04);
|
||||
}
|
||||
|
||||
.presets-title {
|
||||
font-size: 26rpx;
|
||||
font-weight: 700;
|
||||
font-size: 32rpx;
|
||||
font-weight: 800;
|
||||
color: $text-1;
|
||||
}
|
||||
|
||||
@@ -1279,38 +1249,26 @@ $shadow-sm: 0 4rpx 12rpx rgba(15, 23, 42, 0.04);
|
||||
.preset {
|
||||
flex: 1;
|
||||
background: #f8fafc;
|
||||
border: 2rpx solid transparent;
|
||||
border-radius: 20rpx;
|
||||
padding: 20rpx 6rpx 18rpx;
|
||||
border: 3rpx solid transparent;
|
||||
border-radius: 24rpx;
|
||||
padding: 32rpx 8rpx 28rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 4rpx;
|
||||
gap: 10rpx;
|
||||
transition: all 0.18s;
|
||||
|
||||
.preset-icon {
|
||||
font-size: 32rpx;
|
||||
line-height: 1;
|
||||
margin-bottom: 4rpx;
|
||||
}
|
||||
|
||||
.preset-name {
|
||||
font-size: 26rpx;
|
||||
font-weight: 700;
|
||||
font-size: 40rpx;
|
||||
font-weight: 800;
|
||||
color: $text-1;
|
||||
}
|
||||
.preset-bpm {
|
||||
font-size: 22rpx;
|
||||
font-size: 26rpx;
|
||||
color: $text-2;
|
||||
font-weight: 700;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.preset-desc {
|
||||
font-size: 18rpx;
|
||||
color: $text-3;
|
||||
text-align: center;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: scale(0.97);
|
||||
@@ -1349,17 +1307,34 @@ $shadow-sm: 0 4rpx 12rpx rgba(15, 23, 42, 0.04);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 4rpx;
|
||||
padding: 12rpx 6rpx;
|
||||
border-radius: 16rpx;
|
||||
|
||||
&:active {
|
||||
background: #f8fafc;
|
||||
}
|
||||
}
|
||||
|
||||
.target-title {
|
||||
font-size: 26rpx;
|
||||
font-weight: 700;
|
||||
font-size: 32rpx;
|
||||
font-weight: 800;
|
||||
color: $text-1;
|
||||
}
|
||||
|
||||
.target-hint {
|
||||
font-size: 20rpx;
|
||||
.target-current {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.target-current-text {
|
||||
font-size: 30rpx;
|
||||
font-weight: 700;
|
||||
color: $brand-deep;
|
||||
}
|
||||
|
||||
.target-toggle {
|
||||
font-size: 22rpx;
|
||||
color: $text-3;
|
||||
}
|
||||
|
||||
@@ -1370,7 +1345,7 @@ $shadow-sm: 0 4rpx 12rpx rgba(15, 23, 42, 0.04);
|
||||
|
||||
.target-chip {
|
||||
flex: 1;
|
||||
height: 72rpx;
|
||||
height: 88rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@@ -1380,8 +1355,8 @@ $shadow-sm: 0 4rpx 12rpx rgba(15, 23, 42, 0.04);
|
||||
transition: all 0.18s;
|
||||
|
||||
.target-chip-text {
|
||||
font-size: 24rpx;
|
||||
font-weight: 600;
|
||||
font-size: 30rpx;
|
||||
font-weight: 700;
|
||||
color: $text-2;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user