feat: 課程表格
This commit is contained in:
@@ -0,0 +1,112 @@
|
|||||||
|
{
|
||||||
|
"low": {
|
||||||
|
"duration": [
|
||||||
|
{ "day": 1, "content": "快走+下肢肌力", "minutes": 40 },
|
||||||
|
{ "day": 2, "content": "平衡訓練+伸展", "minutes": 30 },
|
||||||
|
{ "day": 3, "content": "快走+上肢肌力", "minutes": 40 },
|
||||||
|
{ "day": 4, "content": "休息或散步", "minutes": 20 },
|
||||||
|
{ "day": 5, "content": "肌力+平衡", "minutes": 40 },
|
||||||
|
{ "day": 6, "content": "有氧運動", "minutes": 40 },
|
||||||
|
{ "day": 7, "content": "休息", "minutes": null }
|
||||||
|
],
|
||||||
|
"profile": {
|
||||||
|
"summary": "根據研究,平衡、敏捷與肌肉品質是老化的預測指標。40 歲後肌力與神經控制能力每年流失約 1–2%,而平衡能力下降是跌倒的重要預測因子。",
|
||||||
|
"items": [
|
||||||
|
"肌力、平衡與心肺功能均較差。",
|
||||||
|
"跌倒風險高。",
|
||||||
|
"容易出現肌少症與脂肪浸潤。",
|
||||||
|
"日常生活功能開始下降。"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"prescription": [
|
||||||
|
{
|
||||||
|
"title": "有氧(20–30 分鐘)",
|
||||||
|
"detail": "快走。室內腳踏車。水中走路。強度:最大心率 50–60%。能正常講話但略喘。"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "肌力",
|
||||||
|
"detail": "每個動作:1–2 組。10–12 次。坐站訓練。靠牆伏地挺身。彈力帶划船。抬腳。階梯上下。"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "平衡(10–15 分鐘)",
|
||||||
|
"detail": "單腳站立。走直線。腳跟碰腳尖。"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outcome": {
|
||||||
|
"label": "解決問題",
|
||||||
|
"items": ["肌肉流失。", "平衡能力下降。", "心肺儲備不足。", "跌倒風險升高。"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"medium": {
|
||||||
|
"duration": [
|
||||||
|
{ "day": 1, "content": "重量訓練", "minutes": 60 },
|
||||||
|
{ "day": 2, "content": "慢跑+伸展", "minutes": 45 },
|
||||||
|
{ "day": 3, "content": "核心訓練", "minutes": 45 },
|
||||||
|
{ "day": 4, "content": "間歇有氧", "minutes": 40 },
|
||||||
|
{ "day": 5, "content": "重量訓練", "minutes": 60 },
|
||||||
|
{ "day": 6, "content": "球類或登山", "minutes": 60 },
|
||||||
|
{ "day": 7, "content": "休息", "minutes": null }
|
||||||
|
],
|
||||||
|
"profile": {
|
||||||
|
"summary": "根據功能儲備理論,中年族群常因日常生活僅使用約 30% 的生理能力,忽略功能退化。研究強調,中年時期若沒有累積足夠肌力,老年可能出現「斷崖式功能崩潰」。",
|
||||||
|
"items": ["尚未失能。", "已出現部分衰退。", "屬於「衰弱前期」。"]
|
||||||
|
},
|
||||||
|
"prescription": [
|
||||||
|
{
|
||||||
|
"title": "有氧(150–300 分鐘)",
|
||||||
|
"detail": "慢跑。游泳。羽球。登山。自行車。強度:最大心率 60–75%。"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "肌力",
|
||||||
|
"detail": "每個動作:2–3 組。8–12 次。深蹲。弓箭步。啞鈴推舉。划船。核心棒式。"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "爆發力與敏捷(1–2 次)",
|
||||||
|
"detail": "快速起立。側向移動。腳跟碰腳尖。障礙步行。階梯訓練。"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outcome": {
|
||||||
|
"label": "任務",
|
||||||
|
"items": [
|
||||||
|
"建立「功能儲備」。",
|
||||||
|
"增加肌肉量。",
|
||||||
|
"維持柔軟度。",
|
||||||
|
"降低體脂。",
|
||||||
|
"提升心肺能力。"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"high": {
|
||||||
|
"duration": [
|
||||||
|
{ "day": 1, "content": "重量訓練", "minutes": 70 },
|
||||||
|
{ "day": 2, "content": "間歇跑", "minutes": 50 },
|
||||||
|
{ "day": 3, "content": "核心+平衡", "minutes": 45 },
|
||||||
|
{ "day": 4, "content": "重量訓練", "minutes": 70 },
|
||||||
|
{ "day": 5, "content": "HIIT", "minutes": 30 },
|
||||||
|
{ "day": 6, "content": "球類/戶外", "minutes": 90 },
|
||||||
|
{ "day": 7, "content": "主動恢復", "minutes": 40 }
|
||||||
|
],
|
||||||
|
"profile": {
|
||||||
|
"summary": "根據研究,老化並不會停止,只能透過長期運動與良好飲食延後功能下降。",
|
||||||
|
"items": ["功能儲備高。", "肌力與平衡佳。", "跌倒風險低。", "屬於成功老化族群。"]
|
||||||
|
},
|
||||||
|
"prescription": [
|
||||||
|
{
|
||||||
|
"title": "肌力",
|
||||||
|
"detail": "每個動作:3–4 組。6–10 次。強度:70–85% 1RM。坐站訓練。靠牆伏地挺身。彈力帶划船。抬腳。階梯上下。"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "心肺耐力(300 分鐘以上)",
|
||||||
|
"detail": "間歇跑。游泳。羽球。登山。"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "神經肌肉訓練",
|
||||||
|
"detail": "每週 2 次。敏捷梯。單腳跳。反應訓練。協調訓練。"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outcome": {
|
||||||
|
"label": "目標",
|
||||||
|
"items": ["延緩衰退速度。", "老化仍不可避免,因此維持功能儲備是關鍵。"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import type { FitnessGroup } from '@/api/types'
|
||||||
|
import courseDetail from './course-detail.json'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 各體適能組別的靜態內容,資料來源為 course-detail.json。
|
||||||
|
* 內容取自 doc/運動時間建議.png 與 doc/課程說明.md,
|
||||||
|
* 日後只需修改 JSON 檔並重新 build 即可更新,不必動這支或頁面。
|
||||||
|
*
|
||||||
|
* 註:檔名刻意與 course-detail.json 不同 —— vite.config.mts 的
|
||||||
|
* resolve.extensions 把 '.json' 排在 '.ts' 前面,同名時 import 會解析到 JSON。
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** 一天的運動時間建議;minutes 為 null 代表原表時間欄留空(休息日) */
|
||||||
|
export interface DurationRow {
|
||||||
|
day: number
|
||||||
|
content: string
|
||||||
|
minutes: number | null
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 精準運動處方建議的單一項目 */
|
||||||
|
export interface PrescriptionItem {
|
||||||
|
title: string
|
||||||
|
detail: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CourseDetail {
|
||||||
|
duration: DurationRow[]
|
||||||
|
profile: { summary: string, items: string[] }
|
||||||
|
prescription: PrescriptionItem[]
|
||||||
|
/** label 三組各異:低=解決問題、中=任務、高=目標 */
|
||||||
|
outcome: { label: string, items: string[] }
|
||||||
|
}
|
||||||
|
|
||||||
|
export const COURSE_DETAIL = courseDetail as Record<FitnessGroup, CourseDetail>
|
||||||
@@ -78,7 +78,7 @@
|
|||||||
|
|
||||||
<!-- 一週課表:仿 doc/課程表示意 的表格版型,星期為列、時段為欄 -->
|
<!-- 一週課表:仿 doc/課程表示意 的表格版型,星期為列、時段為欄 -->
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<v-card class="rounded-xl elevation-1 mb-4 overflow-hidden">
|
<v-card class="rounded-lg elevation-1 mb-4 overflow-hidden">
|
||||||
<v-img :alt="`${store.groupMeta.label}課表示意圖`" :src="groupIllustration" />
|
<v-img :alt="`${store.groupMeta.label}課表示意圖`" :src="groupIllustration" />
|
||||||
</v-card>
|
</v-card>
|
||||||
|
|
||||||
@@ -117,6 +117,114 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</v-table>
|
</v-table>
|
||||||
</v-card>
|
</v-card>
|
||||||
|
|
||||||
|
<!-- 運動時間建議:對應 doc/運動時間建議.png -->
|
||||||
|
<v-card class="rounded-lg elevation-1 mt-4 overflow-hidden">
|
||||||
|
<v-card-title class="text-title-medium font-weight-bold text-primary d-flex align-center">
|
||||||
|
<v-icon class="mr-2" color="primary" icon="mdi-timer-outline" size="20" />
|
||||||
|
運動時間建議
|
||||||
|
</v-card-title>
|
||||||
|
|
||||||
|
<v-divider />
|
||||||
|
|
||||||
|
<v-table class="course-table" density="comfortable">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="day-col bg-primary text-white text-center">星期</th>
|
||||||
|
<th class="bg-primary text-white text-center">運動內容</th>
|
||||||
|
<th class="time-col bg-primary text-white text-center">時間</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody>
|
||||||
|
<tr
|
||||||
|
v-for="row in detail.duration"
|
||||||
|
:key="row.day"
|
||||||
|
:class="{ 'bg-teal-lighten-5': row.day === todayDay }"
|
||||||
|
>
|
||||||
|
<td class="day-col text-center font-weight-bold text-primary">
|
||||||
|
{{ DAY_NAMES[row.day - 1] }}
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td class="text-body-medium text-wrap">{{ row.content }}</td>
|
||||||
|
|
||||||
|
<td class="time-col text-center text-body-medium">
|
||||||
|
{{ row.minutes === null ? '—' : `${row.minutes} 分` }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</v-table>
|
||||||
|
</v-card>
|
||||||
|
|
||||||
|
<!-- 課程說明:對應 doc/課程說明.md -->
|
||||||
|
<v-card class="rounded-lg elevation-1 mt-4 overflow-hidden">
|
||||||
|
<v-card-title class="text-title-medium font-weight-bold text-primary d-flex align-center">
|
||||||
|
<v-icon class="mr-2" color="primary" icon="mdi-book-open-variant" size="20" />
|
||||||
|
課程說明
|
||||||
|
</v-card-title>
|
||||||
|
|
||||||
|
<v-divider />
|
||||||
|
|
||||||
|
<v-expansion-panels v-model="openPanels" multiple variant="accordion">
|
||||||
|
<v-expansion-panel>
|
||||||
|
<v-expansion-panel-title class="font-weight-bold">族群特徵</v-expansion-panel-title>
|
||||||
|
|
||||||
|
<v-expansion-panel-text>
|
||||||
|
<p class="text-body-medium text-grey-darken-2 mb-3">
|
||||||
|
{{ detail.profile.summary }}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-for="(item, index) in detail.profile.items"
|
||||||
|
:key="index"
|
||||||
|
class="d-flex align-start ga-3 mb-2"
|
||||||
|
>
|
||||||
|
<v-icon color="secondary" :icon="`mdi-numeric-${index + 1}-circle`" size="20" />
|
||||||
|
<div class="text-body-medium">{{ item }}</div>
|
||||||
|
</div>
|
||||||
|
</v-expansion-panel-text>
|
||||||
|
</v-expansion-panel>
|
||||||
|
|
||||||
|
<v-expansion-panel>
|
||||||
|
<v-expansion-panel-title class="font-weight-bold">
|
||||||
|
精準運動處方建議
|
||||||
|
</v-expansion-panel-title>
|
||||||
|
|
||||||
|
<v-expansion-panel-text>
|
||||||
|
<div
|
||||||
|
v-for="(item, index) in detail.prescription"
|
||||||
|
:key="index"
|
||||||
|
class="d-flex align-start ga-3 mb-4"
|
||||||
|
>
|
||||||
|
<v-icon color="secondary" :icon="`mdi-numeric-${index + 1}-circle`" size="20" />
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div class="font-weight-bold">{{ item.title }}</div>
|
||||||
|
|
||||||
|
<div class="text-body-medium text-grey-darken-2 mt-1">{{ item.detail }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</v-expansion-panel-text>
|
||||||
|
</v-expansion-panel>
|
||||||
|
|
||||||
|
<v-expansion-panel>
|
||||||
|
<v-expansion-panel-title class="font-weight-bold">
|
||||||
|
{{ detail.outcome.label }}
|
||||||
|
</v-expansion-panel-title>
|
||||||
|
|
||||||
|
<v-expansion-panel-text>
|
||||||
|
<div
|
||||||
|
v-for="(item, index) in detail.outcome.items"
|
||||||
|
:key="index"
|
||||||
|
class="d-flex align-start ga-3 mb-2"
|
||||||
|
>
|
||||||
|
<v-icon color="secondary" :icon="`mdi-numeric-${index + 1}-circle`" size="20" />
|
||||||
|
<div class="text-body-medium">{{ item }}</div>
|
||||||
|
</div>
|
||||||
|
</v-expansion-panel-text>
|
||||||
|
</v-expansion-panel>
|
||||||
|
</v-expansion-panels>
|
||||||
|
</v-card>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
</Layout>
|
</Layout>
|
||||||
@@ -124,15 +232,22 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { FitnessGroup, TimeSlot, YesNo } from '@/api/types'
|
import type { FitnessGroup, TimeSlot, YesNo } from '@/api/types'
|
||||||
import { computed, onMounted } from 'vue'
|
import { computed, onMounted, ref } from 'vue'
|
||||||
import highIllustration from '@/assets/course/high.png'
|
import highIllustration from '@/assets/course/high.png'
|
||||||
import lowIllustration from '@/assets/course/low.png'
|
import lowIllustration from '@/assets/course/low.png'
|
||||||
import mediumIllustration from '@/assets/course/medium.png'
|
import mediumIllustration from '@/assets/course/medium.png'
|
||||||
import Layout from '@/components/layout/DefaultLayout.vue'
|
import Layout from '@/components/layout/DefaultLayout.vue'
|
||||||
|
import { COURSE_DETAIL } from '@/data/courseDetail'
|
||||||
import { useCourseStore } from '@/stores/course'
|
import { useCourseStore } from '@/stores/course'
|
||||||
|
|
||||||
const store = useCourseStore()
|
const store = useCourseStore()
|
||||||
|
|
||||||
|
// 運動時間建議與課程說明的靜態內容,依組別對應
|
||||||
|
const detail = computed(() => COURSE_DETAIL[store.group])
|
||||||
|
|
||||||
|
// 課程說明預設只展開第一個面板(族群特徵)
|
||||||
|
const openPanels = ref([0])
|
||||||
|
|
||||||
const GROUP_ILLUSTRATIONS: Record<FitnessGroup, string> = {
|
const GROUP_ILLUSTRATIONS: Record<FitnessGroup, string> = {
|
||||||
low: lowIllustration,
|
low: lowIllustration,
|
||||||
medium: mediumIllustration,
|
medium: mediumIllustration,
|
||||||
@@ -187,4 +302,8 @@ onMounted(() => {
|
|||||||
.day-col {
|
.day-col {
|
||||||
width: 15%;
|
width: 15%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.time-col {
|
||||||
|
width: 22%;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user