From 564fc88fc8ed4121736d159274c537878ef0f7a9 Mon Sep 17 00:00:00 2001
From: g1307092382 <1307092382@qq.com>
Date: Fri, 11 Sep 2026 17:02:42 +0800
Subject: [PATCH] feat: establish admin visual foundation
---
admin-web/src/layouts/MainLayout.vue | 530 +++++++++++++++++++++++---
admin-web/src/theme/index.css | 32 +-
ui-tests/admin-redesign-contracts.mjs | 32 ++
3 files changed, 537 insertions(+), 57 deletions(-)
create mode 100644 ui-tests/admin-redesign-contracts.mjs
diff --git a/admin-web/src/layouts/MainLayout.vue b/admin-web/src/layouts/MainLayout.vue
index 4f49c5a..ad9e71e 100644
--- a/admin-web/src/layouts/MainLayout.vue
+++ b/admin-web/src/layouts/MainLayout.vue
@@ -2,67 +2,495 @@
+
-
+
+
+
+
+
+
+
+
\ No newline at end of file
+.main-layout {
+ width: 100%;
+ height: 100vh;
+}
+
+.sidebar {
+ display: flex;
+ flex-direction: column;
+ overflow: hidden;
+ color: var(--admin-sidebar-text);
+ background: var(--admin-sidebar-bg);
+ border-right: 1px solid rgba(255, 255, 255, 0.06);
+ transition: width 0.2s ease;
+}
+
+.brand {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ height: 92px;
+ padding: 20px 18px;
+ border-bottom: 1px solid rgba(255, 255, 255, 0.08);
+}
+
+.brand.compact {
+ justify-content: center;
+ padding: 18px 10px;
+}
+
+.brand-mark {
+ width: 44px;
+ height: 44px;
+ flex: none;
+ object-fit: contain;
+}
+
+.brand-copy {
+ display: flex;
+ flex-direction: column;
+ white-space: nowrap;
+}
+
+.brand-copy strong {
+ color: #FFFFFF;
+ font-size: 18px;
+ letter-spacing: 1px;
+}
+
+.brand-copy span {
+ margin-top: 4px;
+ color: var(--admin-sidebar-muted);
+ font-size: 11px;
+}
+
+.menu-scroll {
+ flex: 1;
+}
+
+.sidebar-menu {
+ padding: 12px 10px;
+ border: 0;
+ background: transparent;
+}
+
+.sidebar-menu :deep(.el-menu-item) {
+ height: 42px;
+ margin: 3px 0;
+ color: var(--admin-sidebar-text);
+ border-radius: 9px;
+}
+
+.sidebar-menu :deep(.el-menu-item:hover) {
+ color: #FFFFFF;
+ background: var(--admin-sidebar-surface);
+}
+
+.sidebar-menu :deep(.el-menu-item.is-active) {
+ color: #FFFFFF;
+ font-weight: 600;
+ background: var(--admin-primary);
+}
+
+.sidebar-menu :deep(.el-menu-item-group__title) {
+ padding: 18px 12px 5px !important;
+ overflow: hidden;
+ color: var(--admin-sidebar-muted);
+ font-size: 11px;
+ letter-spacing: 0.5px;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.collapse-control {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 8px;
+ height: 52px;
+ margin: 10px;
+ color: var(--admin-sidebar-muted);
+ cursor: pointer;
+ background: transparent;
+ border: 0;
+ border-top: 1px solid rgba(255, 255, 255, 0.08);
+}
+
+.collapse-control:hover {
+ color: #FFFFFF;
+}
+
+.workspace {
+ min-width: 0;
+}
+
+.header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ height: var(--header-height);
+ padding: 0 24px;
+ background: var(--admin-header-bg);
+ border-bottom: 1px solid var(--color-border-light);
+}
+
+.page-context {
+ min-width: 180px;
+}
+
+.context-kicker {
+ margin-bottom: 4px;
+ color: var(--admin-primary);
+ font-size: 10px;
+ letter-spacing: 1px;
+}
+
+.header-tools,
+.user-info {
+ display: flex;
+ align-items: center;
+}
+
+.header-tools {
+ gap: 16px;
+}
+
+.global-search {
+ width: 260px;
+}
+
+.global-search :deep(.el-input__wrapper) {
+ background: #F5F6F7;
+ border-radius: 10px;
+ box-shadow: inset 0 0 0 1px #E8EAED;
+}
+
+.notification-button {
+ position: relative;
+ display: grid;
+ width: 38px;
+ height: 38px;
+ color: var(--color-text-secondary);
+ cursor: pointer;
+ background: #FFFFFF;
+ border: 1px solid var(--color-border-light);
+ border-radius: 10px;
+ place-items: center;
+}
+
+.notification-dot {
+ position: absolute;
+ top: 8px;
+ right: 8px;
+ width: 7px;
+ height: 7px;
+ background: var(--admin-primary);
+ border: 2px solid #FFFFFF;
+ border-radius: 50%;
+}
+
+.user-info {
+ gap: 9px;
+ cursor: pointer;
+}
+
+.user-info .el-avatar {
+ color: #FFFFFF;
+ background: var(--admin-primary);
+}
+
+.user-copy {
+ display: flex;
+ flex-direction: column;
+}
+
+.user-copy strong {
+ font-size: 13px;
+}
+
+.user-copy small {
+ margin-top: 2px;
+ color: var(--color-text-tertiary);
+ font-size: 11px;
+}
+
+.main-content {
+ padding: 0;
+ overflow: auto;
+ background: var(--admin-page-bg);
+}
+
+@media (max-width: 1000px) {
+ .sidebar {
+ width: 72px !important;
+ }
+
+ .brand-copy,
+ .collapse-control span,
+ .sidebar-menu :deep(.el-menu-item-group__title) {
+ display: none;
+ }
+
+ .brand {
+ justify-content: center;
+ }
+}
+
+@media (max-width: 760px) {
+ .header {
+ padding: 0 14px;
+ }
+
+ .page-context,
+ .global-search,
+ .user-copy {
+ display: none;
+ }
+}
+
diff --git a/admin-web/src/theme/index.css b/admin-web/src/theme/index.css
index 510632b..5b35439 100644
--- a/admin-web/src/theme/index.css
+++ b/admin-web/src/theme/index.css
@@ -1,6 +1,19 @@
:root {
- --color-primary: #E96B6B;
- --color-primary-hover: #D95F5F;
+ --admin-sidebar-bg: #172733;
+ --admin-sidebar-surface: #213542;
+ --admin-sidebar-text: #D9E1E6;
+ --admin-sidebar-muted: #8FA1AC;
+ --admin-page-bg: #F7F7F5;
+ --admin-primary: #EE666B;
+ --admin-primary-hover: #D9545A;
+ --admin-card-radius: 14px;
+ --admin-header-bg: #FFFFFF;
+ --admin-soft-pink: #FFF0F1;
+ --admin-soft-blue: #EEF5FF;
+ --admin-soft-green: #EDF8F1;
+ --admin-soft-purple: #F4EFFF;
+ --color-primary: var(--admin-primary);
+ --color-primary-hover: var(--admin-primary-hover);
--color-primary-light: #FFF3F2;
--color-primary-lighter: #FFF8F7;
--color-success: #2E9B68;
@@ -12,13 +25,13 @@
--color-text-tertiary: #888888;
--color-bg-primary: #FFFFFF;
--color-bg-secondary: #FFF8F7;
- --color-bg-page: #FAFAF8;
+ --color-bg-page: var(--admin-page-bg);
--color-border: #E8E5E2;
--color-border-light: #F0EDE9;
--sidebar-width: 220px;
--header-height: 64px;
--border-radius-sm: 8px;
- --border-radius-md: 12px;
+ --border-radius-md: var(--admin-card-radius);
--border-radius-lg: 16px;
--shadow-card: 0 8px 24px rgba(75, 55, 46, 0.06);
--el-color-primary: var(--color-primary);
@@ -34,6 +47,13 @@ html, body, #app {
background-color: var(--color-bg-page);
}
body { min-width: 1100px; }
+button:focus-visible,
+a:focus-visible,
+input:focus-visible,
+[tabindex]:focus-visible {
+ outline: 3px solid rgba(238, 102, 107, 0.32);
+ outline-offset: 2px;
+}
.el-button--primary {
--el-button-bg-color: var(--color-primary);
--el-button-border-color: var(--color-primary);
@@ -45,7 +65,7 @@ body { min-width: 1100px; }
border-color: var(--color-primary);
}
.el-card { --el-card-border-radius: var(--border-radius-md); --el-card-border-color: var(--color-border-light); }
-.el-table { --el-table-header-bg-color: #FCFAF8; --el-table-border-color: var(--color-border-light); }
+.el-table { --el-table-header-bg-color: #FBFAF8; --el-table-border-color: var(--color-border-light); }
.el-tag { border-radius: 999px; }
.page-container { padding: 24px; }
.page-shell { max-width: 1440px; margin: 0 auto; }
@@ -64,4 +84,4 @@ body { min-width: 1100px; }
.mb-lg { margin-bottom: 24px; }
.flex-center { display: flex; align-items: center; justify-content: center; }
@media (max-width: 1200px) { body { min-width: 0; } }
-@media (max-width: 900px) { .page-container { padding: 16px; } .page-heading { align-items: flex-start; flex-direction: column; } }
\ No newline at end of file
+@media (max-width: 900px) { .page-container { padding: 16px; } .page-heading { align-items: flex-start; flex-direction: column; } }
diff --git a/ui-tests/admin-redesign-contracts.mjs b/ui-tests/admin-redesign-contracts.mjs
new file mode 100644
index 0000000..414c139
--- /dev/null
+++ b/ui-tests/admin-redesign-contracts.mjs
@@ -0,0 +1,32 @@
+import assert from 'node:assert/strict'
+import { readFileSync } from 'node:fs'
+import { resolve } from 'node:path'
+
+const root = resolve(import.meta.dirname, '..')
+const read = (file) => readFileSync(resolve(root, file), 'utf8')
+const theme = read('admin-web/src/theme/index.css')
+const layout = read('admin-web/src/layouts/MainLayout.vue')
+
+for (const token of ['--admin-sidebar-bg', '--admin-page-bg', '--admin-primary', '--admin-card-radius']) {
+ assert.ok(theme.includes(token), `缺少管理端令牌 ${token}`)
+}
+
+for (const group of [
+ '工作台',
+ '用户与资料审核',
+ '匹配与活动服务',
+ '公益咨询与转介',
+ '服务站、预约与专项服务',
+ 'Banner 与内容运营',
+ '举报、申诉与审计',
+ '组织、人员与权限',
+ '数据统计',
+]) {
+ assert.ok(layout.includes(group), `缺少菜单组 ${group}`)
+}
+
+assert.ok(layout.includes('/brand/logo-mark.png'), '管理端未使用正式品牌标志')
+assert.ok(layout.includes('globalSearch'), '管理端顶部搜索没有可操作状态')
+assert.ok(layout.includes('showNotifications'), '管理端通知入口没有可操作反馈')
+
+console.log('Admin redesign contracts passed')