diff --git a/app/citizen-frontend/src/components/layout/DefaultLayout.vue b/app/citizen-frontend/src/components/layout/DefaultLayout.vue
index 720cebd..4493df3 100644
--- a/app/citizen-frontend/src/components/layout/DefaultLayout.vue
+++ b/app/citizen-frontend/src/components/layout/DefaultLayout.vue
@@ -100,6 +100,15 @@
to="/"
/>
+
+
-
-
@@ -270,48 +272,6 @@
-
-
-
-
-
-
- 緊急聯絡資訊
-
-
-
-
-
-
-
-
-
-
- 儲存並關閉
-
-
-
-
@@ -327,7 +287,6 @@ const route = useRoute();
const drawer = ref(false);
const activeTab = ref("home");
const showPrivacy = ref(false);
-const showEmergency = ref(false);
// 無障礙狀態 (可持久化儲存於 localStorage)
const isLargeText = ref(localStorage.getItem("mode-large-text") === "true");
diff --git a/app/citizen-frontend/src/pages/login.vue b/app/citizen-frontend/src/pages/login.vue
index 367809d..3a01c19 100644
--- a/app/citizen-frontend/src/pages/login.vue
+++ b/app/citizen-frontend/src/pages/login.vue
@@ -50,6 +50,7 @@
color="primary"
hide-details="auto"
label="登入密碼"
+ placeholder="例如: 00000000"
prepend-inner-icon="mdi-lock-outline"
required
rounded="lg"
diff --git a/app/citizen-frontend/src/pages/profile.vue b/app/citizen-frontend/src/pages/profile.vue
new file mode 100644
index 0000000..d3f4907
--- /dev/null
+++ b/app/citizen-frontend/src/pages/profile.vue
@@ -0,0 +1,363 @@
+
+
+
+
+
+ 個人資料修改
+ 更新您的基本資料與緊急聯絡資訊。
+
+
+
+ 基本資料
+
+
+ {{ errorMessage }}
+
+
+
+ {{ successMessage }}
+
+
+
+
+
+
+
+
+ 手機門號不可由個人資料修改
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 緊急聯絡資訊
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 取消
+
+
+ 儲存個人資料
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/citizen-frontend/src/router/index.ts b/app/citizen-frontend/src/router/index.ts
index b837345..d041a34 100644
--- a/app/citizen-frontend/src/router/index.ts
+++ b/app/citizen-frontend/src/router/index.ts
@@ -6,6 +6,7 @@ import Course from "@/pages/course.vue";
import Index from "@/pages/index.vue";
import Introduction from "@/pages/introduction.vue";
import Login from "@/pages/login.vue";
+import Profile from "@/pages/profile.vue";
import Register from "@/pages/register.vue";
import SelfAssessment from "@/pages/self-assessment.vue";
import { useAppStore } from "@/stores/app";
@@ -43,6 +44,12 @@ const router = createRouter({
name: "self-assessment",
component: SelfAssessment,
},
+ {
+ path: "/profile",
+ name: "profile",
+ component: Profile,
+ meta: { requiresAuth: true, title: "個人資料修改|運動玩轉健康力" },
+ },
{
path: "/appointment",
name: "appointment",
diff --git a/app/citizen-frontend/src/stores/app.ts b/app/citizen-frontend/src/stores/app.ts
index 3ea0987..9eeded0 100644
--- a/app/citizen-frontend/src/stores/app.ts
+++ b/app/citizen-frontend/src/stores/app.ts
@@ -54,6 +54,8 @@ export interface UserProfile {
consentVersion: ConsentVersion;
}
+type EditableProfile = Omit;
+
export interface SelfAssessment {
exerciseFrequency: ExerciseFrequency | "";
painLevel: PainLevel | "";
@@ -168,6 +170,25 @@ function toProfileRequest(
};
}
+function toProfileRequestFromStore(profile: UserProfile): ProfileRequest {
+ if (profile.gender !== "男" && profile.gender !== "女") {
+ throw new Error("請先完成性別資料");
+ }
+
+ return {
+ name: profile.name,
+ birthday: profile.birthday,
+ gender: profile.gender === "男" ? "male" : "female",
+ heightCm: profile.height,
+ weightKg: profile.weight,
+ email: profile.email || null,
+ emergencyContact: profile.emergencyContact || null,
+ emergencyPhone: profile.emergencyPhone || null,
+ consent: profile.consent,
+ consentVersion: profile.consentVersion,
+ };
+}
+
function createDefaultAppointments(): Appointment[] {
const measurementItems = [
"血壓",
@@ -417,8 +438,21 @@ export const useAppStore = defineStore("app", () => {
}
}
- function updateProfile(profile: Partial) {
- userProfile.value = { ...userProfile.value, ...profile };
+ async function updateProfile(
+ profile: Partial,
+ ): Promise {
+ const updatedProfile = { ...userProfile.value, ...profile };
+
+ if (USE_MOCK) {
+ userProfile.value = updatedProfile;
+ return userProfile.value;
+ }
+
+ const response = await apiUpdateProfile(
+ toProfileRequestFromStore(updatedProfile),
+ );
+ userProfile.value = applyProfileToStore(response);
+ return userProfile.value;
}
function setSelfAssessment(response: SelfAssessmentResponse) {