From 889e44f68ef6e9b3975ffead07cbc022b6b1168b Mon Sep 17 00:00:00 2001 From: skytek_xinliang Date: Wed, 5 Aug 2026 15:45:45 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E8=AA=B2=E7=A8=8B=E5=BB=BA=E8=AD=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CLAUDE.md | 2 +- app/citizen-frontend/.env.example | 5 + app/citizen-frontend/env.d.ts | 11 + app/citizen-frontend/package.json | 13 +- app/citizen-frontend/src/api/client.ts | 12 + app/citizen-frontend/src/api/course.ts | 32 +++ app/citizen-frontend/src/api/types.ts | 32 +++ .../src/components/layout/DefaultLayout.vue | 8 + app/citizen-frontend/src/pages/course.vue | 170 +++++++++++++ app/citizen-frontend/src/pages/index.vue | 106 +------- app/citizen-frontend/src/router/index.ts | 7 + app/citizen-frontend/src/stores/course.ts | 235 ++++++++++++++++++ pnpm-lock.yaml | 9 + 13 files changed, 538 insertions(+), 104 deletions(-) mode change 100644 => 120000 CLAUDE.md create mode 100644 app/citizen-frontend/.env.example create mode 100644 app/citizen-frontend/src/api/client.ts create mode 100644 app/citizen-frontend/src/api/course.ts create mode 100644 app/citizen-frontend/src/api/types.ts create mode 100644 app/citizen-frontend/src/pages/course.vue create mode 100644 app/citizen-frontend/src/stores/course.ts diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index d6a7a90..0000000 --- a/CLAUDE.md +++ /dev/null @@ -1 +0,0 @@ -MUST READ @AGENTS.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 120000 index 0000000..47dc3e3 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +AGENTS.md \ No newline at end of file diff --git a/app/citizen-frontend/.env.example b/app/citizen-frontend/.env.example new file mode 100644 index 0000000..3f0bad7 --- /dev/null +++ b/app/citizen-frontend/.env.example @@ -0,0 +1,5 @@ +# 後端 API base URL;留空則使用同網域的 /api +VITE_API_BASE_URL= + +# 後端尚未完成前保持 true,改為 false 才會實際發出 API 請求 +VITE_USE_MOCK=true diff --git a/app/citizen-frontend/env.d.ts b/app/citizen-frontend/env.d.ts index db56195..c3be18c 100644 --- a/app/citizen-frontend/env.d.ts +++ b/app/citizen-frontend/env.d.ts @@ -1,2 +1,13 @@ /// /// + +interface ImportMetaEnv { + /** 後端 API 的 base URL,未設定時走 Vite dev server 的 /api */ + readonly VITE_API_BASE_URL?: string + /** 設為 'false' 才會真正打後端,其餘情況一律使用前端模擬資料 */ + readonly VITE_USE_MOCK?: string +} + +interface ImportMeta { + readonly env: ImportMetaEnv +} diff --git a/app/citizen-frontend/package.json b/app/citizen-frontend/package.json index a441a9e..b3f3eb1 100644 --- a/app/citizen-frontend/package.json +++ b/app/citizen-frontend/package.json @@ -14,24 +14,25 @@ }, "dependencies": { "@mdi/font": "7.4.47", - "vue": "^3.5.30", - "vuetify": "^4.0.2", + "ky": "^2.0.2", "pinia": "^3.0.4", + "vue": "^3.5.30", "vue-i18n": "^11.3.0", - "vue-router": "^5.0.3" + "vue-router": "^5.0.3", + "vuetify": "^4.0.2" }, "devDependencies": { "@tsconfig/node22": "^22.0.5", "@types/node": "^24.12.0", "@vitejs/plugin-vue": "^6.0.5", "@vue/tsconfig": "^0.9.0", + "eslint": "^9.39.4", + "eslint-config-vuetify": "^4.3.4", "npm-run-all2": "^8.0.4", "sass-embedded": "^1.98.0", "typescript": "~5.9.3", "vite": "^8.0.0", "vite-plugin-vuetify": "^2.1.3", - "vue-tsc": "^3.2.5", - "eslint": "^9.39.4", - "eslint-config-vuetify": "^4.3.4" + "vue-tsc": "^3.2.5" } } diff --git a/app/citizen-frontend/src/api/client.ts b/app/citizen-frontend/src/api/client.ts new file mode 100644 index 0000000..3e91487 --- /dev/null +++ b/app/citizen-frontend/src/api/client.ts @@ -0,0 +1,12 @@ +import ky from 'ky' + +/** + * 全站共用的 HTTP client。 + * base URL、逾時與重試策略集中在此設定,各 API 模組只描述端點與型別。 + */ +export const api = ky.create({ + // prefix 同時接受相對路徑(/api)與絕對網址,兩種部署情境都涵蓋 + prefix: import.meta.env.VITE_API_BASE_URL || '/api', + timeout: 10_000, + retry: { limit: 2 }, +}) diff --git a/app/citizen-frontend/src/api/course.ts b/app/citizen-frontend/src/api/course.ts new file mode 100644 index 0000000..1159ee5 --- /dev/null +++ b/app/citizen-frontend/src/api/course.ts @@ -0,0 +1,32 @@ +import type { CourseList, CourseListParams, FitnessGroup, YesNo } from './types' +import { api } from './client' + +/** + * 課程建議的 5 支 API。 + * 端點路徑集中在此檔,後端定案後只需改這裡。 + */ + +/** 取得使用者的體適能組別 */ +export function fetchGroup (): Promise { + return api.get('course/group').json() +} + +/** 取得是否納入早上時段 */ +export function fetchMorning (): Promise { + return api.get('course/morning').json() +} + +/** 取得是否納入中午時段 */ +export function fetchNoon (): Promise { + return api.get('course/noon').json() +} + +/** 取得是否納入晚上時段 */ +export function fetchNight (): Promise { + return api.get('course/night').json() +} + +/** 以組別與時段條件取得一週課表 */ +export function fetchCourseList (params: CourseListParams): Promise { + return api.get('course/list', { searchParams: { ...params } }).json() +} diff --git a/app/citizen-frontend/src/api/types.ts b/app/citizen-frontend/src/api/types.ts new file mode 100644 index 0000000..b9dc8e8 --- /dev/null +++ b/app/citizen-frontend/src/api/types.ts @@ -0,0 +1,32 @@ +/** + * 課程建議相關的 API 契約。 + * 後端尚未開發,這份型別即為前後端約定的介面。 + */ + +/** 體適能組別 */ +export type FitnessGroup = 'low' | 'medium' | 'high' + +/** 後端以 Y/N 字串表示布林值 */ +export type YesNo = 'Y' | 'N' + +/** 課表的三個時段 */ +export type TimeSlot = 'morning' | 'noon' | 'night' + +/** 課表的一天;未被選取的時段其欄位為 undefined */ +export interface CourseRow { + /** 1 = 週一 … 7 = 週日 */ + day: number + morning?: string + noon?: string + night?: string +} + +export type CourseList = CourseRow[] + +/** 取得課表所需的查詢參數 */ +export interface CourseListParams { + group: FitnessGroup + morning: YesNo + noon: YesNo + night: YesNo +} diff --git a/app/citizen-frontend/src/components/layout/DefaultLayout.vue b/app/citizen-frontend/src/components/layout/DefaultLayout.vue index 69414e3..74b4c38 100644 --- a/app/citizen-frontend/src/components/layout/DefaultLayout.vue +++ b/app/citizen-frontend/src/components/layout/DefaultLayout.vue @@ -102,6 +102,14 @@ to="/self-assessment" /> + + + + + + + + + + diff --git a/app/citizen-frontend/src/pages/index.vue b/app/citizen-frontend/src/pages/index.vue index 5472ec5..b963be9 100644 --- a/app/citizen-frontend/src/pages/index.vue +++ b/app/citizen-frontend/src/pages/index.vue @@ -54,18 +54,18 @@ - + - -
+ + - 運動處方建議: -
+ 課程建議 + -

- {{ currentWeeklyPrescription }} -

-
+ + 依你的體適能組別,查看一週的每日課表 + +
@@ -110,82 +110,6 @@ title="預約" to="/appointment" /> - - - - - - - 本週運動處方明細 - - - -

每週訓練任務進度

- -
-
- 任務完成度 (2/7堂課) - 28% -
- - -
- - - - - - - 下肢肌力:坐站起立訓練 - - - 每週 2 次,每次 3 組,每組 10-12 次。
- * 扶手保護,站起時吐氣。 -
-
- - - - - 靜態平衡:扶椅單腳站立 - - - 每週 3 次,左右腳各 30 秒,重複 3 次。
- 手輕扶椅背,確保安全。 -
-
- - - - - 心肺耐力:每日快走 - - - 每日 20-30 分鐘,達到微喘但仍可說話的強度。
- * 若感覺頭暈或膝蓋疼痛請暫停! -
-
-
-
- - - - 確認關閉 - - -
-