feat: draft
This commit is contained in:
@@ -0,0 +1,711 @@
|
||||
<template>
|
||||
<Layout>
|
||||
<template #main>
|
||||
<template v-if="store.isLoggedIn">
|
||||
<!-- 頂部:我的健康力護照卡片 (Taipei Masters Hub) -->
|
||||
<v-card
|
||||
class="passport-card rounded-xl text-white pa-5 elevation-8 mb-6 position-relative overflow-hidden"
|
||||
>
|
||||
<!-- 背景裝飾圓圈 -->
|
||||
<div class="bg-circle-1" />
|
||||
<div class="bg-circle-2" />
|
||||
|
||||
<div class="d-flex justify-space-between align-center mb-6 z-index-1 position-relative">
|
||||
<div class="d-flex align-center">
|
||||
<v-avatar class="mr-3 border-white" color="accent" size="44">
|
||||
<span class="text-white font-weight-black text-subtitle-1">{{
|
||||
store.userProfile.name.substring(1)
|
||||
}}</span>
|
||||
</v-avatar>
|
||||
|
||||
<div>
|
||||
<h2 class="text-subtitle-1 font-weight-black mb-0">
|
||||
{{ store.userProfile.name }} 先生
|
||||
</h2>
|
||||
|
||||
<span class="text-caption text-white-70">更新於今日 {{ todayTimeString }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 健康力分數大圓盤 -->
|
||||
<div class="score-disk d-flex flex-column justify-center align-center">
|
||||
<span class="score-val">{{ store.healthScore }}</span>
|
||||
<span class="score-unit">分</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 功能儲備與運動處方簡介 -->
|
||||
<div class="z-index-1 position-relative mb-4">
|
||||
<div class="d-inline-flex align-center bg-white-20 px-3 py-1 rounded-pill mb-3">
|
||||
<v-icon class="mr-1 text-accent-light" icon="mdi-heart-pulse" size="16" />
|
||||
<span class="text-caption font-weight-bold">功能儲備:{{ functionalReserve }}</span>
|
||||
</div>
|
||||
|
||||
<div class="text-body-2 font-weight-medium bg-white-10 rounded-lg pa-3">
|
||||
<div class="font-weight-black mb-1 text-accent-light d-flex align-center">
|
||||
<v-icon class="mr-1" icon="mdi-notebook-check-outline" size="16" />
|
||||
本週處方建議:
|
||||
</div>
|
||||
{{ currentWeeklyPrescription }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<v-divider class="bg-white-20 my-3 z-index-1 position-relative" />
|
||||
|
||||
<!-- 四項核心能力數值簡覽 -->
|
||||
<v-row class="z-index-1 position-relative text-center" dense>
|
||||
<v-col cols="3">
|
||||
<div class="text-caption text-white-70">肌力</div>
|
||||
|
||||
<div class="text-subtitle-2 font-weight-black">
|
||||
{{ store.radarData.strength }}
|
||||
</div>
|
||||
</v-col>
|
||||
|
||||
<v-col cols="3">
|
||||
<div class="text-caption text-white-70">平衡</div>
|
||||
|
||||
<div class="text-subtitle-2 font-weight-black">
|
||||
{{ store.radarData.balance }}
|
||||
</div>
|
||||
</v-col>
|
||||
|
||||
<v-col cols="3">
|
||||
<div class="text-caption text-white-70">敏捷</div>
|
||||
|
||||
<div class="text-subtitle-2 font-weight-black">
|
||||
{{ store.radarData.agility }}
|
||||
</div>
|
||||
</v-col>
|
||||
|
||||
<v-col cols="3">
|
||||
<div class="text-caption text-white-70">心肺</div>
|
||||
|
||||
<div class="text-subtitle-2 font-weight-black">
|
||||
{{ store.radarData.cardio }}
|
||||
</div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card>
|
||||
|
||||
<!-- 中間:功能指標與五維雷達圖 -->
|
||||
<v-card class="rounded-xl elevation-3 border-accent-light mb-6 pa-4" color="surface">
|
||||
<h3 class="text-subtitle-1 font-weight-black text-primary mb-2 d-flex align-center">
|
||||
<v-icon class="mr-2" color="secondary" icon="mdi-radar" />
|
||||
個人五維健康力指標
|
||||
</h3>
|
||||
|
||||
<p class="text-caption text-grey mb-4">
|
||||
綜合 11 項現場普測數值計算得出,藍色為同齡正常參考值區間。
|
||||
</p>
|
||||
|
||||
<!-- 純 SVG 雷達圖,不依賴任何外部圖表庫,保證完美渲染 -->
|
||||
<div class="d-flex justify-center my-2 position-relative">
|
||||
<svg class="radar-svg" height="260" viewBox="0 0 120 120" width="260">
|
||||
<!-- 5 個同心五邊形網格 (背景線) -->
|
||||
<polygon
|
||||
v-for="grid in [20, 40, 60, 80, 100]"
|
||||
:key="grid"
|
||||
fill="none"
|
||||
:points="getPolygonPoints(grid)"
|
||||
stroke="#E0E0E0"
|
||||
stroke-width="0.3"
|
||||
/>
|
||||
|
||||
<!-- 五個維度軸線 -->
|
||||
<line
|
||||
v-for="i in 5"
|
||||
:key="`axis-${i}`"
|
||||
stroke="#E0E0E0"
|
||||
stroke-width="0.3"
|
||||
x1="60"
|
||||
:x2="getAxisEndPoint(i - 1).x"
|
||||
y1="60"
|
||||
:y2="getAxisEndPoint(i - 1).y"
|
||||
/>
|
||||
|
||||
<!-- 同齡參考區間 (同齡常模基準:設為 65 分) -->
|
||||
<polygon
|
||||
fill="rgba(33, 150, 243, 0.12)"
|
||||
:points="getPolygonPoints(65)"
|
||||
stroke="#2196F3"
|
||||
stroke-dasharray="1 1"
|
||||
stroke-width="0.5"
|
||||
/>
|
||||
|
||||
<!-- 使用者真實數據填充面 -->
|
||||
<polygon
|
||||
fill="rgba(71, 194, 166, 0.35)"
|
||||
:points="userDataPoints"
|
||||
stroke="#1D7063"
|
||||
stroke-width="1.2"
|
||||
/>
|
||||
|
||||
<!-- 使用者數據頂點標記圓圈 -->
|
||||
<circle
|
||||
v-for="(pt, idx) in userDataCoordArray"
|
||||
:key="`dot-${idx}`"
|
||||
:cx="pt.x"
|
||||
:cy="pt.y"
|
||||
fill="#E66926"
|
||||
r="1.5"
|
||||
stroke="#FFFFFF"
|
||||
stroke-width="0.5"
|
||||
/>
|
||||
|
||||
<!-- 頂點文字標記 (Cardio, Strength, Balance, Flexibility, Agility) -->
|
||||
<text
|
||||
fill="#1D7063"
|
||||
font-size="4"
|
||||
font-weight="bold"
|
||||
text-anchor="middle"
|
||||
x="60"
|
||||
y="8"
|
||||
>
|
||||
心肺 ({{ store.radarData.cardio }})
|
||||
</text>
|
||||
|
||||
<text
|
||||
fill="#1D7063"
|
||||
font-size="4"
|
||||
font-weight="bold"
|
||||
text-anchor="start"
|
||||
x="110"
|
||||
y="44"
|
||||
>
|
||||
肌力 ({{ store.radarData.strength }})
|
||||
</text>
|
||||
|
||||
<text
|
||||
fill="#1D7063"
|
||||
font-size="4"
|
||||
font-weight="bold"
|
||||
text-anchor="start"
|
||||
x="94"
|
||||
y="104"
|
||||
>
|
||||
平衡 ({{ store.radarData.balance }})
|
||||
</text>
|
||||
|
||||
<text
|
||||
fill="#1D7063"
|
||||
font-size="4"
|
||||
font-weight="bold"
|
||||
text-anchor="end"
|
||||
x="26"
|
||||
y="104"
|
||||
>
|
||||
柔軟 ({{ store.radarData.flexibility }})
|
||||
</text>
|
||||
|
||||
<text
|
||||
fill="#1D7063"
|
||||
font-size="4"
|
||||
font-weight="bold"
|
||||
text-anchor="end"
|
||||
x="10"
|
||||
y="44"
|
||||
>
|
||||
敏捷 ({{ store.radarData.agility }})
|
||||
</text>
|
||||
</svg>
|
||||
|
||||
<!-- 圖例標示 -->
|
||||
<div class="radar-legend d-flex flex-column">
|
||||
<div class="d-flex align-center text-caption mb-1">
|
||||
<span class="legend-color bg-primary mr-1" />
|
||||
<span>我的指標</span>
|
||||
</div>
|
||||
|
||||
<div class="d-flex align-center text-caption">
|
||||
<span class="legend-color bg-blue mr-1 border-dashed" />
|
||||
<span>同齡參考</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</v-card>
|
||||
|
||||
<!-- 下一步行動引導 (根據量測進度動態顯示) -->
|
||||
<h3 class="text-subtitle-2 font-weight-black text-primary mb-3">下一步行動推薦</h3>
|
||||
|
||||
<!-- 情況 1:普測量測正在進行中 -->
|
||||
<v-card
|
||||
v-if="isIncompleteMeasurement"
|
||||
class="rounded-xl elevation-3 mb-4 transition-card text-left"
|
||||
color="surface"
|
||||
to="/checkin"
|
||||
>
|
||||
<v-card-text class="pa-4 d-flex align-center justify-between">
|
||||
<div class="d-flex align-center">
|
||||
<v-avatar class="mr-3 text-white elevation-1" color="secondary" size="48">
|
||||
<v-icon icon="mdi-run-fast" />
|
||||
</v-avatar>
|
||||
|
||||
<div>
|
||||
<div class="font-weight-bold text-subtitle-2 text-grey-darken-4">
|
||||
繼續現場體能普測
|
||||
</div>
|
||||
|
||||
<div class="text-caption text-grey">
|
||||
目前已完成 {{ completedCount }} / 11 站,繼續完成登錄。
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<v-icon color="primary" icon="mdi-chevron-right" />
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
|
||||
<!-- 情況 2:量測全部完成,提醒可以看運動處方任務 -->
|
||||
<v-card
|
||||
v-else
|
||||
class="rounded-xl elevation-3 mb-4 transition-card text-left"
|
||||
color="surface"
|
||||
@click="prescriptionDialog = true"
|
||||
>
|
||||
<v-card-text class="pa-4 d-flex align-center justify-between">
|
||||
<div class="d-flex align-center">
|
||||
<v-avatar class="mr-3 text-white elevation-1" color="secondary" size="48">
|
||||
<v-icon icon="mdi-calendar-multiselect" />
|
||||
</v-avatar>
|
||||
|
||||
<div>
|
||||
<div class="font-weight-bold text-subtitle-2 text-grey-darken-4">
|
||||
執行本週運動處方課程
|
||||
</div>
|
||||
|
||||
<div class="text-caption text-grey">
|
||||
依普測結果產生,肌力 2 次、心肺 2 次、平衡 3 次。
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<v-icon color="primary" icon="mdi-chevron-right" />
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
|
||||
<!-- 普測預約快捷卡片 -->
|
||||
<v-card
|
||||
class="rounded-xl elevation-3 mb-6 transition-card text-left"
|
||||
color="surface"
|
||||
to="/appointment"
|
||||
>
|
||||
<v-card-text class="pa-4 d-flex align-center justify-between">
|
||||
<div class="d-flex align-center">
|
||||
<v-avatar class="mr-3 text-white elevation-1" color="primary" size="48">
|
||||
<v-icon icon="mdi-calendar-clock" />
|
||||
</v-avatar>
|
||||
|
||||
<div>
|
||||
<div class="font-weight-bold text-subtitle-2 text-grey-darken-4">
|
||||
預約 30 秒坐站與平衡回測
|
||||
</div>
|
||||
|
||||
<div class="text-caption text-grey">4 週 / 12 週定期回測,動態追蹤體能進步狀態。</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<v-icon color="primary" icon="mdi-chevron-right" />
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
|
||||
<!-- 最近一次量測結果摘要 -->
|
||||
<v-card class="rounded-xl elevation-3 mb-8 pa-4" color="surface">
|
||||
<div class="d-flex justify-space-between align-center mb-3">
|
||||
<h3 class="text-subtitle-1 font-weight-black text-primary d-flex align-center">
|
||||
<v-icon class="mr-2" color="secondary" icon="mdi-file-eye-outline" />
|
||||
最近量測數值摘要
|
||||
</h3>
|
||||
|
||||
<v-btn
|
||||
class="font-weight-bold px-0"
|
||||
color="primary"
|
||||
size="small"
|
||||
to="/checkin"
|
||||
variant="text"
|
||||
>查看全部</v-btn>
|
||||
</div>
|
||||
|
||||
<v-row dense>
|
||||
<v-col v-for="item in recentItems" :key="item.name" class="mb-2" cols="6">
|
||||
<div class="bg-grey-lighten-4 rounded-lg pa-3 h-100">
|
||||
<div class="text-caption text-grey-darken-1 mb-1">{{ item.name }}</div>
|
||||
|
||||
<div class="text-body-1 font-weight-black text-grey-darken-4">
|
||||
{{ item.value }}
|
||||
</div>
|
||||
</div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card>
|
||||
|
||||
<!-- 運動處方對話框 -->
|
||||
<v-dialog v-model="prescriptionDialog" max-width="500">
|
||||
<v-card class="rounded-xl">
|
||||
<v-card-title class="bg-primary text-white font-weight-bold d-flex align-center">
|
||||
<v-icon class="mr-2" icon="mdi-dumbbell" />
|
||||
本週運動處方明細
|
||||
</v-card-title>
|
||||
|
||||
<v-card-text class="pt-4 text-body-1">
|
||||
<h4 class="font-weight-bold text-primary mb-2">每週訓練任務進度</h4>
|
||||
|
||||
<div class="mb-4">
|
||||
<div class="d-flex justify-space-between align-center text-caption mb-1">
|
||||
<span>任務完成度 (2/7堂課)</span>
|
||||
<span>28%</span>
|
||||
</div>
|
||||
|
||||
<v-progress-linear color="secondary" height="6" model-value="28" rounded />
|
||||
</div>
|
||||
|
||||
<!-- 動作說明清單 -->
|
||||
<v-list class="pa-0" density="comfortable">
|
||||
<v-list-item class="bg-teal-lighten-5 rounded-lg mb-2 py-2">
|
||||
<template #prepend>
|
||||
<v-icon color="secondary" icon="mdi-numeric-1-circle" size="24" />
|
||||
</template>
|
||||
|
||||
<v-list-item-title class="font-weight-bold">下肢肌力:坐站起立訓練</v-list-item-title>
|
||||
|
||||
<v-list-item-subtitle class="text-caption mt-1">
|
||||
每週 2 次,每次 3 組,每組 10-12 次。<br />
|
||||
<span class="text-error font-weight-bold">* 扶手保護,站起時吐氣。</span>
|
||||
</v-list-item-subtitle>
|
||||
</v-list-item>
|
||||
|
||||
<v-list-item class="bg-teal-lighten-5 rounded-lg mb-2 py-2">
|
||||
<template #prepend>
|
||||
<v-icon color="secondary" icon="mdi-numeric-2-circle" size="24" />
|
||||
</template>
|
||||
|
||||
<v-list-item-title class="font-weight-bold">靜態平衡:扶椅單腳站立</v-list-item-title>
|
||||
|
||||
<v-list-item-subtitle class="text-caption mt-1">
|
||||
每週 3 次,左右腳各 30 秒,重複 3 次。<br />
|
||||
<span class="text-grey-darken-1">手輕扶椅背,確保安全。</span>
|
||||
</v-list-item-subtitle>
|
||||
</v-list-item>
|
||||
|
||||
<v-list-item class="bg-teal-lighten-5 rounded-lg py-2">
|
||||
<template #prepend>
|
||||
<v-icon color="secondary" icon="mdi-numeric-3-circle" size="24" />
|
||||
</template>
|
||||
|
||||
<v-list-item-title class="font-weight-bold">心肺耐力:每日快走</v-list-item-title>
|
||||
|
||||
<v-list-item-subtitle class="text-caption mt-1">
|
||||
每日 20-30 分鐘,達到微喘但仍可說話的強度。<br />
|
||||
<span class="text-error font-weight-bold">* 若感覺頭暈或膝蓋疼痛請暫停!</span>
|
||||
</v-list-item-subtitle>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-card-text>
|
||||
|
||||
<v-card-actions class="justify-end px-4 pb-4">
|
||||
<v-btn
|
||||
class="rounded-lg px-4"
|
||||
color="primary"
|
||||
variant="flat"
|
||||
@click="prescriptionDialog = false"
|
||||
>確認關閉</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<div class="text-center mb-6">
|
||||
<v-avatar class="elevation-3 mb-4 border-accent-light" color="primary" size="72">
|
||||
<v-icon color="accent" icon="mdi-heart-pulse" size="42" />
|
||||
</v-avatar>
|
||||
|
||||
<h1 class="text-h5 font-weight-black text-primary mb-2">運動玩轉健康力</h1>
|
||||
|
||||
<p class="text-body-2 text-grey-darken-1 mb-0">
|
||||
不登入也可以先完成線上健康自評,登入後可預約普測與查看量測紀錄。
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<v-card class="rounded-xl elevation-3 border-accent-light mb-4" color="surface">
|
||||
<v-card-text class="pa-5">
|
||||
<div class="d-flex align-center mb-4">
|
||||
<v-avatar class="mr-3 text-white" color="secondary" size="48">
|
||||
<v-icon icon="mdi-clipboard-text-outline" />
|
||||
</v-avatar>
|
||||
|
||||
<div class="text-left">
|
||||
<div class="text-subtitle-1 font-weight-black text-primary">先做線上健康自評</div>
|
||||
<div class="text-caption text-grey">約 3 分鐘完成,取得初步健康風險分級。</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<v-btn
|
||||
block
|
||||
class="rounded-lg font-weight-bold mb-3"
|
||||
color="primary"
|
||||
prepend-icon="mdi-arrow-right-circle"
|
||||
to="/self-assessment"
|
||||
variant="flat"
|
||||
>
|
||||
開始健康自評
|
||||
</v-btn>
|
||||
|
||||
<v-btn
|
||||
block
|
||||
class="rounded-lg font-weight-bold"
|
||||
color="primary"
|
||||
prepend-icon="mdi-login"
|
||||
to="/login"
|
||||
variant="outlined"
|
||||
>
|
||||
登入健康平台
|
||||
</v-btn>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
|
||||
<v-card class="rounded-xl elevation-1 border-accent-light" color="surface">
|
||||
<v-card-text class="pa-4 d-flex align-center text-left">
|
||||
<v-icon class="mr-3" color="primary" icon="mdi-lock-outline" size="28" />
|
||||
|
||||
<div>
|
||||
<div class="font-weight-bold text-grey-darken-4">登入後可使用完整功能</div>
|
||||
|
||||
<div class="text-caption text-grey">
|
||||
普測預約、現場報到、量測紀錄與運動處方需要登入狀態。
|
||||
</div>
|
||||
</div>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</template>
|
||||
</template>
|
||||
</Layout>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue'
|
||||
import Layout from '@/components/layout/DefaultLayout.vue'
|
||||
import { useAppStore } from '@/stores/app'
|
||||
|
||||
const store = useAppStore()
|
||||
const prescriptionDialog = ref(false)
|
||||
|
||||
const todayTimeString = computed(() => {
|
||||
const now = new Date()
|
||||
return `${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}`
|
||||
})
|
||||
|
||||
// 功能儲備評估
|
||||
const functionalReserve = computed(() => {
|
||||
const score = store.healthScore
|
||||
if (score >= 80) return '良好 (無衰弱風險)'
|
||||
if (score >= 65) return '正常 (需持續運動維持)'
|
||||
return '待加強 (有衰弱與跌倒風險)'
|
||||
})
|
||||
|
||||
// 每週處方文字
|
||||
const currentWeeklyPrescription = computed(() => {
|
||||
if (store.healthScore >= 80) {
|
||||
return '本週處方:1次肌力循環 + 1次平衡課 + 每日20分鐘快走'
|
||||
} else if (store.healthScore >= 65) {
|
||||
return '本週處方:2次肌力循環 + 1次平衡課 + 每日20分鐘快走'
|
||||
} else {
|
||||
return '本週處方:3次防跌平衡引導 + 每日15分鐘扶椅坐站與慢走'
|
||||
}
|
||||
})
|
||||
|
||||
// 檢測是否還有未完成的測量站
|
||||
const isIncompleteMeasurement = computed(() => {
|
||||
return store.completedStationsCount < store.totalStationsCount
|
||||
})
|
||||
|
||||
const completedCount = computed(() => store.completedStationsCount)
|
||||
|
||||
// 最近一次量測結果摘要列表
|
||||
const recentItems = computed(() => {
|
||||
const m = store.measurements
|
||||
const items = []
|
||||
|
||||
if (m.BloodPressure.completed) {
|
||||
items.push({
|
||||
name: '血壓量測',
|
||||
value: `${m.BloodPressure.values.systolic}/${m.BloodPressure.values.diastolic} mmHg`,
|
||||
})
|
||||
}
|
||||
if (m.BodyFat.completed) {
|
||||
items.push({ name: '體組成 BMI', value: `${m.BodyFat.values.bmi}` })
|
||||
}
|
||||
if (m.GripStrength.completed) {
|
||||
items.push({ name: '右手最大握力', value: `${m.GripStrength.values.rightHand} kg` })
|
||||
} else {
|
||||
items.push({ name: '左右握力', value: '未測量' })
|
||||
}
|
||||
if (m.OneLegTest.completed) {
|
||||
items.push({
|
||||
name: '單腳站立平衡',
|
||||
value: `${Math.max(m.OneLegTest.values.leftSeconds, m.OneLegTest.values.rightSeconds)} 秒`,
|
||||
})
|
||||
} else {
|
||||
items.push({ name: '開眼單足立', value: '未測量' })
|
||||
}
|
||||
|
||||
return items
|
||||
})
|
||||
|
||||
// --- SVG 雷達圖點計算邏輯 ---
|
||||
const cx = 60
|
||||
const cy = 60
|
||||
const r = 45
|
||||
|
||||
// 計算 5 角多邊形的頂點
|
||||
// 頂點順序: i=0(心肺), i=1(肌力), i=2(平衡), i=3(柔軟), i=4(敏捷)
|
||||
function getAxisEndPoint (i: number, radius = r) {
|
||||
const angle = -Math.PI / 2 + (i * 2 * Math.PI) / 5
|
||||
return {
|
||||
x: cx + radius * Math.cos(angle),
|
||||
y: cy + radius * Math.sin(angle),
|
||||
}
|
||||
}
|
||||
|
||||
// 根據百分比半徑繪製背景網格五邊形
|
||||
function getPolygonPoints (percent: number) {
|
||||
const radius = (percent / 100) * r
|
||||
const pts = []
|
||||
for (let i = 0; i < 5; i++) {
|
||||
const pt = getAxisEndPoint(i, radius)
|
||||
pts.push(`${pt.x},${pt.y}`)
|
||||
}
|
||||
return pts.join(' ')
|
||||
}
|
||||
|
||||
// 使用者五維數據對應的雷達圖頂點座標
|
||||
const userDataCoordArray = computed(() => {
|
||||
const data = store.radarData
|
||||
// 順序與頂點對應:
|
||||
// 0: cardio (心肺), 1: strength (肌力), 2: balance (平衡), 3: flexibility (柔軟), 4: agility (敏捷)
|
||||
const values = [data.cardio, data.strength, data.balance, data.flexibility, data.agility]
|
||||
return values.map((val, i) => {
|
||||
const radius = (Math.max(5, Math.min(100, val)) / 100) * r
|
||||
return getAxisEndPoint(i, radius)
|
||||
})
|
||||
})
|
||||
|
||||
const userDataPoints = computed(() => {
|
||||
return userDataCoordArray.value.map(pt => `${pt.x},${pt.y}`).join(' ')
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.passport-card {
|
||||
background: linear-gradient(135deg, #1d7063 0%, #145248 100%);
|
||||
border: 1px solid rgba(71, 194, 166, 0.3);
|
||||
box-shadow: 0 8px 16px rgba(20, 82, 72, 0.25) !important;
|
||||
}
|
||||
|
||||
.bg-circle-1 {
|
||||
position: absolute;
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
border-radius: 50%;
|
||||
background: rgba(71, 194, 166, 0.15);
|
||||
top: -50px;
|
||||
right: -50px;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.bg-circle-2 {
|
||||
position: absolute;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
border-radius: 50%;
|
||||
background: rgba(230, 105, 38, 0.1);
|
||||
bottom: -30px;
|
||||
left: -20px;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.z-index-1 {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.bg-white-20 {
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.bg-white-10 {
|
||||
background-color: rgba(255, 255, 255, 0.08);
|
||||
border: 1px solid rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
.text-white-70 {
|
||||
color: rgba(255, 255, 255, 0.7) !important;
|
||||
}
|
||||
|
||||
.text-accent-light {
|
||||
color: #8cebd2 !important;
|
||||
}
|
||||
|
||||
.score-disk {
|
||||
background: radial-gradient(circle, #e66926 0%, #cc561b 100%);
|
||||
border: 3px solid #ffffff;
|
||||
width: 72px;
|
||||
height: 72px;
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.score-val {
|
||||
font-size: 1.75rem;
|
||||
font-weight: 900;
|
||||
line-height: 1;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.score-unit {
|
||||
font-size: 0.65rem;
|
||||
font-weight: bold;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
|
||||
.border-accent-light {
|
||||
border: 1px solid rgba(71, 194, 166, 0.2) !important;
|
||||
}
|
||||
|
||||
.transition-card {
|
||||
transition: all 0.25s ease;
|
||||
border: 1px solid rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.transition-card:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
/* 雷達圖圖例 */
|
||||
.radar-legend {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
background-color: rgba(255, 255, 255, 0.85);
|
||||
border: 1px solid rgba(0, 0, 0, 0.05);
|
||||
padding: 6px 8px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.legend-color {
|
||||
display: inline-block;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.border-dashed {
|
||||
border: 1px dashed #2196f3;
|
||||
background-color: rgba(33, 150, 243, 0.12) !important;
|
||||
}
|
||||
|
||||
.radar-svg text {
|
||||
font-family: inherit;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user