feat(login): add phone and Google login page with i18n
Add complete login flow with phone verification and country picker. Add login-related translations for en-US, zh-CN and th-TH locales. Add country calling codes constants and selection utility function. Register Vant UI component type definitions for TypeScript. Update development environment authentication token.
This commit is contained in:
@@ -14,6 +14,6 @@ VITE_SOCKET_BASE_URL = "wss://onefeel.brother7.cn/ingress/agent/ws/chat"
|
||||
VITE_CLIENT_ID = "6"
|
||||
|
||||
# Token
|
||||
VITE_TOKEN = "eyJraWQiOiJiMTVhZTk0Mi03MjI5LTMyOWUtODA1Yi0wNjFlNmRjYTE1MDQiLCJhbGciOiJSUzI1NiJ9.eyJ0ZW5hbnRfaWQiOjEsInN1YiI6Im94T3NGN2lqTkxvbEFIdkhDZDYtek1acE5kNWsiLCJjbGllbnRJZCI6ImN1c3RvbSIsImlzcyI6Imh0dHBzOi8vcGlnNGNsb3VkLmNvbSIsImNsaWVudF9pZCI6ImN1c3RvbSIsImF1dGhvcml0aWVzIjpbXSwiYXVkIjoiY3VzdG9tIiwibGljZW5zZSI6Imh0dHBzOi8vcGlnNGNsb3VkLmNvbSIsIndlY2hhdF9vcGVuaWQiOiJveE9zRjdpak5Mb2xBSHZIQ2Q2LXpNWnBOZDVrIiwibmJmIjoxNzgwMjk1MzQ0LCJ1c2VyX2lkIjoiMjAwNTEwMjg0NDQ2OTM4NzI2NSIsInNjb3BlIjpbInNlcnZlciJdLCJleHAiOjE3ODAzMDUzNDQsImlhdCI6MTc4MDI5NTM0NCwianRpIjoiZDgwMmZiZTktMjgxMi00MWU1LWE5MTQtMjBlMTdhZWJiYzA5IiwidXNlcm5hbWUiOiJveE9zRjdpak5Mb2xBSHZIQ2Q2LXpNWnBOZDVrIn0.HXA_IaLKX4M4AnBRHJvk9UbegccWTBWsJ9871TAeAX3SowYBhwyKJMxYhYfFt79WX30_UEa8MNmI49djlDM2ODuaS0Hr-rMsih-QJOasCvAc3AKv8dF8dlxIN0Yqb0visQs9tIaKjBCFDi9B78Ns24kmVCdEW4M90XrqmEIMvXzAyMdXSPL0FWO2IDLTt4EqWc_-wYvizCD4-VbImK2YnlzCqxQW-bvAamws9_psoV_AHflx7esDaOXOv_EAsvqlPAyh0Nf7Mey1wOK4_e-aJOHC1833xWajsK4dBi8-pVw5bA8NYsZowN36wJztDj76QUU51kkR6c_abyoS8EzZCg"
|
||||
VITE_TOKEN = "eyJraWQiOiJiMTVhZTk0Mi03MjI5LTMyOWUtODA1Yi0wNjFlNmRjYTE1MDQiLCJhbGciOiJSUzI1NiJ9.eyJ0ZW5hbnRfaWQiOjEsInN1YiI6Im94T3NGN2lqTkxvbEFIdkhDZDYtek1acE5kNWsiLCJjbGllbnRJZCI6ImN1c3RvbSIsImlzcyI6Imh0dHBzOi8vcGlnNGNsb3VkLmNvbSIsImNsaWVudF9pZCI6ImN1c3RvbSIsImF1dGhvcml0aWVzIjpbXSwiYXVkIjoiY3VzdG9tIiwibGljZW5zZSI6Imh0dHBzOi8vcGlnNGNsb3VkLmNvbSIsIndlY2hhdF9vcGVuaWQiOiJveE9zRjdpak5Mb2xBSHZIQ2Q2LXpNWnBOZDVrIiwibmJmIjoxNzgwMzE2Mjc3LCJ1c2VyX2lkIjoiMjAwNTEwMjg0NDQ2OTM4NzI2NSIsInNjb3BlIjpbInNlcnZlciJdLCJleHAiOjE3ODAzMjYyNzcsImlhdCI6MTc4MDMxNjI3NywianRpIjoiNmJlNTg2ZjctYzE1YS00MGQwLWIzYTQtNzI4NWJjZDg2OGI0IiwidXNlcm5hbWUiOiJveE9zRjdpak5Mb2xBSHZIQ2Q2LXpNWnBOZDVrIn0.TsNAOgmULtZV7VrLVWFRrN6XziQe0-Uy8ujNMgZDOMu_jicBE-gIpR61FwlCZhHlLrpEounpV24yu1Sr6h0rpfpKQCJWX3VK9XiV6A-CpP1ASCSo08Si0cLmXh8YIh87WZ1BhbqxpvT51AeF2TVdktc297K--Zo41WhbA_QT9E2gi_0ZPOFTPlZlsv9qhnyLfy7R6J-V6u0GxoPQ1zog2py91zEHrtAv9F_RrLAurEtgeycBIMIYHsBxhqBKjkpSy5lmeuNRLHTBfVxNFNcqLaZqkf-xn8Sb-RtEWOeHeXEVhUtKIs94JsHnZn_jf4kQhmYPJOCdbWuGSeqd3VOj8Q"
|
||||
|
||||
|
||||
|
||||
14
components.d.ts
vendored
14
components.d.ts
vendored
@@ -47,15 +47,22 @@ declare module 'vue' {
|
||||
TagsGroup: typeof import('./src/components/TagsGroup/index.vue')['default']
|
||||
TopNavBar: typeof import('./src/components/TopNavBar/index.vue')['default']
|
||||
UseDateRange: typeof import('./src/components/UseDateRange/index.vue')['default']
|
||||
VanButton: typeof import('vant/es')['Button']
|
||||
VanCell: typeof import('vant/es')['Cell']
|
||||
VanCheckbox: typeof import('vant/es')['Checkbox']
|
||||
VanDivider: typeof import('vant/es')['Divider']
|
||||
VanField: typeof import('vant/es')['Field']
|
||||
VanIcon: typeof import('vant/es')['Icon']
|
||||
VanIcons: typeof import('vant/es')['Icons']
|
||||
VanList: typeof import('vant/es')['List']
|
||||
VanPopup: typeof import('vant/es')['Popup']
|
||||
VanPullRefresh: typeof import('vant/es')['PullRefresh']
|
||||
VanSearch: typeof import('vant/es')['Search']
|
||||
VanSwipe: typeof import('vant/es')['Swipe']
|
||||
VanSwipeItem: typeof import('vant/es')['SwipeItem']
|
||||
VanSwiperItem: typeof import('vant/es')['SwiperItem']
|
||||
VanTab: typeof import('vant/es')['Tab']
|
||||
VanTabs: typeof import('vant/es')['Tabs']
|
||||
ZnIcon: typeof import('./src/components/ZnIcon/index.vue')['default']
|
||||
}
|
||||
}
|
||||
@@ -97,14 +104,21 @@ declare global {
|
||||
const TagsGroup: typeof import('./src/components/TagsGroup/index.vue')['default']
|
||||
const TopNavBar: typeof import('./src/components/TopNavBar/index.vue')['default']
|
||||
const UseDateRange: typeof import('./src/components/UseDateRange/index.vue')['default']
|
||||
const VanButton: typeof import('vant/es')['Button']
|
||||
const VanCell: typeof import('vant/es')['Cell']
|
||||
const VanCheckbox: typeof import('vant/es')['Checkbox']
|
||||
const VanDivider: typeof import('vant/es')['Divider']
|
||||
const VanField: typeof import('vant/es')['Field']
|
||||
const VanIcon: typeof import('vant/es')['Icon']
|
||||
const VanIcons: typeof import('vant/es')['Icons']
|
||||
const VanList: typeof import('vant/es')['List']
|
||||
const VanPopup: typeof import('vant/es')['Popup']
|
||||
const VanPullRefresh: typeof import('vant/es')['PullRefresh']
|
||||
const VanSearch: typeof import('vant/es')['Search']
|
||||
const VanSwipe: typeof import('vant/es')['Swipe']
|
||||
const VanSwipeItem: typeof import('vant/es')['SwipeItem']
|
||||
const VanSwiperItem: typeof import('vant/es')['SwiperItem']
|
||||
const VanTab: typeof import('vant/es')['Tab']
|
||||
const VanTabs: typeof import('vant/es')['Tabs']
|
||||
const ZnIcon: typeof import('./src/components/ZnIcon/index.vue')['default']
|
||||
}
|
||||
93
src/constants/countryCallingCodes.ts
Normal file
93
src/constants/countryCallingCodes.ts
Normal file
@@ -0,0 +1,93 @@
|
||||
export type CountryCallingCode = {
|
||||
name: string;
|
||||
iso2: string;
|
||||
dialCode: string;
|
||||
};
|
||||
|
||||
export const COUNTRY_CALLING_CODES: CountryCallingCode[] = [
|
||||
{ name: "China", iso2: "CN", dialCode: "+86" },
|
||||
{ name: "Hong Kong", iso2: "HK", dialCode: "+852" },
|
||||
{ name: "Macao", iso2: "MO", dialCode: "+853" },
|
||||
{ name: "Taiwan", iso2: "TW", dialCode: "+886" },
|
||||
{ name: "Thailand", iso2: "TH", dialCode: "+66" },
|
||||
{ name: "Singapore", iso2: "SG", dialCode: "+65" },
|
||||
{ name: "Malaysia", iso2: "MY", dialCode: "+60" },
|
||||
{ name: "Indonesia", iso2: "ID", dialCode: "+62" },
|
||||
{ name: "Vietnam", iso2: "VN", dialCode: "+84" },
|
||||
{ name: "Philippines", iso2: "PH", dialCode: "+63" },
|
||||
{ name: "Japan", iso2: "JP", dialCode: "+81" },
|
||||
{ name: "Korea, Republic of", iso2: "KR", dialCode: "+82" },
|
||||
{ name: "India", iso2: "IN", dialCode: "+91" },
|
||||
{ name: "Pakistan", iso2: "PK", dialCode: "+92" },
|
||||
{ name: "Bangladesh", iso2: "BD", dialCode: "+880" },
|
||||
{ name: "Sri Lanka", iso2: "LK", dialCode: "+94" },
|
||||
{ name: "Cambodia", iso2: "KH", dialCode: "+855" },
|
||||
{ name: "Laos", iso2: "LA", dialCode: "+856" },
|
||||
{ name: "Myanmar", iso2: "MM", dialCode: "+95" },
|
||||
{ name: "Australia", iso2: "AU", dialCode: "+61" },
|
||||
{ name: "New Zealand", iso2: "NZ", dialCode: "+64" },
|
||||
{ name: "United States", iso2: "US", dialCode: "+1" },
|
||||
{ name: "Canada", iso2: "CA", dialCode: "+1" },
|
||||
{ name: "Mexico", iso2: "MX", dialCode: "+52" },
|
||||
{ name: "Brazil", iso2: "BR", dialCode: "+55" },
|
||||
{ name: "Argentina", iso2: "AR", dialCode: "+54" },
|
||||
{ name: "Chile", iso2: "CL", dialCode: "+56" },
|
||||
{ name: "Colombia", iso2: "CO", dialCode: "+57" },
|
||||
{ name: "Peru", iso2: "PE", dialCode: "+51" },
|
||||
{ name: "Ecuador", iso2: "EC", dialCode: "+593" },
|
||||
{ name: "Bolivia", iso2: "BO", dialCode: "+591" },
|
||||
{ name: "Venezuela", iso2: "VE", dialCode: "+58" },
|
||||
{ name: "Uruguay", iso2: "UY", dialCode: "+598" },
|
||||
{ name: "Paraguay", iso2: "PY", dialCode: "+595" },
|
||||
{ name: "United Kingdom", iso2: "GB", dialCode: "+44" },
|
||||
{ name: "Ireland", iso2: "IE", dialCode: "+353" },
|
||||
{ name: "France", iso2: "FR", dialCode: "+33" },
|
||||
{ name: "Germany", iso2: "DE", dialCode: "+49" },
|
||||
{ name: "Italy", iso2: "IT", dialCode: "+39" },
|
||||
{ name: "Spain", iso2: "ES", dialCode: "+34" },
|
||||
{ name: "Portugal", iso2: "PT", dialCode: "+351" },
|
||||
{ name: "Netherlands", iso2: "NL", dialCode: "+31" },
|
||||
{ name: "Belgium", iso2: "BE", dialCode: "+32" },
|
||||
{ name: "Switzerland", iso2: "CH", dialCode: "+41" },
|
||||
{ name: "Austria", iso2: "AT", dialCode: "+43" },
|
||||
{ name: "Sweden", iso2: "SE", dialCode: "+46" },
|
||||
{ name: "Norway", iso2: "NO", dialCode: "+47" },
|
||||
{ name: "Denmark", iso2: "DK", dialCode: "+45" },
|
||||
{ name: "Finland", iso2: "FI", dialCode: "+358" },
|
||||
{ name: "Poland", iso2: "PL", dialCode: "+48" },
|
||||
{ name: "Czech Republic", iso2: "CZ", dialCode: "+420" },
|
||||
{ name: "Hungary", iso2: "HU", dialCode: "+36" },
|
||||
{ name: "Greece", iso2: "GR", dialCode: "+30" },
|
||||
{ name: "Romania", iso2: "RO", dialCode: "+40" },
|
||||
{ name: "Bulgaria", iso2: "BG", dialCode: "+359" },
|
||||
{ name: "Russia", iso2: "RU", dialCode: "+7" },
|
||||
{ name: "Ukraine", iso2: "UA", dialCode: "+380" },
|
||||
{ name: "Turkey", iso2: "TR", dialCode: "+90" },
|
||||
{ name: "Israel", iso2: "IL", dialCode: "+972" },
|
||||
{ name: "Saudi Arabia", iso2: "SA", dialCode: "+966" },
|
||||
{ name: "United Arab Emirates", iso2: "AE", dialCode: "+971" },
|
||||
{ name: "Qatar", iso2: "QA", dialCode: "+974" },
|
||||
{ name: "Kuwait", iso2: "KW", dialCode: "+965" },
|
||||
{ name: "Bahrain", iso2: "BH", dialCode: "+973" },
|
||||
{ name: "Oman", iso2: "OM", dialCode: "+968" },
|
||||
{ name: "Jordan", iso2: "JO", dialCode: "+962" },
|
||||
{ name: "Lebanon", iso2: "LB", dialCode: "+961" },
|
||||
{ name: "Iraq", iso2: "IQ", dialCode: "+964" },
|
||||
{ name: "Iran", iso2: "IR", dialCode: "+98" },
|
||||
{ name: "Egypt", iso2: "EG", dialCode: "+20" },
|
||||
{ name: "Morocco", iso2: "MA", dialCode: "+212" },
|
||||
{ name: "Algeria", iso2: "DZ", dialCode: "+213" },
|
||||
{ name: "Tunisia", iso2: "TN", dialCode: "+216" },
|
||||
{ name: "South Africa", iso2: "ZA", dialCode: "+27" },
|
||||
{ name: "Nigeria", iso2: "NG", dialCode: "+234" },
|
||||
{ name: "Kenya", iso2: "KE", dialCode: "+254" },
|
||||
{ name: "Ghana", iso2: "GH", dialCode: "+233" },
|
||||
{ name: "Ethiopia", iso2: "ET", dialCode: "+251" },
|
||||
{ name: "Tanzania", iso2: "TZ", dialCode: "+255" },
|
||||
];
|
||||
|
||||
export function findCountryCallingCode(iso2: string): CountryCallingCode | null {
|
||||
const normalized = iso2.trim().toUpperCase();
|
||||
return COUNTRY_CALLING_CODES.find((item) => item.iso2 === normalized) ?? null;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,34 @@ export default {
|
||||
errors: {
|
||||
network: "Network error. Please try again later.",
|
||||
},
|
||||
login: {
|
||||
method: {
|
||||
google: "Sign in with Google",
|
||||
phone: "Phone + Code",
|
||||
},
|
||||
fields: {
|
||||
country: "Country/Code",
|
||||
phone: "Phone",
|
||||
code: "Code",
|
||||
},
|
||||
placeholders: {
|
||||
selectCountry: "Select country code",
|
||||
phone: "Enter phone number",
|
||||
code: "Enter verification code",
|
||||
},
|
||||
dividersText: "Or",
|
||||
actions: {
|
||||
sendCode: "Send code",
|
||||
login: "Sign in",
|
||||
},
|
||||
errors: {
|
||||
missingPhone: "Please enter your phone number",
|
||||
missingCode: "Please enter the verification code",
|
||||
},
|
||||
tips: {
|
||||
smsApiMissing: "SMS sending API is not connected",
|
||||
},
|
||||
},
|
||||
state: {
|
||||
empty: "No data",
|
||||
loading: "Loading...",
|
||||
|
||||
@@ -7,6 +7,34 @@ export default {
|
||||
errors: {
|
||||
network: "เครือข่ายผิดพลาด โปรดลองอีกครั้งในภายหลัง",
|
||||
},
|
||||
login: {
|
||||
method: {
|
||||
google: "เข้าสู่ระบบด้วย Google",
|
||||
phone: "โทรศัพท์ + รหัส",
|
||||
},
|
||||
fields: {
|
||||
country: "ประเทศ/รหัส",
|
||||
phone: "โทรศัพท์",
|
||||
code: "รหัสยืนยัน",
|
||||
},
|
||||
placeholders: {
|
||||
selectCountry: "เลือกรหัสประเทศ",
|
||||
phone: "กรอกหมายเลขโทรศัพท์",
|
||||
code: "กรอกรหัสยืนยัน",
|
||||
},
|
||||
dividersText: "หรือ",
|
||||
actions: {
|
||||
sendCode: "ส่งรหัส",
|
||||
login: "เข้าสู่ระบบ",
|
||||
},
|
||||
errors: {
|
||||
missingPhone: "กรอกหมายเลขโทรศัพท์",
|
||||
missingCode: "กรอกรหัสยืนยัน",
|
||||
},
|
||||
tips: {
|
||||
smsApiMissing: "ยังไม่ได้เชื่อมต่อ API สำหรับส่ง SMS",
|
||||
},
|
||||
},
|
||||
state: {
|
||||
empty: "ไม่มีข้อมูล",
|
||||
loading: "กำลังโหลด...",
|
||||
|
||||
@@ -7,6 +7,34 @@ export default {
|
||||
errors: {
|
||||
network: "网络异常,请稍后重试",
|
||||
},
|
||||
login: {
|
||||
method: {
|
||||
google: "谷歌账号登录",
|
||||
phone: "手机号验证码登录",
|
||||
},
|
||||
fields: {
|
||||
country: "国家/区号",
|
||||
phone: "手机号",
|
||||
code: "验证码",
|
||||
},
|
||||
placeholders: {
|
||||
selectCountry: "请选择国家/区号",
|
||||
phone: "请输入手机号",
|
||||
code: "请输入验证码",
|
||||
},
|
||||
dividersText: "或",
|
||||
actions: {
|
||||
sendCode: "获取验证码",
|
||||
login: "登录",
|
||||
},
|
||||
errors: {
|
||||
missingPhone: "请输入手机号",
|
||||
missingCode: "请输入验证码",
|
||||
},
|
||||
tips: {
|
||||
smsApiMissing: "验证码发送接口未接入",
|
||||
},
|
||||
},
|
||||
state: {
|
||||
empty: "暂无数据",
|
||||
loading: "加载中...",
|
||||
|
||||
@@ -1,15 +1,176 @@
|
||||
<template>
|
||||
<div class="h-screen flex flex-col items-center justify-center">
|
||||
<div id="buttonDiv" class="w-[304px] mt-[20px]"></div>
|
||||
<div class="h-[100dvh] flex flex-col items-center justify-center px-4 overflow-hidden bg-white">
|
||||
<div class="px-[12px]">
|
||||
<section>
|
||||
<van-cell :title="t('common.login.fields.country')" :value="selectedCountryDisplay" is-link
|
||||
@click="countryPopupVisible = true" />
|
||||
|
||||
<van-field v-model="phone" type="tel" clearable :label="t('common.login.fields.phone')"
|
||||
:placeholder="t('common.login.placeholders.phone')" autocomplete="tel" />
|
||||
|
||||
<van-field v-model="code" type="digit" clearable maxlength="8" :label="t('common.login.fields.code')"
|
||||
:placeholder="t('common.login.placeholders.code')" autocomplete="one-time-code">
|
||||
<template #button>
|
||||
<van-button class="rounded-full" size="small" @click="handleSendCode">
|
||||
{{ t('common.login.actions.sendCode') }}
|
||||
</van-button>
|
||||
</template>
|
||||
</van-field>
|
||||
|
||||
<div class="mt-4">
|
||||
<van-button class="login-main-btn" type="primary" block :loading="phoneSubmitting"
|
||||
:disabled="!canSubmitPhoneLogin" @click="handlePhoneLogin">
|
||||
{{ t('common.login.actions.login') }}
|
||||
</van-button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<van-divider>{{ t('common.login.dividersText') }}</van-divider>
|
||||
|
||||
<div class="flex flex-col items-center justify-center">
|
||||
<div ref="googleButtonEl" id="buttonDiv" class="google-login-btn"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<van-popup v-model:show="countryPopupVisible" round position="bottom" class="h-[70vh]">
|
||||
<div class="h-full flex flex-col">
|
||||
<div class="p-3">
|
||||
<van-search v-model="countrySearch" :placeholder="t('common.login.placeholders.selectCountry')" shape="round" />
|
||||
</div>
|
||||
<div class="flex-1 overflow-y-auto scrollbar-none [-ms-overflow-style:none] [&::-webkit-scrollbar]:hidden">
|
||||
<van-cell v-for="item in filteredCountries" :key="`${item.iso2}-${item.dialCode}`" :title="item.name"
|
||||
:label="item.iso2" :value="item.dialCode" @click="handleSelectCountry(item)" />
|
||||
</div>
|
||||
</div>
|
||||
</van-popup>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted } from "vue";
|
||||
import { oauthToken } from '@/api/login'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { computed, nextTick, onMounted, ref } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { oauthToken } from "@/api/login";
|
||||
import { COUNTRY_CALLING_CODES, findCountryCallingCode } from "@/constants/countryCallingCodes";
|
||||
import { getCurrentLocale } from "@/i18n";
|
||||
import { useRouter } from "vue-router";
|
||||
|
||||
const router = useRouter()
|
||||
const router = useRouter();
|
||||
const { t } = useI18n();
|
||||
|
||||
const googleButtonEl = ref<HTMLElement | null>(null);
|
||||
const googleInited = ref(false);
|
||||
const googleButtonRendered = ref(false);
|
||||
const countryPopupVisible = ref(false);
|
||||
const countrySearch = ref("");
|
||||
|
||||
const selectedCountry = ref(
|
||||
findCountryCallingCode(
|
||||
(() => {
|
||||
const locale = getCurrentLocale();
|
||||
if (locale === "th-TH") return "TH";
|
||||
if (locale === "en-US") return "US";
|
||||
return "CN";
|
||||
})(),
|
||||
) ?? COUNTRY_CALLING_CODES[0],
|
||||
);
|
||||
|
||||
const phone = ref("");
|
||||
const code = ref("");
|
||||
const phoneSubmitting = ref(false);
|
||||
|
||||
const selectedCountryDisplay = computed(() => {
|
||||
return `${selectedCountry.value.name} (${selectedCountry.value.dialCode})`;
|
||||
});
|
||||
|
||||
const filteredCountries = computed(() => {
|
||||
const q = countrySearch.value.trim().toLowerCase();
|
||||
if (!q) {
|
||||
return COUNTRY_CALLING_CODES;
|
||||
}
|
||||
return COUNTRY_CALLING_CODES.filter((item) => {
|
||||
const haystack = `${item.name} ${item.iso2} ${item.dialCode}`.toLowerCase();
|
||||
return haystack.includes(q);
|
||||
});
|
||||
});
|
||||
|
||||
const canSubmitPhoneLogin = computed(() => {
|
||||
const phoneDigits = phone.value.replace(/\D/g, "");
|
||||
return (
|
||||
phoneDigits.length >= 6 &&
|
||||
code.value.trim().length >= 4 &&
|
||||
!phoneSubmitting.value
|
||||
);
|
||||
});
|
||||
|
||||
function handleSelectCountry(item: { name: string; iso2: string; dialCode: string }) {
|
||||
selectedCountry.value = item;
|
||||
countryPopupVisible.value = false;
|
||||
countrySearch.value = "";
|
||||
}
|
||||
|
||||
function handleSendCode() {
|
||||
showToast(t("common.login.tips.smsApiMissing"));
|
||||
}
|
||||
|
||||
async function waitForGoogleButtonContainer(): Promise<HTMLElement> {
|
||||
for (let i = 0; i < 30; i += 1) {
|
||||
await nextTick();
|
||||
const el = googleButtonEl.value ?? document.getElementById("buttonDiv");
|
||||
if (el) {
|
||||
return el;
|
||||
}
|
||||
await new Promise<void>((resolve) => setTimeout(resolve, 50));
|
||||
}
|
||||
throw new Error("Missing #buttonDiv");
|
||||
}
|
||||
|
||||
async function ensureRenderGoogleButton(): Promise<void> {
|
||||
if (!googleInited.value || googleButtonRendered.value) {
|
||||
return;
|
||||
}
|
||||
if (!window.google?.accounts?.id) {
|
||||
return;
|
||||
}
|
||||
const container = await waitForGoogleButtonContainer();
|
||||
window.google.accounts.id.renderButton(container, {
|
||||
theme: "outline",
|
||||
size: "large",
|
||||
width: 350,
|
||||
shape: "rectangular",
|
||||
logo_alignment: "center",
|
||||
});
|
||||
googleButtonRendered.value = true;
|
||||
}
|
||||
|
||||
async function handlePhoneLogin() {
|
||||
const phoneDigits = phone.value.replace(/\D/g, "");
|
||||
const codeValue = code.value.trim();
|
||||
|
||||
if (!phoneDigits) {
|
||||
showToast(t("common.login.errors.missingPhone"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (codeValue.length < 4) {
|
||||
showToast(t("common.login.errors.missingCode"));
|
||||
return;
|
||||
}
|
||||
|
||||
phoneSubmitting.value = true;
|
||||
try {
|
||||
await oauthToken({
|
||||
clientId: import.meta.env.VITE_CLIENT_ID,
|
||||
openIdCode: [`${selectedCountry.value.dialCode}${phoneDigits}`, codeValue],
|
||||
grant_type: "sms",
|
||||
});
|
||||
router.go(-1);
|
||||
} catch (e: unknown) {
|
||||
console.error(e);
|
||||
showToast(t("common.errors.network"));
|
||||
} finally {
|
||||
phoneSubmitting.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
type GoogleIdentityServices = {
|
||||
accounts: {
|
||||
@@ -97,22 +258,41 @@ onMounted(() => {
|
||||
},
|
||||
context: "signin",
|
||||
});
|
||||
|
||||
const container = document.getElementById("buttonDiv");
|
||||
if (!container) {
|
||||
throw new Error("Missing #buttonDiv");
|
||||
}
|
||||
|
||||
window.google.accounts.id.renderButton(container, {
|
||||
theme: "outline",
|
||||
size: "large",
|
||||
width: 304,
|
||||
shape: "rectangular",
|
||||
logo_alignment: "center",
|
||||
});
|
||||
googleInited.value = true;
|
||||
return ensureRenderGoogleButton();
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(e);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
:deep(.van-button) {
|
||||
border-radius: 50px !important;
|
||||
}
|
||||
|
||||
.login-main-btn {
|
||||
height: 44px;
|
||||
}
|
||||
|
||||
:deep(#buttonDiv),
|
||||
:deep(#buttonDiv > div),
|
||||
:deep(#buttonDiv > div > div),
|
||||
:deep(#buttonDiv iframe) {
|
||||
border-radius: 50px !important;
|
||||
height: 44px !important;
|
||||
}
|
||||
|
||||
:deep([class~="nsm7Bb-HzV7m-LgbsSe"]) {
|
||||
height: 44px !important;
|
||||
border-radius: 50px !important;
|
||||
overflow: visible !important;
|
||||
}
|
||||
|
||||
:deep([class*="nsm7Bb-HzV7m-LgbsSe-MJoBVe"]) {
|
||||
height: 44px !important;
|
||||
border-radius: 50px !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user