feat: v1.5 — plan editor, voice prompts, UI polish
Major features: - Training plan editor: edit preset days/duration per plan (in-place override via custom_plans storage; preset ids preserved so existing records stay valid) - Voice prompts at 4 fixed points during training (halfway / 30s / 10s / done) via Tencent Cloud TTS (cloudfunctions/tts + utils/voice.js) - Free-training button: switched from outline (transparent) to ghost variant (theme-tinted) for visual weight Bug fixes: - 4 functional pages: SVG data URIs failed to render in WeChat because '\#' in colors was parsed as a data-URI fragment delimiter. encode the whole SVG via encodeURIComponent in utils/icons.js build(). - Old records (saved before id field was added) could not be deleted (data-id was empty, triggered defensive guard). Backfill stable legacy-<month>-<index>-<duration> ids in getRecords() and persist. - settings.js plan-row editor button event was bubbling up to the row's bindtap (which also fired onSelectPlan). Wrapped in catch:tap. UI: - Settings page: 3 hardcoded plans replaced with dynamic buildPlansList that overlays custom_plans on top of presets - Plan editor: bottom-sheet modal in settings page (regular view, not ui-modal — WeChat custom component root element drops position:fixed in this runtime) - Free-training button: rounded pill style, full-width, 24rpx gap from primary action; bottom sheet uses max-height: 88vh + internal scroll - Version bumped to v1.5, last updated 2026-06-10 Removed: - Daily reminder section (dailyReminder / reminderTime) — replaced by the 4 voice prompts which cover the same user need without requiring long-term scheduling that WeChat mini-programs can't actually do Misc: - utils/plan.js refactored to formula-driven: presets declare totalDays/startTarget/increment/cycleDays, days[] is generated. Same formula applies to custom plans. - Timer _remind() guards each prompt on minimum duration so short free-mode sessions don't fire 'last30' at the start - Cloud storage cloud_plans field added to data push payload; restored on first install via _restoreFromCloud - .gitignore added for local AI tool caches (.reasonix/, reasonix.toml, .codegraph/daemon.pid)
This commit is contained in:
+127
-47
@@ -1,8 +1,8 @@
|
||||
<view class="container" style="{{themeStyle}}">
|
||||
<!-- 配色方案 -->
|
||||
<view class="card">
|
||||
<ui-card variant="in">
|
||||
<view class="section-header">
|
||||
<text class="section-emoji">🎨</text>
|
||||
<image class="section-icon" src="{{icons.skinFill}}" mode="aspectFit"></image>
|
||||
<text class="section-title">配色方案</text>
|
||||
</view>
|
||||
<view class="theme-list">
|
||||
@@ -15,15 +15,15 @@
|
||||
>
|
||||
<view class="theme-color-dot" style="background: {{item.primary}};"></view>
|
||||
<text class="theme-name">{{item.name}}</text>
|
||||
<text class="plan-check" wx:if="{{currentThemeId === item.id}}">✓</text>
|
||||
<image class="theme-check-img" wx:if="{{currentThemeId === item.id}}" src="{{icons.check}}" mode="aspectFit"></image>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</ui-card>
|
||||
|
||||
<!-- 训练计划选择 -->
|
||||
<view class="card">
|
||||
<ui-card variant="in">
|
||||
<view class="section-header">
|
||||
<text class="section-emoji">📋</text>
|
||||
<image class="section-icon" src="{{icons.formFill}}" mode="aspectFit"></image>
|
||||
<text class="section-title">训练计划</text>
|
||||
</view>
|
||||
<view class="plan-list">
|
||||
@@ -41,82 +41,68 @@
|
||||
<text class="plan-item-name">{{item.name}}</text>
|
||||
<text class="plan-item-desc">{{item.desc}}</text>
|
||||
</view>
|
||||
<text class="plan-check" wx:if="{{currentPlanId === item.id}}">✓</text>
|
||||
<view class="plan-actions">
|
||||
<view class="plan-edit-btn" catch:tap="onTapEditPlan" data-id="{{item.id}}">
|
||||
<image class="plan-edit-img" src="{{icons.edit}}" mode="aspectFit"></image>
|
||||
</view>
|
||||
<image class="plan-check-img" wx:if="{{currentPlanId === item.id}}" src="{{icons.check}}" mode="aspectFit"></image>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 提醒设置 -->
|
||||
<view class="card">
|
||||
<view class="section-header">
|
||||
<text class="section-emoji">🔔</text>
|
||||
<text class="section-title">提醒</text>
|
||||
</view>
|
||||
<view class="setting-row">
|
||||
<view class="setting-left">
|
||||
<text class="setting-icon">📅</text>
|
||||
<text class="setting-label">每日提醒</text>
|
||||
</view>
|
||||
<switch checked="{{dailyReminder}}" bindchange="onToggleReminder" color="{{theme.primary}}"/>
|
||||
</view>
|
||||
<view class="setting-row" wx:if="{{dailyReminder}}">
|
||||
<view class="setting-left">
|
||||
<text class="setting-icon">🕐</text>
|
||||
<text class="setting-label">提醒时间</text>
|
||||
</view>
|
||||
<picker mode="time" value="{{reminderTime}}" bindchange="onReminderTimeChange">
|
||||
<text class="setting-value">{{reminderTime}}</text>
|
||||
</picker>
|
||||
</view>
|
||||
</view>
|
||||
</ui-card>
|
||||
|
||||
<!-- 训练偏好 -->
|
||||
<view class="card">
|
||||
<ui-card variant="in">
|
||||
<view class="section-header">
|
||||
<text class="section-emoji">⚙</text>
|
||||
<image class="section-icon" src="{{icons.settingsFill}}" mode="aspectFit"></image>
|
||||
<text class="section-title">训练偏好</text>
|
||||
</view>
|
||||
<view class="setting-row">
|
||||
<view class="setting-left">
|
||||
<text class="setting-icon">🔊</text>
|
||||
<image class="setting-icon" src="{{icons.voiceFill}}" mode="aspectFit"></image>
|
||||
<text class="setting-label">语音引导</text>
|
||||
</view>
|
||||
<switch checked="{{voiceGuide}}" bindchange="onToggleVoice" color="{{theme.primary}}"/>
|
||||
</view>
|
||||
<view class="setting-row">
|
||||
<view class="setting-left">
|
||||
<text class="setting-icon">📳</text>
|
||||
<image class="setting-icon" src="{{icons.notificationFill}}" mode="aspectFit"></image>
|
||||
<text class="setting-label">振动反馈</text>
|
||||
</view>
|
||||
<switch checked="{{vibrate}}" bindchange="onToggleVibrate" color="{{theme.primary}}"/>
|
||||
</view>
|
||||
</view>
|
||||
</ui-card>
|
||||
|
||||
<!-- 数据管理 -->
|
||||
<view class="card">
|
||||
<ui-card variant="in">
|
||||
<view class="section-header">
|
||||
<text class="section-emoji">🗑</text>
|
||||
<image class="section-icon" src="{{icons.deleteActive}}" mode="aspectFit"></image>
|
||||
<text class="section-title">数据管理</text>
|
||||
</view>
|
||||
<button class="btn-danger" bindtap="onClearData">
|
||||
<text>清除所有训练记录</text>
|
||||
</button>
|
||||
</view>
|
||||
<ui-btn
|
||||
variant="danger"
|
||||
size="md"
|
||||
block
|
||||
text="清除所有训练记录"
|
||||
icon-src="{{icons.delete}}"
|
||||
bindtap="onClearData"
|
||||
></ui-btn>
|
||||
</ui-card>
|
||||
|
||||
<!-- 关于 -->
|
||||
<view class="card about-card">
|
||||
<ui-card variant="in">
|
||||
<view class="section-header">
|
||||
<text class="section-emoji">ℹ</text>
|
||||
<image class="section-icon" src="{{icons.infoFill}}" mode="aspectFit"></image>
|
||||
<text class="section-title">关于</text>
|
||||
</view>
|
||||
<view class="about-list">
|
||||
<view class="about-row">
|
||||
<text class="about-label">版本</text>
|
||||
<text class="about-value">v1.2.0</text>
|
||||
<text class="about-value">v1.5</text>
|
||||
</view>
|
||||
<view class="about-row">
|
||||
<text class="about-label">更新日期</text>
|
||||
<text class="about-value">2026-06-04</text>
|
||||
<text class="about-value">2026-06-10</text>
|
||||
</view>
|
||||
<view class="about-row" bindtap="onCopyWechat">
|
||||
<text class="about-label">开发者</text>
|
||||
@@ -126,5 +112,99 @@
|
||||
<view class="about-footer">
|
||||
<text class="about-copy">Made with ❤️ for better fitness</text>
|
||||
</view>
|
||||
</ui-card>
|
||||
|
||||
<!-- 训练计划编辑器:用普通 view 不用 ui-modal,
|
||||
因为 WeChat 自定义组件根元素上 position: fixed 会被吃,导致 body 被推到容器末尾。
|
||||
普通 view 的 position: fixed 在小程序里 100% 生效。 -->
|
||||
<view wx:if="{{showPlanEditor}}" class="plan-editor-mask" catchtouchmove="onNoop">
|
||||
<view class="plan-editor-mask__bg" bindtap="onCloseEditor"></view>
|
||||
<view class="plan-editor-sheet" catchtap="onNoop">
|
||||
<view class="plan-editor-handle"></view>
|
||||
<view class="editor-title">编辑训练计划</view>
|
||||
|
||||
<view class="editor-field">
|
||||
<text class="editor-label">计划名称</text>
|
||||
<input
|
||||
class="editor-input"
|
||||
type="text"
|
||||
value="{{editingDraft.name}}"
|
||||
bindinput="onEditName"
|
||||
placeholder="例如:初级"
|
||||
maxlength="20"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<view class="editor-field">
|
||||
<text class="editor-label">总天数</text>
|
||||
<input
|
||||
class="editor-input"
|
||||
type="number"
|
||||
value="{{editingDraft.totalDays}}"
|
||||
bindinput="onEditTotalDays"
|
||||
placeholder="1-365"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<view class="editor-row">
|
||||
<view class="editor-field-half">
|
||||
<text class="editor-label">起始时长(秒)</text>
|
||||
<input
|
||||
class="editor-input"
|
||||
type="number"
|
||||
value="{{editingDraft.startTarget}}"
|
||||
bindinput="onEditStartTarget"
|
||||
placeholder="5-3600"
|
||||
/>
|
||||
</view>
|
||||
<view class="editor-field-half">
|
||||
<text class="editor-label">每次增加(秒)</text>
|
||||
<input
|
||||
class="editor-input"
|
||||
type="number"
|
||||
value="{{editingDraft.increment}}"
|
||||
bindinput="onEditIncrement"
|
||||
placeholder="0-300"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="editor-field">
|
||||
<text class="editor-label">每几天增加</text>
|
||||
<input
|
||||
class="editor-input"
|
||||
type="number"
|
||||
value="{{editingDraft.cycleDays}}"
|
||||
bindinput="onEditCycleDays"
|
||||
placeholder="1-30"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<view class="editor-preview">
|
||||
<view class="editor-preview-head">
|
||||
<text class="editor-preview-title">预览</text>
|
||||
<text class="editor-preview-meta">共 {{editorPreview.length}} 天</text>
|
||||
</view>
|
||||
<view class="editor-preview-list">
|
||||
<view class="editor-preview-row" wx:for="{{editorPreview}}" wx:key="day">
|
||||
<text class="editor-preview-day">第 {{item.day}} 天</text>
|
||||
<text class="editor-preview-target">{{item.target}} 秒</text>
|
||||
</view>
|
||||
<view class="editor-preview-empty" wx:if="{{editorPreview.length === 0}}">
|
||||
<text>请填写总天数</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="editor-actions">
|
||||
<view class="editor-action-reset" wx:if="{{editingIsCustom}}" bindtap="onResetPlan">
|
||||
<text>恢复默认</text>
|
||||
</view>
|
||||
<view class="editor-action-save" bindtap="onSavePlan">
|
||||
<text>保存</text>
|
||||
</view>
|
||||
</view>
|
||||
<text class="editor-cancel" bindtap="onCloseEditor">取消</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
Reference in New Issue
Block a user