From 4de487e05730cff72b29c25db25376fce11fa006 Mon Sep 17 00:00:00 2001 From: lingniu Date: Mon, 20 Jul 2026 05:57:05 +0800 Subject: [PATCH] feat(web): polish account login workspace --- .../apps/web/src/v2/auth/AuthGate.test.tsx | 5 +- .../apps/web/src/v2/auth/AuthGate.tsx | 14 +++- .../apps/web/src/v2/styles/workspace.css | 80 ++++++++++++++++--- 3 files changed, 84 insertions(+), 15 deletions(-) diff --git a/vehicle-data-platform/apps/web/src/v2/auth/AuthGate.test.tsx b/vehicle-data-platform/apps/web/src/v2/auth/AuthGate.test.tsx index ccdf763b..5de34bcc 100644 --- a/vehicle-data-platform/apps/web/src/v2/auth/AuthGate.test.tsx +++ b/vehicle-data-platform/apps/web/src/v2/auth/AuthGate.test.tsx @@ -42,7 +42,7 @@ test('uses a branded Semi UI session state while restoring authentication', () = test('presents failed credentials as a clear Semi UI alert without losing the login form', async () => { mocks.session.mockRejectedValue(new Error('未登录')); - mocks.login.mockRejectedValue(new Error('账号或密码错误')); + mocks.login.mockRejectedValue(new Error('账号或密码错误 (traceId: trace-login-test)')); const client = new QueryClient({ defaultOptions: { queries: { retry: false } } }); const view = render(
受保护工作台
); @@ -52,6 +52,9 @@ test('presents failed credentials as a clear Semi UI alert without losing the lo expect(await screen.findByRole('alert')).toHaveTextContent('登录未完成'); expect(screen.getByRole('alert')).toHaveTextContent('账号或密码错误'); + expect(screen.getByRole('alert')).toHaveTextContent('请确认账号和密码后重试'); + expect(screen.getByRole('alert')).not.toHaveTextContent('trace-login-test'); + expect(view.container.querySelector('.v2-auth-error')).toHaveAttribute('data-support-trace', 'trace-login-test'); expect(view.container.querySelector('.v2-auth-error .semi-banner')).toBeInTheDocument(); expect(screen.getByPlaceholderText('请输入用户名')).toBeInTheDocument(); }); diff --git a/vehicle-data-platform/apps/web/src/v2/auth/AuthGate.tsx b/vehicle-data-platform/apps/web/src/v2/auth/AuthGate.tsx index 41baaad0..5fb5ed9e 100644 --- a/vehicle-data-platform/apps/web/src/v2/auth/AuthGate.tsx +++ b/vehicle-data-platform/apps/web/src/v2/auth/AuthGate.tsx @@ -7,6 +7,7 @@ import { PasswordInput } from '../shared/PasswordInput'; import { clearAccessToken, getAccessToken, PLATFORM_UNAUTHORIZED_EVENT, PlatformSession, setAccessToken } from './session'; const { Title, Text } = Typography; +const TRACE_ID_SUFFIX = /\s*\(traceId:\s*([^)]+)\)\s*$/i; type AuthContextValue = { session: PlatformSession; @@ -55,15 +56,24 @@ function AuthLoadingState() { ; } +function authErrorPresentation(message: string) { + const match = message.match(TRACE_ID_SUFFIX); + return { + message: message.replace(TRACE_ID_SUFFIX, '').trim() || '登录失败,请稍后重试', + traceId: match?.[1]?.trim() ?? '' + }; +} + function AuthErrorMessage({ message }: { message: string }) { - return
+ const presentation = authErrorPresentation(message); + return
} title="登录未完成" - description={message} + description={{presentation.message}请确认账号和密码后重试;连续失败可能会临时锁定账号。} />
; } diff --git a/vehicle-data-platform/apps/web/src/v2/styles/workspace.css b/vehicle-data-platform/apps/web/src/v2/styles/workspace.css index 7b099ad7..7ef4de90 100644 --- a/vehicle-data-platform/apps/web/src/v2/styles/workspace.css +++ b/vehicle-data-platform/apps/web/src/v2/styles/workspace.css @@ -5853,6 +5853,12 @@ .v2-auth-form-heading { display: grid; justify-items: center; + margin: -32px -34px 0; + border-bottom: 1px solid #e4ebf4; + background: + radial-gradient(circle at 50% -38%, rgba(18, 104, 243, .15), transparent 62%), + linear-gradient(180deg, #fbfdff 0%, #f7faff 100%); + padding: 30px 34px 24px; } .v2-auth-form-heading > h3.semi-typography { @@ -5888,10 +5894,18 @@ color: #d92d20; } +.v2-auth-error .semi-banner-content-body { + display: grid; + min-width: 0; + flex: 1 1 auto; + gap: 3px; +} + .v2-auth-error .semi-banner-title { color: #9f2d25; font-size: 11px; font-weight: 700; + white-space: nowrap; } .v2-auth-error .semi-banner-description { @@ -5901,6 +5915,24 @@ line-height: 1.5; } +.v2-auth-error .semi-banner-description > span { + display: grid; + gap: 3px; +} + +.v2-auth-error .semi-banner-description strong { + color: #a13d35; + font-size: 11px; + font-weight: 650; + line-height: 1.45; +} + +.v2-auth-error .semi-banner-description small { + color: #b26861; + font-size: 9px; + line-height: 1.5; +} + .v2-auth-form > .v2-auth-error + .semi-button-primary { margin-top: 16px; } @@ -5931,6 +5963,7 @@ .v2-auth-screen:not(.is-session-loading) { grid-template-columns: minmax(430px, 1.12fr) minmax(420px, 460px); column-gap: clamp(34px, 5vw, 82px); + justify-content: center; background: radial-gradient(circle at 18% 14%, rgba(18, 104, 243, .11), transparent 30%), linear-gradient(135deg, #f7faff 0%, #f2f6fb 56%, #eef3f9 100%); @@ -6062,8 +6095,9 @@ .v2-auth-screen:not(.is-session-loading) > .v2-auth-card.semi-card { width: min(460px, 100%); justify-self: end; + overflow: hidden; border-color: rgba(207, 219, 234, .96); - border-radius: 24px; + border-radius: 22px; background: rgba(255, 255, 255, .98); box-shadow: 0 28px 78px rgba(25, 43, 69, .14); } @@ -6098,7 +6132,7 @@ } .v2-auth-form > .v2-auth-fields { - margin-top: 20px; + margin-top: 22px; } .v2-auth-fields { @@ -6113,18 +6147,22 @@ gap: 7px; margin-top: 14px; color: #405269; - font-size: 12px; + font-size: 13px; font-weight: 680; } .v2-auth-fields > label .semi-input-wrapper { - height: 44px; + height: 48px; border-color: #dbe4ef; border-radius: 10px; background: #fbfcfe; box-shadow: none; } +.v2-auth-fields > label .semi-input { + font-size: 14px; +} + .v2-auth-fields > label .semi-input-wrapper:hover { border-color: #b8c9dd; background: #fff; @@ -6137,16 +6175,20 @@ } .v2-auth-form > .semi-button-primary { - height: 46px; + height: 48px; margin-top: 18px; border-radius: 10px; - font-size: 13px; + font-size: 14px; font-weight: 750; box-shadow: 0 10px 22px rgba(18, 104, 243, .2); } .v2-auth-trust-note { - margin-top: 12px; + min-height: 48px; + margin: 16px -34px -28px; + border-top: 1px solid #e8eef5; + background: #fbfcfe; + padding: 11px 34px; } .v2-auth-trust-note > .semi-typography { @@ -6285,8 +6327,12 @@ @media (max-width: 680px) { .v2-auth-screen:not(.is-session-loading) { - display: block; + display: grid; + width: 100%; min-height: 100dvh; + grid-template-columns: minmax(0, 1fr); + place-items: center; + overflow: auto; background: radial-gradient(circle at 50% 0, rgba(18, 104, 243, .1), transparent 34%), #f3f6fa; @@ -6305,6 +6351,11 @@ padding: 24px 18px 20px; } + .v2-auth-form-heading { + margin: -24px -18px 0; + padding: 24px 18px 20px; + } + .v2-auth-logo { width: 132px; margin-bottom: 14px; @@ -6371,16 +6422,21 @@ } .v2-auth-fields > label { - font-size: 12px; + font-size: 13px; } .v2-auth-fields > label .semi-input-wrapper { - height: 46px; + height: 48px; } .v2-auth-form > .semi-button-primary { - height: 46px; - font-size: 13px; + height: 48px; + font-size: 14px; + } + + .v2-auth-trust-note { + margin: 14px -18px -20px; + padding: 10px 18px; } .v2-route-error > .v2-route-error-card.semi-card {