/** * OmniCard AI (CRMmp) - 统一身份认证登录业务驱动层 * 第一版可用骨架:支持账号密码真实登录对齐后端、手机号验证码、邮箱MagicLink与社交登录入口 */ import { getCurrentLocale, getOmniText, OmniLocale } from '../../common/locale.uts'; import { setAuthToken, setTenantId, httpPost } from '../../common/request.uts'; import { collectHallContact } from '../../common/api.uts'; import { consumePendingCollectCard } from '../owner/hall/useHall.uts'; export type LoginTab = 'password' | 'phone' | 'email'; export type SocialProvider = 'google' | 'apple'; export type LoginCopyKey = | 'title' | 'brandTag' | 'subtitle' | 'desc' | 'tabPassword' | 'tabPhone' | 'tabEmail' | 'usernamePlaceholder' | 'passwordPlaceholder' | 'accountRequired' | 'passwordRequired' | 'testAccountTip' | 'countryCode' | 'phonePlaceholder' | 'sendCode' | 'smsCode' | 'login' | 'magicLink' | 'emailPlaceholder' | 'orContinueWith' | 'google' | 'apple' | 'comingSoon' | 'phoneRequired' | 'emailRequired' | 'codeRequired' | 'termsNotice' | 'termsOfService' | 'privacyPolicy' | 'and' | 'countdownSuffix' | 'loginSuccess'; export type LoginState = { tab: LoginTab; username: string; password: string; countryCode: string; phone: string; smsCode: string; email: string; submitting: boolean; errorMessage: string; countdown: number; }; export type LoginAdminResponse = { token: string; expire?: number; refreshToken?: string; refreshExpire?: number; }; function isZhLocale(): boolean { return getCurrentLocale() === 'zh-CN'; } /** * 登录文案:能复用 locale.uts 的走词典,缺 key 时中英简单分支 */ export function resolveLoginText(key: LoginCopyKey | string, targetLocale?: string): string { const loc = targetLocale != null && targetLocale !== '' ? targetLocale : getCurrentLocale(); if (key === 'loginSuccess') { return getOmniText(loc as OmniLocale, 'common.success'); } const zh = loc === 'zh-CN'; if (key === 'title') { return zh ? '登录 OmniCard' : 'Sign in to OmniCard'; } if (key === 'brandTag') { return zh ? '全球智能数字名片' : 'Global Digital Identity'; } if (key === 'subtitle') { return zh ? '极简出海商务,连接全球商机' : 'Minimalist global commerce & identity'; } if (key === 'tabPassword') { return zh ? '账号密码' : 'Password'; } if (key === 'tabPhone') { return zh ? '手机号' : 'Phone'; } if (key === 'tabEmail') { return zh ? '邮箱' : 'Email'; } if (key === 'usernamePlaceholder') { return zh ? '用户名或手机号' : 'Username or phone'; } if (key === 'passwordPlaceholder') { return zh ? '登录密码' : 'Password'; } if (key === 'accountRequired') { return zh ? '请填写账号' : 'Enter your username'; } if (key === 'passwordRequired') { return zh ? '请填写密码' : 'Enter your password'; } if (key === 'testAccountTip') { return zh ? '测试账号: admin / 123456 (点击一键填入)' : 'Test account: admin / 123456 (Click to autofill)'; } if (key === 'desc') { return zh ? '使用国际手机号或邮箱继续' : 'Continue with your phone or email'; } if (key === 'countryCode') { return zh ? '区号' : 'Code'; } if (key === 'phonePlaceholder') { return zh ? '手机号' : 'Phone number'; } if (key === 'sendCode') { return zh ? '发送验证码' : 'Send code'; } if (key === 'smsCode') { return zh ? '验证码' : 'Verification code'; } if (key === 'login') { return zh ? '登录' : 'Sign in'; } if (key === 'magicLink') { return zh ? '发送登录链接' : 'Send sign-in link'; } if (key === 'emailPlaceholder') { return zh ? '工作邮箱' : 'Work email'; } if (key === 'orContinueWith') { return zh ? '或通过以下方式继续' : 'Or continue with'; } if (key === 'google') { return zh ? 'Google' : 'Google'; } if (key === 'apple') { return zh ? 'Apple' : 'Apple'; } if (key === 'comingSoon') { return zh ? '即将开放' : 'Coming soon'; } if (key === 'phoneRequired') { return zh ? '请填写手机号' : 'Enter your phone number'; } if (key === 'emailRequired') { return zh ? '请填写邮箱' : 'Enter your email'; } if (key === 'codeRequired') { return zh ? '请填写验证码' : 'Enter the verification code'; } if (key === 'termsNotice') { return zh ? '登录即代表您已同意' : 'By continuing, you agree to our'; } if (key === 'termsOfService') { return zh ? '服务条款' : 'Terms of Service'; } if (key === 'privacyPolicy') { return zh ? '隐私政策' : 'Privacy Policy'; } if (key === 'and') { return zh ? '与' : 'and'; } if (key === 'countdownSuffix') { return zh ? '秒后重发' : 's'; } return ''; } function toastNone(title: string): void { uni.showToast({ title, icon: 'none', }); } function finishAfterLogin(): void { try { uni.showToast({ title: resolveLoginText('loginSuccess'), icon: 'success', }); } catch (e) {} const pendingCardId = consumePendingCollectCard(); if (pendingCardId !== '') { collectHallContact(pendingCardId).catch(() => {}); } try { if (typeof uni.switchTab === 'function') { uni.switchTab({ url: '/pages/owner/card/index', }); if (typeof window !== 'undefined' && window.location != null) { try { setTimeout(() => { const currentHash = window.location.hash || ''; if (!currentHash.includes('/pages/owner/card/index')) { window.location.hash = '#/pages/owner/card/index'; } }, 300); } catch (e) {} } return; } } catch (e) {} try { if (typeof uni.reLaunch === 'function') { uni.reLaunch({ url: '/pages/owner/card/index', }); } } catch (e) {} } export function createLoginState(): LoginState { return { tab: 'password', username: '', password: '', countryCode: '+86', phone: '', smsCode: '', email: '', submitting: false, errorMessage: '', countdown: 0, }; } export function switchLoginTab(state: LoginState, tab: LoginTab): void { state.tab = tab; state.errorMessage = ''; } /** * 一键填充测试账号 */ export function fillTestAccount(state: LoginState): void { state.username = 'admin'; state.password = '123456'; state.errorMessage = ''; } /** * 倒计时推进函数 */ export function tickCountdown(state: LoginState): void { if (state.countdown > 0) { state.countdown -= 1; } } /** * 登录成功路径:写入 token / 可选 tenantId,再返回上一页或 toast */ export function completeLogin(token: string, tenantId?: string): boolean { const safeToken = token != null ? token.trim() : ''; if (safeToken === '') { return false; } setAuthToken(safeToken); const safeTenant = tenantId != null ? tenantId.trim() : ''; if (safeTenant !== '') { setTenantId(safeTenant); } finishAfterLogin(); return true; } /** * 账号密码真实登录:对齐后端 Cool-Admin POST /admin/base/open/login */ export async function submitPasswordLogin(state: LoginState): Promise { const username = state.username != null ? state.username.trim() : ''; if (username === '') { state.errorMessage = resolveLoginText('accountRequired'); toastNone(state.errorMessage); return false; } const password = state.password != null ? state.password.trim() : ''; if (password === '') { state.errorMessage = resolveLoginText('passwordRequired'); toastNone(state.errorMessage); return false; } if (state.submitting) { return false; } state.submitting = true; state.errorMessage = ''; try { const res = await httpPost('/admin/base/open/login', { username, password, }); state.submitting = false; if (res != null && res.token != null && res.token !== '') { completeLogin(res.token); return true; } state.errorMessage = '登录失败,未获取到有效凭证'; toastNone(state.errorMessage); return false; } catch (err: any) { state.submitting = false; const msg = err && err.message ? err.message : '账户或密码不正确~'; state.errorMessage = msg; return false; } } export function sendSmsCode(state: LoginState): boolean { if (state.countdown > 0) { return false; } const phone = state.phone != null ? state.phone.trim() : ''; if (phone === '') { state.errorMessage = resolveLoginText('phoneRequired'); toastNone(state.errorMessage); return false; } state.errorMessage = ''; state.countdown = 60; toastNone(resolveLoginText('comingSoon')); return true; } /** * 国际手机号验证码登录。空手机号不提交;第一版不伪造成功登录 */ export function submitPhoneLogin(state: LoginState): boolean { const phone = state.phone != null ? state.phone.trim() : ''; if (phone === '') { state.errorMessage = resolveLoginText('phoneRequired'); toastNone(state.errorMessage); return false; } const smsCode = state.smsCode != null ? state.smsCode.trim() : ''; if (smsCode === '') { state.errorMessage = resolveLoginText('codeRequired'); toastNone(state.errorMessage); return false; } if (state.submitting) { return false; } state.errorMessage = ''; toastNone(resolveLoginText('comingSoon')); return false; } /** * 邮箱 Magic Link。空邮箱不提交;第一版不伪造成功登录 */ export function sendMagicLink(state: LoginState): boolean { const email = state.email != null ? state.email.trim() : ''; if (email === '') { state.errorMessage = resolveLoginText('emailRequired'); toastNone(state.errorMessage); return false; } if (state.submitting) { return false; } state.errorMessage = ''; toastNone(resolveLoginText('comingSoon')); return false; } /** * Google / Apple 仅展示即将开放,不写凭证 */ export function tapSocialLogin(_provider: SocialProvider): void { toastNone(resolveLoginText('comingSoon')); }