From 4ef1113c2cf155954866bfe106f7c4620cd075c5 Mon Sep 17 00:00:00 2001 From: DEV_DSW <562304744@qq.com> Date: Tue, 2 Jun 2026 16:55:53 +0800 Subject: [PATCH] feat(login): implement mobile SMS login flow Add utility function for form URL encoded data serialization, update the oauthToken API to use proper form data and basic authentication, add sendCode API for sending mobile verification codes, and rewrite the phone login flow with input validation and correct OAuth parameters. --- src/api/login.ts | 42 ++++++++++++++++++++++++++++++++------- src/pages/login/index.vue | 26 +++++++++++++++++++----- 2 files changed, 56 insertions(+), 12 deletions(-) diff --git a/src/api/login.ts b/src/api/login.ts index d9ff205..e96ab3e 100644 --- a/src/api/login.ts +++ b/src/api/login.ts @@ -6,23 +6,51 @@ export interface OauthTokenRequest { grant_type?: string; openIdCode?: string[]; scope?: string; + mobile?: string; + code?: string; [property: string]: any; } -export function oauthToken(args: OauthTokenRequest) { +export function buildFormUrlEncodedParams( + data: Record, +): URLSearchParams { + const params = new URLSearchParams(); + Object.entries(data).forEach(([key, value]) => { + if (value === undefined || value === null) return; + if (Array.isArray(value)) { + value.forEach((item) => { + if (item === undefined || item === null) return; + params.append(key, String(item)); + }); + return; + } + params.append(key, String(value)); + }); + return params; +} + +export function oauthToken(data: OauthTokenRequest) { + const params = buildFormUrlEncodedParams(data as Record); + return request({ url: "/auth/oauth2/token", method: "post", - data: args, + data: params, + headers: { + "Content-Type": "application/x-www-form-urlencoded", + Authorization: "Basic Y3VzdG9tOmN1c3RvbQ==", + }, }); } -// 绑定用户手机号 -export function bindUserPhone(args: any) { +// 发送手机验证码 +export function sendCode(mobile: string) { + const value = (mobile ?? "").trim(); + const encoded = encodeURIComponent(value); + return request({ - url: "/hotelBiz/user/bindUserPhone", - method: "post", - data: args, + url: `/admin/mobile/${encoded}`, + method: "get", }); } diff --git a/src/pages/login/index.vue b/src/pages/login/index.vue index 5cab2bb..df91535 100644 --- a/src/pages/login/index.vue +++ b/src/pages/login/index.vue @@ -79,7 +79,8 @@ import { computed, nextTick, onMounted, ref } from "vue"; import { useRouter } from "vue-router"; import { useI18n } from "vue-i18n"; -import { oauthToken } from "@/api/login"; +import { showToast } from "vant"; +import { oauthToken, sendCode } from "@/api/login"; import { COUNTRY_CALLING_CODES, findCountryCallingCode } from "@/constants/countryCallingCodes"; import { getCurrentLocale, setLocale } from "@/i18n"; import { Globe } from '@lucide/vue' @@ -148,8 +149,20 @@ function handleSelectCountry(item: { name: string; iso2: string; dialCode: strin countrySearch.value = ""; } -function handleSendCode() { - showToast(t("common.login.tips.smsApiMissing")); +async function handleSendCode() { + const phoneDigits = phone.value.replace(/\D/g, ""); + + if (!phoneDigits) { + showToast(t("common.login.errors.missingPhone")); + return; + } + + try { + await sendCode(`${selectedCountry.value.dialCode}${phoneDigits}`); + } catch (e: unknown) { + console.error(e); + showToast(t("common.errors.network")); + } } async function waitForGoogleButtonContainer(): Promise { @@ -197,11 +210,14 @@ async function handlePhoneLogin() { } phoneSubmitting.value = true; + try { await oauthToken({ clientId: import.meta.env.VITE_CLIENT_ID, - openIdCode: [`${selectedCountry.value.dialCode}${phoneDigits}`, codeValue], - grant_type: "sms", + scope: "server", + mobile: phoneDigits, + code: codeValue, + grant_type: "mobile", }); router.go(-1); } catch (e: unknown) {