feat(mpuniapp): 重构登录页面为瑞士国际主义极简品牌感,优化宽屏居中与移动端自适应
这个提交包含在:
@@ -32,6 +32,7 @@ type LoginState = {
|
|||||||
email: string;
|
email: string;
|
||||||
submitting: boolean;
|
submitting: boolean;
|
||||||
errorMessage: string;
|
errorMessage: string;
|
||||||
|
countdown: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
type UseLoginModule = {
|
type UseLoginModule = {
|
||||||
@@ -41,6 +42,8 @@ type UseLoginModule = {
|
|||||||
sendMagicLink: (state: LoginState) => boolean;
|
sendMagicLink: (state: LoginState) => boolean;
|
||||||
sendSmsCode: (state: LoginState) => boolean;
|
sendSmsCode: (state: LoginState) => boolean;
|
||||||
tapSocialLogin: (provider: 'google' | 'apple') => void;
|
tapSocialLogin: (provider: 'google' | 'apple') => void;
|
||||||
|
resolveLoginText: (key: string) => string;
|
||||||
|
tickCountdown: (state: LoginState) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
describe('OmniCard AI 用户端统一登录业务逻辑测试 (useLogin.uts)', () => {
|
describe('OmniCard AI 用户端统一登录业务逻辑测试 (useLogin.uts)', () => {
|
||||||
@@ -49,6 +52,7 @@ describe('OmniCard AI 用户端统一登录业务逻辑测试 (useLogin.uts)', (
|
|||||||
let lastRequestOptions: UniRequestOption | null;
|
let lastRequestOptions: UniRequestOption | null;
|
||||||
let requestCallCount: number;
|
let requestCallCount: number;
|
||||||
let navigateBackCount: number;
|
let navigateBackCount: number;
|
||||||
|
let switchTabCount: number;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
storageMap = new Map();
|
storageMap = new Map();
|
||||||
@@ -56,6 +60,7 @@ describe('OmniCard AI 用户端统一登录业务逻辑测试 (useLogin.uts)', (
|
|||||||
lastRequestOptions = null;
|
lastRequestOptions = null;
|
||||||
requestCallCount = 0;
|
requestCallCount = 0;
|
||||||
navigateBackCount = 0;
|
navigateBackCount = 0;
|
||||||
|
switchTabCount = 0;
|
||||||
jest.resetModules();
|
jest.resetModules();
|
||||||
|
|
||||||
(global as { uni?: Record<string, unknown> }).uni = {
|
(global as { uni?: Record<string, unknown> }).uni = {
|
||||||
@@ -66,6 +71,9 @@ describe('OmniCard AI 用户端统一登录业务逻辑测试 (useLogin.uts)', (
|
|||||||
toastCalls.push(options);
|
toastCalls.push(options);
|
||||||
},
|
},
|
||||||
reLaunch: jest.fn(),
|
reLaunch: jest.fn(),
|
||||||
|
switchTab: (_options: { url: string }) => {
|
||||||
|
switchTabCount += 1;
|
||||||
|
},
|
||||||
navigateBack: () => {
|
navigateBack: () => {
|
||||||
navigateBackCount += 1;
|
navigateBackCount += 1;
|
||||||
},
|
},
|
||||||
@@ -98,13 +106,14 @@ describe('OmniCard AI 用户端统一登录业务逻辑测试 (useLogin.uts)', (
|
|||||||
}
|
}
|
||||||
|
|
||||||
describe('1. completeLogin 写入本地凭证', () => {
|
describe('1. completeLogin 写入本地凭证', () => {
|
||||||
it('1.1 completeLogin 会 setStorage token', () => {
|
it('1.1 completeLogin 会 setStorage token 并跳转名片页', () => {
|
||||||
const { completeLogin } = loadUseLogin();
|
const { completeLogin } = loadUseLogin();
|
||||||
|
|
||||||
const ok = completeLogin('mock_jwt_login_t09');
|
const ok = completeLogin('mock_jwt_login_t09');
|
||||||
|
|
||||||
expect(ok).toBe(true);
|
expect(ok).toBe(true);
|
||||||
expect(storageMap.get('token')).toBe('mock_jwt_login_t09');
|
expect(storageMap.get('token')).toBe('mock_jwt_login_t09');
|
||||||
|
expect(switchTabCount).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('1.2 传入 tenantId 时同步写入 tenant_id', () => {
|
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('token')).toBe('mock_jwt_login_t09');
|
||||||
expect(storageMap.get('tenant_id')).toBe('tenant_omni_88');
|
expect(storageMap.get('tenant_id')).toBe('tenant_omni_88');
|
||||||
|
expect(switchTabCount).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('1.3 未传 tenantId 时不写入 tenant_id', () => {
|
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.get('token')).toBe('mock_jwt_only_token');
|
||||||
expect(storageMap.has('tenant_id')).toBe(false);
|
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('即将开放');
|
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 {
|
function t(key: LoginCopyKey | string): string {
|
||||||
return resolveLoginText(key);
|
return resolveLoginText(key, currentLang.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
const codeButtonText = computed((): string => {
|
const codeButtonText = computed((): string => {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
* 第一版可用骨架:不接真实 Google / Apple SDK,不伪造成功登录
|
* 第一版可用骨架:不接真实 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';
|
import { setAuthToken, setTenantId } from '../../common/request.uts';
|
||||||
|
|
||||||
export type LoginTab = 'phone' | 'email';
|
export type LoginTab = 'phone' | 'email';
|
||||||
@@ -55,12 +55,13 @@ function isZhLocale(): boolean {
|
|||||||
/**
|
/**
|
||||||
* 登录文案:能复用 locale.uts 的走词典,缺 key 时中英简单分支
|
* 登录文案:能复用 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') {
|
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') {
|
if (key === 'title') {
|
||||||
return zh ? '登录 OmniCard' : 'Sign in to OmniCard';
|
return zh ? '登录 OmniCard' : 'Sign in to OmniCard';
|
||||||
}
|
}
|
||||||
|
|||||||
在新工单中引用
屏蔽一个用户