This commit is contained in:
2026-10-20 23:34:11 +08:00
parent f155976f52
commit 13e4aa56ec
321 changed files with 6756 additions and 2110 deletions
+14
View File
@@ -0,0 +1,14 @@
# 平台登录页逐操作矩阵
依据:2026-10-20 用户确认全 Admin 推广清爽蓝白风格;视觉参考为工作区 `docs/界面参考/蓝白卡片式工作台风格参考.md` 与其中的用户参考图。当前页面入口为 `pages/LoginView.vue`;本轮只调整展示,不改变认证状态、接口和路由跳转。
| 页面 | 操作 | 当前承载与依据 | 本轮结论与验证 |
| --- | --- | --- | --- |
| 平台员工登录 | 输入账号、密码与提交 | `LoginView.vue` 的 `el-form` 与 `submit`;独立登录页,品牌区与表单左右布局 | 接入公共蓝白面板、图标和主题;保留原 `large` 表单尺寸、账号密码字段、自动完成、密码显隐与回车提交,不新增文案或字段 |
| 平台员工登录 | 必填校验与加载 | 原 `rules`、`loading` 与表单标签 | 原必填提示及提交加载保持;不增加验证码或改变认证步骤 |
| 平台员工登录 | 登录失败 | 原 `loginError`、`ApiErrorMeta` 与 `role="alert"` | 错误区域复用公共提示外观,服务端错误文案、诊断信息和可恢复表单保持 |
| 平台员工登录 | 登录成功后跳转 | 原 `auth.login`、`initializeAfterLogin` 与 `sanitizeInternalPath` | 成功提示、安全跳转、首个授权入口及无权限回退均保持,不修改 router/store |
| 平台员工登录 | 已有窄屏品牌布局 | 原 `960px`、`360px` 响应式规则 | 保留既有窄屏能力,不新增多视口验收范围 |
| 平台员工登录 | 详情、新增、编辑、审核、文件预览、删除 | 当前页面无上述操作 | 不新增操作或浮层 |
自动回归入口:`pages/LoginView.test.ts`。本模块未独立执行检查,统一由本轮总控完成。视觉待人工 `1920×1080`、浏览器缩放 `100%` 截图确认。
+9 -5
View File
@@ -43,14 +43,18 @@ describe('LoginView authentication lifecycle', () => {
expect(loginSource).not.toContain('demo-tip')
})
it('uses the admin design tokens and keeps the brand visible at 320px', () => {
expect(loginSource).toContain('<h1>柏悦物流管理后台</h1>')
expect(loginSource).toContain('class="login-mobile-brand"')
it('uses the shared blue-white theme and preserves the existing compact brand layout', () => {
expect(loginSource).toContain("import '@/styles/adminWorkbench.css'")
expect(loginSource).toContain('class="login-page admin-workbench"')
expect(loginSource).toContain('<h1 class="workbench-title">柏悦物流管理后台</h1>')
expect(loginSource).toContain('class="login-mobile-brand workbench-brand-mark"')
expect(loginSource).toContain('class="login-card workbench-panel"')
expect(loginSource).toContain('class="login-error workbench-notice workbench-tone" data-tone="rose"')
expect(loginSource).toContain('aria-label="平台员工登录"')
expect(loginSource).toContain('label-position="top" size="large"')
expect(loginSource).toContain('autocomplete="username" maxlength="100" autofocus')
expect(loginSource).toContain('aria-live="polite"')
expect(loginSource).toContain('background: var(--cc-surface-muted)')
expect(loginSource).toContain('border-radius: var(--cc-radius-lg)')
expect(loginSource).not.toContain('var(--cc-navy)')
expect(loginSource).toContain('@media (max-width: 360px)')
expect(loginSource).not.toContain('让跨境物流管理')
})
+35 -29
View File
@@ -9,6 +9,7 @@ import ApiErrorMeta from '@/shared/components/ApiErrorMeta.vue'
import { sanitizeInternalPath } from '@/core/routing/accessDecision'
import { useAccessPreviewStore } from '@/core/access/accessPreview'
import { useAuthStore } from '@/core/auth/auth'
import '@/styles/adminWorkbench.css'
const route = useRoute()
const router = useRouter()
@@ -54,25 +55,25 @@ async function submit() {
</script>
<template>
<div class="login-page">
<div class="login-page admin-workbench">
<section class="login-intro">
<div class="login-brand"><img class="login-brand-logo" :src="baiyueLogoWhite" alt="柏悦物流" /></div>
<div>
<h1>柏悦物流管理后台</h1>
<p class="intro-copy">平台内部运营与管理系统</p>
<div class="login-brand workbench-brand-mark"><img class="login-brand-logo" :src="baiyueLogoWhite" alt="柏悦物流" /></div>
<div class="workbench-heading">
<h1 class="workbench-title">柏悦物流管理后台</h1>
<p class="login-intro-copy workbench-note">平台内部运营与管理系统</p>
</div>
<div class="security-note">
<ShieldCheck :size="22" />
<div class="login-security-note workbench-meta">
<ShieldCheck :size="22" aria-hidden="true" />
<span>平台内部系统,请使用授权账号登录</span>
</div>
</section>
<section class="login-panel">
<div class="login-card">
<div class="login-mobile-brand"><img :src="baiyueLogoWhite" alt="柏悦物流" /></div>
<div class="login-card-icon"><LockKeyhole :size="24" /></div>
<h2>登录管理后台</h2>
<p>使用平台内部员工账号登录</p>
<div class="login-card workbench-panel">
<div class="login-mobile-brand workbench-brand-mark"><img :src="baiyueLogoWhite" alt="柏悦物流" /></div>
<div class="login-card-icon workbench-icon"><LockKeyhole :size="24" aria-hidden="true" /></div>
<h2 class="workbench-title">登录管理后台</h2>
<p class="workbench-note">使用平台内部员工账号登录</p>
<el-form ref="formRef" :model="form" :rules="rules" label-position="top" size="large" aria-label="平台员工登录" @submit.prevent="submit">
<el-form-item label="账号" prop="account" required>
<el-input v-model.trim="form.account" placeholder="请输入账号" :prefix-icon="UserRound" autocomplete="username" maxlength="100" autofocus />
@@ -84,38 +85,43 @@ async function submit() {
登录 <ArrowRight :size="17" />
</el-button>
</el-form>
<div v-if="loginError" class="login-error" role="alert" aria-live="polite"><p>{{ loginError.message }}</p><ApiErrorMeta :details="loginError" /></div>
<div v-if="loginError" class="login-error workbench-notice workbench-tone" data-tone="rose" role="alert" aria-live="polite"><p>{{ loginError.message }}</p><ApiErrorMeta :details="loginError" /></div>
</div>
</section>
</div>
</template>
<style scoped>
.login-page { min-width: 0; min-height: 100svh; background: var(--cc-surface); color: var(--cc-text); }
.login-intro { min-width: 0; background: var(--cc-navy); }
.login-intro h1 { max-width: 560px; margin: 16px 0 14px; font-size: 40px; line-height: 1.2; overflow-wrap: anywhere; }
.intro-copy { margin: 0; color: #cbd5e1; font-size: 15px; line-height: 1.7; overflow-wrap: anywhere; }
.login-panel { min-width: 0; background: var(--cc-surface-muted); }
.login-card { min-width: 0; padding: 32px; border: 1px solid var(--cc-border); border-radius: var(--cc-radius-lg); background: var(--cc-surface); box-shadow: var(--cc-shadow-md); }
.login-card-icon { border-radius: var(--cc-radius-lg); background: var(--cc-primary-soft); }
.login-card h2, .login-card > p { overflow-wrap: anywhere; }
.login-page { display: grid; min-width: 0; min-height: 100svh; grid-template-columns: minmax(420px, 1.05fr) minmax(420px, .95fr); background: var(--workbench-surface); color: var(--workbench-text); }
.login-intro { display: flex; min-width: 0; flex-direction: column; justify-content: space-between; padding: 46px clamp(42px, 7vw, 100px); }
.login-brand { display: flex; width: fit-content; min-height: 64px; align-items: center; padding: 8px 20px; }
.login-brand-logo { display: block; width: 205px; height: 64px; object-fit: contain; object-position: left center; }
.login-intro h1 { max-width: 560px; }
.login-intro-copy { max-width: 560px; }
.login-security-note > svg { flex: 0 0 auto; }
.login-panel { display: grid; min-width: 0; place-items: center; padding: 48px; background: var(--workbench-surface-tint); }
.login-card.workbench-panel { width: min(410px, 100%); min-width: 0; padding: 32px; }
.login-card h2 { margin-top: 22px; }
.login-card > p { margin: 8px 0 30px; }
.login-mobile-brand { display: none; }
.login-submit { min-height: 44px; }
.login-error { min-width: 0; padding: 12px 14px; border: 1px solid #fecdca; border-radius: var(--cc-radius-md); background: var(--cc-danger-soft); color: var(--cc-danger); overflow-wrap: anywhere; }
.login-submit { width: 100%; min-height: 44px; }
.login-submit :deep(> span) { display: flex; align-items: center; gap: 8px; }
.login-error.workbench-notice { min-width: 0; margin-top: 14px; }
.login-error > p { margin: 0; }
.login-error :deep(*) { max-width: 100%; overflow-wrap: anywhere; }
.login-card :deep(.el-input__wrapper:focus-within) { box-shadow: 0 0 0 1px var(--cc-primary) inset, 0 0 0 3px var(--cc-primary-soft); }
@media (max-width: 960px) {
.login-page { grid-template-columns: 1fr; }
.login-intro { display: none; }
.login-panel { align-items: start; padding: 24px; }
.login-card { width: min(440px, 100%); margin: auto; padding: 28px; }
.login-mobile-brand { display: flex; min-height: 52px; align-items: center; margin: -28px -28px 28px; padding: 12px 20px; border-radius: var(--cc-radius-lg) var(--cc-radius-lg) 0 0; background: var(--cc-navy); }
.login-card.workbench-panel { width: min(440px, 100%); margin: auto; padding: 28px; }
.login-mobile-brand { display: flex; width: fit-content; min-height: 52px; align-items: center; margin-bottom: 28px; padding: 8px 16px; }
.login-mobile-brand img { display: block; width: 154px; height: 48px; object-fit: contain; object-position: left center; }
}
@media (max-width: 360px) {
.login-panel { padding: 12px; }
.login-card { padding: 20px; }
.login-mobile-brand { margin: -20px -20px 24px; padding-inline: 16px; }
.login-card h2 { font-size: 24px; }
.login-card.workbench-panel { padding: 20px; }
.login-mobile-brand { margin-bottom: 24px; }
}
</style>