From c924ffa16df032a13ca162b3708ed590509af114 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E8=87=AA=E8=BE=BE?= <980324341@qq.com> Date: Sat, 5 Sep 2026 20:47:17 +0800 Subject: [PATCH] =?UTF-8?q?task-241(admin.html=E8=A7=82=E6=84=9F=E5=AF=B9?= =?UTF-8?q?=E9=BD=90):=20=E8=93=9D=E7=99=BD=E6=B5=85=E8=89=B2=20token=20?= =?UTF-8?q?=E5=9F=BA=E7=BA=BF=E7=BF=BB=E8=BD=AC=EF=BC=88=E6=B5=85=E8=89=B2?= =?UTF-8?q?=E4=BE=A7=E6=A0=8F+=E8=93=9D=E7=81=B0=E4=B8=BB=E8=89=B2=20#4f78?= =?UTF-8?q?a5=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- admin-frontend-vue/src/styles/main.css | 27 +++++--- admin-frontend-vue/src/styles/theme.ts | 20 +++--- admin-frontend-vue/tests/task-241.test.ts | 80 +++++++++++++++++++++++ admin-frontend-vue/tests/task-4.test.ts | 15 +++-- 4 files changed, 116 insertions(+), 26 deletions(-) create mode 100644 admin-frontend-vue/tests/task-241.test.ts diff --git a/admin-frontend-vue/src/styles/main.css b/admin-frontend-vue/src/styles/main.css index 8555cc2f..de7e9c83 100644 --- a/admin-frontend-vue/src/styles/main.css +++ b/admin-frontend-vue/src/styles/main.css @@ -1,13 +1,22 @@ :root { - --admin-sidebar: #192132; - --admin-sidebar-deep: #121827; - --admin-primary: #6366f1; - --admin-primary-soft: #eef0ff; - --admin-bg: #f3f5f9; - --admin-border: #e5e7ef; - --admin-text: #1f2937; - --admin-muted: #7b8495; - font-family: "Microsoft YaHei", "PingFang SC", "Helvetica Neue", sans-serif; + --admin-sidebar: #f8fbfe; + --admin-sidebar-deep: #edf3f9; + --admin-primary: #4f78a5; + --admin-primary-soft: #e7f0f8; + --admin-bg: #f4f7fb; + --admin-border: #d8e3ee; + --admin-text: #24384d; + --admin-muted: #5b6f83; + font-family: Inter, "SF Pro Display", "Microsoft YaHei", "PingFang SC", "Helvetica Neue", sans-serif; + /* Element Plus 主色对齐 admin.html 蓝白浅色皮肤(渐变主键 #5f85ad→#4f78a5) */ + --el-color-primary: #4f78a5; + --el-color-primary-light-3: #7d9abf; + --el-color-primary-light-5: #a5bad2; + --el-color-primary-light-7: #cddbe6; + --el-color-primary-light-8: #e1e9f0; + --el-color-primary-light-9: #f0f4f8; + --el-color-primary-dark-2: #3f628a; + --el-border-radius-base: 6px; } * { box-sizing: border-box; } diff --git a/admin-frontend-vue/src/styles/theme.ts b/admin-frontend-vue/src/styles/theme.ts index d09484a8..5474f4e6 100644 --- a/admin-frontend-vue/src/styles/theme.ts +++ b/admin-frontend-vue/src/styles/theme.ts @@ -1,17 +1,17 @@ /** - * Admin 主题 token 单源(任务 4):深色侧边栏 + 浅色内容区。 + * Admin 主题 token 单源(任务 4 契约,module 13 task 241 翻转为蓝白浅色)。 * main.css 使用同名 --admin-* CSS 变量承载渲染值;tests 用本模块校验 - * “侧边栏为暗、内容区为亮、正文对比度达标”的不变量,防止误改成浅色侧栏。 + * “侧边栏与内容区均为浅色、主色为蓝灰、正文对比度达标”的不变量,防止回退到靛蓝深色侧栏。 */ export const ADMIN_THEME = { - sidebar: '#192132', - sidebarDeep: '#121827', - primary: '#6366f1', - primarySoft: '#eef0ff', - bg: '#f3f5f9', - border: '#e5e7ef', - text: '#1f2937', - muted: '#7b8495', + sidebar: '#f8fbfe', + sidebarDeep: '#edf3f9', + primary: '#4f78a5', + primarySoft: '#e7f0f8', + bg: '#f4f7fb', + border: '#d8e3ee', + text: '#24384d', + muted: '#5b6f83', } as const export type AdminThemeKey = keyof typeof ADMIN_THEME diff --git a/admin-frontend-vue/tests/task-241.test.ts b/admin-frontend-vue/tests/task-241.test.ts new file mode 100644 index 00000000..119c9517 --- /dev/null +++ b/admin-frontend-vue/tests/task-241.test.ts @@ -0,0 +1,80 @@ +import test from 'node:test' +import assert from 'node:assert/strict' +import { readSource } from './helpers.ts' +import { + ADMIN_THEME, + ADMIN_THEME_KEYS, + contrastRatio, + isDarkSurface, + relativeLuminance, + tokenHex, +} from '../src/styles/theme.ts' + +// module 13 task 241:蓝白浅色基线翻转。旧「深色侧栏 + indigo」token 需整体替换为 +// admin.html 末层(L4478)生效值:浅色侧栏、蓝灰主色 #4f78a5、淡蓝内容底。 + +test('test_task_241_baseline_normal_primary_path', () => { + // 正常主路径:token 命中 admin.html 蓝白浅色基线的关键 hex。 + assert.equal(ADMIN_THEME.sidebar, '#f8fbfe', '侧栏顶色应为浅蓝白') + assert.equal(ADMIN_THEME.sidebarDeep, '#edf3f9', '侧栏底/加深色应为浅蓝灰') + assert.equal(ADMIN_THEME.primary, '#4f78a5', '主色应为蓝灰(非旧 indigo)') + assert.equal(ADMIN_THEME.primarySoft, '#e7f0f8', '主色软底应为浅蓝') + assert.equal(ADMIN_THEME.bg, '#f4f7fb', '内容底应为淡蓝') + assert.equal(ADMIN_THEME.text, '#24384d', '正文应为深蓝灰') +}) + +test('test_task_241_baseline_normal_variant_input', () => { + // 正常变体:侧栏与内容区均为浅色(翻转核心语义),不再要求侧栏为暗。 + assert.equal(isDarkSurface(ADMIN_THEME.sidebar), false, '侧栏顶色必须为浅色') + assert.equal(isDarkSurface(ADMIN_THEME.sidebarDeep), false, '侧栏底必须为浅色') + assert.equal(isDarkSurface(ADMIN_THEME.bg), false, '内容区必须为浅色') + assert.equal(isDarkSurface(ADMIN_THEME.border), false, '边框须为亮面中性色') +}) + +test('test_task_241_baseline_normal_repeated_operation_is_idempotent', () => { + // 正常重复:浅色判定与对比度幂等。 + assert.equal(isDarkSurface(ADMIN_THEME.sidebar), isDarkSurface(ADMIN_THEME.sidebar)) + assert.equal(relativeLuminance(ADMIN_THEME.bg), relativeLuminance(ADMIN_THEME.bg)) +}) + +test('test_task_241_baseline_boundary_empty_input', () => { + // 边界:token 键集合不变(防丢语义键),全部 #rrggbb。 + assert.ok(ADMIN_THEME_KEYS.length >= 7, '必须覆盖侧栏/内容区/正文等核心语义面') + for (const key of ADMIN_THEME_KEYS) { + assert.match(ADMIN_THEME[key], /^#[0-9a-fA-F]{6}$/, `${key} 须为 #rrggbb`) + } +}) + +test('test_task_241_baseline_boundary_single_item', () => { + // 边界单元素:侧栏与内容区仍是两个可区分的浅色面(不得同色)。 + assert.notEqual(ADMIN_THEME.sidebar, ADMIN_THEME.bg, '侧栏与内容底必须可区分') + assert.notEqual(ADMIN_THEME.sidebar, ADMIN_THEME.sidebarDeep) + assert.equal(isDarkSurface(ADMIN_THEME.sidebar), false) +}) + +test('test_task_241_baseline_boundary_limit_or_missing_field', () => { + // 边界上限:正文/次要文本在浅色底上满足 WCAG AA;CSS 变量名映射稳定。 + assert.ok(contrastRatio(ADMIN_THEME.text, ADMIN_THEME.bg) >= 4.5, '正文/内容区需 ≥4.5') + assert.ok(contrastRatio(ADMIN_THEME.primary, ADMIN_THEME.bg) >= 3.0, '主色按钮/内容区 ≥3') + assert.ok(contrastRatio(ADMIN_THEME.muted, ADMIN_THEME.bg) >= 3.0, '次要文本 ≥3') + assert.equal(tokenHex('sidebar'), '#f8fbfe') +}) + +test('test_task_241_baseline_invalid_input_rejected', () => { + // 异常:theme.ts 不得残留旧 indigo 主色与深色侧栏值(防只改一半漂移)。 + const ts = readSource('src/styles/theme.ts') + assert.equal(/#6366f1/.test(ts), false, 'theme.ts 不得残留旧 indigo #6366f1') + assert.equal(/#192132|#121827/.test(ts), false, 'theme.ts 不得残留旧深色侧栏') + assert.throws(() => tokenHex('unknown' as never), /未知 Admin 主题 token/) +}) + +test('test_task_241_baseline_dependency_failure_returns_actionable_message', () => { + // 依赖失败:theme.ts 与 main.css 渲染变量交叉校验一致(防双源漂移)。 + const css = readSource('src/styles/main.css') + const missing = ADMIN_THEME_KEYS.filter((key) => { + const tokenVar = `--admin-${key.replace(/[A-Z]/g, (c) => `-${c.toLowerCase()}`)}` + return !css.includes(`${tokenVar}: ${ADMIN_THEME[key]}`) + }) + assert.deepEqual(missing, [], `theme.ts 与 main.css 不一致,缺失变量: ${missing.join(', ')}`) + assert.equal(/@import/.test(css), false, 'main.css 不得 @import 旧后台整份样式') +}) diff --git a/admin-frontend-vue/tests/task-4.test.ts b/admin-frontend-vue/tests/task-4.test.ts index 6c67d694..7cfbdf9a 100644 --- a/admin-frontend-vue/tests/task-4.test.ts +++ b/admin-frontend-vue/tests/task-4.test.ts @@ -12,16 +12,16 @@ import { } from '../src/styles/theme.ts' test('test_task_004_theme_tokens_normal_primary_path', () => { - // 正常主路径:侧边栏与加深面是暗色,内容区/卡片底是亮色。 - assert.equal(isDarkSurface(ADMIN_THEME.sidebar), true, '侧边栏必须为暗色') - assert.equal(isDarkSurface(ADMIN_THEME.sidebarDeep), true, '侧边栏加深面必须为暗色') + // 正常主路径(module 13 翻转):侧边栏与内容区均为浅色,主色为蓝灰。 + assert.equal(isDarkSurface(ADMIN_THEME.sidebar), false, '侧边栏必须为浅色') + assert.equal(isDarkSurface(ADMIN_THEME.sidebarDeep), false, '侧边栏加深面必须为浅色') assert.equal(isDarkSurface(ADMIN_THEME.bg), false, '内容区背景必须为亮色') assert.equal(isDarkSurface(ADMIN_THEME.border), false, '边框须为亮面中性色') }) test('test_task_004_theme_tokens_normal_variant_input', () => { - // 正常变体:强调色与软底/正文高对比,正文 token 自成一致体系。 - assert.equal(relativeLuminance(ADMIN_THEME.primary), relativeLuminance('#6366f1')) + // 正常变体:主色为蓝灰基线、正文 token 自成一致体系。 + assert.equal(relativeLuminance(ADMIN_THEME.primary), relativeLuminance('#4f78a5')) assert.ok(contrastRatio(ADMIN_THEME.text, ADMIN_THEME.bg) >= 4.5, '正文/内容区需满足 WCAG AA 正文') assert.ok(contrastRatio(ADMIN_THEME.primary, ADMIN_THEME.bg) >= 3.0, '品牌色按钮须达到 3:1') }) @@ -42,9 +42,10 @@ test('test_task_004_theme_tokens_boundary_empty_input', () => { }) test('test_task_004_theme_tokens_boundary_single_item', () => { - // 边界单元素:单个中性边界 token 与内容区可区分;侧边栏深于内容区。 + // 边界单元素:单个中性边界 token 与内容区可区分;侧栏为独立浅色面(不再要求深于内容区)。 assert.notEqual(ADMIN_THEME.border, ADMIN_THEME.bg) - assert.ok(relativeLuminance(ADMIN_THEME.sidebar) < relativeLuminance(ADMIN_THEME.bg)) + assert.notEqual(ADMIN_THEME.sidebar, ADMIN_THEME.bg) + assert.equal(isDarkSurface(ADMIN_THEME.sidebar), false, '侧栏须保持浅色') }) test('test_task_004_theme_tokens_boundary_limit_or_missing_field', () => {