chore: lint
This commit is contained in:
@@ -47,23 +47,58 @@
|
||||
stroke-width="0.5"
|
||||
/>
|
||||
|
||||
<text fill="#1D7063" font-size="4" font-weight="bold" text-anchor="middle" x="60" y="8">
|
||||
<text
|
||||
fill="#1D7063"
|
||||
font-size="4"
|
||||
font-weight="bold"
|
||||
text-anchor="middle"
|
||||
x="60"
|
||||
y="8"
|
||||
>
|
||||
心肺 ({{ radarData.cardio }})
|
||||
</text>
|
||||
|
||||
<text fill="#1D7063" font-size="4" font-weight="bold" text-anchor="start" x="110" y="44">
|
||||
<text
|
||||
fill="#1D7063"
|
||||
font-size="4"
|
||||
font-weight="bold"
|
||||
text-anchor="start"
|
||||
x="110"
|
||||
y="44"
|
||||
>
|
||||
肌力 ({{ radarData.strength }})
|
||||
</text>
|
||||
|
||||
<text fill="#1D7063" font-size="4" font-weight="bold" text-anchor="start" x="94" y="104">
|
||||
<text
|
||||
fill="#1D7063"
|
||||
font-size="4"
|
||||
font-weight="bold"
|
||||
text-anchor="start"
|
||||
x="94"
|
||||
y="104"
|
||||
>
|
||||
平衡 ({{ radarData.balance }})
|
||||
</text>
|
||||
|
||||
<text fill="#1D7063" font-size="4" font-weight="bold" text-anchor="end" x="26" y="104">
|
||||
<text
|
||||
fill="#1D7063"
|
||||
font-size="4"
|
||||
font-weight="bold"
|
||||
text-anchor="end"
|
||||
x="26"
|
||||
y="104"
|
||||
>
|
||||
柔軟 ({{ radarData.flexibility }})
|
||||
</text>
|
||||
|
||||
<text fill="#1D7063" font-size="4" font-weight="bold" text-anchor="end" x="10" y="44">
|
||||
<text
|
||||
fill="#1D7063"
|
||||
font-size="4"
|
||||
font-weight="bold"
|
||||
text-anchor="end"
|
||||
x="10"
|
||||
y="44"
|
||||
>
|
||||
敏捷 ({{ radarData.agility }})
|
||||
</text>
|
||||
</svg>
|
||||
@@ -138,7 +173,7 @@ const userDataCoordArray = computed(() => {
|
||||
})
|
||||
|
||||
const userDataPoints = computed(() => {
|
||||
return userDataCoordArray.value.map((point) => `${point.x},${point.y}`).join(' ')
|
||||
return userDataCoordArray.value.map(point => `${point.x},${point.y}`).join(' ')
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
@@ -295,20 +295,20 @@ const showEmergency = ref(false)
|
||||
// 無障礙狀態 (可持久化儲存於 localStorage)
|
||||
const isLargeText = ref(localStorage.getItem('mode-large-text') === 'true')
|
||||
|
||||
watch(isLargeText, (newVal) => {
|
||||
watch(isLargeText, newVal => {
|
||||
localStorage.setItem('mode-large-text', String(newVal))
|
||||
})
|
||||
|
||||
// 同步底部導航狀態與當前路由
|
||||
watch(
|
||||
() => route.path,
|
||||
(path) => {
|
||||
path => {
|
||||
if (path === '/') activeTab.value = 'home'
|
||||
else if (path.startsWith('/self-assessment')) activeTab.value = 'self-assessment'
|
||||
else if (path.startsWith('/appointment')) activeTab.value = 'appointment'
|
||||
else if (path.startsWith('/checkin')) activeTab.value = 'checkin'
|
||||
},
|
||||
{ immediate: true }
|
||||
{ immediate: true },
|
||||
)
|
||||
|
||||
function getBottomNavIconColor (value: string) {
|
||||
|
||||
@@ -66,8 +66,7 @@
|
||||
剩餘名額:<span
|
||||
class="font-weight-bold"
|
||||
:class="apt.slots < 10 ? 'text-error' : 'text-success'"
|
||||
>{{ apt.slots }}</span
|
||||
>
|
||||
>{{ apt.slots }}</span>
|
||||
位
|
||||
</div>
|
||||
</div>
|
||||
@@ -181,40 +180,142 @@
|
||||
<path d="M 0,75 V 100 H 25 V 92 H 8 V 75 Z" fill="#1D7063" />
|
||||
<path d="M 92,75 V 92 H 75 V 100 H 100 V 75 Z" fill="#1D7063" />
|
||||
<!-- 內嵌二維碼方塊 -->
|
||||
<rect fill="#1D7063" height="20" width="20" x="12" y="12" />
|
||||
<rect
|
||||
fill="#1D7063"
|
||||
height="20"
|
||||
width="20"
|
||||
x="12"
|
||||
y="12"
|
||||
/>
|
||||
|
||||
<rect fill="white" height="12" width="12" x="16" y="16" />
|
||||
<rect
|
||||
fill="white"
|
||||
height="12"
|
||||
width="12"
|
||||
x="16"
|
||||
y="16"
|
||||
/>
|
||||
|
||||
<rect fill="#E66926" height="6" width="6" x="19" y="19" />
|
||||
<rect
|
||||
fill="#E66926"
|
||||
height="6"
|
||||
width="6"
|
||||
x="19"
|
||||
y="19"
|
||||
/>
|
||||
|
||||
<rect fill="#1D7063" height="20" width="20" x="68" y="12" />
|
||||
<rect
|
||||
fill="#1D7063"
|
||||
height="20"
|
||||
width="20"
|
||||
x="68"
|
||||
y="12"
|
||||
/>
|
||||
|
||||
<rect fill="white" height="12" width="12" x="72" y="16" />
|
||||
<rect
|
||||
fill="white"
|
||||
height="12"
|
||||
width="12"
|
||||
x="72"
|
||||
y="16"
|
||||
/>
|
||||
|
||||
<rect fill="#E66926" height="6" width="6" x="75" y="75" />
|
||||
<rect
|
||||
fill="#E66926"
|
||||
height="6"
|
||||
width="6"
|
||||
x="75"
|
||||
y="75"
|
||||
/>
|
||||
|
||||
<rect fill="#1D7063" height="20" width="20" x="12" y="68" />
|
||||
<rect
|
||||
fill="#1D7063"
|
||||
height="20"
|
||||
width="20"
|
||||
x="12"
|
||||
y="68"
|
||||
/>
|
||||
|
||||
<rect fill="white" height="12" width="12" x="16" y="72" />
|
||||
<rect
|
||||
fill="white"
|
||||
height="12"
|
||||
width="12"
|
||||
x="16"
|
||||
y="72"
|
||||
/>
|
||||
|
||||
<rect fill="#1D7063" height="6" width="6" x="19" y="75" />
|
||||
<rect
|
||||
fill="#1D7063"
|
||||
height="6"
|
||||
width="6"
|
||||
x="19"
|
||||
y="75"
|
||||
/>
|
||||
|
||||
<!-- 中間隨機小方塊 -->
|
||||
<rect fill="#1D7063" height="8" width="8" x="40" y="20" />
|
||||
<rect
|
||||
fill="#1D7063"
|
||||
height="8"
|
||||
width="8"
|
||||
x="40"
|
||||
y="20"
|
||||
/>
|
||||
|
||||
<rect fill="#E66926" height="14" width="6" x="52" y="12" />
|
||||
<rect
|
||||
fill="#E66926"
|
||||
height="14"
|
||||
width="6"
|
||||
x="52"
|
||||
y="12"
|
||||
/>
|
||||
|
||||
<rect fill="#1D7063" height="6" width="16" x="42" y="36" />
|
||||
<rect
|
||||
fill="#1D7063"
|
||||
height="6"
|
||||
width="16"
|
||||
x="42"
|
||||
y="36"
|
||||
/>
|
||||
|
||||
<rect fill="#47C2A6" height="10" width="28" x="36" y="48" />
|
||||
<rect
|
||||
fill="#47C2A6"
|
||||
height="10"
|
||||
width="28"
|
||||
x="36"
|
||||
y="48"
|
||||
/>
|
||||
|
||||
<rect fill="#1D7063" height="12" width="14" x="68" y="42" />
|
||||
<rect
|
||||
fill="#1D7063"
|
||||
height="12"
|
||||
width="14"
|
||||
x="68"
|
||||
y="42"
|
||||
/>
|
||||
|
||||
<rect fill="#E66926" height="16" width="16" x="72" y="60" />
|
||||
<rect
|
||||
fill="#E66926"
|
||||
height="16"
|
||||
width="16"
|
||||
x="72"
|
||||
y="60"
|
||||
/>
|
||||
|
||||
<rect fill="#1D7063" height="12" width="18" x="44" y="68" />
|
||||
<rect
|
||||
fill="#1D7063"
|
||||
height="12"
|
||||
width="18"
|
||||
x="44"
|
||||
y="68"
|
||||
/>
|
||||
|
||||
<rect fill="#47C2A6" height="6" width="24" x="40" y="82" />
|
||||
<rect
|
||||
fill="#47C2A6"
|
||||
height="6"
|
||||
width="24"
|
||||
x="40"
|
||||
y="82"
|
||||
/>
|
||||
</svg>
|
||||
<!-- 紅色掃描雷射線 -->
|
||||
<div class="laser-line" />
|
||||
@@ -268,7 +369,7 @@ const selectedApt = ref<Appointment | null>(null)
|
||||
|
||||
const filteredAppointments = computed(() => {
|
||||
if (filterTab.value === 'registered') {
|
||||
return store.appointments.filter((a) => a.status === '已報名' || a.status === '已報到')
|
||||
return store.appointments.filter(a => a.status === '已報名' || a.status === '已報到')
|
||||
}
|
||||
return store.appointments
|
||||
})
|
||||
|
||||
@@ -36,9 +36,8 @@
|
||||
<div class="mt-4">
|
||||
<div class="d-flex justify-space-between align-center mb-1">
|
||||
<span class="text-caption font-weight-bold text-grey-darken-2">測量站點完成度</span>
|
||||
<span class="text-caption font-weight-bold text-primary"
|
||||
>{{ completedCount }} / {{ totalCount }} ({{ progressPercent }}%)</span
|
||||
>
|
||||
|
||||
<span class="text-caption font-weight-bold text-primary">{{ completedCount }} / {{ totalCount }} ({{ progressPercent }}%)</span>
|
||||
</div>
|
||||
|
||||
<v-progress-linear
|
||||
@@ -57,9 +56,11 @@
|
||||
<v-card v-else class="rounded-xl elevation-3 pa-6 text-center mb-6" color="surface">
|
||||
<v-icon class="mb-3" color="warning" icon="mdi-qrcode-scan" size="64" />
|
||||
<h3 class="text-subtitle-1 font-weight-bold mb-2">尚未在活動現場合對報到</h3>
|
||||
|
||||
<p class="text-body-2 text-grey mb-4">
|
||||
請先前往「活動預約」頁面,出示您的報到條碼給現場志工進行核對。
|
||||
</p>
|
||||
|
||||
<v-btn class="rounded-lg" color="primary" to="/appointment">前往查看預約活動</v-btn>
|
||||
</v-card>
|
||||
|
||||
@@ -127,9 +128,7 @@
|
||||
<!-- 已完成,顯示數值 -->
|
||||
<div v-if="station.completed" class="text-caption text-grey-darken-2">
|
||||
{{ getMeasurementSummary(key, station.values) }}
|
||||
<span class="ml-2 text-grey-lighten-1"
|
||||
>| {{ station.time?.split(' ')[1] }}</span
|
||||
>
|
||||
<span class="ml-2 text-grey-lighten-1">| {{ station.time?.split(' ')[1] }}</span>
|
||||
</div>
|
||||
<!-- 未完成,顯示待測 -->
|
||||
<div v-else class="text-caption text-grey-darken-1">等待現場檢測員登錄</div>
|
||||
@@ -199,7 +198,7 @@ const reportSuccessDialog = ref(false)
|
||||
|
||||
// 尋找已報到的活動
|
||||
const activeAppointment = computed(() => {
|
||||
return store.appointments.find((a) => a.status === '已報到')
|
||||
return store.appointments.find(a => a.status === '已報到')
|
||||
})
|
||||
|
||||
const completedCount = computed(() => store.completedStationsCount)
|
||||
|
||||
@@ -27,9 +27,7 @@
|
||||
<div>
|
||||
<h1 class="text-h6 font-weight-black">{{ station.name }}</h1>
|
||||
|
||||
<span class="text-caption text-white-70"
|
||||
>第 {{ currentStationIndex }} 站 / 共 11 站</span
|
||||
>
|
||||
<span class="text-caption text-white-70">第 {{ currentStationIndex }} 站 / 共 11 站</span>
|
||||
</div>
|
||||
</div>
|
||||
</v-card-item>
|
||||
@@ -361,19 +359,19 @@ const formValues = reactive<Record<string, any>>({})
|
||||
// 監聽 code 改變,初始化表單預設值
|
||||
watch(
|
||||
code,
|
||||
(newCode) => {
|
||||
newCode => {
|
||||
if (stationsSpec[newCode]) {
|
||||
// 優先從 store 中讀取已有的測量數據
|
||||
const existing = store.measurements[newCode]
|
||||
for (const field of stationsSpec[newCode].fields) {
|
||||
formValues[field.key] =
|
||||
existing && existing.completed && existing.values[field.key] !== undefined
|
||||
formValues[field.key]
|
||||
= existing && existing.completed && existing.values[field.key] !== undefined
|
||||
? existing.values[field.key]
|
||||
: field.defaultValue
|
||||
}
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
{ immediate: true },
|
||||
)
|
||||
|
||||
function handleSave () {
|
||||
|
||||
@@ -181,6 +181,7 @@
|
||||
:color="assessmentForm.wantedImprovements.includes(imp) ? 'primary' : 'grey'"
|
||||
:icon="getImprovementIcon(imp)"
|
||||
/>
|
||||
|
||||
<div class="text-caption font-weight-bold">{{ imp }}</div>
|
||||
</v-card>
|
||||
</v-col>
|
||||
@@ -342,7 +343,13 @@
|
||||
</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
|
||||
block
|
||||
class="rounded-lg py-2"
|
||||
color="primary"
|
||||
to="/appointment"
|
||||
variant="flat"
|
||||
>
|
||||
立即預約普測
|
||||
</v-btn>
|
||||
</div>
|
||||
|
||||
+147
@@ -0,0 +1,147 @@
|
||||
# 運動玩轉健康力
|
||||
|
||||
## 頁面路由結構
|
||||
|
||||
```text
|
||||
/
|
||||
├── /login
|
||||
├── /self-assessment
|
||||
├── /appointment
|
||||
├── /checkin
|
||||
└── /checkin/:code
|
||||
```
|
||||
|
||||
## 路由對應頁面
|
||||
|
||||
```text
|
||||
/
|
||||
├── / 首頁 / 市民端主控台
|
||||
├── /login 登入頁
|
||||
├── /self-assessment 線上自評頁
|
||||
├── /appointment 預約場次頁
|
||||
├── /checkin 現場報到與量測清單頁
|
||||
└── /checkin/:code 量測子頁面
|
||||
├── /checkin/BloodPressure
|
||||
├── /checkin/BodyFat
|
||||
├── /checkin/GripStrength
|
||||
├── /checkin/Sit5x
|
||||
├── /checkin/Sit30s
|
||||
├── /checkin/StepTest
|
||||
├── /checkin/ArmTest
|
||||
├── /checkin/BackTest
|
||||
├── /checkin/SitReachTest
|
||||
├── /checkin/UpGo8ft
|
||||
├── /checkin/OneLegTest
|
||||
└── /checkin/ResultReview
|
||||
```
|
||||
|
||||
## 備註
|
||||
|
||||
- `/checkin/:code` 使用同一個 Vue 頁面,透過 route param 顯示不同量測站內容。
|
||||
- 目前路由來源以 [src/router/index.ts](../health-platform/hp-frontend/src/router/index.ts:1) 為準。
|
||||
|
||||
## 預計功能
|
||||
|
||||
1. 帳號與身分
|
||||
|
||||
- 手機號碼登入
|
||||
- 基本資料維護:姓名、生日、性別、身高、體重、聯絡方式
|
||||
- 緊急聯絡人
|
||||
- 個資同意與資料授權狀態
|
||||
|
||||
2. 健康自評
|
||||
|
||||
- 3 分鐘線上自評問卷
|
||||
- 運動頻率、疼痛狀況、跌倒史、慢性病、用藥提醒
|
||||
- 生活功能自評:走路、上下樓、起身、平衡、柔軟度
|
||||
- 自評結果分級:低風險 / 需追蹤 / 建議諮詢
|
||||
|
||||
3. 活動與預約
|
||||
|
||||
- 公益普測活動列表
|
||||
- 活動詳情:地點、時間、名額、主辦單位、量測項目
|
||||
- 線上報名 / 取消報名
|
||||
- QR Code 報到
|
||||
- 報名紀錄與到場狀態
|
||||
|
||||
4. 現場量測結果
|
||||
|
||||
- 血壓 BloodPressure
|
||||
- 體脂 BodyFat
|
||||
- 握力 GripStrength
|
||||
- 5 次坐站 Sit5x
|
||||
- 30 秒椅子坐立 Sit30s
|
||||
- 原地抬膝踏步 StepTest
|
||||
- 肱二頭肌手臂屈舉 ArmTest
|
||||
- 抓背測驗 BackTest
|
||||
- 坐姿體前彎 SitReachTest
|
||||
- 2.44 公尺椅子坐立繞物 UpGo8ft
|
||||
- 開眼單足立 OneLegTest
|
||||
|
||||
5. 個人健康力儀表板
|
||||
|
||||
- 健康力總分
|
||||
- 肌力、平衡、柔軟、敏捷、心肺等能力分項
|
||||
- 與同年齡區間參考值比較
|
||||
- 風險提示:跌倒風險、血壓異常、肌力不足等
|
||||
- 最近一次量測摘要
|
||||
- 歷史趨勢圖
|
||||
|
||||
6. 運動處方
|
||||
|
||||
- 依量測結果產生建議訓練
|
||||
- 每週訓練任務
|
||||
- 動作說明:次數、組數、頻率、注意事項
|
||||
- 禁忌提醒:血壓過高、疼痛、暈眩時暫停
|
||||
- 訓練完成回填
|
||||
|
||||
7. 追蹤與提醒
|
||||
|
||||
- 下次建議量測時間
|
||||
- 回訪提醒
|
||||
- 運動任務提醒
|
||||
- 異常數值提醒
|
||||
- 報名活動前提醒
|
||||
|
||||
8. 健康紀錄
|
||||
|
||||
- 每次自評紀錄
|
||||
- 每次量測紀錄
|
||||
- 運動任務完成紀錄
|
||||
- 健康報告下載 / 分享
|
||||
- 可出示給教練、社區據點或照護人員的摘要頁
|
||||
|
||||
9. 家屬 / 照護者協作
|
||||
|
||||
- 授權家屬查看摘要
|
||||
- 家屬提醒通知
|
||||
- 協助預約活動
|
||||
- 緊急聯絡資訊更新
|
||||
|
||||
10. 無障礙與高齡友善
|
||||
|
||||
- 大字模式
|
||||
- 高對比模式
|
||||
- 簡化操作流程
|
||||
- 語音提示 / 朗讀友善
|
||||
- 明確的下一步按鈕
|
||||
- 表單錯誤提示清楚可讀
|
||||
|
||||
## 使用者流程
|
||||
|
||||
登入 / 建檔 → 線上自評 → 預約普測 → QR Code 報到 → 查看量測結果 → 取得運動處方 → 追蹤回訪
|
||||
|
||||
訪客進站
|
||||
→ 點擊「開始自評」
|
||||
→ 建立帳號 / LINE 登入 / 手機驗證
|
||||
→ 基本資料與同意書
|
||||
→ 自評問卷
|
||||
→ 顯示初步分級
|
||||
→ 推薦普測或課程
|
||||
→ 預約場館 / 社區據點
|
||||
→ 報到 QR Code
|
||||
→ 完成普測與運動處方
|
||||
→ 4 週 / 12 週提醒回測
|
||||
→ 個人報告與獎勵點數
|
||||
|
||||
[cloudflare](https://health-platform-8u1.pages.dev/)
|
||||
Reference in New Issue
Block a user