skill-calorie 0.2.4 → 0.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/SKILL.md +10 -9
- package/dist/.gen-inputs.json +16 -100
- package/dist/analysis/anomaly/common.d.ts +11 -1
- package/dist/analysis/anomaly/common.d.ts.map +1 -1
- package/dist/analysis/anomaly/common.js +11 -12
- package/dist/analysis/anomaly/common.js.map +1 -1
- package/dist/analysis/anomaly/diet.d.ts.map +1 -1
- package/dist/analysis/anomaly/diet.js +11 -3
- package/dist/analysis/anomaly/diet.js.map +1 -1
- package/dist/analysis/deficit.d.ts +4 -2
- package/dist/analysis/deficit.d.ts.map +1 -1
- package/dist/analysis/deficit.js +2 -1
- package/dist/analysis/deficit.js.map +1 -1
- package/dist/analysis/historyStore.d.ts +2 -2
- package/dist/analysis/historyStore.d.ts.map +1 -1
- package/dist/analysis/historyStore.js +2 -2
- package/dist/analysis/historyStore.js.map +1 -1
- package/dist/analysis/index.d.ts +7 -13
- package/dist/analysis/index.d.ts.map +1 -1
- package/dist/analysis/index.js +15 -21
- package/dist/analysis/index.js.map +1 -1
- package/dist/analysis/reportDocTracked.d.ts.map +1 -1
- package/dist/analysis/reportDocTracked.js +3 -2
- package/dist/analysis/reportDocTracked.js.map +1 -1
- package/dist/analysis/review.d.ts.map +1 -1
- package/dist/analysis/review.js +5 -3
- package/dist/analysis/review.js.map +1 -1
- package/dist/analysis/simulate.d.ts +4 -2
- package/dist/analysis/simulate.d.ts.map +1 -1
- package/dist/analysis/simulate.js +5 -2
- package/dist/analysis/simulate.js.map +1 -1
- package/dist/analysis/simulate2.d.ts.map +1 -1
- package/dist/analysis/simulate2.js +4 -1
- package/dist/analysis/simulate2.js.map +1 -1
- package/dist/analysis/utils.d.ts +21 -25
- package/dist/analysis/utils.d.ts.map +1 -1
- package/dist/analysis/utils.js +34 -38
- package/dist/analysis/utils.js.map +1 -1
- package/dist/body/commands.d.ts +5 -1
- package/dist/body/commands.d.ts.map +1 -1
- package/dist/body/commands.js +5 -4
- package/dist/body/commands.js.map +1 -1
- package/dist/body/index.d.ts +9 -13
- package/dist/body/index.d.ts.map +1 -1
- package/dist/body/index.js +14 -19
- package/dist/body/index.js.map +1 -1
- package/dist/body/wizardDocs.js +1 -1
- package/dist/body/wizardPlate.d.ts +1 -1
- package/dist/cli/cmd_read.d.ts.map +1 -1
- package/dist/cli/cmd_read.js +23 -4
- package/dist/cli/cmd_read.js.map +1 -1
- package/dist/cli/config.d.ts +16 -0
- package/dist/cli/config.d.ts.map +1 -0
- package/dist/cli/config.js +68 -0
- package/dist/cli/config.js.map +1 -0
- package/dist/cli/delivery.d.ts +2 -1
- package/dist/cli/delivery.d.ts.map +1 -1
- package/dist/cli/delivery.js +4 -3
- package/dist/cli/delivery.js.map +1 -1
- package/dist/cli/health.d.ts +7 -0
- package/dist/cli/health.d.ts.map +1 -0
- package/dist/cli/health.js +21 -0
- package/dist/cli/health.js.map +1 -0
- package/dist/cli/keys.d.ts +1 -2
- package/dist/cli/keys.d.ts.map +1 -1
- package/dist/cli/keys.js.map +1 -1
- package/dist/cli/readArgs.d.ts.map +1 -1
- package/dist/cli/readArgs.js +6 -5
- package/dist/cli/readArgs.js.map +1 -1
- package/dist/cli/write.d.ts +9 -12
- package/dist/cli/write.d.ts.map +1 -1
- package/dist/cli/write.js +7 -27
- package/dist/cli/write.js.map +1 -1
- package/dist/config.d.ts +55 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +64 -0
- package/dist/config.js.map +1 -0
- package/dist/diet/commands.d.ts +6 -1
- package/dist/diet/commands.d.ts.map +1 -1
- package/dist/diet/commands.js +15 -14
- package/dist/diet/commands.js.map +1 -1
- package/dist/diet/dietEngine.d.ts.map +1 -1
- package/dist/diet/dietEngine.js +3 -1
- package/dist/diet/dietEngine.js.map +1 -1
- package/dist/diet/dietUi.d.ts +8 -3
- package/dist/diet/dietUi.d.ts.map +1 -1
- package/dist/diet/dietUi.js +18 -25
- package/dist/diet/dietUi.js.map +1 -1
- package/dist/diet/index.d.ts +7 -11
- package/dist/diet/index.d.ts.map +1 -1
- package/dist/diet/index.js +18 -19
- package/dist/diet/index.js.map +1 -1
- package/dist/diet/nutritionPort.d.ts +2 -5
- package/dist/diet/nutritionPort.d.ts.map +1 -1
- package/dist/diet/nutritionPort.js +14 -12
- package/dist/diet/nutritionPort.js.map +1 -1
- package/dist/diet/precheckPort.d.ts.map +1 -1
- package/dist/diet/precheckPort.js +3 -1
- package/dist/diet/precheckPort.js.map +1 -1
- package/dist/diet/review.d.ts.map +1 -1
- package/dist/diet/review.js +3 -18
- package/dist/diet/review.js.map +1 -1
- package/dist/exercise/cardio.js +1 -1
- package/dist/exercise/cardio.js.map +1 -1
- package/dist/exercise/commands.d.ts +1 -1
- package/dist/exercise/commands.d.ts.map +1 -1
- package/dist/exercise/commands.js +3 -3
- package/dist/exercise/commands.js.map +1 -1
- package/dist/exercise/distribution.js +1 -1
- package/dist/exercise/distribution.js.map +1 -1
- package/dist/exercise/edit.d.ts.map +1 -1
- package/dist/exercise/edit.js +4 -2
- package/dist/exercise/edit.js.map +1 -1
- package/dist/exercise/exerciseStore.d.ts.map +1 -1
- package/dist/exercise/exerciseStore.js +16 -10
- package/dist/exercise/exerciseStore.js.map +1 -1
- package/dist/exercise/index.d.ts +7 -12
- package/dist/exercise/index.d.ts.map +1 -1
- package/dist/exercise/index.js +14 -19
- package/dist/exercise/index.js.map +1 -1
- package/dist/exercise/recap.js +1 -1
- package/dist/exercise/recap.js.map +1 -1
- package/dist/exercise/sportPortDocs.d.ts +9 -0
- package/dist/exercise/sportPortDocs.d.ts.map +1 -0
- package/dist/exercise/sportPortDocs.js +896 -0
- package/dist/exercise/sportPortDocs.js.map +1 -0
- package/dist/exercise/sportUi.d.ts +4 -2
- package/dist/exercise/sportUi.d.ts.map +1 -1
- package/dist/exercise/sportUi.js +13 -44
- package/dist/exercise/sportUi.js.map +1 -1
- package/dist/exercise/strength.js +1 -1
- package/dist/exercise/strength.js.map +1 -1
- package/dist/exercise/trend.js +1 -1
- package/dist/exercise/trend.js.map +1 -1
- package/dist/fetch/body.d.ts.map +1 -1
- package/dist/fetch/body.js +15 -12
- package/dist/fetch/body.js.map +1 -1
- package/dist/fetch/diet.d.ts +6 -5
- package/dist/fetch/diet.d.ts.map +1 -1
- package/dist/fetch/diet.js +19 -25
- package/dist/fetch/diet.js.map +1 -1
- package/dist/goal/commands.d.ts +5 -1
- package/dist/goal/commands.d.ts.map +1 -1
- package/dist/goal/commands.js +7 -6
- package/dist/goal/commands.js.map +1 -1
- package/dist/goal/goalExtraPlate.d.ts.map +1 -1
- package/dist/goal/goalExtraPlate.js +3 -1
- package/dist/goal/goalExtraPlate.js.map +1 -1
- package/dist/goal/goalHistory.d.ts +2 -2
- package/dist/goal/goalHistory.d.ts.map +1 -1
- package/dist/goal/goalHistory.js +4 -9
- package/dist/goal/goalHistory.js.map +1 -1
- package/dist/goal/index.d.ts +6 -10
- package/dist/goal/index.d.ts.map +1 -1
- package/dist/goal/index.js +11 -19
- package/dist/goal/index.js.map +1 -1
- package/dist/goal/nutritionGoal.d.ts.map +1 -1
- package/dist/goal/nutritionGoal.js +4 -1
- package/dist/goal/nutritionGoal.js.map +1 -1
- package/dist/goal/read.d.ts +1 -1
- package/dist/goal/read.d.ts.map +1 -1
- package/dist/goal/read.js +1 -1
- package/dist/goal/read.js.map +1 -1
- package/dist/goal/set.d.ts.map +1 -1
- package/dist/goal/set.js +2 -2
- package/dist/goal/set.js.map +1 -1
- package/dist/health.d.ts +41 -0
- package/dist/health.d.ts.map +1 -0
- package/dist/health.js +501 -0
- package/dist/health.js.map +1 -0
- package/dist/help-missing.json +1 -1
- package/dist/home/index.d.ts +8 -9
- package/dist/home/index.d.ts.map +1 -1
- package/dist/home/index.js +11 -11
- package/dist/home/index.js.map +1 -1
- package/dist/index.d.ts +24 -16
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +24 -12
- package/dist/index.js.map +1 -1
- package/dist/migrate/migrate.js +2 -2
- package/dist/migrate/migrate.js.map +1 -1
- package/dist/output.d.ts +4 -3
- package/dist/output.d.ts.map +1 -1
- package/dist/output.js +12 -5
- package/dist/output.js.map +1 -1
- package/dist/paths.d.ts +6 -2
- package/dist/paths.d.ts.map +1 -1
- package/dist/paths.js +28 -17
- package/dist/paths.js.map +1 -1
- package/dist/photo/commands.d.ts +1 -1
- package/dist/photo/commands.d.ts.map +1 -1
- package/dist/photo/commands.js +3 -3
- package/dist/photo/commands.js.map +1 -1
- package/dist/photo/compareDoc.js +1 -1
- package/dist/photo/compareDoc.js.map +1 -1
- package/dist/photo/dir.d.ts +1 -1
- package/dist/photo/dir.d.ts.map +1 -1
- package/dist/photo/dir.js +6 -5
- package/dist/photo/dir.js.map +1 -1
- package/dist/photo/gifDoc.d.ts.map +1 -1
- package/dist/photo/gifDoc.js +10 -4
- package/dist/photo/gifDoc.js.map +1 -1
- package/dist/photo/helpCenter.js +1 -1
- package/dist/photo/helpCenter.js.map +1 -1
- package/dist/photo/helpFile.js +1 -1
- package/dist/photo/helpFile.js.map +1 -1
- package/dist/photo/helpPaths.d.ts +1 -1
- package/dist/photo/helpPaths.js +1 -1
- package/dist/photo/helpShell.d.ts +16 -0
- package/dist/photo/helpShell.d.ts.map +1 -0
- package/dist/photo/helpShell.js +27 -0
- package/dist/photo/helpShell.js.map +1 -0
- package/dist/photo/index.d.ts +7 -11
- package/dist/photo/index.d.ts.map +1 -1
- package/dist/photo/index.js +11 -19
- package/dist/photo/index.js.map +1 -1
- package/dist/photo/photos.d.ts +5 -3
- package/dist/photo/photos.d.ts.map +1 -1
- package/dist/photo/photos.js +15 -14
- package/dist/photo/photos.js.map +1 -1
- package/dist/photo/pickerDoc.js +1 -1
- package/dist/photo/templates.d.ts +4 -0
- package/dist/photo/templates.d.ts.map +1 -0
- package/dist/photo/templates.js +29 -0
- package/dist/photo/templates.js.map +1 -0
- package/dist/photo/viewerDoc.js +1 -1
- package/dist/photo/viewerDoc.js.map +1 -1
- package/dist/photo/wizard.d.ts +1 -1
- package/dist/photo/wizard.js +2 -2
- package/dist/photo/wizard.js.map +1 -1
- package/dist/photo/wizardPort.d.ts +70 -0
- package/dist/photo/wizardPort.d.ts.map +1 -0
- package/dist/photo/wizardPort.js +232 -0
- package/dist/photo/wizardPort.js.map +1 -0
- package/dist/photo/wizardPortDocs.d.ts +6 -0
- package/dist/photo/wizardPortDocs.d.ts.map +1 -0
- package/dist/photo/wizardPortDocs.js +322 -0
- package/dist/photo/wizardPortDocs.js.map +1 -0
- package/dist/profile/commands.d.ts +5 -1
- package/dist/profile/commands.d.ts.map +1 -1
- package/dist/profile/commands.js +4 -3
- package/dist/profile/commands.js.map +1 -1
- package/dist/profile/index.d.ts +7 -13
- package/dist/profile/index.d.ts.map +1 -1
- package/dist/profile/index.js +14 -19
- package/dist/profile/index.js.map +1 -1
- package/dist/profile/setup.d.ts +1 -1
- package/dist/render/analysisPlate.d.ts +3 -3
- package/dist/render/analysisPlate.d.ts.map +1 -1
- package/dist/render/analysisPlate.js +4 -21
- package/dist/render/analysisPlate.js.map +1 -1
- package/dist/render/diet.d.ts +10 -6
- package/dist/render/diet.d.ts.map +1 -1
- package/dist/render/diet.js +8 -23
- package/dist/render/diet.js.map +1 -1
- package/dist/render/exercisePort.d.ts +1 -1
- package/dist/render/html.d.ts +0 -6
- package/dist/render/html.d.ts.map +1 -1
- package/dist/render/html.js +2 -44
- package/dist/render/html.js.map +1 -1
- package/dist/render/index.d.ts +0 -3
- package/dist/render/index.d.ts.map +1 -1
- package/dist/render/index.js +2 -2
- package/dist/render/index.js.map +1 -1
- package/dist/render/planWizardDocs.js +1 -1
- package/dist/render/planWizardDocs.js.map +1 -1
- package/dist/render/trendMiscPort.d.ts.map +1 -1
- package/dist/render/trendMiscPort.js +6 -4
- package/dist/render/trendMiscPort.js.map +1 -1
- package/dist/render/workoutPlanDocs.js +1 -1
- package/dist/render/workoutPlanDocs.js.map +1 -1
- package/dist/shared/alive.d.ts +22 -0
- package/dist/shared/alive.d.ts.map +1 -0
- package/dist/shared/alive.js +22 -0
- package/dist/shared/alive.js.map +1 -0
- package/dist/shared/commandSpec.d.ts +14 -4
- package/dist/shared/commandSpec.d.ts.map +1 -1
- package/dist/shared/copyBlock.d.ts +4 -0
- package/dist/shared/copyBlock.d.ts.map +1 -0
- package/dist/shared/copyBlock.js +26 -0
- package/dist/shared/copyBlock.js.map +1 -0
- package/dist/shared/docPage.d.ts +2 -1
- package/dist/shared/docPage.d.ts.map +1 -1
- package/dist/shared/docPage.js +21 -33
- package/dist/shared/docPage.js.map +1 -1
- package/dist/shared/healthRate.d.ts +21 -0
- package/dist/shared/healthRate.d.ts.map +1 -0
- package/dist/shared/healthRate.js +21 -0
- package/dist/shared/healthRate.js.map +1 -0
- package/dist/shared/kcalPerKg.d.ts +16 -0
- package/dist/shared/kcalPerKg.d.ts.map +1 -0
- package/dist/shared/kcalPerKg.js +16 -0
- package/dist/shared/kcalPerKg.js.map +1 -0
- package/dist/shared/meal.d.ts +46 -0
- package/dist/shared/meal.d.ts.map +1 -0
- package/dist/shared/meal.js +75 -0
- package/dist/shared/meal.js.map +1 -0
- package/dist/shared/nutritionRange.d.ts +28 -0
- package/dist/shared/nutritionRange.d.ts.map +1 -0
- package/dist/shared/nutritionRange.js +28 -0
- package/dist/shared/nutritionRange.js.map +1 -0
- package/dist/shared/pageStrips.d.ts +81 -0
- package/dist/shared/pageStrips.d.ts.map +1 -0
- package/dist/shared/pageStrips.js +166 -0
- package/dist/shared/pageStrips.js.map +1 -0
- package/dist/shared/planCopyBlock.d.ts +32 -0
- package/dist/shared/planCopyBlock.d.ts.map +1 -0
- package/dist/shared/planCopyBlock.js +34 -0
- package/dist/shared/planCopyBlock.js.map +1 -0
- package/dist/shared/sourceLine.d.ts +5 -5
- package/dist/shared/sourceLine.d.ts.map +1 -1
- package/dist/shared/sourceLine.js +3 -1
- package/dist/shared/sourceLine.js.map +1 -1
- package/dist/shared/time.d.ts +28 -0
- package/dist/shared/time.d.ts.map +1 -0
- package/dist/shared/time.js +37 -0
- package/dist/shared/time.js.map +1 -0
- package/dist/triggers/routeSpec.d.ts +3 -3
- package/dist/triggers/routeSpec.js +3 -3
- package/dist/triggers/routes.generated.d.ts +2 -2
- package/dist/weight/commands.d.ts +4 -1
- package/dist/weight/commands.d.ts.map +1 -1
- package/dist/weight/commands.js +5 -4
- package/dist/weight/commands.js.map +1 -1
- package/dist/weight/figures.d.ts.map +1 -1
- package/dist/weight/figures.js +3 -1
- package/dist/weight/figures.js.map +1 -1
- package/dist/weight/index.d.ts +11 -17
- package/dist/weight/index.d.ts.map +1 -1
- package/dist/weight/index.js +16 -19
- package/dist/weight/index.js.map +1 -1
- package/dist/weight/records.d.ts.map +1 -1
- package/dist/weight/records.js +4 -2
- package/dist/weight/records.js.map +1 -1
- package/dist/weight/review.js +2 -2
- package/dist/weight/review.js.map +1 -1
- package/dist/weight/weightUi.d.ts +16 -4
- package/dist/weight/weightUi.d.ts.map +1 -1
- package/dist/weight/weightUi.js +23 -53
- package/dist/weight/weightUi.js.map +1 -1
- package/dist/workout/commands.d.ts +16 -2
- package/dist/workout/commands.d.ts.map +1 -1
- package/dist/workout/commands.js +17 -16
- package/dist/workout/commands.js.map +1 -1
- package/dist/workout/index.d.ts +10 -19
- package/dist/workout/index.d.ts.map +1 -1
- package/dist/workout/index.js +17 -31
- package/dist/workout/index.js.map +1 -1
- package/dist/workout/land.d.ts +1 -1
- package/dist/workout/land.js +3 -3
- package/dist/workout/land.js.map +1 -1
- package/dist/workout/landBatch.d.ts +7 -5
- package/dist/workout/landBatch.d.ts.map +1 -1
- package/dist/workout/landBatch.js +17 -15
- package/dist/workout/landBatch.js.map +1 -1
- package/dist/workout/landBatchPages.d.ts.map +1 -1
- package/dist/workout/landBatchPages.js +2 -23
- package/dist/workout/landBatchPages.js.map +1 -1
- package/dist/workout/landPages.d.ts.map +1 -1
- package/dist/workout/landPages.js +2 -23
- package/dist/workout/landPages.js.map +1 -1
- package/dist/workout/landRunner.d.ts +8 -10
- package/dist/workout/landRunner.d.ts.map +1 -1
- package/dist/workout/landRunner.js +35 -56
- package/dist/workout/landRunner.js.map +1 -1
- package/dist/workout/planCopyBlock.d.ts +9 -0
- package/dist/workout/planCopyBlock.d.ts.map +1 -0
- package/dist/workout/planCopyBlock.js +9 -0
- package/dist/workout/planCopyBlock.js.map +1 -0
- package/dist/workout/planEditor.d.ts +94 -0
- package/dist/workout/planEditor.d.ts.map +1 -0
- package/dist/workout/planEditor.js +38 -0
- package/dist/workout/planEditor.js.map +1 -0
- package/dist/workout/planEditorCss.d.ts +13 -0
- package/dist/workout/planEditorCss.d.ts.map +1 -0
- package/dist/workout/planEditorCss.js +140 -0
- package/dist/workout/planEditorCss.js.map +1 -0
- package/dist/workout/planEditorDocs.d.ts +7 -0
- package/dist/workout/planEditorDocs.d.ts.map +1 -0
- package/dist/workout/planEditorDocs.js +56 -0
- package/dist/workout/planEditorDocs.js.map +1 -0
- package/dist/workout/planEditorPort.d.ts +45 -0
- package/dist/workout/planEditorPort.d.ts.map +1 -0
- package/dist/workout/planEditorPort.js +217 -0
- package/dist/workout/planEditorPort.js.map +1 -0
- package/dist/workout/planEditorRuntime.d.ts +15 -0
- package/dist/workout/planEditorRuntime.d.ts.map +1 -0
- package/dist/workout/planEditorRuntime.js +427 -0
- package/dist/workout/planEditorRuntime.js.map +1 -0
- package/dist/workout/planStore.d.ts.map +1 -1
- package/dist/workout/planStore.js +1 -3
- package/dist/workout/planStore.js.map +1 -1
- package/dist/workout/receipt.d.ts +1 -1
- package/dist/workout/receipt.js +1 -1
- package/dist/workout/receipt.js.map +1 -1
- package/dist/workout/review.d.ts +1 -1
- package/dist/workout/review.js +1 -1
- package/dist/workout/review.js.map +1 -1
- package/dist/workout/reviewDocs.d.ts +3 -0
- package/dist/workout/reviewDocs.d.ts.map +1 -0
- package/dist/{render → workout}/reviewDocs.js +2 -2
- package/dist/workout/reviewDocs.js.map +1 -0
- package/dist/workout/reviewDocsCss.d.ts.map +1 -0
- package/dist/{render → workout}/reviewDocsCss.js +1 -1
- package/dist/workout/reviewDocsCss.js.map +1 -0
- package/dist/workout/xunjiBackfill.d.ts.map +1 -1
- package/dist/workout/xunjiBackfill.js +4 -25
- package/dist/workout/xunjiBackfill.js.map +1 -1
- package/dist/workout/xunjiPush.d.ts.map +1 -1
- package/dist/workout/xunjiPush.js +4 -25
- package/dist/workout/xunjiPush.js.map +1 -1
- package/dist/workout/xunjiRunner.d.ts +11 -5
- package/dist/workout/xunjiRunner.d.ts.map +1 -1
- package/dist/workout/xunjiRunner.js +29 -29
- package/dist/workout/xunjiRunner.js.map +1 -1
- package/dist/xunji/backfill.d.ts +5 -3
- package/dist/xunji/backfill.d.ts.map +1 -1
- package/dist/xunji/backfill.js +11 -5
- package/dist/xunji/backfill.js.map +1 -1
- package/dist/xunji/catalog.d.ts +3 -1
- package/dist/xunji/catalog.d.ts.map +1 -1
- package/dist/xunji/catalog.js +12 -6
- package/dist/xunji/catalog.js.map +1 -1
- package/dist/xunji/fetch.d.ts +3 -4
- package/dist/xunji/fetch.d.ts.map +1 -1
- package/dist/xunji/fetch.js +8 -9
- package/dist/xunji/fetch.js.map +1 -1
- package/dist/xunji/key.d.ts +16 -56
- package/dist/xunji/key.d.ts.map +1 -1
- package/dist/xunji/key.js +44 -104
- package/dist/xunji/key.js.map +1 -1
- package/dist/xunji/planSource.d.ts +3 -3
- package/dist/xunji/planSource.js +6 -6
- package/dist/xunji/planSource.js.map +1 -1
- package/dist/xunji/rateLimit.d.ts +7 -2
- package/dist/xunji/rateLimit.d.ts.map +1 -1
- package/dist/xunji/rateLimit.js +12 -3
- package/dist/xunji/rateLimit.js.map +1 -1
- package/dist/xunji/run-sync.d.ts +3 -3
- package/dist/xunji/run-sync.d.ts.map +1 -1
- package/dist/xunji/run-sync.js +5 -5
- package/dist/xunji/run-sync.js.map +1 -1
- package/dist/xunji/run.d.ts +2 -4
- package/dist/xunji/run.d.ts.map +1 -1
- package/dist/xunji/run.js +5 -5
- package/dist/xunji/run.js.map +1 -1
- package/dist/xunji/subcommands.d.ts.map +1 -1
- package/dist/xunji/subcommands.js +2 -3
- package/dist/xunji/subcommands.js.map +1 -1
- package/dist/xunji/upsert.d.ts +4 -4
- package/dist/xunji/upsert.d.ts.map +1 -1
- package/dist/xunji/upsert.js +6 -6
- package/dist/xunji/upsert.js.map +1 -1
- package/package.json +2 -9
- package/dist/cli/legacy/index.d.ts +0 -25
- package/dist/cli/legacy/index.d.ts.map +0 -1
- package/dist/cli/legacy/index.js +0 -37
- package/dist/cli/legacy/index.js.map +0 -1
- package/dist/cli/legacy/routes/index.d.ts +0 -8
- package/dist/cli/legacy/routes/index.d.ts.map +0 -1
- package/dist/cli/legacy/routes/index.js +0 -23
- package/dist/cli/legacy/routes/index.js.map +0 -1
- package/dist/cli/legacy/routes/scene-01.d.ts +0 -12
- package/dist/cli/legacy/routes/scene-01.d.ts.map +0 -1
- package/dist/cli/legacy/routes/scene-01.js +0 -2
- package/dist/cli/legacy/routes/scene-01.js.map +0 -1
- package/dist/cli/legacy/routes/scene-02.d.ts +0 -18
- package/dist/cli/legacy/routes/scene-02.d.ts.map +0 -1
- package/dist/cli/legacy/routes/scene-02.js +0 -2
- package/dist/cli/legacy/routes/scene-02.js.map +0 -1
- package/dist/cli/legacy/routes/scene-03.d.ts +0 -13
- package/dist/cli/legacy/routes/scene-03.d.ts.map +0 -1
- package/dist/cli/legacy/routes/scene-03.js +0 -2
- package/dist/cli/legacy/routes/scene-03.js.map +0 -1
- package/dist/cli/legacy/routes/scene-04.d.ts +0 -10
- package/dist/cli/legacy/routes/scene-04.d.ts.map +0 -1
- package/dist/cli/legacy/routes/scene-04.js +0 -2
- package/dist/cli/legacy/routes/scene-04.js.map +0 -1
- package/dist/cli/legacy/routes/scene-05.d.ts +0 -10
- package/dist/cli/legacy/routes/scene-05.d.ts.map +0 -1
- package/dist/cli/legacy/routes/scene-05.js +0 -2
- package/dist/cli/legacy/routes/scene-05.js.map +0 -1
- package/dist/cli/legacy/routes/scene-06.d.ts +0 -10
- package/dist/cli/legacy/routes/scene-06.d.ts.map +0 -1
- package/dist/cli/legacy/routes/scene-06.js +0 -2
- package/dist/cli/legacy/routes/scene-06.js.map +0 -1
- package/dist/cli/legacy/routes/scene-07.d.ts +0 -10
- package/dist/cli/legacy/routes/scene-07.d.ts.map +0 -1
- package/dist/cli/legacy/routes/scene-07.js +0 -2
- package/dist/cli/legacy/routes/scene-07.js.map +0 -1
- package/dist/cli/legacy/routes/scene-08.d.ts +0 -12
- package/dist/cli/legacy/routes/scene-08.d.ts.map +0 -1
- package/dist/cli/legacy/routes/scene-08.js +0 -2
- package/dist/cli/legacy/routes/scene-08.js.map +0 -1
- package/dist/cli/legacy/routes/scene-09.d.ts +0 -12
- package/dist/cli/legacy/routes/scene-09.d.ts.map +0 -1
- package/dist/cli/legacy/routes/scene-09.js +0 -2
- package/dist/cli/legacy/routes/scene-09.js.map +0 -1
- package/dist/cli/legacy/routes/scene-10.d.ts +0 -10
- package/dist/cli/legacy/routes/scene-10.d.ts.map +0 -1
- package/dist/cli/legacy/routes/scene-10.js +0 -2
- package/dist/cli/legacy/routes/scene-10.js.map +0 -1
- package/dist/cli/legacy/scene-01.d.ts +0 -13
- package/dist/cli/legacy/scene-01.d.ts.map +0 -1
- package/dist/cli/legacy/scene-01.js +0 -2
- package/dist/cli/legacy/scene-01.js.map +0 -1
- package/dist/cli/legacy/scene-02.d.ts +0 -12
- package/dist/cli/legacy/scene-02.d.ts.map +0 -1
- package/dist/cli/legacy/scene-02.js +0 -2
- package/dist/cli/legacy/scene-02.js.map +0 -1
- package/dist/cli/legacy/scene-03.d.ts +0 -14
- package/dist/cli/legacy/scene-03.d.ts.map +0 -1
- package/dist/cli/legacy/scene-03.js +0 -2
- package/dist/cli/legacy/scene-03.js.map +0 -1
- package/dist/cli/legacy/scene-04.d.ts +0 -12
- package/dist/cli/legacy/scene-04.d.ts.map +0 -1
- package/dist/cli/legacy/scene-04.js +0 -2
- package/dist/cli/legacy/scene-04.js.map +0 -1
- package/dist/cli/legacy/scene-05.d.ts +0 -14
- package/dist/cli/legacy/scene-05.d.ts.map +0 -1
- package/dist/cli/legacy/scene-05.js +0 -2
- package/dist/cli/legacy/scene-05.js.map +0 -1
- package/dist/cli/legacy/scene-06.d.ts +0 -15
- package/dist/cli/legacy/scene-06.d.ts.map +0 -1
- package/dist/cli/legacy/scene-06.js +0 -2
- package/dist/cli/legacy/scene-06.js.map +0 -1
- package/dist/cli/legacy/scene-07.d.ts +0 -15
- package/dist/cli/legacy/scene-07.d.ts.map +0 -1
- package/dist/cli/legacy/scene-07.js +0 -2
- package/dist/cli/legacy/scene-07.js.map +0 -1
- package/dist/cli/legacy/scene-08.d.ts +0 -13
- package/dist/cli/legacy/scene-08.d.ts.map +0 -1
- package/dist/cli/legacy/scene-08.js +0 -2
- package/dist/cli/legacy/scene-08.js.map +0 -1
- package/dist/cli/legacy/scene-09.d.ts +0 -13
- package/dist/cli/legacy/scene-09.d.ts.map +0 -1
- package/dist/cli/legacy/scene-09.js +0 -2
- package/dist/cli/legacy/scene-09.js.map +0 -1
- package/dist/cli/legacy/scene-10.d.ts +0 -13
- package/dist/cli/legacy/scene-10.d.ts.map +0 -1
- package/dist/cli/legacy/scene-10.js +0 -2
- package/dist/cli/legacy/scene-10.js.map +0 -1
- package/dist/cli/legacy/types.d.ts +0 -16
- package/dist/cli/legacy/types.d.ts.map +0 -1
- package/dist/cli/legacy/types.js +0 -2
- package/dist/cli/legacy/types.js.map +0 -1
- package/dist/render/reviewDocs.d.ts +0 -3
- package/dist/render/reviewDocs.d.ts.map +0 -1
- package/dist/render/reviewDocs.js.map +0 -1
- package/dist/render/reviewDocsCss.d.ts.map +0 -1
- package/dist/render/reviewDocsCss.js.map +0 -1
- /package/dist/{render → workout}/reviewDocsCss.d.ts +0 -0
|
@@ -0,0 +1,896 @@
|
|
|
1
|
+
/** #111 · 运动移植 6 键全文档装配(数据→区块→填充器)。
|
|
2
|
+
*
|
|
3
|
+
* 范围(t71「需移植」之运动 6 项):`calorie.view.exercise-strength`
|
|
4
|
+
* (exercise_strength:按动作聚合+重量轨迹)/`calorie.view.exercise-cardio`
|
|
5
|
+
* (exercise_cardio:按类型聚合+配速)/`calorie.view.exercise-distribution`
|
|
6
|
+
* (exercise_distribution:分类占比+摄入/TDEE 联动)/
|
|
7
|
+
* `calorie.view.exercise-recap`(exercise_recap:多窗复盘+TOP5+一句话)/
|
|
8
|
+
* `calorie.view.exercise-review`(exercise_review:计划 vs 实绩)/
|
|
9
|
+
* `calorie.view.exercise-trend`(exercise_trend:日序列+周频次+峰值)。
|
|
10
|
+
* 不碰:process_progress(落地/训记二期,O3/oosLanding/oosXunji 命中但不执行,
|
|
11
|
+
* 路由不动)/营养 4(→ #112)/趋势 2+其他 6(→ #113)/47 页已有(#108–#110 已关)。
|
|
12
|
+
*
|
|
13
|
+
* 做法(#104 §4 用法,照抄 trendDocs 头 90 行):内容 = base-paint/blocks 12 区块
|
|
14
|
+
* (B-01 壳/B-02 KPI/B-03 表/B-04 图/B-05 列表/B-08 折叠/B-09 参数表单/
|
|
15
|
+
* B-11 复制区),文档 = fillTemplate 包裹(资产裸文本+填充器包裹;
|
|
16
|
+
* sharedCss = buildStyleSheet().css + blocksCss(),不走 extraCss;
|
|
17
|
+
* 图表页另加 CHARTS-HELPERS+buildChartsHelpersJs,图表 CSS 由其运行时注入)。
|
|
18
|
+
* 复制文本一律 buildDataText(#77 契约):stat 投影 metrics 只收确定数字。
|
|
19
|
+
* 本层不做取数(数据由 render/exercisePort.ts 备齐),不返空(缺失由数据层抛 missing-data)。
|
|
20
|
+
*
|
|
21
|
+
* **#453(地图 #156 页面族第 4 票,形状照已验样板 #423)**:分布/力量/有氧三支换融合版式——
|
|
22
|
+
* 卡清单(`Card`)各自判空+`renderTocBlock` 页内导航+`renderCaliberLine` 口径行+
|
|
23
|
+
* `shared/sourceLine.ts` 来源脚注+`assembleDocPage({printable:true})`(#448 透传位)+
|
|
24
|
+
* `shared/copyArea.ts` 三格式复制+`shared/emptyGuide.ts` 空态带下一句;类别色只走
|
|
25
|
+
* `exercise/categoryColors.ts` 的 `categoryColor()`。**#454(第 5 票)把趋势(`buildTrendDoc`)
|
|
26
|
+
* 与复盘(`buildRecapDoc`)两支也换成同一套融合版式**(卡清单逐卡判空+五窗同版+逐日表截断明示);
|
|
27
|
+
* `buildReviewDoc`(计划复盘)仍走老版式。本族不接四态头与变更卡载具(那是写操作器件,
|
|
28
|
+
* 只读页硬套会印出与事实不符的态标签)。
|
|
29
|
+
*/
|
|
30
|
+
import { renderCaliberLine, renderChartBlock, renderChips, renderDataTable, renderDisclosure, renderDistributionRows, renderFeedbackBlock, renderKpiGrid, renderListRows, renderParamForm, renderTocBlock, } from 'base-paint/blocks';
|
|
31
|
+
import { categoryColor } from '../exercise/categoryColors.js';
|
|
32
|
+
import { exerciseUiCss, factStrip, fmtNum, windowStrip } from '../exercise/sportUi.js';
|
|
33
|
+
import { assembleDocPage, metricsOf } from '../shared/docPage.js';
|
|
34
|
+
import { copyArea } from '../shared/copyArea.js';
|
|
35
|
+
import { emptyGuide } from '../shared/emptyGuide.js';
|
|
36
|
+
/** envelope 头(值冻结对齐 cli/keys.ts ENVELOPE_VERSION/CALORIE_SKILL;测试钉死一致)。 */
|
|
37
|
+
const DOC_VERSION = '0.1.0';
|
|
38
|
+
const DOC_SKILL = 'calorie';
|
|
39
|
+
/** 本文件各页共用的 head 标题(整页模板住 `src/shared/docPage.ts`,标题走参数)。 */
|
|
40
|
+
const DOC_TITLE = '卡路里·运动移植';
|
|
41
|
+
function fmt(n) {
|
|
42
|
+
if (n === null || n === undefined)
|
|
43
|
+
return '—';
|
|
44
|
+
return String(n);
|
|
45
|
+
}
|
|
46
|
+
function fmtPace(p) {
|
|
47
|
+
if (p === null || p === undefined)
|
|
48
|
+
return '—';
|
|
49
|
+
return fmtNum(p, 1) + ' 分/km';
|
|
50
|
+
}
|
|
51
|
+
/** 逐条记录表(100 条截断明示,沿 R3 口径;备注仅展示,不做筛选维度)。 */
|
|
52
|
+
const RECORD_CAP = 100;
|
|
53
|
+
function windowForm(start, end, extra) {
|
|
54
|
+
return renderParamForm({
|
|
55
|
+
fields: [
|
|
56
|
+
{ name: 'start', label: '开始', value: start },
|
|
57
|
+
{ name: 'end', label: '结束', value: end },
|
|
58
|
+
],
|
|
59
|
+
description: extra,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
function shell(card) {
|
|
63
|
+
return '<section id="' + card.id + '">'
|
|
64
|
+
+ (card.head === undefined ? '' : '<h2>' + card.head + '</h2>')
|
|
65
|
+
+ card.html + '</section>';
|
|
66
|
+
}
|
|
67
|
+
/** 窗口卡:这一页看的是哪一段(开始/结束两个真日期)。 */
|
|
68
|
+
function windowCard(start, end, description) {
|
|
69
|
+
return { id: 'sec-window', label: '窗口', html: windowForm(start, end, description) };
|
|
70
|
+
}
|
|
71
|
+
/** 数值格的人话写法:缺值一律「—」(不空着、也不编 0);`unit` 空串即不带单位。
|
|
72
|
+
* #544:显示层取整走 `sportUi.fmtNum()`(整数不带小数点;库内浮点尘不上屏)。
|
|
73
|
+
* 计数类单位(次/组/条/个/分钟/空串)取 0 位小数,量值类(卡/kg/km/公里)取 1 位。 */
|
|
74
|
+
const INT_UNITS = new Set(['', '次', '组', '条', '个', '分钟', '卡']);
|
|
75
|
+
function numUnit(v, unit) {
|
|
76
|
+
if (v === null || v === undefined)
|
|
77
|
+
return '—';
|
|
78
|
+
const n = fmtNum(v, INT_UNITS.has(unit) ? 0 : 1);
|
|
79
|
+
return unit === '' ? n : n + ' ' + unit;
|
|
80
|
+
}
|
|
81
|
+
/** 窗内天数(闭区间;只做天数差,不当取数口径)——窗口条胶囊的唯一出处。 */
|
|
82
|
+
function windowDays(start, end) {
|
|
83
|
+
return Math.round((Date.parse(end) - Date.parse(start)) / 86400000) + 1;
|
|
84
|
+
}
|
|
85
|
+
/** 折线纵轴三件套(#544 视觉第 1 轮整改;形状照 `analysis/multiTrendPage.ts:65` 的 `weightAxisOf`)。
|
|
86
|
+
*
|
|
87
|
+
* ① **刻度**:共享层折线**缺省不给刻度标注**(`spec/charts.ts` 的 `yTicks` 缺省 → 0 条,图中只剩网格线),
|
|
88
|
+
* 不传就读不出量级 —— 同族先例 `analysis-deficit-385` 已把这条定成「本页漏传参数,本页补 `yTicks:3`」,
|
|
89
|
+
* 本族三张折线照办;`format` 给整数原样、带小数的一位(口径与 KPI 卡同:读数不印浮点尘)。
|
|
90
|
+
* ② **量程**:一律显式给 `yMin`/`yMax`(见 `valueAxisOf()`),理由有三条,都是量程的语义而不是观感:
|
|
91
|
+
* ① **最小值恒为 0**:这三条纵轴量的都是「消耗了多少卡/举起了多少 kg」,负值不存在——
|
|
92
|
+
* 原来的 `lo` 由共享层缺省域「数据两端各外扩 6%」算出来,窗内最小日要是 0,`lo` 就成了负数
|
|
93
|
+
* (#268 终审席打回项 P1-5:38/39 两页纵轴最低刻度印出 `-87.4`);
|
|
94
|
+
* ② **刻度落在整数上**:不给界时刻度值是「域两端内插」的产物,实测印出过 `1065.1`/`2217.7`
|
|
95
|
+
* (38)、`31.4`/`1118.0`/`2204.5`(37)这类带 `.0`/`.5` 尾巴的读数——刻度是**刻度**,不是量出来的数;
|
|
96
|
+
* ③ **线不贴上下沿**:显式给界会让共享层的「峰值余量」(`lineDomainOf` 只补派生域)失效,
|
|
97
|
+
* 故峰值与顶端刻度之间那点余量由 `setAxisRange()` 自己保证。 */
|
|
98
|
+
function valueAxisOf(values) {
|
|
99
|
+
// 整数原样、小数一位:刻度是取整后的整数,`toFixed(1)` 只是兜住极小量程的尾巴(不印浮点尘)。
|
|
100
|
+
const format = (v) => (Number.isInteger(v) ? String(v) : v.toFixed(1));
|
|
101
|
+
const nums = values.filter((v) => typeof v === 'number' && Number.isFinite(v));
|
|
102
|
+
// 数的顶点走 `reduce`(不铺参:窗内最多 366 个点,`Math.max(...nums)` 会压栈)。
|
|
103
|
+
// 全空或全零:`maxOf` 取 1,域退化成 0…1 而不是 0…0(量程为 0 会让刻度三条叠在同一处)。
|
|
104
|
+
let peak = 0;
|
|
105
|
+
for (const v of nums)
|
|
106
|
+
if (v > peak)
|
|
107
|
+
peak = v;
|
|
108
|
+
const maxOf = peak > 0 ? peak : 1;
|
|
109
|
+
// 刻度=`hi·i/(条数−1)`:`hi` 是 `step×(条数−1)` 的整数倍,三条刻度才全是步长的整数倍
|
|
110
|
+
// (否则会印出 `600.2`/`1200.4` 这种「峰值本身」——那是读数,不是刻度)。
|
|
111
|
+
return { yTicks: TICK_COUNT, yMin: 0, yMax: axisHeightOf(maxOf), format };
|
|
112
|
+
}
|
|
113
|
+
/** 纵轴刻度条数(#544:三条 —— 共享层 `yTicks` 的缺省是 0 条,不显式给就一个刻度都没有)。 */
|
|
114
|
+
const TICK_COUNT = 3;
|
|
115
|
+
/** 线位下限/上限:峰值至少离顶端刻度一档边距(0.88),也别把自己缩在图高一角(0.45)。 */
|
|
116
|
+
const LINE_TOP_LIMIT = 0.88;
|
|
117
|
+
const LINE_BOTTOM_LIMIT = 0.45;
|
|
118
|
+
/** 刻度步长候选序列(1/2/5×10ᵏ 里 ≥ 入参的最小一个)。 */
|
|
119
|
+
function niceStepAtOrAbove(v) {
|
|
120
|
+
const mag = Math.pow(10, Math.floor(Math.log10(v)));
|
|
121
|
+
for (const m of [1, 2, 5, 10])
|
|
122
|
+
if (v <= m * mag)
|
|
123
|
+
return Math.max(1, m * mag);
|
|
124
|
+
return Math.max(1, 10 * mag);
|
|
125
|
+
}
|
|
126
|
+
/** 上界(=顶端那条刻度):**让峰值落在图高 0.60 处**是这里的唯一目标——量程与刻度是同一件事的两面
|
|
127
|
+
* (`hi = step×(条数−1)`),故直接按「哪一档 1/2/5×10ᵏ 步长把峰值放在最合适的位置」来挑:
|
|
128
|
+
* 比值 `peak/hi` 落在 0.45–0.88 之间给低分(离 0.60 越近越低),越界按距离重罚,
|
|
129
|
+
* **步长不是整数再加一点罚**(刻度值会带 `.5` 尾巴,那正是 K2 那条打回项),取罚分最小、并列取较大步长。
|
|
130
|
+
*
|
|
131
|
+
* 为什么步长非要是 1/2/5×10ᵏ:三条刻度=`hi·i/2`,只有 `hi` 是步长整数倍时刻度才全是整数
|
|
132
|
+
* (否则会印出 `600.2`/`1200.4` 这种「峰值本身」——那是读数,不是刻度)。
|
|
133
|
+
* 例:峰值 500 ⇒ `0/500/1000`;峰值 1250 ⇒ `0/1000/2000`;峰值 2753 ⇒ `0/2000/4000`。 */
|
|
134
|
+
function axisHeightOf(maxOf) {
|
|
135
|
+
const base = niceStepAtOrAbove(Math.max(maxOf, 1) * 0.4);
|
|
136
|
+
let best = { step: base, score: Infinity };
|
|
137
|
+
for (const k of [0.5, 1, 2, 5, 10, 20, 50, 100, 200]) {
|
|
138
|
+
const step = Math.max(1, base * k);
|
|
139
|
+
if (step * (TICK_COUNT - 1) < maxOf)
|
|
140
|
+
continue; // 刻度带撑不住峰值 ⇒ 这一档不用看
|
|
141
|
+
const ratio = maxOf / (step * (TICK_COUNT - 1));
|
|
142
|
+
const integer = Number.isInteger(step) ? 0 : 0.1; // 刻度值带小数尾巴(`2204.5` 那类)要罚
|
|
143
|
+
const score = ratio > LINE_TOP_LIMIT || ratio < LINE_BOTTOM_LIMIT
|
|
144
|
+
? Math.abs(ratio - 0.6) * 2 + integer
|
|
145
|
+
: Math.abs(ratio - 0.6) * 0.1 + integer;
|
|
146
|
+
if (score <= best.score)
|
|
147
|
+
best = { step, score };
|
|
148
|
+
}
|
|
149
|
+
return best.step * (TICK_COUNT - 1);
|
|
150
|
+
}
|
|
151
|
+
/** 折线卡的横轴日期标尺(#268 终审席 K4 的处置,第 2 版)。
|
|
152
|
+
*
|
|
153
|
+
* **为什么不加这把尺的初版被视觉席打回**(复评 r3:39 页桌面表尺标签压在 07-10 一带、读成
|
|
154
|
+
* `02-2704-01 05-05 …`):初版一页印 **7** 条(含窗口首尾两条),条与条只隔 4.05% 宽(桌面 36px、
|
|
155
|
+
* 手机 20px),而标签本身就要 31px —— 必然叠字;且首尾两条与折线**自带**的首尾横轴标签逐字重复
|
|
156
|
+
* (共享层折线的 X 标签口径是 `'edge'`)。
|
|
157
|
+
*
|
|
158
|
+
* 本版的形状:**只出窗口内部 5 条**(首尾让给折线自己的那两条),按**等宽槽**摆(`flex:1 1 0`),
|
|
159
|
+
* 槽宽=容器宽÷5(桌面 176px、手机 71px),标签 26px ⇒ 相邻最小空档约 45px,三档都不叠。
|
|
160
|
+
* 一把尺要两样东西才算「轴」:一条细轴线 + 等距刻度点,故每个槽里自带一段轴线与一个小圆点,
|
|
161
|
+
* 尺因此读成一条时间轴,而不是一行浮在空中的日期。
|
|
162
|
+
*
|
|
163
|
+
* 退化:点数 < 45(短窗)不出——那一档日期本来就稀,多点两条标签反而添乱;点数不整除 5 时
|
|
164
|
+
* 取 4 条(见 `INTERIOR_COUNT`)。 */
|
|
165
|
+
const RULER_MIN_DAYS = 45;
|
|
166
|
+
/** 标尺槽数:内部日期的枚数(首尾归折线自己的标签;5 条在 390 档仍有约 45px 空档)。 */
|
|
167
|
+
const INTERIOR_COUNT = 5;
|
|
168
|
+
/** 折线块画布容器的开标签(日期轴从**结构位**注入,见 `withXRuler`)。 */
|
|
169
|
+
const S544_CANVAS_OPEN = '<div class="ilife-block-chart-block-canvas">';
|
|
170
|
+
/** 日期轴的槽串(只出窗口内部几条日期;取数与退化见 `RULER_MIN_DAYS`)。 */
|
|
171
|
+
function xRulerOf(items) {
|
|
172
|
+
const n = items.length;
|
|
173
|
+
if (n < RULER_MIN_DAYS)
|
|
174
|
+
return '';
|
|
175
|
+
const count = n % INTERIOR_COUNT === 0 ? INTERIOR_COUNT - 1 : INTERIOR_COUNT;
|
|
176
|
+
const cells = [];
|
|
177
|
+
for (let i = 1; i <= count; i += 1) {
|
|
178
|
+
const idx = Math.round((n - 1) * (i / (count + 1)));
|
|
179
|
+
const label = items[idx] === undefined ? '' : items[idx].label; // 逐字复用图上那几条日期
|
|
180
|
+
cells.push('<span class="sui-xruler-t"><i></i>' + label + '</span>');
|
|
181
|
+
}
|
|
182
|
+
return '<div class="sui-xruler" aria-hidden="true">' + cells.join('') + '</div>';
|
|
183
|
+
}
|
|
184
|
+
/** 折线卡 + 日期轴(#268 K4 第二版):标尺**必须挂在卡片内**(视觉复评 r3b:初版挂在卡外,
|
|
185
|
+
* 整条轴线比绘图区左宽 100px、还把卡片下边框顶出去),落点=画布容器的**末尾**(SVG 之后)。
|
|
186
|
+
* `renderChartBlock` 的 title 只会进 `esc()` 后的文本节点(哨兵都逃不出来),故只从结构位注入。 */
|
|
187
|
+
function withXRuler(chart, items) {
|
|
188
|
+
const ruler = xRulerOf(items);
|
|
189
|
+
if (ruler === '')
|
|
190
|
+
return chart;
|
|
191
|
+
const open = chart.indexOf(S544_CANVAS_OPEN);
|
|
192
|
+
const end = chart.lastIndexOf('</div>'); // 容器的收标签=整卡的收标签
|
|
193
|
+
if (open === -1 || end <= open)
|
|
194
|
+
return chart; // 结构变了就退回不带轴(不硬塞)
|
|
195
|
+
return chart.slice(0, end) + ruler + chart.slice(end);
|
|
196
|
+
}
|
|
197
|
+
/** 本族页内小样式(#544)。落本件常量,不新开样式件;`<style>` 段不进可见文本,探针无感。
|
|
198
|
+
* **#544 视觉第 1 轮整改三处**(都在本件内联规则里做,不碰共用层):
|
|
199
|
+
* ① 窄屏页内导航:共享页框在 ≤640 把胶囊轨切成横滑(`nowrap + overflow-x:auto`,滚动条还被藏),
|
|
200
|
+
* 实测 390 档第 5、6 枚胶囊被拦腰切断且无滚动提示 —— 本族窄屏改回**换行铺开**(与桌面同规则);
|
|
201
|
+
* ② 窗口卡的「开始/结束」两格是只读回显(字段本身由 `exercise-port-111` 钉死,不能删),
|
|
202
|
+
* 但共用件给每格 `margin:8px 0`,桌面档拉出约 200px 空洞 —— 本族把它收成两格一行;
|
|
203
|
+
* ③ 窄屏分布条给 4px 下沿(`K5`:占比 <1% 的条实渲不到 1px,肉眼读成空行)。
|
|
204
|
+
*
|
|
205
|
+
* **#544 终审席(票 #268 §4/§5)四条打回项**也落在这一段(都不出本件):
|
|
206
|
+
* ① `E1` 触摸目标:本族页级规则原写 `min-height:38px`,把 #525 配方给全宽档的 44px **压下来了**
|
|
207
|
+
* (实测 31–39 九页输入框 1440 档 434×38)——改回 44px(= HELP 参照页与 `.ilife-copy-btn` 同值);
|
|
208
|
+
* ② `K1` 字号混用:原本只把 `#sec-figures` **首卡**值放大到 28px,一排四卡字号 28/22 混用、基线错开——
|
|
209
|
+
* 整排退回公共层那一档 22px(同批 01–30 页的读数),本段不再改 KPI 字号;
|
|
210
|
+
* ③ `K5` 细条:`renderDistributionRows` 的填充宽=占比(行内联 `width:<pct>%`,共用件只收百分比),
|
|
211
|
+
* 给 `.ilife-block-dist-row-fill` 加 4px 下限——**几何在 CSS 层解决**,不改共用件的入参口径,
|
|
212
|
+
* 也不在页面里新算第二个百分比;
|
|
213
|
+
* ④ `P1-6/K6` 三节无可见标题(核心数字/类型分布/高频运动):节标题由 `shell()` 写进 `<section>`
|
|
214
|
+
* 首个子节点,样式落本段 `#sec-figures > h2,…` 一条(沿用公共层卡题那一档:15px/700)。 */
|
|
215
|
+
const S544_CSS = '<style>'
|
|
216
|
+
+ '@media (max-width:820px){.ilife-page .ilife-block-toc{flex-wrap:wrap;overflow-x:visible}'
|
|
217
|
+
+ '.ilife-page .ilife-block-toc a{flex:0 1 auto}}'
|
|
218
|
+
+ '#sec-window .ilife-block-param-form{margin:12px 0 0}'
|
|
219
|
+
+ '#sec-window .ilife-block-param-form-field{display:inline-block;width:calc(50% - 6px);margin:6px 0}'
|
|
220
|
+
+ '#sec-window .ilife-block-param-form-field + .ilife-block-param-form-field{margin-left:12px}'
|
|
221
|
+
+ '#sec-window .ilife-block-param-form-input{min-height:44px}'
|
|
222
|
+
+ '.ilife-page .ilife-block-dist-row-fill{min-width:4px}'
|
|
223
|
+
// 日期轴(#268 K4 第二版):一条细轴线 + 等宽槽里的刻度点与日期。轴线与刻度点必须是
|
|
224
|
+
// **同一层两个元素**(同一层各自的伪元素会互相盖住),刻度的圆点用 `i` 的伪元素画。
|
|
225
|
+
// 内距**按百分比**(不能写死 px):折线 SVG 的绘图区两侧在 viewBox 里的位置是定值
|
|
226
|
+
// (`charts.ts`:x0=58、x1=566,viewBox 宽 580),而 SVG 盒宽随卡片变(桌面 850px、手机 328px)
|
|
227
|
+
// ⇒ 左=58/580=10%,右=1−566/580=2.414%。`padding` 里再让出一半槽宽(5 个槽的槽心因此落到
|
|
228
|
+
// 58+槽宽×(i−0.5),与绘图区 1/6…5/6 的锚点同列)。写成定值 px 时手机档会偏 25px(≈18 天)——
|
|
229
|
+
// 视觉复评 r3b/r3c 连量两轮的那一笔。
|
|
230
|
+
+ '.sui-xruler{position:relative;display:flex;align-items:flex-start;margin:2px 2.414% 0 10%;'
|
|
231
|
+
+ 'padding-left:4%;padding-right:4%}'
|
|
232
|
+
+ '.sui-xruler::before{content:"";position:absolute;left:0;right:0;top:3px;height:1px;background:var(--line)}'
|
|
233
|
+
+ '.sui-xruler-t{position:relative;flex:1 1 0;min-width:26px;text-align:center;font-size:11px;'
|
|
234
|
+
+ 'line-height:1.3;color:var(--fg2);font-variant-numeric:tabular-nums;white-space:nowrap}'
|
|
235
|
+
+ '.sui-xruler-t > i{position:relative;display:inline-block;width:7px;height:7px}'
|
|
236
|
+
+ '.sui-xruler-t > i::before{content:"";position:absolute;left:3px;top:0;width:1px;height:3px;background:var(--line)}'
|
|
237
|
+
+ '.sui-xruler-t > i::after{content:"";position:absolute;left:1.5px;top:3px;width:4px;height:4px;'
|
|
238
|
+
+ 'border-radius:50%;background:var(--fg3)}'
|
|
239
|
+
+ '#sec-figures > h2,#sec-category > h2,#sec-badges > h2{margin:0;font-size:15px;font-weight:700;color:var(--fg)}'
|
|
240
|
+
+ '</style>';
|
|
241
|
+
/** 来源脚注卡(#544 形状化,照 #523 的 `footFacts`):键值行「数据来源/窗口/记录数」,
|
|
242
|
+
* 三个独立文本节点,不再产 `数据来源 · <来源> · 起 → 止 · 共 N 条` 那种 `·` 串,
|
|
243
|
+
* 来源名给读者话(库表名只留复制载荷里)。共用层口径统一归 #470。 */
|
|
244
|
+
function footFacts(source, start, end, count) {
|
|
245
|
+
return {
|
|
246
|
+
id: 'sec-source',
|
|
247
|
+
label: '数据来源',
|
|
248
|
+
html: factStrip([
|
|
249
|
+
{ k: '数据来源', v: source },
|
|
250
|
+
{ k: '窗口', v: start + ' → ' + end },
|
|
251
|
+
{ k: '记录数', v: '共 ' + fmtNum(count, 0) + ' 条' },
|
|
252
|
+
]),
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
/** 页尾装配(#544,照 #523 的 `pageBody` 顺序):页内导航 + 口径行(一条事实一行)
|
|
256
|
+
* + 卡 + 三格式复制区(五页装配的唯一出口,免得各页抄一遍)。
|
|
257
|
+
* 页内样式(`exerciseUiCss`+本族小样式)与窗口条由各页组在 `content` 首两项(同 #523)。 */
|
|
258
|
+
function finishPage(cards, calibers, envelopeKey, metrics) {
|
|
259
|
+
return [
|
|
260
|
+
renderTocBlock({ items: cards.map((c) => ({ id: c.id, text: c.label })) }),
|
|
261
|
+
calibers.map((t) => renderCaliberLine(t)).join(''),
|
|
262
|
+
cards.map(shell).join(''),
|
|
263
|
+
copyArea({
|
|
264
|
+
data: {
|
|
265
|
+
envelope: {
|
|
266
|
+
version: DOC_VERSION, skill: DOC_SKILL, shape: 'stat', key: envelopeKey,
|
|
267
|
+
data: { metrics: metricsOf(metrics) },
|
|
268
|
+
},
|
|
269
|
+
},
|
|
270
|
+
}),
|
|
271
|
+
].join('');
|
|
272
|
+
}
|
|
273
|
+
/** 库内分类 → 类别色键(`categoryColors.ts` 的四键);表外分类不给键。 */
|
|
274
|
+
const CATEGORY_KEYS = { 力量: 'strength', 有氧: 'cardio', 柔韧: 'flex', 日常: 'daily' };
|
|
275
|
+
/** 分类色:只从 `categoryColor()`(技能层单源色表)取 hex;表外分类返回 `{}`
|
|
276
|
+
* (不给色=区块回落到冻结 token 缺省色,本层不写第二份色表、也不写 token 名)。 */
|
|
277
|
+
function colorOf(category) {
|
|
278
|
+
const key = CATEGORY_KEYS[category];
|
|
279
|
+
const hex = key === undefined ? undefined : categoryColor(key);
|
|
280
|
+
return hex === undefined ? {} : { color: hex };
|
|
281
|
+
}
|
|
282
|
+
/* ── 力量训练总览(exercise_strength.html 对照:#453 融合版式:动作表+重量轨迹+逐条记录) ── */
|
|
283
|
+
/** 力量页的来源句(读者话;库表名不上屏)。 */
|
|
284
|
+
const STRENGTH_SOURCE = '运动记录(本窗未删除的力量行)';
|
|
285
|
+
/** 力量页口径行(#544:一条事实一行;第一条保留 `口径:` 前缀,回归判据读它)。 */
|
|
286
|
+
const STRENGTH_CALIBERS = [
|
|
287
|
+
'口径:组数=本窗动作记录条数',
|
|
288
|
+
'总重量=逐条重量乘次数累加,缺一即显「—」',
|
|
289
|
+
'总次数=本窗各条次数之和',
|
|
290
|
+
];
|
|
291
|
+
export function buildStrengthDoc(v) {
|
|
292
|
+
const hasRows = v.byMovement.length > 0;
|
|
293
|
+
const cards = [
|
|
294
|
+
windowCard(v.start, v.end, '只数分类为力量的记录。库内分类实填优先,缺失按名推断。配速与时长口径不在此页'),
|
|
295
|
+
{
|
|
296
|
+
id: 'sec-figures',
|
|
297
|
+
label: '核心数字', head: '核心数字',
|
|
298
|
+
// #544:主角(总重量)排首卡吃 28px;窗口与口径事实住窗口条与口径行,卡里只报数。
|
|
299
|
+
html: renderKpiGrid([
|
|
300
|
+
{ label: '总重量', value: numUnit(v.totalVolumeKg, 'kg') },
|
|
301
|
+
{ label: '总次数', value: numUnit(v.totalReps, '次') },
|
|
302
|
+
{ label: '动作数', value: String(v.movementCount), unit: '个' },
|
|
303
|
+
{ label: '总组数', value: String(v.totalSets), unit: '组' },
|
|
304
|
+
]),
|
|
305
|
+
},
|
|
306
|
+
];
|
|
307
|
+
// 口径写在动作表的标题里(票面第 2 条):窗口 + 「单侧口径 Σkg×次数」,全页只此一处。
|
|
308
|
+
if (hasRows) {
|
|
309
|
+
cards.push({
|
|
310
|
+
id: 'sec-table',
|
|
311
|
+
label: '按动作聚合',
|
|
312
|
+
html: renderDataTable({
|
|
313
|
+
columns: [
|
|
314
|
+
{ key: 'movement', label: '动作' },
|
|
315
|
+
{ key: 'sets', label: '组数', align: 'right' },
|
|
316
|
+
{ key: 'volumeKg', label: '总重量', align: 'right' },
|
|
317
|
+
{ key: 'reps', label: '总次数', align: 'right' },
|
|
318
|
+
],
|
|
319
|
+
rows: v.byMovement.map((m) => ({
|
|
320
|
+
movement: m.movement,
|
|
321
|
+
sets: numUnit(m.sets, '组'),
|
|
322
|
+
volumeKg: numUnit(m.volumeKg, 'kg'),
|
|
323
|
+
reps: numUnit(m.reps, '次'),
|
|
324
|
+
})),
|
|
325
|
+
caption: '按动作聚合(' + v.start + ' ~ ' + v.end + ')。单侧口径 Σkg×次数:逐条重量乘次数累加,'
|
|
326
|
+
+ '重量或次数缺一即显「—」',
|
|
327
|
+
}),
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
const trail = v.trail.filter((p) => p.volumeKg !== null);
|
|
331
|
+
// #623:轨迹轴按轨迹自身首末年派生 crossYear(与周柱按页窗口派生不同:轨迹只看近 10 个训练日,
|
|
332
|
+
// 页窗口跨年而轨迹同年时不许补年份,反之页窗口同年而轨迹跨年仍补全年;与 axisDateLabel 同一口径)。
|
|
333
|
+
const trailCrossYear = trail.length > 0
|
|
334
|
+
&& trail[0].date.slice(0, 4) !== trail[trail.length - 1].date.slice(0, 4);
|
|
335
|
+
const trailItems = trail.map((p) => ({ label: axisDateLabel(trailCrossYear)(p.date), value: Number(fmtNum(p.volumeKg)) }));
|
|
336
|
+
const charts = trailItems.length > 0;
|
|
337
|
+
if (charts) {
|
|
338
|
+
cards.push({
|
|
339
|
+
id: 'sec-chart',
|
|
340
|
+
label: '重量轨迹',
|
|
341
|
+
html: renderChartBlock({
|
|
342
|
+
kind: 'line',
|
|
343
|
+
title: '重量轨迹(近 10 个训练日)',
|
|
344
|
+
// #544:纵轴刻度与量程走 `valueAxisOf()`(全等序列不再贴底,同族先例见该函数注释)。
|
|
345
|
+
input: { items: trailItems, options: valueAxisOf(trailItems.map((p) => p.value)) },
|
|
346
|
+
}),
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
if (hasRows) {
|
|
350
|
+
const total = v.rows.length;
|
|
351
|
+
cards.push({
|
|
352
|
+
id: 'sec-detail',
|
|
353
|
+
label: '逐条记录',
|
|
354
|
+
html: renderDataTable({
|
|
355
|
+
columns: [
|
|
356
|
+
{ key: 'date', label: '日期' },
|
|
357
|
+
{ key: 'type', label: '动作' },
|
|
358
|
+
{ key: 'load', label: '重量×次数' },
|
|
359
|
+
{ key: 'note', label: '备注' },
|
|
360
|
+
],
|
|
361
|
+
rows: v.rows.slice(0, RECORD_CAP).map((r) => ({
|
|
362
|
+
date: r.date,
|
|
363
|
+
type: r.type,
|
|
364
|
+
load: numUnit(r.loadKg, 'kg') + '×' + numUnit(r.reps, ''),
|
|
365
|
+
note: r.note.trim() === '' ? '—' : r.note,
|
|
366
|
+
})),
|
|
367
|
+
caption: '力量逐条记录(共 ' + total + ' 条'
|
|
368
|
+
+ (total > RECORD_CAP ? ',本页只列前 ' + RECORD_CAP + ' 条' : '') + ')',
|
|
369
|
+
}),
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
else {
|
|
373
|
+
cards.push({
|
|
374
|
+
id: 'sec-empty',
|
|
375
|
+
label: '按动作聚合',
|
|
376
|
+
html: emptyGuide({ icon: '🏋️', text: '本窗还没有力量训练记录', hint: '说「记力量训练」就能记下第一条' }),
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
cards.push(footFacts(STRENGTH_SOURCE, v.start, v.end, v.rows.length));
|
|
380
|
+
return assembleDocPage({
|
|
381
|
+
docTitle: '卡路里 力量训练总览',
|
|
382
|
+
title: '力量训练总览',
|
|
383
|
+
// #544 视觉第 1 轮:眉标曾试改成与 H1 逐字一致(`力量训练总览`),实测撞 `exercise-accept-267.test.mjs:528`
|
|
384
|
+
// 的「眉标=它自己那一族」钉子(`'力量训练总览' !== '运动力量总览'`)——那件不在本票写集,故**回退**,
|
|
385
|
+
// 两处命名并存记进证据件 §八(转票处置,本票不自行放宽别席判据)。
|
|
386
|
+
eyebrow: '力量训练总览',
|
|
387
|
+
subtitle: '按动作聚合+重量轨迹(缺值显「—」,不编数)',
|
|
388
|
+
content: exerciseUiCss() + S544_CSS
|
|
389
|
+
+ windowStrip(v.start, v.end, windowDays(v.start, v.end) + ' 天')
|
|
390
|
+
+ finishPage(cards, STRENGTH_CALIBERS, '力量训练总览', {
|
|
391
|
+
movementCount: v.movementCount, totalSets: v.totalSets,
|
|
392
|
+
totalVolumeKg: v.totalVolumeKg, totalReps: v.totalReps,
|
|
393
|
+
}),
|
|
394
|
+
charts,
|
|
395
|
+
printable: true,
|
|
396
|
+
pageUi: true,
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
/* ── 有氧训练总览(exercise_cardio.html 对照:#453 融合版式:类型表+类型图+逐条记录) ── */
|
|
400
|
+
/** 有氧页的来源句(读者话;库表名不上屏)。 */
|
|
401
|
+
const CARDIO_SOURCE = '运动记录(本窗未删除的有氧行)';
|
|
402
|
+
/** 有氧页口径行(#544:一条事实一行;第一条保留 `口径:` 前缀,回归判据读它)。 */
|
|
403
|
+
const CARDIO_CALIBERS = [
|
|
404
|
+
'口径:次数=本窗记录条数',
|
|
405
|
+
'时长=分钟,距离=km',
|
|
406
|
+
'步速=分钟÷km,没有距离就不算步速',
|
|
407
|
+
'缺一格显「—」,不空着也不编 0',
|
|
408
|
+
];
|
|
409
|
+
export function buildCardioDoc(v) {
|
|
410
|
+
const hasRows = v.byType.length > 0;
|
|
411
|
+
const cards = [
|
|
412
|
+
windowCard(v.start, v.end, '只数分类为有氧的记录。库内分类实填优先,缺失按名推断。柔韧与日常不在此页'),
|
|
413
|
+
{
|
|
414
|
+
id: 'sec-figures',
|
|
415
|
+
label: '核心数字', head: '核心数字',
|
|
416
|
+
// #544:主角(总时长)排首卡吃 28px;窗口与口径事实住窗口条与口径行,卡里只报数。
|
|
417
|
+
html: renderKpiGrid([
|
|
418
|
+
{ label: '总时长', value: numUnit(v.totalMinutes, '分钟') },
|
|
419
|
+
{ label: '总距离', value: numUnit(v.totalDistanceKm, 'km') },
|
|
420
|
+
{ label: '次数', value: String(v.sessions), unit: '次' },
|
|
421
|
+
{ label: '平均步速', value: fmtPace(v.avgPaceMinPerKm) },
|
|
422
|
+
]),
|
|
423
|
+
},
|
|
424
|
+
];
|
|
425
|
+
if (hasRows) {
|
|
426
|
+
cards.push({
|
|
427
|
+
id: 'sec-table',
|
|
428
|
+
label: '按类型聚合',
|
|
429
|
+
html: renderDataTable({
|
|
430
|
+
columns: [
|
|
431
|
+
{ key: 'type', label: '类型' },
|
|
432
|
+
{ key: 'sessions', label: '次数', align: 'right' },
|
|
433
|
+
{ key: 'minutes', label: '时长', align: 'right' },
|
|
434
|
+
{ key: 'km', label: '距离', align: 'right' },
|
|
435
|
+
{ key: 'pace', label: '步速', align: 'right' },
|
|
436
|
+
],
|
|
437
|
+
rows: v.byType.map((t) => ({
|
|
438
|
+
type: t.type,
|
|
439
|
+
sessions: numUnit(t.sessions, '次'),
|
|
440
|
+
minutes: numUnit(t.minutes, '分钟'),
|
|
441
|
+
km: numUnit(t.distanceKm, 'km'),
|
|
442
|
+
pace: fmtPace(t.paceMinPerKm),
|
|
443
|
+
})),
|
|
444
|
+
caption: '按类型聚合。缺一格显「—」,不空着',
|
|
445
|
+
}),
|
|
446
|
+
});
|
|
447
|
+
cards.push({
|
|
448
|
+
id: 'sec-chart',
|
|
449
|
+
label: '按类型次数',
|
|
450
|
+
html: renderChartBlock({
|
|
451
|
+
kind: 'bar',
|
|
452
|
+
title: '按类型次数',
|
|
453
|
+
input: { items: v.byType.map((t) => ({ label: t.type, value: t.sessions })) },
|
|
454
|
+
}),
|
|
455
|
+
});
|
|
456
|
+
cards.push({
|
|
457
|
+
id: 'sec-detail',
|
|
458
|
+
label: '逐条记录',
|
|
459
|
+
html: renderDataTable({
|
|
460
|
+
columns: [
|
|
461
|
+
{ key: 'date', label: '日期' },
|
|
462
|
+
{ key: 'type', label: '类型' },
|
|
463
|
+
{ key: 'minutes', label: '时长' },
|
|
464
|
+
{ key: 'km', label: '距离' },
|
|
465
|
+
{ key: 'note', label: '备注' },
|
|
466
|
+
],
|
|
467
|
+
rows: v.rows.slice(0, RECORD_CAP).map((r) => ({
|
|
468
|
+
date: r.date,
|
|
469
|
+
type: r.type,
|
|
470
|
+
minutes: numUnit(r.minutes, '分钟'),
|
|
471
|
+
km: numUnit(r.distanceKm, 'km'),
|
|
472
|
+
note: r.note.trim() === '' ? '—' : r.note,
|
|
473
|
+
})),
|
|
474
|
+
caption: '有氧逐条记录(共 ' + v.rows.length + ' 条'
|
|
475
|
+
+ (v.rows.length > RECORD_CAP ? ',本页只列前 ' + RECORD_CAP + ' 条' : '') + ')',
|
|
476
|
+
}),
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
else {
|
|
480
|
+
cards.push({
|
|
481
|
+
id: 'sec-empty',
|
|
482
|
+
label: '按类型聚合',
|
|
483
|
+
html: emptyGuide({ icon: '🏃', text: '本窗还没有有氧运动记录', hint: '说「记有氧运动」就能记下第一条' }),
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
cards.push(footFacts(CARDIO_SOURCE, v.start, v.end, v.sessions));
|
|
487
|
+
return assembleDocPage({
|
|
488
|
+
docTitle: '卡路里 有氧训练总览',
|
|
489
|
+
title: '有氧训练总览',
|
|
490
|
+
// #544 视觉第 1 轮:同力量页,眉标与 H1 并存两名是既成钉子(`exercise-accept-267.test.mjs:528`),本票回退。
|
|
491
|
+
eyebrow: '有氧训练总览',
|
|
492
|
+
subtitle: '按类型聚合+步速(没有距离就不算步速,缺值显「—」)',
|
|
493
|
+
content: exerciseUiCss() + S544_CSS
|
|
494
|
+
+ windowStrip(v.start, v.end, windowDays(v.start, v.end) + ' 天')
|
|
495
|
+
+ finishPage(cards, CARDIO_CALIBERS, '有氧训练总览', {
|
|
496
|
+
sessions: v.sessions, totalMinutes: v.totalMinutes,
|
|
497
|
+
totalDistanceKm: v.totalDistanceKm, avgPaceMinPerKm: v.avgPaceMinPerKm,
|
|
498
|
+
}),
|
|
499
|
+
charts: hasRows,
|
|
500
|
+
printable: true,
|
|
501
|
+
pageUi: true,
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
/* ── 运动类型分布(exercise_distribution.html 对照:#453 融合版式:占比迷你条+分类表+共享图表) ── */
|
|
505
|
+
/** 分布页的来源句(读者话;库表名不上屏)。 */
|
|
506
|
+
const DIST_SOURCE = '运动记录(本窗未删除的行)';
|
|
507
|
+
/** 分布页口径行(#544:一条事实一行;第一条保留 `口径:` 前缀,回归判据读它)。 */
|
|
508
|
+
const DIST_CALIBERS = [
|
|
509
|
+
'口径:占比=该类热量除以本窗合计热量',
|
|
510
|
+
'次数=本窗记录条数',
|
|
511
|
+
'时长=分钟,没有时长的分类不进合计',
|
|
512
|
+
'缺口=TDEE×天+运动−摄入,缺摄入即显「—」',
|
|
513
|
+
];
|
|
514
|
+
export function buildDistributionDoc(v) {
|
|
515
|
+
const hasRows = v.buckets.length > 0;
|
|
516
|
+
// 合计行:四类桶逐项累加;时长只在有已知值的桶间累加(缺值不进合计,也不印假 0)。
|
|
517
|
+
let totalMinutes = 0;
|
|
518
|
+
let minutesKnown = false;
|
|
519
|
+
for (const b of v.buckets) {
|
|
520
|
+
if (b.minutes !== null) {
|
|
521
|
+
totalMinutes += b.minutes;
|
|
522
|
+
minutesKnown = true;
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
const share = (n) => (n === null ? '—' : String(n) + '%');
|
|
526
|
+
const cards = [
|
|
527
|
+
windowCard(v.start, v.end, '分类先看库内填写,四类之外归入其他。占比按该类热量占本窗合计'),
|
|
528
|
+
{
|
|
529
|
+
id: 'sec-figures',
|
|
530
|
+
label: '核心数字', head: '核心数字',
|
|
531
|
+
// #544:主角(总消耗)排首卡吃 28px;窗口天数住窗口条胶囊,会话卡只报活跃天数。
|
|
532
|
+
html: renderKpiGrid([
|
|
533
|
+
{ label: '总消耗', value: numUnit(v.totalBurned, '卡') },
|
|
534
|
+
{ label: '会话', value: String(v.sessions), unit: '次', detail: '活跃 ' + v.activeDays + ' 天' },
|
|
535
|
+
{ label: '摄入', value: numUnit(v.intakeCal, '卡'),
|
|
536
|
+
// #544 视觉第 1 轮:有值时不再挂「没有饮食记录即「—」」这句 fallback 说明
|
|
537
|
+
// (有值却写「没有饮食记录」=自相矛盾);缺值那一支才说为什么是「—」。
|
|
538
|
+
detail: v.intakeCal === null ? '窗内没有饮食记录' : '窗内饮食合计' },
|
|
539
|
+
{ label: '缺口', value: numUnit(v.deficit, '卡') },
|
|
540
|
+
]),
|
|
541
|
+
},
|
|
542
|
+
];
|
|
543
|
+
if (hasRows) {
|
|
544
|
+
// 分类占比:迷你条与数字同格;条色走 `categoryColor()` 的 hex(四类色不在此处另写一份)。
|
|
545
|
+
cards.push({
|
|
546
|
+
id: 'sec-ratio',
|
|
547
|
+
label: '分类占比', head: '分类占比',
|
|
548
|
+
html: renderDistributionRows({
|
|
549
|
+
rows: v.buckets.map((b) => ({
|
|
550
|
+
label: b.category,
|
|
551
|
+
value: share(b.shareByBurned),
|
|
552
|
+
pct: b.shareByBurned ?? 0,
|
|
553
|
+
...colorOf(b.category),
|
|
554
|
+
})),
|
|
555
|
+
}),
|
|
556
|
+
});
|
|
557
|
+
cards.push({
|
|
558
|
+
id: 'sec-table',
|
|
559
|
+
label: '分类明细',
|
|
560
|
+
html: renderDataTable({
|
|
561
|
+
columns: [
|
|
562
|
+
{ key: 'category', label: '分类' },
|
|
563
|
+
{ key: 'sessions', label: '次数', align: 'right' },
|
|
564
|
+
{ key: 'burned', label: '热量', align: 'right' },
|
|
565
|
+
{ key: 'share', label: '占比', align: 'right' },
|
|
566
|
+
{ key: 'minutes', label: '时长', align: 'right' },
|
|
567
|
+
],
|
|
568
|
+
rows: [
|
|
569
|
+
...v.buckets.map((b) => ({
|
|
570
|
+
category: b.category,
|
|
571
|
+
sessions: numUnit(b.sessions, '次'),
|
|
572
|
+
burned: numUnit(b.burned, '卡'),
|
|
573
|
+
share: share(b.shareByBurned),
|
|
574
|
+
minutes: numUnit(b.minutes, '分钟'),
|
|
575
|
+
})),
|
|
576
|
+
{
|
|
577
|
+
category: '合计',
|
|
578
|
+
sessions: numUnit(v.sessions, '次'),
|
|
579
|
+
burned: numUnit(v.totalBurned, '卡'),
|
|
580
|
+
share: '100%',
|
|
581
|
+
minutes: numUnit(minutesKnown ? totalMinutes : null, '分钟'),
|
|
582
|
+
},
|
|
583
|
+
],
|
|
584
|
+
caption: '分类明细(' + v.start + ' ~ ' + v.end + ',末行为合计)',
|
|
585
|
+
}),
|
|
586
|
+
});
|
|
587
|
+
cards.push({
|
|
588
|
+
id: 'sec-chart',
|
|
589
|
+
label: '按分类热量分布',
|
|
590
|
+
html: renderChartBlock({
|
|
591
|
+
kind: 'bar',
|
|
592
|
+
title: '按分类热量分布',
|
|
593
|
+
input: { items: v.buckets.map((b) => ({ label: b.category, value: Number(fmtNum(b.burned, 0)) })) },
|
|
594
|
+
}),
|
|
595
|
+
});
|
|
596
|
+
cards.push({
|
|
597
|
+
id: 'sec-link',
|
|
598
|
+
label: '摄入/TDEE 联动',
|
|
599
|
+
html: renderDisclosure({
|
|
600
|
+
title: '摄入/TDEE 联动(运动贡献)',
|
|
601
|
+
contentHtml: renderListRows({
|
|
602
|
+
items: [
|
|
603
|
+
{ left: '摄入合计', main: numUnit(v.intakeCal, '卡'), right: v.days + ' 天' },
|
|
604
|
+
{ left: '运动消耗', main: numUnit(v.totalBurned, '卡'), right: v.sessions + ' 次' },
|
|
605
|
+
{ left: 'TDEE 合计', main: numUnit(v.tdeeTotal, '卡'), right: '档案静态值×天' },
|
|
606
|
+
{ left: '缺口', main: numUnit(v.deficit, '卡'), right: v.deficit === null ? '缺摄入未算' : 'TDEE+运动−摄入' },
|
|
607
|
+
],
|
|
608
|
+
}),
|
|
609
|
+
}),
|
|
610
|
+
});
|
|
611
|
+
}
|
|
612
|
+
else {
|
|
613
|
+
cards.push({
|
|
614
|
+
id: 'sec-empty',
|
|
615
|
+
label: '分类明细',
|
|
616
|
+
html: emptyGuide({ icon: '📊', text: '本窗还没有运动记录', hint: '说「记运动」就能记下第一条' }),
|
|
617
|
+
});
|
|
618
|
+
}
|
|
619
|
+
cards.push(footFacts(DIST_SOURCE, v.start, v.end, v.sessions));
|
|
620
|
+
return assembleDocPage({
|
|
621
|
+
docTitle: '卡路里 运动类型分布',
|
|
622
|
+
title: '运动类型分布',
|
|
623
|
+
eyebrow: '运动类型分布',
|
|
624
|
+
subtitle: '分类占比+摄入/TDEE 联动(缺摄入不编缺口,缺值显「—」)',
|
|
625
|
+
content: exerciseUiCss() + S544_CSS
|
|
626
|
+
+ windowStrip(v.start, v.end, v.days + ' 天')
|
|
627
|
+
+ finishPage(cards, DIST_CALIBERS, '运动类型分布', {
|
|
628
|
+
sessions: v.sessions, activeDays: v.activeDays, days: v.days, totalBurned: v.totalBurned,
|
|
629
|
+
intakeCal: v.intakeCal, tdeeTotal: v.tdeeTotal, deficit: v.deficit,
|
|
630
|
+
}),
|
|
631
|
+
charts: hasRows,
|
|
632
|
+
printable: true,
|
|
633
|
+
pageUi: true,
|
|
634
|
+
});
|
|
635
|
+
}
|
|
636
|
+
/* ── 运动复盘(exercise_recap.html 对照:#454 融合版式:一句话结论+类型分布+高频徽章+每日消耗) ── */
|
|
637
|
+
/** 折线横轴日期的上屏口径(#615):与汇总族(`sportDocs.ts` 的 `buildExerciseDoc`)**同一口径**——
|
|
638
|
+
* 窗口**跨年**时印全年 `YYYY-MM-DD`,同年仍只印 `MM-DD`。
|
|
639
|
+
*
|
|
640
|
+
* 为什么必须补:跨年窗的左端 `09-08` 排在右端 `09-07` 前头,两条标签同形(都只是月日),读者按字面
|
|
641
|
+
* 读成「起点比终点晚一天」——#577 的 N2 判据当时只枚举了汇总页,趋势族(`buildTrendDoc` 的每日折线)
|
|
642
|
+
* 与复盘族(`buildRecapDoc` 的每日消耗趋势)漏在判据面外(与 N1 漏目标两页 23/24 同型)。
|
|
643
|
+
* 同年窗**不许**补年份:本族一半的图是「本周」「本月」短窗,补年份只会把轴挤满,是另一处走样。
|
|
644
|
+
* 两族同住本件,故这一处改、两族受益(#268 终审席 K4 那条日期标尺逐字复用图上标签,跟着一起变)。 */
|
|
645
|
+
function axisDateLabel(crossYear) {
|
|
646
|
+
return (date) => (crossYear ? date : date.slice(5));
|
|
647
|
+
}
|
|
648
|
+
/** 复盘页的来源句(读者话;库表名不上屏)。 */
|
|
649
|
+
const RECAP_SOURCE = '运动记录(本窗未删除的行)';
|
|
650
|
+
/** 复盘页口径行(#544:一条事实一行;第一条保留 `口径:` 前缀,回归判据读它)。 */
|
|
651
|
+
const RECAP_CALIBERS = [
|
|
652
|
+
'口径:结论句按本窗记录算,频次指记录条数',
|
|
653
|
+
'分布条占比=该类次数÷本窗次数,条色是该类别的固定色',
|
|
654
|
+
'徽章取本窗次数前 5 的运动',
|
|
655
|
+
'折线按窗口每一天画点,没有记录的日子留空,不断 0',
|
|
656
|
+
'时长单位分钟,消耗单位卡',
|
|
657
|
+
];
|
|
658
|
+
/** 五个时间窗(本周/本月/最近 90 天/今年/自定义时间)共用这一个装配:窗口只从数据侧进,
|
|
659
|
+
* 模板零分支——同一组锚点 id 与区块类名,差异只在文本与数据;`sessions === 0` 才改走空态。 */
|
|
660
|
+
export function buildRecapDoc(v) {
|
|
661
|
+
const hasRows = v.sessions > 0;
|
|
662
|
+
const cards = [
|
|
663
|
+
windowCard(v.start, v.end, '按你选的起止日期出这一页。说「本周」「本月」这类叫法时,落到同一张页上,只是窗口不同'),
|
|
664
|
+
];
|
|
665
|
+
// 结论条=页内静态提示形态(浅色、不可点掉),一句话由数据侧给,页面不另编措辞;
|
|
666
|
+
// 取数层结论句里的 `;` 并列由显示层换成逗号承接(一句话结论不断句;`,` 不在探针并列集里)。
|
|
667
|
+
// 取数层(`exercisePort.ts`)一个字不动,#523 明细页的取数路径天然不受影响。
|
|
668
|
+
// **空窗不印结论条**:取数层那句在本窗无记录时是「共运动 0 天、0 次、累计消耗 0 卡」——
|
|
669
|
+
// 三个零在核心数字卡已各有一处,再印一遍既是冗余,也是一处 `、` 三连并列(#508 的 R3 债);
|
|
670
|
+
// 空窗的交代交给下面那张空态引导块(图标+下一句话),页形与分布/力量/有氧三页一致。
|
|
671
|
+
if (hasRows) {
|
|
672
|
+
cards.push({
|
|
673
|
+
id: 'sec-conclusion',
|
|
674
|
+
label: '一句话结论',
|
|
675
|
+
html: renderFeedbackBlock({
|
|
676
|
+
staticNotice: true,
|
|
677
|
+
toast: { msg: v.summary.split(';').join(','), icon: 'ok' },
|
|
678
|
+
}),
|
|
679
|
+
});
|
|
680
|
+
}
|
|
681
|
+
cards.push({
|
|
682
|
+
id: 'sec-figures',
|
|
683
|
+
label: '核心数字',
|
|
684
|
+
// #268 终审席 P1-6:核心数字这三节在正文里没有落点(节身份只在顶部导航的胶囊里),补可见节标题。
|
|
685
|
+
head: '核心数字',
|
|
686
|
+
// #544:主角(总消耗)排首卡;窗口住窗口条胶囊,活跃天数只报分子。
|
|
687
|
+
// (第 1 轮曾把首卡值放大到 28px 当主角,终审席 K1 判「一排四卡字号 28/22 混用、基线错开」,
|
|
688
|
+
// 故整排退回公共层那一档 22px,见 `S544_CSS` 件头。)
|
|
689
|
+
html: renderKpiGrid([
|
|
690
|
+
{ label: '总消耗', value: numUnit(v.totalBurned, '卡') },
|
|
691
|
+
{ label: '总时长', value: numUnit(v.totalMinutes, '分钟') },
|
|
692
|
+
{ label: '频次', value: String(v.sessions), unit: '次', detail: '活跃 ' + v.activeDays + ' 天' },
|
|
693
|
+
{ label: '覆盖分类', value: String(v.byCategory.length), unit: '类' },
|
|
694
|
+
]),
|
|
695
|
+
});
|
|
696
|
+
if (hasRows) {
|
|
697
|
+
// 类型分布条:条色只走 `categoryColor()` 的 hex;占比按次数(与结论句同一口径)。
|
|
698
|
+
// #615:折线轴标签走窗口口径(跨年印全年),不再对日期一律 `slice(5)`。
|
|
699
|
+
const axisDate = axisDateLabel(v.start.slice(0, 4) !== v.end.slice(0, 4));
|
|
700
|
+
const dailyItems = v.daily.map((d) => ({
|
|
701
|
+
label: axisDate(d.date),
|
|
702
|
+
value: d.burned === null ? null : Number(fmtNum(d.burned, 0)),
|
|
703
|
+
}));
|
|
704
|
+
cards.push({
|
|
705
|
+
id: 'sec-category',
|
|
706
|
+
label: '类型分布',
|
|
707
|
+
head: '类型分布',
|
|
708
|
+
html: renderDistributionRows({
|
|
709
|
+
rows: v.byCategory.map((b) => ({
|
|
710
|
+
label: b.category,
|
|
711
|
+
value: b.sessions + ' 次 ' + fmtNum(b.burned, 0) + ' 卡',
|
|
712
|
+
pct: Math.round((b.sessions / v.sessions) * 1000) / 10,
|
|
713
|
+
...colorOf(b.category),
|
|
714
|
+
})),
|
|
715
|
+
}),
|
|
716
|
+
});
|
|
717
|
+
// 高频运动徽章:本窗次数前 5(次数并列时取并到的顺手序,页面不另排序)。
|
|
718
|
+
cards.push({
|
|
719
|
+
id: 'sec-badges',
|
|
720
|
+
label: '高频运动',
|
|
721
|
+
head: '高频运动',
|
|
722
|
+
html: renderChips({ items: v.top5.map((t) => ({ text: t.type + ' ×' + t.sessions })) }),
|
|
723
|
+
});
|
|
724
|
+
cards.push({
|
|
725
|
+
id: 'sec-daily',
|
|
726
|
+
label: '每日消耗趋势',
|
|
727
|
+
// #268 K4:长窗(≥45 天)在折线卡下方补一条日期轴(只出窗口内部日期,短窗不出)。
|
|
728
|
+
html: withXRuler(renderChartBlock({
|
|
729
|
+
kind: 'line',
|
|
730
|
+
title: '每日消耗趋势(空缺断点不断 0)',
|
|
731
|
+
// #544:纵轴刻度与量程走 `valueAxisOf()`(最小值锚 0、刻度落整数、线不贴上下沿)。
|
|
732
|
+
input: { items: dailyItems, options: valueAxisOf(dailyItems.map((p) => p.value)) },
|
|
733
|
+
}), dailyItems),
|
|
734
|
+
});
|
|
735
|
+
}
|
|
736
|
+
else {
|
|
737
|
+
cards.push({
|
|
738
|
+
id: 'sec-empty',
|
|
739
|
+
label: '类型分布',
|
|
740
|
+
html: emptyGuide({ icon: '📊', text: '本窗还没有运动记录', hint: '说「记运动」就能记下第一条' }),
|
|
741
|
+
});
|
|
742
|
+
}
|
|
743
|
+
cards.push(footFacts(RECAP_SOURCE, v.start, v.end, v.sessions));
|
|
744
|
+
return assembleDocPage({
|
|
745
|
+
docTitle: '卡路里 运动复盘',
|
|
746
|
+
title: '运动复盘 ' + v.start + ' ~ ' + v.end,
|
|
747
|
+
eyebrow: '运动复盘',
|
|
748
|
+
subtitle: '结论一句话,类型分布,高频运动,每日消耗趋势',
|
|
749
|
+
content: exerciseUiCss() + S544_CSS
|
|
750
|
+
+ windowStrip(v.start, v.end, v.days + ' 天')
|
|
751
|
+
+ finishPage(cards, RECAP_CALIBERS, '运动复盘', {
|
|
752
|
+
sessions: v.sessions, totalMinutes: v.totalMinutes, totalBurned: v.totalBurned,
|
|
753
|
+
activeDays: v.activeDays, days: v.days,
|
|
754
|
+
}),
|
|
755
|
+
charts: hasRows,
|
|
756
|
+
printable: true,
|
|
757
|
+
pageUi: true,
|
|
758
|
+
});
|
|
759
|
+
}
|
|
760
|
+
/* ── 计划复盘(exercise_review.html 对照) ──
|
|
761
|
+
*
|
|
762
|
+
* **T351-v7:整支已搬进姊妹件 `./reviewDocs.ts`**(键 `calorie.view.exercise-review`,
|
|
763
|
+
* order201–206)——本件已超 350 行告警线(见包 `AGENTS.md` 台账挂号行),复盘族的页内样式
|
|
764
|
+
* 与热力图再挤进来只会更糟;搬走后本件只留「分布/力量/有氧/运动复盘/趋势」五键,
|
|
765
|
+
* `buildReviewDoc` 改由 `../workout/review.ts` 直接 import 姊妹件。
|
|
766
|
+
* 台账许诺的「趋势/复盘一族分住两件」这次只走完「复盘」这一半:`buildRecapDoc`(运动复盘)
|
|
767
|
+
* 与 `buildTrendDoc`(运动趋势)是 #454 刚换过版式的融合版式,属另一半,留给收口票。
|
|
768
|
+
*/
|
|
769
|
+
/* ── 运动趋势(exercise_trend.html 对照:#454 融合版式:每日折线+每周柱图+逐日表) ── */
|
|
770
|
+
/** 趋势页的来源句(读者话;库表名不上屏)。 */
|
|
771
|
+
const TREND_SOURCE = '运动记录(本窗未删除的行)';
|
|
772
|
+
/** 逐日表上限:超出即印截断(页眉条数与可见行数同口径,沿 R3 的截断明示)。 */
|
|
773
|
+
const TREND_ROWS_CAP = 100;
|
|
774
|
+
/** 趋势页口径行(#544:一条事实一行;第一条保留 `口径:` 前缀,回归判据读它)。 */
|
|
775
|
+
const TREND_CALIBERS = [
|
|
776
|
+
'口径:折线与逐日表按窗口每一天画一行,没有记录的日子留空「—」,不补 0',
|
|
777
|
+
'次数=本窗记录条数',
|
|
778
|
+
'时长单位分钟,消耗单位卡',
|
|
779
|
+
'逐日表最多列 ' + TREND_ROWS_CAP + ' 天,超出即印截断,页眉天数与表内可见行数同口径',
|
|
780
|
+
];
|
|
781
|
+
export function buildTrendDoc(v) {
|
|
782
|
+
const hasRows = v.activeDays > 0;
|
|
783
|
+
const shown = Math.min(v.days.length, TREND_ROWS_CAP);
|
|
784
|
+
const truncated = v.days.length > TREND_ROWS_CAP;
|
|
785
|
+
const sessions = v.days.reduce((n, d) => n + d.sessions, 0);
|
|
786
|
+
const cards = [
|
|
787
|
+
windowCard(v.start, v.end, '按天看这段的走势。只说天数时默认三十天,这里按起止日期定窗'),
|
|
788
|
+
{
|
|
789
|
+
id: 'sec-figures',
|
|
790
|
+
label: '核心数字', head: '核心数字',
|
|
791
|
+
// #544:主角(总消耗)排首卡吃 28px;窗口住窗口条胶囊,只留峰值那一处点睛。
|
|
792
|
+
html: renderKpiGrid([
|
|
793
|
+
{ label: '总消耗', value: numUnit(v.totalBurned, '卡') },
|
|
794
|
+
{ label: '总时长', value: numUnit(v.totalMinutes, '分钟') },
|
|
795
|
+
{ label: '运动天数', value: String(v.activeDays), unit: '天' },
|
|
796
|
+
{
|
|
797
|
+
label: '峰值',
|
|
798
|
+
value: v.peak === null ? '—' : numUnit(v.peak.burned, '卡'),
|
|
799
|
+
detail: v.peak === null ? '本窗无记录' : '单日最高 ' + v.peak.date,
|
|
800
|
+
},
|
|
801
|
+
]),
|
|
802
|
+
},
|
|
803
|
+
];
|
|
804
|
+
if (hasRows) {
|
|
805
|
+
// 每日消耗折线:消耗实线(共享刻度)+时长虚线(`ownScale` 独立归一,不另出第二套刻度);
|
|
806
|
+
// 没有记录的日子当场是 `null`(不是 0),点自然断开——「空缺断点不断 0」由数据结构保证。
|
|
807
|
+
// #544:图表入参过显示层取整(空值仍 `null`,不断 0 口径不动)。
|
|
808
|
+
// #615:两条序列的轴标签走窗口口径(跨年印全年),否则跨年窗左端 `09-08` 与右端 `09-07` 同形。
|
|
809
|
+
const axisDate = axisDateLabel(v.start.slice(0, 4) !== v.end.slice(0, 4));
|
|
810
|
+
const burnItems = v.days.map((d) => ({
|
|
811
|
+
label: axisDate(d.date),
|
|
812
|
+
value: d.burned === null ? null : Number(fmtNum(d.burned, 0)),
|
|
813
|
+
}));
|
|
814
|
+
const minItems = v.days.map((d) => ({
|
|
815
|
+
label: axisDate(d.date),
|
|
816
|
+
value: d.minutes === null ? null : Number(fmtNum(d.minutes, 0)),
|
|
817
|
+
}));
|
|
818
|
+
cards.push({
|
|
819
|
+
id: 'sec-line',
|
|
820
|
+
label: '每日消耗折线',
|
|
821
|
+
// #268 K4:长窗(≥45 天)在折线卡下方补一条日期轴(只出窗口内部日期,短窗不出)。
|
|
822
|
+
html: withXRuler(renderChartBlock({
|
|
823
|
+
kind: 'line',
|
|
824
|
+
title: '每日消耗与时长(消耗实线,时长虚线)',
|
|
825
|
+
input: {
|
|
826
|
+
items: burnItems,
|
|
827
|
+
options: {
|
|
828
|
+
// #544:纵轴刻度与量程走 `valueAxisOf()`(共享域=消耗那条;时长是 `ownScale` 独立刻度)。
|
|
829
|
+
...valueAxisOf(burnItems.map((p) => p.value)),
|
|
830
|
+
series: [
|
|
831
|
+
{ name: '消耗(卡)', items: burnItems },
|
|
832
|
+
{ name: '时长(分)', items: minItems, dashed: true, ownScale: true },
|
|
833
|
+
],
|
|
834
|
+
},
|
|
835
|
+
},
|
|
836
|
+
}), burnItems),
|
|
837
|
+
});
|
|
838
|
+
// #623:周柱轴按页窗口首末年派生 crossYear(与 sec-line 同一个 axisDate:周起点可能落在窗口外,
|
|
839
|
+
// 仍按窗口派生;与轨迹按自身首末派生不同;与 axisDateLabel 同一口径:跨年印全年、同年只印月日)。
|
|
840
|
+
cards.push({
|
|
841
|
+
id: 'sec-weekly',
|
|
842
|
+
label: '每周频次',
|
|
843
|
+
html: renderChartBlock({
|
|
844
|
+
kind: 'bar',
|
|
845
|
+
title: '每周运动频次',
|
|
846
|
+
input: { items: v.weekly.map((w) => ({ label: axisDate(w.weekStart) + '周', value: w.sessions })) },
|
|
847
|
+
}),
|
|
848
|
+
});
|
|
849
|
+
cards.push({
|
|
850
|
+
id: 'sec-daily',
|
|
851
|
+
label: '逐日明细',
|
|
852
|
+
html: renderDataTable({
|
|
853
|
+
columns: [
|
|
854
|
+
{ key: 'date', label: '日期' },
|
|
855
|
+
{ key: 'sessions', label: '次数', align: 'right' },
|
|
856
|
+
{ key: 'minutes', label: '时长', align: 'right' },
|
|
857
|
+
{ key: 'burned', label: '消耗', align: 'right' },
|
|
858
|
+
],
|
|
859
|
+
// 空缺日(窗内没有记录的那天)一律「—」:0 次/0 卡都不是那天的实话。
|
|
860
|
+
rows: v.days.slice(0, TREND_ROWS_CAP).map((d) => ({
|
|
861
|
+
date: d.date,
|
|
862
|
+
sessions: d.sessions === 0 ? '—' : d.sessions + ' 次',
|
|
863
|
+
minutes: numUnit(d.minutes, '分钟'),
|
|
864
|
+
burned: numUnit(d.burned, '卡'),
|
|
865
|
+
})),
|
|
866
|
+
caption: '逐日明细(共 ' + v.days.length + ' 天,本表列出 ' + shown + ' 天'
|
|
867
|
+
+ (truncated ? ',已截断,只列前 ' + TREND_ROWS_CAP + ' 天' : ',未截断')
|
|
868
|
+
+ ',空缺日「—」不断 0)',
|
|
869
|
+
}),
|
|
870
|
+
});
|
|
871
|
+
}
|
|
872
|
+
else {
|
|
873
|
+
cards.push({
|
|
874
|
+
id: 'sec-empty',
|
|
875
|
+
label: '逐日明细',
|
|
876
|
+
html: emptyGuide({ icon: '📈', text: '本窗还没有运动记录', hint: '说「记运动」就能记下第一笔' }),
|
|
877
|
+
});
|
|
878
|
+
}
|
|
879
|
+
cards.push(footFacts(TREND_SOURCE, v.start, v.end, sessions));
|
|
880
|
+
return assembleDocPage({
|
|
881
|
+
docTitle: '卡路里 运动趋势',
|
|
882
|
+
title: '运动趋势 ' + v.start + ' ~ ' + v.end,
|
|
883
|
+
eyebrow: '运动趋势',
|
|
884
|
+
subtitle: '每日消耗与时长,每周频次,逐日明细(空缺日留空,不补 0)',
|
|
885
|
+
content: exerciseUiCss() + S544_CSS
|
|
886
|
+
+ windowStrip(v.start, v.end, v.days.length + ' 天')
|
|
887
|
+
+ finishPage(cards, TREND_CALIBERS, '运动趋势', {
|
|
888
|
+
activeDays: v.activeDays, totalMinutes: v.totalMinutes,
|
|
889
|
+
totalBurned: v.totalBurned, peakBurned: v.peak === null ? null : v.peak.burned,
|
|
890
|
+
}),
|
|
891
|
+
charts: hasRows,
|
|
892
|
+
printable: true,
|
|
893
|
+
pageUi: true,
|
|
894
|
+
});
|
|
895
|
+
}
|
|
896
|
+
//# sourceMappingURL=sportPortDocs.js.map
|