feat(mpuniapp): 重构登录页面为瑞士国际主义极简品牌感,优化宽屏居中与移动端自适应
这个提交包含在:
@@ -32,6 +32,7 @@ type LoginState = {
|
||||
email: string;
|
||||
submitting: boolean;
|
||||
errorMessage: string;
|
||||
countdown: number;
|
||||
};
|
||||
|
||||
type UseLoginModule = {
|
||||
@@ -41,6 +42,8 @@ type UseLoginModule = {
|
||||
sendMagicLink: (state: LoginState) => boolean;
|
||||
sendSmsCode: (state: LoginState) => boolean;
|
||||
tapSocialLogin: (provider: 'google' | 'apple') => void;
|
||||
resolveLoginText: (key: string) => string;
|
||||
tickCountdown: (state: LoginState) => void;
|
||||
};
|
||||
|
||||
describe('OmniCard AI 用户端统一登录业务逻辑测试 (useLogin.uts)', () => {
|
||||
@@ -49,6 +52,7 @@ describe('OmniCard AI 用户端统一登录业务逻辑测试 (useLogin.uts)', (
|
||||
let lastRequestOptions: UniRequestOption | null;
|
||||
let requestCallCount: number;
|
||||
let navigateBackCount: number;
|
||||
let switchTabCount: number;
|
||||
|
||||
beforeEach(() => {
|
||||
storageMap = new Map();
|
||||
@@ -56,6 +60,7 @@ describe('OmniCard AI 用户端统一登录业务逻辑测试 (useLogin.uts)', (
|
||||
lastRequestOptions = null;
|
||||
requestCallCount = 0;
|
||||
navigateBackCount = 0;
|
||||
switchTabCount = 0;
|
||||
jest.resetModules();
|
||||
|
||||
(global as { uni?: Record<string, unknown> }).uni = {
|
||||
@@ -66,6 +71,9 @@ describe('OmniCard AI 用户端统一登录业务逻辑测试 (useLogin.uts)', (
|
||||
toastCalls.push(options);
|
||||
},
|
||||
reLaunch: jest.fn(),
|
||||
switchTab: (_options: { url: string }) => {
|
||||
switchTabCount += 1;
|
||||
},
|
||||
navigateBack: () => {
|
||||
navigateBackCount += 1;
|
||||
},
|
||||
@@ -98,13 +106,14 @@ describe('OmniCard AI 用户端统一登录业务逻辑测试 (useLogin.uts)', (
|
||||
}
|
||||
|
||||
describe('1. completeLogin 写入本地凭证', () => {
|
||||
it('1.1 completeLogin 会 setStorage token', () => {
|
||||
it('1.1 completeLogin 会 setStorage token 并跳转名片页', () => {
|
||||
const { completeLogin } = loadUseLogin();
|
||||
|
||||
const ok = completeLogin('mock_jwt_login_t09');
|
||||
|
||||
expect(ok).toBe(true);
|
||||
expect(storageMap.get('token')).toBe('mock_jwt_login_t09');
|
||||
expect(switchTabCount).toBe(1);
|
||||
});
|
||||
|
||||
it('1.2 传入 tenantId 时同步写入 tenant_id', () => {
|
||||
@@ -114,6 +123,7 @@ describe('OmniCard AI 用户端统一登录业务逻辑测试 (useLogin.uts)', (
|
||||
|
||||
expect(storageMap.get('token')).toBe('mock_jwt_login_t09');
|
||||
expect(storageMap.get('tenant_id')).toBe('tenant_omni_88');
|
||||
expect(switchTabCount).toBe(1);
|
||||
});
|
||||
|
||||
it('1.3 未传 tenantId 时不写入 tenant_id', () => {
|
||||
@@ -123,6 +133,7 @@ describe('OmniCard AI 用户端统一登录业务逻辑测试 (useLogin.uts)', (
|
||||
|
||||
expect(storageMap.get('token')).toBe('mock_jwt_only_token');
|
||||
expect(storageMap.has('tenant_id')).toBe(false);
|
||||
expect(switchTabCount).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -175,4 +186,38 @@ describe('OmniCard AI 用户端统一登录业务逻辑测试 (useLogin.uts)', (
|
||||
expect(toastCalls[1].title).toContain('即将开放');
|
||||
});
|
||||
});
|
||||
|
||||
describe('4. 极简品牌感文案与国际化解析', () => {
|
||||
it('4.1 正确解析品牌感文案,杜绝空字符串与硬编码', () => {
|
||||
const { resolveLoginText } = loadUseLogin();
|
||||
|
||||
expect(resolveLoginText('brandTag').length).toBeGreaterThan(0);
|
||||
expect(resolveLoginText('subtitle').length).toBeGreaterThan(0);
|
||||
expect(resolveLoginText('orContinueWith').length).toBeGreaterThan(0);
|
||||
expect(resolveLoginText('termsNotice').length).toBeGreaterThan(0);
|
||||
expect(resolveLoginText('termsOfService').length).toBeGreaterThan(0);
|
||||
expect(resolveLoginText('privacyPolicy').length).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('5. 验证码倒计时机制', () => {
|
||||
it('5.1 手机号合法时发送验证码触发 60s 倒计时', () => {
|
||||
const { createLoginState, sendSmsCode, tickCountdown } = loadUseLogin();
|
||||
|
||||
const state = createLoginState();
|
||||
state.phone = '13800138000';
|
||||
|
||||
sendSmsCode(state);
|
||||
expect(state.countdown).toBe(60);
|
||||
|
||||
// 递减一次
|
||||
tickCountdown(state);
|
||||
expect(state.countdown).toBe(59);
|
||||
|
||||
// 倒计时中不能重复触发
|
||||
const again = sendSmsCode(state);
|
||||
expect(again).toBe(false);
|
||||
expect(state.countdown).toBe(59);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -190,7 +190,7 @@ const isRtlMode = computed((): boolean => {
|
||||
});
|
||||
|
||||
function t(key: LoginCopyKey | string): string {
|
||||
return resolveLoginText(key);
|
||||
return resolveLoginText(key, currentLang.value);
|
||||
}
|
||||
|
||||
const codeButtonText = computed((): string => {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* 第一版可用骨架:不接真实 Google / Apple SDK,不伪造成功登录
|
||||
*/
|
||||
|
||||
import { getCurrentLocale, getOmniText } from '../../common/locale.uts';
|
||||
import { getCurrentLocale, getOmniText, OmniLocale } from '../../common/locale.uts';
|
||||
import { setAuthToken, setTenantId } from '../../common/request.uts';
|
||||
|
||||
export type LoginTab = 'phone' | 'email';
|
||||
@@ -55,12 +55,13 @@ function isZhLocale(): boolean {
|
||||
/**
|
||||
* 登录文案:能复用 locale.uts 的走词典,缺 key 时中英简单分支
|
||||
*/
|
||||
export function resolveLoginText(key: LoginCopyKey | string): string {
|
||||
export function resolveLoginText(key: LoginCopyKey | string, targetLocale?: string): string {
|
||||
const loc = targetLocale != null && targetLocale !== '' ? targetLocale : getCurrentLocale();
|
||||
if (key === 'loginSuccess') {
|
||||
return getOmniText(getCurrentLocale(), 'common.success');
|
||||
return getOmniText(loc as OmniLocale, 'common.success');
|
||||
}
|
||||
|
||||
const zh = isZhLocale();
|
||||
const zh = loc === 'zh-CN';
|
||||
if (key === 'title') {
|
||||
return zh ? '登录 OmniCard' : 'Sign in to OmniCard';
|
||||
}
|
||||
|
||||
在新工单中引用
屏蔽一个用户