506 lines
17 KiB
Vue
506 lines
17 KiB
Vue
<template>
|
||
<Layout>
|
||
<template #main>
|
||
<div class="mb-4 text-center">
|
||
<h1 class="text-h5 font-weight-black text-primary">線上健康自評</h1>
|
||
<p class="text-caption text-grey">只需 3 分鐘,評估您的運動風險與健康狀態</p>
|
||
</div>
|
||
|
||
<!-- 進度指示器 -->
|
||
<v-row class="mb-6 justify-center">
|
||
<v-col v-for="(stepName, idx) in stepNames" :key="idx" class="text-center" cols="3">
|
||
<v-avatar
|
||
class="text-white font-weight-bold mb-1 elevation-1"
|
||
:color="
|
||
currentStep > idx ? 'primary' : currentStep === idx ? 'secondary' : 'grey-lighten-2'
|
||
"
|
||
size="28"
|
||
>
|
||
<v-icon v-if="currentStep > idx" icon="mdi-check" size="14" />
|
||
<span v-else class="text-caption">{{ idx + 1 }}</span>
|
||
</v-avatar>
|
||
|
||
<div
|
||
class="text-caption font-weight-bold"
|
||
:class="
|
||
currentStep === idx
|
||
? 'text-secondary'
|
||
: currentStep > idx
|
||
? 'text-primary'
|
||
: 'text-grey'
|
||
"
|
||
>
|
||
{{ stepName }}
|
||
</div>
|
||
</v-col>
|
||
</v-row>
|
||
|
||
<!-- 步驟內容區 -->
|
||
<v-card class="rounded-xl elevation-3 border-accent-light mb-6" color="surface">
|
||
<v-card-text class="pa-5">
|
||
<!-- 步驟 1:基本資料 -->
|
||
<div v-if="currentStep === 0">
|
||
<h2 class="text-subtitle-1 font-weight-bold text-primary mb-4 d-flex align-center">
|
||
<v-icon class="mr-2" color="secondary" icon="mdi-account-circle" />
|
||
基本資料確認
|
||
</h2>
|
||
|
||
<v-row>
|
||
<v-col cols="12" sm="6">
|
||
<v-text-field
|
||
v-model="profileForm.name"
|
||
class="mb-3"
|
||
color="primary"
|
||
density="comfortable"
|
||
hide-details="auto"
|
||
label="姓名"
|
||
variant="outlined"
|
||
/>
|
||
</v-col>
|
||
|
||
<v-col cols="12" sm="6">
|
||
<v-select
|
||
v-model="profileForm.gender"
|
||
class="mb-3"
|
||
color="primary"
|
||
density="comfortable"
|
||
hide-details="auto"
|
||
:items="['男', '女']"
|
||
label="性別"
|
||
variant="outlined"
|
||
/>
|
||
</v-col>
|
||
|
||
<v-col cols="12" sm="6">
|
||
<v-text-field
|
||
v-model="profileForm.birthday"
|
||
class="mb-3"
|
||
color="primary"
|
||
density="comfortable"
|
||
hide-details="auto"
|
||
label="生日"
|
||
type="date"
|
||
variant="outlined"
|
||
/>
|
||
</v-col>
|
||
|
||
<v-col cols="6">
|
||
<v-text-field
|
||
v-model.number="profileForm.height"
|
||
class="mb-3"
|
||
color="primary"
|
||
density="comfortable"
|
||
hide-details="auto"
|
||
label="身高 (公分)"
|
||
type="number"
|
||
variant="outlined"
|
||
/>
|
||
</v-col>
|
||
|
||
<v-col cols="6">
|
||
<v-text-field
|
||
v-model.number="profileForm.weight"
|
||
class="mb-3"
|
||
color="primary"
|
||
density="comfortable"
|
||
hide-details="auto"
|
||
label="體重 (公斤)"
|
||
type="number"
|
||
variant="outlined"
|
||
/>
|
||
</v-col>
|
||
</v-row>
|
||
|
||
<v-checkbox
|
||
v-model="profileForm.consent"
|
||
class="mt-2 text-caption"
|
||
color="primary"
|
||
density="compact"
|
||
hide-details
|
||
label="我已閱讀並同意個資授權條款"
|
||
/>
|
||
</div>
|
||
|
||
<!-- 步驟 2:活動量與自評 -->
|
||
<div v-if="currentStep === 1">
|
||
<h2 class="text-subtitle-1 font-weight-bold text-primary mb-4 d-flex align-center">
|
||
<v-icon class="mr-2" color="secondary" icon="mdi-walk" />
|
||
活動習慣與想要改善的能力
|
||
</h2>
|
||
|
||
<p class="text-body-2 font-weight-bold text-grey-darken-2 mb-2">
|
||
1. 最近 7 天,您每週進行大運動幾次?
|
||
</p>
|
||
|
||
<v-radio-group v-model="assessmentForm.exerciseFrequency" class="mb-4" color="primary">
|
||
<v-card
|
||
class="mb-2 rounded-lg pa-1 cursor-pointer transition-card"
|
||
:color="assessmentForm.exerciseFrequency === '0' ? 'secondary' : 'grey-lighten-1'"
|
||
variant="outlined"
|
||
@click="assessmentForm.exerciseFrequency = '0'"
|
||
>
|
||
<v-radio label="0 次,幾乎沒有固定運動" value="0" />
|
||
</v-card>
|
||
|
||
<v-card
|
||
class="mb-2 rounded-lg pa-1 cursor-pointer transition-card"
|
||
:color="assessmentForm.exerciseFrequency === '1-2' ? 'secondary' : 'grey-lighten-1'"
|
||
variant="outlined"
|
||
@click="assessmentForm.exerciseFrequency = '1-2'"
|
||
>
|
||
<v-radio label="1-2 次,偶爾散步或伸展" value="1-2" />
|
||
</v-card>
|
||
|
||
<v-card
|
||
class="rounded-lg pa-1 cursor-pointer transition-card"
|
||
:color="assessmentForm.exerciseFrequency === '3+' ? 'secondary' : 'grey-lighten-1'"
|
||
variant="outlined"
|
||
@click="assessmentForm.exerciseFrequency = '3+'"
|
||
>
|
||
<v-radio label="3 次以上,有固定運動習慣" value="3+" />
|
||
</v-card>
|
||
</v-radio-group>
|
||
|
||
<p class="text-body-2 font-weight-bold text-grey-darken-2 mb-2">
|
||
2. 您目前最想改善的身體功能? (可複選)
|
||
</p>
|
||
|
||
<v-row>
|
||
<v-col v-for="imp in improvementsOptions" :key="imp" cols="6">
|
||
<v-card
|
||
class="rounded-lg py-2 px-1 text-center cursor-pointer transition-card"
|
||
:class="{ 'bg-teal-lighten-5': assessmentForm.wantedImprovements.includes(imp) }"
|
||
:color="
|
||
assessmentForm.wantedImprovements.includes(imp) ? 'primary' : 'grey-lighten-1'
|
||
"
|
||
variant="outlined"
|
||
@click="toggleImprovement(imp)"
|
||
>
|
||
<v-icon
|
||
class="mb-1"
|
||
:color="assessmentForm.wantedImprovements.includes(imp) ? 'primary' : 'grey'"
|
||
:icon="getImprovementIcon(imp)"
|
||
/>
|
||
|
||
<div class="text-caption font-weight-bold">{{ imp }}</div>
|
||
</v-card>
|
||
</v-col>
|
||
</v-row>
|
||
</div>
|
||
|
||
<!-- 步驟 3:身體狀況自評 -->
|
||
<div v-if="currentStep === 2">
|
||
<h2 class="text-subtitle-1 font-weight-bold text-primary mb-4 d-flex align-center">
|
||
<v-icon class="mr-2" color="secondary" icon="mdi-alert-decagram" />
|
||
身體限制與慢性病史
|
||
</h2>
|
||
|
||
<p class="text-body-2 font-weight-bold text-grey-darken-2 mb-2">
|
||
1. 過去一年是否有過跌倒史?
|
||
</p>
|
||
|
||
<v-radio-group v-model="assessmentForm.hasFalls" class="mb-4" color="primary" inline>
|
||
<v-radio label="無跌倒" :value="false" />
|
||
<v-radio label="有跌倒過" :value="true" />
|
||
</v-radio-group>
|
||
|
||
<p class="text-body-2 font-weight-bold text-grey-darken-2 mb-2">
|
||
2. 目前身體關節疼痛狀況?
|
||
</p>
|
||
|
||
<v-select
|
||
v-model="assessmentForm.painLevel"
|
||
class="mb-4"
|
||
color="primary"
|
||
density="comfortable"
|
||
:items="['無', '輕微', '中度', '重度']"
|
||
variant="outlined"
|
||
/>
|
||
|
||
<p class="text-body-2 font-weight-bold text-grey-darken-2 mb-2">
|
||
3. 您有哪些慢性疾病? (可複選)
|
||
</p>
|
||
|
||
<v-row class="mb-4">
|
||
<v-col v-for="disease in diseaseOptions" :key="disease" cols="6">
|
||
<v-checkbox
|
||
v-model="assessmentForm.chronicDiseases"
|
||
color="primary"
|
||
density="compact"
|
||
hide-details
|
||
:label="disease"
|
||
:value="disease"
|
||
/>
|
||
</v-col>
|
||
</v-row>
|
||
|
||
<v-text-field
|
||
v-model="medicationInput"
|
||
append-inner-icon="mdi-plus"
|
||
color="primary"
|
||
density="comfortable"
|
||
label="用藥提醒設定 (例如:每日晨間降血壓藥)"
|
||
placeholder="請輸入常用藥物或提醒"
|
||
variant="outlined"
|
||
@click:append-inner="addMedication"
|
||
@keyup.enter="addMedication"
|
||
/>
|
||
|
||
<div
|
||
v-if="assessmentForm.medicationReminders.length > 0"
|
||
class="d-flex flex-wrap gap-2 mt-2"
|
||
>
|
||
<v-chip
|
||
v-for="(med, idx) in assessmentForm.medicationReminders"
|
||
:key="idx"
|
||
closable
|
||
color="secondary"
|
||
size="small"
|
||
@click:close="removeMedication(idx)"
|
||
>
|
||
{{ med }}
|
||
</v-chip>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 步驟 4:自評結果 -->
|
||
<div v-if="currentStep === 3" class="text-center py-4">
|
||
<v-avatar class="elevation-2 mb-4" :color="getResultColor(resultLevel)" size="72">
|
||
<v-icon color="white" :icon="getResultIcon(resultLevel)" size="40" />
|
||
</v-avatar>
|
||
|
||
<h2
|
||
class="text-h5 font-weight-bold mb-1"
|
||
:class="`text-${getResultColor(resultLevel)}`"
|
||
>
|
||
自評分級:{{ resultLevel }}
|
||
</h2>
|
||
|
||
<p class="text-subtitle-2 text-grey-darken-2 mb-6">您的健康自評報告已產生</p>
|
||
|
||
<v-card class="rounded-xl pa-4 text-left mb-6" color="grey-lighten-4" variant="flat">
|
||
<div class="font-weight-bold mb-2 text-primary d-flex align-center">
|
||
<v-icon class="mr-1" icon="mdi-text-box-search" />
|
||
系統評估建議:
|
||
</div>
|
||
|
||
<p v-if="resultLevel === '低風險'" class="text-body-2 text-grey-darken-3 mb-2">
|
||
您的身體功能與日常活動習慣良好。建議您每週繼續保持 3
|
||
次以上的運動,並可以預約普測活動以獲得精準的五維健康指標與運動處方。
|
||
</p>
|
||
|
||
<p v-else-if="resultLevel === '需追蹤'" class="text-body-2 text-grey-darken-3 mb-2">
|
||
由於您運動頻率偏低,或是過去有跌倒史,建議您可以前往運動中心預約專業普測,由現場教練為您提供適合的肌力與平衡回測建議。
|
||
</p>
|
||
|
||
<p v-else class="text-body-2 text-grey-darken-3 mb-2">
|
||
檢測到您有多項慢性病或中重度疼痛,運動時請特別留意。建議您先向您的家庭醫師或社區據點諮詢,並在運動普測現場合對您的健康狀態,以確保運動安全。
|
||
</p>
|
||
|
||
<div class="font-weight-bold text-caption text-secondary mt-3">
|
||
* 禁忌提醒:若收縮壓 >
|
||
160mmHg、有關節強烈疼痛或暈眩時,請立即暫停所有體能檢測與訓練!
|
||
</div>
|
||
</v-card>
|
||
</div>
|
||
</v-card-text>
|
||
|
||
<!-- 表單操作按鈕 -->
|
||
<v-divider />
|
||
|
||
<v-card-actions class="px-5 py-4 d-flex justify-between">
|
||
<v-btn
|
||
v-if="currentStep > 0 && currentStep < 3"
|
||
class="rounded-lg px-4"
|
||
color="primary"
|
||
variant="outlined"
|
||
@click="currentStep--"
|
||
>
|
||
上一步
|
||
</v-btn>
|
||
|
||
<v-spacer />
|
||
|
||
<v-btn
|
||
v-if="currentStep < 2"
|
||
class="rounded-lg px-6"
|
||
color="primary"
|
||
:disabled="currentStep === 0 && !profileForm.consent"
|
||
variant="flat"
|
||
@click="currentStep++"
|
||
>
|
||
下一步
|
||
</v-btn>
|
||
|
||
<v-btn
|
||
v-else-if="currentStep === 2"
|
||
class="rounded-lg px-6 text-white"
|
||
color="secondary"
|
||
variant="flat"
|
||
@click="handleSubmit"
|
||
>
|
||
看我的健康力分級
|
||
</v-btn>
|
||
|
||
<div v-else class="w-100 d-flex gap-2">
|
||
<v-btn
|
||
block
|
||
class="rounded-lg py-2"
|
||
color="primary"
|
||
to="/appointment"
|
||
variant="flat"
|
||
>
|
||
立即預約普測
|
||
</v-btn>
|
||
</div>
|
||
</v-card-actions>
|
||
</v-card>
|
||
|
||
<!-- 返回按鈕 (結果頁專用) -->
|
||
<v-btn
|
||
v-if="currentStep === 3"
|
||
block
|
||
class="rounded-lg py-2"
|
||
color="primary"
|
||
to="/"
|
||
variant="outlined"
|
||
>
|
||
回首頁健康儀表板
|
||
</v-btn>
|
||
</template>
|
||
</Layout>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { reactive, ref } from 'vue'
|
||
import Layout from '@/components/layout/DefaultLayout.vue'
|
||
import { useAppStore } from '@/stores/app'
|
||
|
||
const store = useAppStore()
|
||
const currentStep = ref(0)
|
||
const stepNames = ['基本資料', '功能狀態', '健康限制', '自評結果']
|
||
|
||
const profileForm = reactive({
|
||
name: store.userProfile.name,
|
||
birthday: store.userProfile.birthday,
|
||
gender: store.userProfile.gender,
|
||
height: store.userProfile.height,
|
||
weight: store.userProfile.weight,
|
||
consent: store.userProfile.consent,
|
||
})
|
||
|
||
const assessmentForm = reactive({
|
||
exerciseFrequency: store.selfAssessment.exerciseFrequency,
|
||
wantedImprovements: [...store.selfAssessment.wantedImprovements],
|
||
hasFalls: store.selfAssessment.hasFalls,
|
||
painLevel: store.selfAssessment.painLevel,
|
||
chronicDiseases: [...store.selfAssessment.chronicDiseases],
|
||
medicationReminders: [...store.selfAssessment.medicationReminders],
|
||
})
|
||
|
||
const medicationInput = ref('')
|
||
const resultLevel = ref(store.selfAssessment.scoreLevel)
|
||
|
||
const improvementsOptions = ['肌力', '平衡', '心肺', '柔軟度', '敏捷', '體脂/BMI']
|
||
const diseaseOptions = ['高血壓', '糖尿病', '高血脂', '心臟病', '關節退化', '骨質疏鬆']
|
||
|
||
function getImprovementIcon (imp: string) {
|
||
switch (imp) {
|
||
case '肌力': {
|
||
return 'mdi-arm-flex'
|
||
}
|
||
case '平衡': {
|
||
return 'mdi-scale-balance'
|
||
}
|
||
case '心肺': {
|
||
return 'mdi-heart-pulse'
|
||
}
|
||
case '柔軟度': {
|
||
return 'mdi-human-stretching'
|
||
}
|
||
case '敏捷': {
|
||
return 'mdi-run-fast'
|
||
}
|
||
case '體脂/BMI': {
|
||
return 'mdi-calculator'
|
||
}
|
||
default: {
|
||
return 'mdi-help-circle'
|
||
}
|
||
}
|
||
}
|
||
|
||
function toggleImprovement (imp: string) {
|
||
const index = assessmentForm.wantedImprovements.indexOf(imp)
|
||
if (index === -1) {
|
||
assessmentForm.wantedImprovements.push(imp)
|
||
} else {
|
||
assessmentForm.wantedImprovements.splice(index, 1)
|
||
}
|
||
}
|
||
|
||
function addMedication () {
|
||
if (medicationInput.value.trim()) {
|
||
assessmentForm.medicationReminders.push(medicationInput.value.trim())
|
||
medicationInput.value = ''
|
||
}
|
||
}
|
||
|
||
function removeMedication (index: number) {
|
||
assessmentForm.medicationReminders.splice(index, 1)
|
||
}
|
||
|
||
function handleSubmit () {
|
||
// 儲存至 store
|
||
store.updateProfile({
|
||
name: profileForm.name,
|
||
birthday: profileForm.birthday,
|
||
gender: profileForm.gender,
|
||
height: profileForm.height,
|
||
weight: profileForm.weight,
|
||
consent: profileForm.consent,
|
||
})
|
||
|
||
store.submitSelfAssessment({
|
||
exerciseFrequency: assessmentForm.exerciseFrequency,
|
||
wantedImprovements: assessmentForm.wantedImprovements,
|
||
hasFalls: assessmentForm.hasFalls,
|
||
painLevel: assessmentForm.painLevel,
|
||
chronicDiseases: assessmentForm.chronicDiseases,
|
||
medicationReminders: assessmentForm.medicationReminders,
|
||
})
|
||
|
||
// 取得計算後的結果
|
||
resultLevel.value = store.selfAssessment.scoreLevel
|
||
currentStep.value = 3
|
||
}
|
||
|
||
function getResultColor (lvl: string) {
|
||
if (lvl === '低風險') return 'success'
|
||
if (lvl === '需追蹤') return 'warning'
|
||
return 'error'
|
||
}
|
||
|
||
function getResultIcon (lvl: string) {
|
||
if (lvl === '低風險') return 'mdi-check-decagram'
|
||
if (lvl === '需追蹤') return 'mdi-alert-decagram'
|
||
return 'mdi-alert-octagon'
|
||
}
|
||
</script>
|
||
|
||
<style scoped>
|
||
.transition-card {
|
||
transition: all 0.25s ease;
|
||
border-width: 1.5px;
|
||
}
|
||
|
||
.transition-card:hover {
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.05);
|
||
}
|
||
|
||
.gap-2 {
|
||
gap: 8px;
|
||
}
|
||
</style>
|