fix: 註冊頁去識別化
This commit is contained in:
@@ -30,7 +30,7 @@ export function login(
|
|||||||
.json<AuthResponse>();
|
.json<AuthResponse>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 註冊帳號並建立完整個人資料。 */
|
/** 使用手機門號、密碼與個資同意狀態註冊帳號。 */
|
||||||
export function register(request: RegisterRequest): Promise<AuthResponse> {
|
export function register(request: RegisterRequest): Promise<AuthResponse> {
|
||||||
return api.post("v1/auth/register", { json: request }).json<AuthResponse>();
|
return api.post("v1/auth/register", { json: request }).json<AuthResponse>();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -113,10 +113,12 @@ export interface Profile extends ProfileRequest {
|
|||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 註冊請求:建立帳號並同時建立完整個人資料。 */
|
/** 註冊請求:僅建立帳號,個人資料由後續流程另行保存。 */
|
||||||
export interface RegisterRequest extends ProfileRequest {
|
export interface RegisterRequest {
|
||||||
phone: string;
|
phone: string;
|
||||||
password: string;
|
password: string;
|
||||||
|
consent: boolean;
|
||||||
|
consentVersion: ConsentVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 登入請求。 */
|
/** 登入請求。 */
|
||||||
|
|||||||
@@ -30,7 +30,6 @@
|
|||||||
v-model="form.phone"
|
v-model="form.phone"
|
||||||
class="mb-3"
|
class="mb-3"
|
||||||
color="primary"
|
color="primary"
|
||||||
hint="手機門號註冊後不可由個人資料修改"
|
|
||||||
label="手機號碼"
|
label="手機號碼"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
type="tel"
|
type="tel"
|
||||||
@@ -59,15 +58,15 @@
|
|||||||
|
|
||||||
<v-divider class="my-5" />
|
<v-divider class="my-5" />
|
||||||
|
|
||||||
<v-text-field
|
<!-- <v-text-field
|
||||||
v-model="form.name"
|
v-model="form.name"
|
||||||
class="mb-3"
|
class="mb-3"
|
||||||
color="primary"
|
color="primary"
|
||||||
label="姓名"
|
label="姓名"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
/>
|
/> -->
|
||||||
|
|
||||||
<v-row>
|
<!-- <v-row>
|
||||||
<v-col cols="12" sm="6">
|
<v-col cols="12" sm="6">
|
||||||
<v-select
|
<v-select
|
||||||
v-model="form.gender"
|
v-model="form.gender"
|
||||||
@@ -112,18 +111,18 @@
|
|||||||
variant="outlined"
|
variant="outlined"
|
||||||
/>
|
/>
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row> -->
|
||||||
|
|
||||||
<v-text-field
|
<!-- <v-text-field
|
||||||
v-model="form.email"
|
v-model="form.email"
|
||||||
class="mb-3"
|
class="mb-3"
|
||||||
color="primary"
|
color="primary"
|
||||||
label="Email(選填)"
|
label="Email(選填)"
|
||||||
type="email"
|
type="email"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
/>
|
/> -->
|
||||||
|
|
||||||
<v-row>
|
<!-- <v-row>
|
||||||
<v-col cols="12" sm="6">
|
<v-col cols="12" sm="6">
|
||||||
<v-text-field
|
<v-text-field
|
||||||
v-model="form.emergencyContact"
|
v-model="form.emergencyContact"
|
||||||
@@ -144,7 +143,7 @@
|
|||||||
variant="outlined"
|
variant="outlined"
|
||||||
/>
|
/>
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row> -->
|
||||||
|
|
||||||
<v-checkbox v-model="form.consent" class="mb-3" color="primary">
|
<v-checkbox v-model="form.consent" class="mb-3" color="primary">
|
||||||
<template #label>
|
<template #label>
|
||||||
@@ -188,9 +187,9 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, reactive, ref } from "vue";
|
import { reactive, ref } from "vue";
|
||||||
import { useRoute, useRouter } from "vue-router";
|
import { useRoute, useRouter } from "vue-router";
|
||||||
import type { Gender, RegisterRequest } from "@/api/types";
|
import type { RegisterRequest } from "@/api/types";
|
||||||
import { CONSENT_VERSION } from "@/api/types";
|
import { CONSENT_VERSION } from "@/api/types";
|
||||||
import BrandAvatar from "@/components/BrandAvatar.vue";
|
import BrandAvatar from "@/components/BrandAvatar.vue";
|
||||||
import PrivacyConsentDialog from "@/components/PrivacyConsentDialog.vue";
|
import PrivacyConsentDialog from "@/components/PrivacyConsentDialog.vue";
|
||||||
@@ -200,10 +199,7 @@ import {
|
|||||||
getApiErrorMessage,
|
getApiErrorMessage,
|
||||||
isUnregisteredPhoneError,
|
isUnregisteredPhoneError,
|
||||||
} from "@/utils/api-error";
|
} from "@/utils/api-error";
|
||||||
import {
|
import { readGuestSelfAssessmentDraft } from "@/utils/guest-self-assessment";
|
||||||
readGuestSelfAssessmentDraft,
|
|
||||||
saveGuestSelfAssessmentDraft,
|
|
||||||
} from "@/utils/guest-self-assessment";
|
|
||||||
|
|
||||||
const store = useAppStore();
|
const store = useAppStore();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
@@ -211,54 +207,21 @@ const router = useRouter();
|
|||||||
const showPrivacyConsent = ref(false);
|
const showPrivacyConsent = ref(false);
|
||||||
const existingDraft = readGuestSelfAssessmentDraft();
|
const existingDraft = readGuestSelfAssessmentDraft();
|
||||||
const routePhone = normalizePhone(String(route.query.phone ?? ""));
|
const routePhone = normalizePhone(String(route.query.phone ?? ""));
|
||||||
const initialProfile = existingDraft?.profile;
|
|
||||||
|
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
phone: routePhone || initialProfile?.phone || "",
|
phone: routePhone || existingDraft?.profile.phone || "",
|
||||||
password: "",
|
password: "",
|
||||||
passwordConfirmation: "",
|
passwordConfirmation: "",
|
||||||
name: initialProfile?.name ?? "",
|
consent: existingDraft?.profile.consent ?? false,
|
||||||
birthday: initialProfile?.birthday ?? "",
|
|
||||||
gender: (initialProfile?.gender ?? "") as Gender | "",
|
|
||||||
heightCm: initialProfile?.heightCm ?? 0,
|
|
||||||
weightKg: initialProfile?.weightKg ?? 0,
|
|
||||||
email: initialProfile?.email ?? "",
|
|
||||||
emergencyContact: initialProfile?.emergencyContact ?? "",
|
|
||||||
emergencyPhone: initialProfile?.emergencyPhone ?? "",
|
|
||||||
consent: initialProfile?.consent ?? false,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const errorMessage = ref("");
|
const errorMessage = ref("");
|
||||||
const genderOptions = [
|
|
||||||
{ title: "男", value: "male" as const },
|
|
||||||
{ title: "女", value: "female" as const },
|
|
||||||
];
|
|
||||||
|
|
||||||
const birthdayDate = computed<Date | null>({
|
|
||||||
get: () => parseDateString(form.birthday),
|
|
||||||
set: (value) => {
|
|
||||||
form.birthday = formatDateString(value);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
function normalizePhone(value: string) {
|
function normalizePhone(value: string) {
|
||||||
return value.replace(/\D/g, "");
|
return value.replace(/\D/g, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseDateString(value: string) {
|
|
||||||
const [year, month, day] = value.split("-").map(Number);
|
|
||||||
return year && month && day ? new Date(year, month - 1, day) : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatDateString(value: Date | null) {
|
|
||||||
if (!value) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
return `${value.getFullYear()}-${String(value.getMonth() + 1).padStart(2, "0")}-${String(value.getDate()).padStart(2, "0")}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function redirectTarget() {
|
function redirectTarget() {
|
||||||
const target = String(route.query.redirect ?? "");
|
const target = String(route.query.redirect ?? "");
|
||||||
return target.startsWith("/") && !target.startsWith("//")
|
return target.startsWith("/") && !target.startsWith("//")
|
||||||
@@ -278,17 +241,6 @@ function validateForm(phone: string) {
|
|||||||
if (form.password !== form.passwordConfirmation) {
|
if (form.password !== form.passwordConfirmation) {
|
||||||
return "兩次輸入的密碼不一致。";
|
return "兩次輸入的密碼不一致。";
|
||||||
}
|
}
|
||||||
if (
|
|
||||||
!form.name.trim() ||
|
|
||||||
!form.birthday ||
|
|
||||||
!form.gender ||
|
|
||||||
form.heightCm < 50 ||
|
|
||||||
form.heightCm > 250 ||
|
|
||||||
form.weightKg < 20 ||
|
|
||||||
form.weightKg > 300
|
|
||||||
) {
|
|
||||||
return "請完整填寫姓名、生日、性別、身高與體重。";
|
|
||||||
}
|
|
||||||
if (!form.consent) {
|
if (!form.consent) {
|
||||||
return "請同意個資授權條款後再註冊。";
|
return "請同意個資授權條款後再註冊。";
|
||||||
}
|
}
|
||||||
@@ -304,32 +256,14 @@ async function handleRegister() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const profile = {
|
const request: RegisterRequest = {
|
||||||
name: form.name.trim(),
|
|
||||||
phone,
|
phone,
|
||||||
birthday: form.birthday,
|
password: form.password,
|
||||||
gender: form.gender as Gender,
|
|
||||||
heightCm: form.heightCm,
|
|
||||||
weightKg: form.weightKg,
|
|
||||||
email: form.email.trim() || null,
|
|
||||||
emergencyContact: form.emergencyContact.trim() || null,
|
|
||||||
emergencyPhone: normalizePhone(form.emergencyPhone) || null,
|
|
||||||
consent: form.consent,
|
consent: form.consent,
|
||||||
consentVersion: CONSENT_VERSION,
|
consentVersion: CONSENT_VERSION,
|
||||||
};
|
};
|
||||||
|
|
||||||
const currentDraft = readGuestSelfAssessmentDraft();
|
const currentDraft = readGuestSelfAssessmentDraft();
|
||||||
if (currentDraft) {
|
|
||||||
saveGuestSelfAssessmentDraft({
|
|
||||||
...currentDraft,
|
|
||||||
profile,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const request: RegisterRequest = {
|
|
||||||
...profile,
|
|
||||||
password: form.password,
|
|
||||||
};
|
|
||||||
|
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -429,23 +429,10 @@ export const useAppStore = defineStore("app", () => {
|
|||||||
const response: AuthResponse = USE_MOCK
|
const response: AuthResponse = USE_MOCK
|
||||||
? {
|
? {
|
||||||
userID: "MOCK-USER",
|
userID: "MOCK-USER",
|
||||||
userName: request.name,
|
userName: "",
|
||||||
phone: request.phone,
|
phone: request.phone,
|
||||||
group: "medium",
|
group: "medium",
|
||||||
profile: {
|
profile: null,
|
||||||
name: request.name,
|
|
||||||
phone: request.phone,
|
|
||||||
birthday: request.birthday,
|
|
||||||
gender: request.gender,
|
|
||||||
heightCm: request.heightCm,
|
|
||||||
weightKg: request.weightKg,
|
|
||||||
email: request.email,
|
|
||||||
emergencyContact: request.emergencyContact,
|
|
||||||
emergencyPhone: request.emergencyPhone,
|
|
||||||
consent: request.consent,
|
|
||||||
consentVersion: request.consentVersion,
|
|
||||||
updatedAt: new Date().toISOString(),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
: await apiRegister(request);
|
: await apiRegister(request);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user