# UI 设计规范 > 适用范围:基于 Vue 3 + Element Plus 2.x 的后台管理系统 Web 页面 > **本文档为唯一设计规范**:色彩、间距、布局、组件样式、全局 CSS 均定义于本文件(§6 分模块说明 + §8.9 完整 CSS)。项目实施时从本文档复制代码即可,不依赖其他设计资产文件。 --- ## 目录 1. [页面整体风格](#1-页面整体风格) 2. [布局标准](#2-布局标准) 3. [字体标准](#3-字体标准) 4. [颜色规范](#4-颜色规范) 5. [图标使用规范](#5-图标使用规范) 6. [组件与页面标准样式](#6-组件与页面标准样式) 7. [交互原则](#7-交互原则) 8. [附录:复刻指南](#8-附录复刻指南) --- ## 1. 页面整体风格 ### 1.1 设计定位 本规范定义一套**浅色、克制、数据导向**的后台界面风格。视觉语言强调可读性与操作效率,避免装饰性元素喧宾夺主。 | 维度 | 规范 | |------|------| | 整体调性 | 专业、冷静、工具感;类似现代 SaaS 控制台 | | 色彩策略 | 大面积中性灰白底 + 青绿品牌色点缀 | | 层次表达 | 靠**字号阶梯**与**灰度阶梯**区分,不靠重阴影 | | 容器形态 | 白底细边框卡片(`1px` + `8px` 圆角),无浮起阴影 | | 信息密度 | 列表/表格偏紧凑(`size="small"`),表单区留白充分 | | 技术底座 | Element Plus 组件 + 全局 CSS 变量覆盖 | ### 1.2 核心设计原则 | 原则 | 说明 | |------|------| | **浅色优先** | 页面背景 `#f4f4f5`,内容区白卡片;禁止深色全屏侧栏 | | **克制用色** | 主色青绿仅用于品牌、主按钮、导航激活态、关键正向状态 | | **细边框卡片** | 白底 + `1px` 浅灰边框 + `8px` 圆角,禁止大面积 box-shadow | | **8px 间距节奏** | 所有间距使用间距 Token(4px 仅用于极紧凑场景) | | **层次靠字号与灰度** | 标题 `#18181b`,正文 `#3f3f46`,辅助 `#71717a` | | **组件复用优先** | 使用 `PageHeader`、`AppCard`、`StatCard` 等标准壳层,禁止页面内自建容器体系 | | **国际化友好** | 文案走 i18n;布局预留中英文长度差异(按钮组 `flex-wrap`) | ### 1.3 视觉示意 ``` ┌──────────────┬────────────────────────────────────┐ │ Sidebar │ Topbar(52px,白底,底部分割线) │ │ 240px ├────────────────────────────────────┤ │ 半透明浅灰 │ Main Content(padding 24px) │ │ 品牌区+导航 │ .page(max-width 1400px) │ │ │ PageHeader → StatGrid → AppCard │ └──────────────┴────────────────────────────────────┘ ``` 登录页为独立全屏居中布局,无侧栏与顶栏。 --- ## 2. 布局标准 ### 2.1 栅格系统 #### PC 端(≥1025px) | 项目 | 规范 | |------|------| | 基准栅格 | **12 列**逻辑栅格(内容区 `max-width: 1400px` 内划分) | | 列间距(gutter) | `16px`(`--space-4`) | | 页面水平边距 | 主内容区内边距 `24px`(`--space-6`) | | 典型列分配 | 统计卡 4×3 列;双栏内容 `1.4fr : 1fr` 或 `1fr : 1fr`;三栏等分 `1fr × 3` | 常用网格类: | 类名 | PC 列数 | 说明 | |------|---------|------| | `.stat-grid` | 4 列 | 概览指标卡 | | `.content-grid--2` | 2 列等宽 | 并列卡片 | | `.content-grid--2-1` | 2 列(约 7:5) | 主内容 + 侧信息 | | `.content-grid--3` | 3 列等宽 | 三列面板(按需扩展) | #### 平板(641px – 1024px) | 项目 | 规范 | |------|------| | 有效栅格 | **8 列**逻辑栅格 | | `.stat-grid` | 2 列 | | `.content-grid--*` | 全部折叠为 **1 列** | | 主内容 padding | 保持 `24px` | #### 移动端(≤640px) | 项目 | 规范 | |------|------| | 有效栅格 | **4 列**逻辑栅格 | | `.stat-grid` | 1 列 | | `.page-header` | 纵向堆叠(标题在上,操作在下) | | 主内容 padding | `16px`(`--space-4`) | | 底部 Tab(若启用) | 固定底部,高度 `56px`,图标 `20px` + 文字 `10px` | > 默认以 PC 为主、移动端通过响应式折叠适配;若启用底部 Tab 导航,须使用本节尺寸,不得自定义高度。 ### 2.2 间距体系(8px 基准) **硬性规则:页面级、组件级外边距与内边距必须使用间距 Token,禁止出现 5px、7px、10px、15px 等非体系数值。** | Token | 值 | 是否 8 的倍数 | 典型用途 | |-------|-----|---------------|----------| | `--space-1` | 4px | ½×8,仅紧凑场景 | 标题与副标题间距、行内 code 水平 padding | | `--space-2` | 8px | ✓ | 按钮组 gap、导航项内边距、表格单元格垂直 padding | | `--space-3` | 12px | — | 列表项间距、卡片底栏提示 | | `--space-4` | 16px | ✓ | 卡片间距、工具栏下边距、移动端页面 padding | | `--space-5` | 20px | — | 卡片水平内边距、统计卡内边距 | | `--space-6` | 24px | ✓ | 页头下边距、主内容 padding、区块分隔 | | `--space-8` | 32px | ✓ | 登录卡片内边距、大区块分隔 | **垂直节奏(页面级)** - `.page-header` 下方:`24px` - `.stat-grid`、`.content-grid` 下方:`24px` - 相邻 `.app-card` 间距:`16px`;页面最后一个卡片无底边距 **已知例外(保留,勿改)** | 位置 | 值 | 原因 | |------|-----|------| | `.form-hint` 的 `margin-top` | `6px` | 紧贴表单项控件底部,视觉上等同 8px 体系 | `--primary-hover` 已定义但未写独立 CSS 规则,主按钮 hover 由 Element Plus 通过 `--el-color-primary-dark-2` 自动处理。 ### 2.3 布局组件尺寸 | 组件 | 尺寸 | 其他规则 | |------|------|----------| | **侧栏** `.sidebar` | 宽 `240px`(`15rem`) | 右边框 `1px`;背景半透明 + `backdrop-filter: blur(8px)` | | **侧栏品牌区** `.sidebar-brand` | 高随内容;padding `20px 16px` | 品牌图标 `32×32px` | | **导航项** `.nav-item` | 高随内容;padding `8px 12px`;margin 左右 `8px` | 圆角 `8px`;图标 `16px` | | **导航分组标签** `.nav-group-label` | padding `16px 16px 8px` | 字号 `12px`,全大写 | | **顶栏** `.topbar` | 高 **`52px`** | 左右 padding `24px`;白底 + 底边框 | | **主内容** `.main-content` | flex 填满剩余高度 | padding `24px`(移动端 `16px`);纵向滚动 | | **页面容器** `.page` | max-width **`1400px`** | 无水平居中强制(随主内容左对齐) | | **底部 Tab**(预留) | 高 **`56px`** | 图标 `20px`,标签 `10px`,最多 5 项 | | **登录卡片** `.login-card` | max-width 外层 `400px`;内边距 `32px` | 品牌图标 `48×48px`,圆角 `12px` | ### 2.4 断点 | 断点 | 媒体查询 | 主要变化 | |------|----------|----------| | 平板 | `max-width: 1024px` | 统计 2 列;内容网格单列 | | 手机 | `max-width: 640px` | 统计 1 列;页头纵向;主内容 16px padding | ### 2.5 布局结构(ASCII) ``` PC (≥1025px) ┌─ sidebar 240px ─┬──────────── main-area ────────────────────┐ │ [brand 32px] │ topbar 52px │ │ [nav × N] ├───────────────────────────────────────────┤ │ │ padding 24px │ │ │ ┌─ page max 1400px ─────────────────┐ │ │ │ │ page-header │ │ │ │ │ stat-grid (4 col, gap 16) │ │ │ │ │ app-card │ │ │ │ └────────────────────────────────────┘ │ └─────────────────┴───────────────────────────────────────────┘ Mobile (≤640px) ┌──────────────── main-area 100% ────────────────┐ │ topbar 52px │ ├────────────────────────────────────────────────┤ │ padding 16px │ │ page-header (column) │ │ stat-grid (1 col) │ │ app-card │ ├────────────────────────────────────────────────┤ │ [optional bottom-tab 56px] │ └────────────────────────────────────────────────┘ ``` --- ## 3. 字体标准 ### 3.1 字体族 | 场景 | 字体栈 | |------|--------| | **中文界面** | `"PingFang SC"`, `"Microsoft YaHei"`, `"Noto Sans SC"`, `"Helvetica Neue"`, `Arial`, `sans-serif` | | **英文界面** | `ui-sans-serif`, `system-ui`, `-apple-system`, `"Segoe UI"`, `Roboto`, `"Helvetica Neue"`, `Arial`, `sans-serif` | | **合并栈(推荐默认)** | `ui-sans-serif`, `system-ui`, `-apple-system`, "Segoe UI", Roboto, "Helvetica Neue", Arial, "PingFang SC", "Microsoft YaHei", "Noto Sans SC", sans-serif` | | **等宽(代码/文件名)** | `ui-monospace`, `SFMono-Regular`, `Menlo`, `Monaco`, `Consolas`, monospace` | > 中英文共用同一 sans 栈:西文优先 system 字体,CJK 由系统自动 fallback 至苹方/微软雅黑。禁止为中文单独引入 Web Font,保持加载性能。 ### 3.2 字号阶梯 根元素 `font-size: 14px`(`1rem = 14px`)。所有字号使用 `rem` 相对根字号。 | 层级 | 类名 / 场景 | 字号 | 字重 | 行高 | 颜色 | |------|-------------|------|------|------|------| | 页面标题 | `.page-title` | `1.5rem`(21px) | 600 | 默认 | `--foreground` | | 登录品牌标题 | `.page-title`(登录页) | 同上 | 600 | 默认 | `--foreground` | | 卡片标题 | `.app-card__title` | `0.9375rem`(13px) | 600 | 默认 | `--foreground` | | 品牌名 | `.brand-name` | `1.125rem`(15.75px) | 600 | 默认 | `--foreground` | | 统计数值 | `.stat-card__value` | `1.75rem`(24.5px) | 600 | 默认 | `--foreground` | | 正文 / 导航 / 顶栏 | `.nav-item`, `.topbar-title`, 表格正文 | `0.875rem`(12.25px) | 400–500 | 1.4(表格) | `--el-text-color-regular` 或 `--muted-foreground` | | 描述 / 标签说明 | `.page-desc`, `.stat-card__label` | `0.875rem` | 400 | 默认 | `--muted-foreground` | | 表头 / 辅助 | 表头 `th`, `.form-hint`, `.nav-group-label` | `0.75rem`(10.5px) | 600(表头)/ 400 | 默认 | `--muted-foreground` | | 极小说明 | `.form-hint`, `code.inline-code` | `0.75rem` | 400 | 默认 | `--muted-foreground` | | 等宽数据 | `.kv-block code`, `.meta-list` | `0.8125rem` | 400 | 默认 | 继承 | ### 3.3 字重与样式 | 样式 | 使用场景 | 禁止场景 | |------|----------|----------| | **600(Semibold)** | 页面标题、卡片标题、表头、导航激活态、分组标题 `.section-title` | 大段正文 | | **500(Medium)** | 导航默认态、顶栏次要文字 | — | | **400(Regular)** | 正文、描述、表单标签 | — | | **加粗 ``** | 行内强调少量关键词 | 整段文字 | | **斜体 ``** | 极少使用;仅引用或术语首次出现 | 按钮、导航、表头 | | **`letter-spacing: -0.02em`** | 仅 `.page-title` | 其他文字 | | **`text-transform: uppercase`** | 仅 `.nav-group-label` | 其他文字 | ### 3.4 Element Plus 表单标签 | 场景 | `label-width` | |------|---------------| | 简单 CRUD 对话框 | `100px` | | 字段名较长 | `110px` | | 系统设置长表单 | `120px` | | 嵌套子表单 | `90px` | | 登录页 | `size="large"`(EP 默认大号控件) | --- ## 4. 颜色规范 ### 4.1 色彩角色一览 ``` 品牌层:primary 系列(操作引导) 中性层:background / card / muted / border(结构) 语义层:success / warning / danger / info(状态) 文本层:foreground / regular / muted-foreground(可读性) ``` ### 4.2 完整色板 #### 品牌色 | Token | 色值 | 用途 | |-------|------|------| | `--primary` | `#0f7a62` | 主按钮、品牌图标底、导航激活文字、链接强调 | | `--primary-hover` | `#0d6b56` | 主按钮 hover | | `--primary-foreground` | `#fafafa` | 主色背景上的文字/图标 | | `--el-color-primary-light-3` | `#4da08c` | EP 组件浅色变体 | | `--el-color-primary-light-5` | `#76b8a8` | — | | `--el-color-primary-light-7` | `#a0d0c4` | — | | `--el-color-primary-light-8` | `#b8dcd2` | — | | `--el-color-primary-light-9` | `#d0e8e1` | 浅色背景提示 | | `--el-color-primary-dark-2` | `#0c624e` | 主色按下/深色态 | #### 中性色 | Token | 色值 | 用途 | |-------|------|------| | `--background` | `#f4f4f5` | 页面底色、登录页背景 | | `--foreground` | `#18181b` | 主标题、强强调正文 | | `--card` | `#ffffff` | 卡片、顶栏、弹窗内容区背景 | | `--card-foreground` | `#18181b` | 卡片上主文字 | | `--muted` | `#f4f4f5` | 表头背景、内嵌块、行内 code 底 | | `--muted-foreground` | `#71717a` | 描述、表头文字、顶栏标题、占位性文字 | | `--border` | `#e4e4e7` | 卡片边框、分割线、顶栏底边 | | `--input` | `#e4e4e7` | 输入框边框(与 border 一致) | #### 侧栏专用 | Token | 色值 | 用途 | |-------|------|------| | `--sidebar` | `rgba(250, 250, 250, 0.85)` | 侧栏背景(半透明) | | `--sidebar-foreground` | `#18181b` | 侧栏主文字 | | `--sidebar-accent` | `#f4f4f5` | 导航 hover / active 背景 | | `--sidebar-border` | `#e4e4e7` | 侧栏右边框 | #### 语义色 | Token / 类名 | 色值 | 用途 | |--------------|------|------| | `--success` | `#047857` | 成功状态(与 EP success 同步) | | `--warning` | `#b45309` | 警告状态 | | `--danger` / `--destructive` | `#dc2626` | 危险、错误、删除 | | `--info` | `#a1a1aa` | 中性、禁用、分类标签 | | `--el-color-success` | `#047857` | `el-tag type="success"` | | `--el-color-warning` | `#b45309` | `el-tag type="warning"` | | `--el-color-danger` | `#dc2626` | `el-tag type="danger"` | | `--el-color-info` | `#a1a1aa` | `el-tag type="info"` | | `.status-good` | `#047857` | 数值/状态优良 | | `.status-warn` | `#b45309` | 数值/状态偏高 | | `.status-bad` | `#dc2626` | 数值/状态很差 | | `.status-muted` | `--muted-foreground` | 无数据/超时/不可用 | | `.meta-list__ok` | `var(--success)` | 列表项就绪 | | `.meta-list__warn` | `var(--warning)` | 列表项缺失/警告 | #### 文本色(Element Plus 对齐) | Token | 色值 | 用途 | |-------|------|------| | `--el-text-color-primary` | `#18181b` | EP 主要文字 | | `--el-text-color-regular` | `#3f3f46` | 表格正文 | | `--el-text-color-secondary` | `#71717a` | EP 次要文字 | ### 4.3 颜色使用规范 | 规则 | 说明 | |------|------| | **禁止硬编码色值** | 新样式必须使用 CSS 变量 | | **主色克制** | 单屏主色面积不超过 15%;除主按钮与激活导航外不大面积铺主色 | | **状态色统一** | 成功/失败/禁用一律用 `el-tag` 语义 type,不自创色块 | | **边框优先于阴影** | 容器分隔用 `1px solid var(--border)`,不用阴影 | | **危险操作** | 破坏性操作用 `type="danger"` + `link`(表格行内)或二次确认弹框 | | **图表** | 遵循 [§6.16](#616-图表规范echarts) 配色与 grid 配置 | ### 4.4 圆角 | Token | 值 | 用途 | |-------|-----|------| | `--radius` | `0.5rem`(8px) | 卡片、按钮、输入框、导航项、内嵌块 | | 登录品牌图标 | `12px` | 仅 `.login-brand-icon`,略大以强化品牌 | --- ## 5. 图标使用规范 ### 5.1 图标体系 | 类型 | 来源 | 说明 | |------|------|------| | **品牌图标** | 项目自有 Logo(自行实现) | 产品标识;仅用于侧栏 `.brand-icon`、登录页 `.login-brand-icon`;**各项目图形不同,本文档不提供参考实现** | | **功能图标** | `@element-plus/icons-vue` | 导航、操作提示;通过 `` 包裹 | | **状态** | 优先 `el-tag` 文字 | 不以图标替代明确状态文案 | ### 5.2 尺寸规范 | 场景 | 尺寸 | |------|------| | 侧栏品牌区 Logo | `20px` | | 登录页品牌 Logo | `24px` | | 侧栏导航图标 | `16px`(``) | | 按钮内图标(若使用) | `14px` | | 底部 Tab(预留) | `20px` | ### 5.3 常见场景图标推荐 图标库统一使用 `@element-plus/icons-vue`(Outlined 线型)。下表为常见导航/功能场景的推荐映射,各项目可按业务替换,但须保持风格一致。 | 场景 | 推荐图标 | 含义 | |------|----------|------| | 首页 / 仪表盘 | `Odometer`、`HomeFilled` | 总览、监控 | | 列表 / 规则 / 配置项 | `List`、`Menu` | 条目化管理 | | 连接 / 网络 | `Connection`、`Share` | 链路、拓扑 | | 外部关联 / 链接 | `Link` | 外部数据源 | | 切换 / 路由 / 策略 | `Switch` | 分流、调度 | | 日志 / 文档 | `Document`、`Tickets` | 记录、流水 | | 用户 / 权限 | `User`、`Lock` | 身份、安全 | | 系统设置 | `Setting` | 全局配置 | | 新建 | `Plus` | 创建资源 | | 编辑 | `Edit` | 修改 | | 删除 | `Delete` | 移除(配合 danger 按钮,不单用图标) | | 刷新 | `Refresh` | 重新加载 | | 导入 / 导出 | `Upload`、`Download` | 数据交换 | ### 5.4 使用原则 | 原则 | 说明 | |------|------| | **导航必带图标** | 侧栏每项左侧固定 `16px` 图标,增强扫读性 | | **颜色跟随文字** | 图标 `currentColor`,不单独设色 | | **禁止混用风格** | 统一 Outlined 线型图标(Element Plus Icons) | | **操作按钮** | 主操作优先文字按钮;图标-only 按钮需 `aria-label` | | **品牌图标不外借** | Logo 仅用于品牌区,不用于普通操作按钮 | --- ## 6. 组件与页面标准样式 > 本节 6.1–6.14 为**分模块说明**(便于查阅);**合并后的完整全局 CSS 见 [§8.9](#89-完整全局样式一键复制)**。两处内容须保持一致。 ### 6.0 技术栈与引入 | 项目 | 要求 | |------|------| | Vue | `^3.4` | | Element Plus | `^2.4`(2.x 均可,须使用 CSS 变量主题) | | 图标 | `@element-plus/icons-vue` | | 图表(可选) | `echarts` `^5` | **`main.ts` 引入顺序(顺序不可颠倒):** ```ts import { createApp } from 'vue' import ElementPlus from 'element-plus' import 'element-plus/dist/index.css' // ① 先 Element Plus 基础样式 import './styles/global.css' // ② 再将本文档 §8.9 复制为全局样式文件后引入 const app = createApp(App) app.use(ElementPlus) // ③ 全量注册(或按需,但变量须生效) app.mount('#app') ``` **依赖说明:** - 全局样式**仅来源于本文档 §8.9**,文件名与存放路径由项目自定(如 `global.css`、`app.css`)。 - 按钮、输入框、弹框、Tabs 等**无需逐组件写 CSS**,通过 `:root` 中的 `--el-*` 变量与品牌色对齐。 - 表格、卡片容器、布局壳层使用本文档自定义 class,须与 §8.1 壳层 DOM 结构配合。 ### 6.1 设计 Token 与基础重置 ```css :root { --background: #f4f4f5; --foreground: #18181b; --card: #ffffff; --card-foreground: #18181b; --primary: #0f7a62; --primary-hover: #0d6b56; --primary-foreground: #fafafa; --muted: #f4f4f5; --muted-foreground: #71717a; --border: #e4e4e7; --input: #e4e4e7; --destructive: #dc2626; --success: #047857; --warning: #b45309; --danger: #dc2626; --info: #a1a1aa; --sidebar: rgba(250, 250, 250, 0.85); --sidebar-foreground: #18181b; --sidebar-accent: #f4f4f5; --sidebar-border: #e4e4e7; --sidebar-width: 15rem; --radius: 0.5rem; --space-1: 4px; --space-2: 8px; --space-3: 12px; --space-4: 16px; --space-5: 20px; --space-6: 24px; --space-8: 32px; --el-color-primary: #0f7a62; --el-color-primary-light-3: #4da08c; --el-color-primary-light-5: #76b8a8; --el-color-primary-light-7: #a0d0c4; --el-color-primary-light-8: #b8dcd2; --el-color-primary-light-9: #d0e8e1; --el-color-primary-dark-2: #0c624e; --el-color-success: #047857; --el-color-warning: #b45309; --el-color-danger: #dc2626; --el-color-info: #a1a1aa; --el-border-radius-base: 0.5rem; --el-border-color: #e4e4e7; --el-bg-color-page: #f4f4f5; --el-text-color-primary: #18181b; --el-text-color-regular: #3f3f46; --el-text-color-secondary: #71717a; --el-font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "PingFang SC", "Microsoft YaHei", "Noto Sans SC", sans-serif; } * { box-sizing: border-box; } html, body, #app { margin: 0; padding: 0; height: 100%; font-size: 14px; font-family: var(--el-font-family); background: var(--background); color: var(--foreground); } ``` ### 6.2 布局壳层(侧栏 / 顶栏 / 主内容) ```css .layout { display: flex; height: 100vh; } .sidebar { width: var(--sidebar-width); flex-shrink: 0; background: var(--sidebar); border-right: 1px solid var(--sidebar-border); backdrop-filter: blur(8px); display: flex; flex-direction: column; overflow-y: auto; } .sidebar-brand { display: flex; align-items: center; gap: var(--space-3); padding: var(--space-5) var(--space-4); } .brand-icon { width: 32px; height: 32px; border-radius: var(--radius); background: var(--primary); color: var(--primary-foreground); display: flex; align-items: center; justify-content: center; } .brand-name { font-size: 1.125rem; font-weight: 600; } .nav-group-label { font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.04em; color: var(--muted-foreground); padding: var(--space-4) var(--space-4) var(--space-2); } .nav-item { display: flex; align-items: center; gap: var(--space-2); padding: var(--space-2) var(--space-3); margin: 0 var(--space-2); border-radius: var(--radius); color: var(--muted-foreground); text-decoration: none; font-size: 0.875rem; font-weight: 500; } .nav-item:hover { background: var(--sidebar-accent); } .nav-item.active { background: var(--sidebar-accent); color: var(--primary); font-weight: 600; } .main-area { flex: 1; display: flex; flex-direction: column; min-width: 0; } .topbar { height: 52px; display: flex; align-items: center; justify-content: space-between; padding: 0 var(--space-6); background: var(--card); border-bottom: 1px solid var(--border); } .topbar-title { font-size: 0.875rem; color: var(--muted-foreground); } .main-content { flex: 1; overflow-y: auto; padding: var(--space-6); } ``` ### 6.3 页面头与网格 ```css .page { max-width: 1400px; } .page-header { display: flex; align-items: flex-start; justify-content: space-between; margin-bottom: var(--space-6); gap: var(--space-4); } .page-actions { display: flex; align-items: center; gap: var(--space-2); flex-shrink: 0; flex-wrap: wrap; } .page-title { font-size: 1.5rem; font-weight: 600; letter-spacing: -0.02em; margin: 0 0 var(--space-1); } .page-desc { font-size: 0.875rem; color: var(--muted-foreground); margin: 0; } .stat-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--space-4); margin-bottom: var(--space-6); } .stat-card { background: var(--card); border: 1px solid var(--border); border-radius: var(--radius); padding: var(--space-5); } .stat-card__label { font-size: 0.875rem; color: var(--muted-foreground); margin-bottom: var(--space-2); } .stat-card__value { font-size: 1.75rem; font-weight: 600; } .content-grid { display: grid; gap: var(--space-4); margin-bottom: var(--space-6); } .content-grid--2-1 { grid-template-columns: 1.4fr 1fr; } .content-grid--2 { grid-template-columns: 1fr 1fr; } .content-grid--3 { grid-template-columns: repeat(3, 1fr); } ``` ### 6.4 卡片与工具栏 ```css .app-card { background: var(--card); border: 1px solid var(--border); border-radius: var(--radius); margin-bottom: var(--space-4); } .page > .app-card:last-child, .page > .content-grid:last-child { margin-bottom: 0; } .app-card__header { padding: var(--space-4) var(--space-5); border-bottom: 1px solid var(--border); } .app-card__title { font-size: 0.9375rem; font-weight: 600; margin: 0; } .app-card__body--padded { padding: var(--space-5); } .card-toolbar { display: flex; align-items: center; gap: var(--space-2); padding: var(--space-4) var(--space-5) 0; margin-bottom: var(--space-4); flex-wrap: wrap; } .card-toolbar--desc { align-items: flex-start; } .card-pager { padding: var(--space-4) var(--space-5); display: flex; justify-content: flex-end; } .card-footer-hint { padding: 0 var(--space-5) var(--space-3); margin: 0; } .card-footer-hint + .card-pager { padding-top: var(--space-3); } .card-inset-block { margin: 0 var(--space-5) var(--space-4); padding: var(--space-3); background: var(--muted); border-radius: var(--radius); font-size: 0.875rem; } .tabs-bar { padding: 0 var(--space-5); border-bottom: 1px solid var(--border); } .tabs-bar .el-tabs__header { margin-bottom: 0; } .page-toolbar { display: flex; align-items: center; gap: var(--space-2); margin-bottom: var(--space-4); flex-wrap: wrap; } .inline-actions { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; } .card-header-row { display: flex; align-items: center; justify-content: space-between; gap: var(--space-4); } ``` ### 6.5 表单、间距工具类 ```css .form-hint { margin-top: 6px; font-size: 0.75rem; color: var(--muted-foreground); } .form-hint--flush { margin-top: 0; } .u-mt-3 { margin-top: var(--space-3); } .u-mt-4 { margin-top: var(--space-4); } .u-mb-3 { margin-bottom: var(--space-3); } .u-mb-4 { margin-bottom: var(--space-4); } .u-mb-6 { margin-bottom: var(--space-6); } .text-center { text-align: center; } .w-full { width: 100%; } code.inline-code { font-size: 0.75rem; padding: 0 var(--space-1); background: var(--muted); border-radius: var(--radius); } .section-title { font-size: 0.875rem; font-weight: 600; margin-bottom: var(--space-4); } .section-title:not(:first-child) { margin-top: var(--space-6); } .el-form .el-select { width: 100%; } ``` ### 6.6 登录页 ```css .login-page { min-height: 100vh; display: flex; align-items: center; justify-content: center; background: var(--background); padding: var(--space-6); } .login-wrap { max-width: 400px; width: 100%; text-align: center; } .login-brand-icon { width: 48px; height: 48px; border-radius: 12px; background: var(--primary); color: var(--primary-foreground); display: inline-flex; align-items: center; justify-content: center; margin-bottom: var(--space-4); } .login-card { background: var(--card); border: 1px solid var(--border); border-radius: var(--radius); padding: var(--space-8); margin-top: var(--space-6); text-align: left; } ``` ### 6.7 表格 ```css .app-card .el-table { --el-table-header-bg-color: var(--muted); font-size: 0.875rem; } .app-card .el-table--small .el-table__cell { padding: var(--space-2) var(--space-3); } .app-card .el-table--small th.el-table__cell { font-size: 0.75rem; font-weight: 600; color: var(--muted-foreground); } .app-card .el-table--small td.el-table__cell { color: var(--el-text-color-regular); } .app-card .el-table--small .el-table__row { height: 40px; } .app-card .el-table .el-button.is-link { padding: 0; height: auto; } .app-card .el-table .cell { line-height: 1.4; } .table-scroll--sm { max-height: 320px; } .table-scroll--md { max-height: 360px; } .table-scroll--lg { max-height: 480px; } .card-pager .el-pagination { font-size: 0.875rem; } ``` **表格列宽约定** | 列类型 | 宽度 | |--------|------| | 时间 | `168px` | | 状态 / 布尔 | `80–100px` | | 操作 | `120–160px`,`fixed="right"` | | 主文本 | `min-width`,不设固定宽 | ### 6.8 工具栏控件宽度 ```css .toolbar-select { width: 120px; } .toolbar-select--country { width: 160px; } .toolbar-select--wide { width: 280px; } .toolbar-input { width: 200px; } .toolbar-input--wide { width: 260px; } .toolbar-input--grow { flex: 1; min-width: 200px; } .table-cell-select { width: 100%; min-width: 200px; } .card-toolbar .toolbar-input--grow { flex: 1; min-width: 200px; } ``` ### 6.9 按钮规范(Element Plus 约定) | 场景 | 写法 | |------|------| | 页面主操作 | `` | | 页头次要操作 | 默认按钮(无 type) | | 表格行内操作 | `` | | 表格行内删除 | `` | | 顶栏退出 | `` | | 登录提交 | `` | | 加载中 | `:loading="true"`,禁止重复点击 | ### 6.10 弹框与抽屉 | 类型 | 宽度 | 属性 | |------|------|------| | 简单 CRUD | `480–520px` | `destroy-on-close` | | 导入/中等表单 | `560–640px` | 同上 | | 复杂列表弹窗 | `720px` | 同上 | | 二级编辑 | 抽屉 `50%` | — | **底部按钮顺序**:取消(默认)在左,确认(`type="primary"`)在右。 ### 6.11 标签(Tag) 统一 `size="small"`: | 语义 | type | |------|------| | 启用 / 成功 / 存活 | `success` | | 禁用 / 中性 | `info` | | 失败 / 错误 | `danger` | | 警告 / 未就绪 | `warning` | | 可选项高亮 | `primary` | ### 6.12 状态色与辅助块 ```css .status-good { color: #047857; } .status-warn { color: #b45309; } .status-bad { color: #dc2626; } .status-muted { color: var(--muted-foreground); } .kv-block p { margin: 0 0 var(--space-2); } .kv-block p:last-child { margin-bottom: 0; } .kv-block .label { display: inline-block; width: 5rem; color: var(--muted-foreground); } .kv-block code { font-size: 0.8125rem; } .cell-stack { display: flex; flex-direction: column; gap: var(--space-2); width: 100%; } .meta-list { margin: 0; padding: 0; list-style: none; font-size: 0.8125rem; color: var(--muted-foreground); } .meta-list li { display: flex; justify-content: space-between; gap: var(--space-3); padding: var(--space-1) 0; } .meta-list__mono { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; } .meta-list__ok { color: var(--el-color-success); } .meta-list__warn { color: var(--el-color-warning); } .chart-box { height: 280px; } ``` ### 6.13 滚动条与响应式 ```css .custom-scrollbar { scrollbar-width: thin; scrollbar-color: #e4e4e7 transparent; } .custom-scrollbar::-webkit-scrollbar { width: 6px; height: 6px; } .custom-scrollbar::-webkit-scrollbar-thumb { background: #e4e4e7; border-radius: 3px; } .custom-scrollbar::-webkit-scrollbar-track { background: transparent; } @media (max-width: 1024px) { .stat-grid { grid-template-columns: repeat(2, 1fr); } .content-grid--2-1, .content-grid--2, .content-grid--3 { grid-template-columns: 1fr; } } @media (max-width: 640px) { .stat-grid { grid-template-columns: 1fr; } .main-content { padding: var(--space-4); } .page-header { flex-direction: column; } } ``` ### 6.14 全局工具类速查 | 类名 | 用途 | |------|------| | `.card-toolbar` | 卡片内筛选行 | | `.card-pager` | 卡片内分页(右对齐) | | `.card-header-row` | 卡片头标题 + 右侧操作 | | `.form-hint` | 表单项下方说明 | | `.section-title` | 长表单分组标题 | | `.page-toolbar` | 页内独立工具条 | | `.tabs-bar` | 卡片内 Tab 容器 | | `.toolbar-select` / `.toolbar-input` | 工具栏控件宽度 | | `.table-scroll--sm/md/lg` | 表格最大高度 | | `.w-full` | 宽度 100% | | `.u-mb-4` / `.u-mt-3` 等 | 标准外边距 | | `.status-good` / `.status-warn` / `.status-bad` | 行内数值/状态语义色 | | `.kv-block` | 键值对结果展示(常配合 `.card-inset-block`) | | `.cell-stack` | 表格单元格内纵向堆叠 | | `.meta-list` | 元数据清单列表 | | `.custom-scrollbar` | 细滚动条(Firefox + WebKit) | ### 6.15 Element Plus 适配说明 以下组件**依靠 EP 默认样式 + `:root` 变量**即可,禁止重复覆盖(除非本节另有说明): | 组件 | 使用约定 | 自定义 CSS | |------|----------|------------| | `el-button` | 主操作 `type="primary"`;行内 `size="small" link` | 无(颜色来自 `--el-color-primary*`) | | `el-input` / `el-select` | 表单内 select 宽度 `100%` | 仅 `.el-form .el-select { width: 100% }` | | `el-dialog` | `destroy-on-close`;宽度见 §6.10 | 无 | | `el-drawer` | 二级编辑 `size="50%"` | 无 | | `el-tabs` | 放在 `.tabs-bar` 内 | 仅 `.tabs-bar .el-tabs__header { margin-bottom: 0 }`;激活色来自 `--el-color-primary` | | `el-pagination` | 放在 `.card-pager` 内 | 仅 `font-size: 0.875rem` | | `el-tag` | 统一 `size="small"` | 语义色来自 `--el-color-success` 等 | | `el-message` / `el-message-box` | 操作反馈 | 无 | | `el-form` | `label-width` 见 §3.4 | 无 | **Tabs 推荐结构:** ```vue
``` **Dialog 推荐结构:** ```vue ``` ### 6.16 图表规范(ECharts) 含折线图的统计页使用 `.chart-box` 容器(高度 `280px`)。图表配置须与品牌色一致: | 配置项 | 值 | |--------|-----| | 容器 class | `chart-box`(`height: 280px`) | | 主系列色 | `#0f7a62`(`var(--primary)`) | | 次系列色 | `#71717a`(`var(--muted-foreground)`) | | 第三系列及以后 | `#4da08c`、`#76b8a8`、`#a0d0c4`(primary-light 阶梯) | | `grid` | `{ left: 40, right: 16, top: 16, bottom: 24 }` | | 坐标轴字号 | `12`(与 `0.857rem` 接近) | | 坐标轴颜色 | `#71717a` | | 分割线 | `#e4e4e7` | | 折线 | `smooth: true`;线宽 `2` | | tooltip | `trigger: 'axis'` | | 图例 | 顶部或底部,字号 `12` | ```ts chart.setOption({ grid: { left: 40, right: 16, top: 16, bottom: 24 }, xAxis: { type: 'category', data: labels, axisLabel: { color: '#71717a', fontSize: 12 }, axisLine: { lineStyle: { color: '#e4e4e7' } }, }, yAxis: { type: 'value', axisLabel: { color: '#71717a', fontSize: 12 }, splitLine: { lineStyle: { color: '#e4e4e7' } }, }, series: [ { name: '主指标', type: 'line', smooth: true, data: a, color: '#0f7a62' }, { name: '次指标', type: 'line', smooth: true, data: b, color: '#71717a' }, ], tooltip: { trigger: 'axis' }, }) ``` --- ## 7. 交互原则 ### 7.1 反馈与状态 | 原则 | 规范 | |------|------| | **操作反馈** | 成功/失败使用消息提示(如 `ElMessage`);破坏性操作须二次确认(如 `ElMessageBox.confirm`) | | **加载态** | 列表 `v-loading`;按钮 `:loading`;全页加载用卡片级 loading | | **空状态** | 表格无数据依赖 EP 默认 empty;需文案时走 i18n | | **禁用态** | 批量任务进行中禁用与之互斥的行内操作 | | **乐观更新** | 不做乐观更新;等 API 成功后再刷新列表 | ### 7.2 导航与层级 | 原则 | 规范 | |------|------| | **路由即导航** | 侧栏链接与当前路由同步,激活态高亮 | | **顶栏标题** | 显示当前一级导航名称,非重复页面 H1 | | **页内标题** | `PageHeader` 的 `title` 为页面 H1,描述可选 | | **退出登录** | 顶栏右侧 `text` 按钮,清除会话后跳转登录页 | ### 7.3 表单与对话框 | 原则 | 规范 | |------|------| | **关闭销毁** | 对话框统一 `destroy-on-close`,避免脏数据残留 | | **提交防重** | 提交按钮 `loading` 期间禁止重复提交 | | **取消不保存** | 取消仅关闭弹层,不触发 API | | **密码字段** | `type="password"` + `show-password` | | **字段说明** | 用 `.form-hint`,不用 placeholder 替代说明 | ### 7.4 列表与表格 | 原则 | 规范 | |------|------| | **主操作位置** | 新建/导入放 `PageHeader#actions`;筛选放 `.card-toolbar` | | **行内操作** | 链接式小按钮,删除放最右 | | **分页** | 放 `.card-pager`,右对齐 | | **刷新** | 提供显式「刷新」按钮;除监控大屏等实时场景外,不依赖隐式自动刷新 | | **固定列** | 操作列 `fixed="right"` | ### 7.5 动效与过渡 | 原则 | 规范 | |------|------| | **克制动效** | 仅使用 EP 默认过渡(对话框、下拉、Message) | | **禁止花哨动画** | 不加自定义 page transition、弹跳、视差 | | **侧栏** | 桌面端固定展开,不用折叠动画(移动端待扩展) | ### 7.6 无障碍与可用性 | 原则 | 规范 | |------|------| | **对比度** | 正文与背景对比度 ≥ 4.5:1 | | **焦点** | 依赖 EP 默认 focus ring,不自行移除 | | **点击目标** | 按钮最小高度遵循 EP(默认 32px,large 40px) | | **错误信息** | 接口错误映射为用户可读的本地化文案(如限流、权限不足) | ### 7.7 国际化 | 原则 | 规范 | |------|------| | **文案** | 所有可见文字走国际化方案(如 i18n),禁止硬编码用户可见字符串 | | **时间** | 显示格式 `YYYY-MM-DD HH:mm:ss`(ISO 替换 `T` 并截断) | | **布局弹性** | 按钮组、工具栏 `flex-wrap: wrap` 适配长文案 | --- ## 8. 附录:复刻指南 按以下顺序实施,可在新项目中复刻本文档定义的全部视觉(Vue 3 + Element Plus 2.x)。 ### 8.1 壳层布局完整模板 **DOM 层级(class 名称不可改):** ``` .layout ├── .sidebar.custom-scrollbar │ ├── .sidebar-brand │ │ ├── .brand-icon → 项目自有 Logo(20px) │ │ └── .brand-name │ ├── .nav-group-label(可选分组) │ └── a.nav-item[.active] × N(含 el-icon 16px) └── .main-area ├── header.topbar │ ├── .topbar-title │ └── 顶栏操作(如 el-button text) └── main.main-content.custom-scrollbar └── router-view → 各页面 .page ``` ```vue ``` ### 8.2 登录页完整模板 ```vue ``` ### 8.3 公共组件完整源码 ```vue ``` ```vue ``` ```vue ``` 品牌 Logo 由各项目自行提供,放入 `.brand-icon`(20px)与 `.login-brand-icon`(24px)容器即可;须使用 `currentColor` 或适配主色底上的 `--primary-foreground`。 ### 8.4 标准页面模板 ```vue ``` ### 8.5 带 Tab 的列表页 ```vue
``` ### 8.6 新页面 Checklist 1. 将 §8.9 复制为项目全局样式文件,按 §6.0 顺序在 `main.ts` 中引入 2. 复制 §8.1 壳层布局、§8.3 公共组件(文件名自定) 3. 页面根节点 `
` 4. `PageHeader`(标题 + 描述 + actions) 5. 主内容 `AppCard`;表单页加 `padded` 6. 列表筛选用 `.card-toolbar`,分页用 `.card-pager` 7. 间距与颜色仅使用 CSS 变量与全局类 8. 在主布局中注册导航项与图标(§5.3) ### 8.7 反模式 | 不要 | 应该 | |------|------| | 深色侧栏 | 浅色半透明侧栏 | | 页面内裸 `el-card` 作主容器 | `AppCard` | | 无标题直接上表格 | 先 `PageHeader` | | 硬编码色值 | CSS 变量 | | 重阴影 | 细边框 | | 每页自定义 `.toolbar` / `.pager` | `.card-toolbar` / `.card-pager` | | 表单卡片不加 `padded` | `AppCard padded` | | `style="margin-top: 12px"` | `.u-mt-3` 等工具类 | | 非 8px 倍数的随意间距 | 间距 Token | | 在 EP 样式之前引入全局样式 | 先 `element-plus/dist/index.css`,再引入 §8.9 全局样式 | ### 8.8 推荐目录结构(示例) ``` styles/global.css ← 由本文档 §8.9 复制生成,路径与文件名自定 components/PageHeader.vue components/StatCard.vue components/AppCard.vue layouts/MainLayout.vue ← 见 §8.1,文件名自定 views/Login.vue ← 见 §8.2,文件名自定 ``` 样式变更时,须同时更新本文档 **§6.1–6.14** 与 **§8.9** 两处 CSS,保持内容一致。 ### 8.9 完整全局样式(一键复制) 以下为**设计规范定义的完整全局 CSS**,直接复制到项目全局样式文件即可(§6.1–6.14 为分模块对照版,内容须与本节一致)。 ```css :root { --background: #f4f4f5; --foreground: #18181b; --card: #ffffff; --card-foreground: #18181b; --primary: #0f7a62; --primary-hover: #0d6b56; --primary-foreground: #fafafa; --muted: #f4f4f5; --muted-foreground: #71717a; --border: #e4e4e7; --input: #e4e4e7; --destructive: #dc2626; --success: #047857; --warning: #b45309; --danger: #dc2626; --info: #a1a1aa; --sidebar: rgba(250, 250, 250, 0.85); --sidebar-foreground: #18181b; --sidebar-accent: #f4f4f5; --sidebar-border: #e4e4e7; --sidebar-width: 15rem; --radius: 0.5rem; --space-1: 4px; --space-2: 8px; --space-3: 12px; --space-4: 16px; --space-5: 20px; --space-6: 24px; --space-8: 32px; --el-color-primary: #0f7a62; --el-color-primary-light-3: #4da08c; --el-color-primary-light-5: #76b8a8; --el-color-primary-light-7: #a0d0c4; --el-color-primary-light-8: #b8dcd2; --el-color-primary-light-9: #d0e8e1; --el-color-primary-dark-2: #0c624e; --el-color-success: #047857; --el-color-warning: #b45309; --el-color-danger: #dc2626; --el-color-info: #a1a1aa; --el-border-radius-base: 0.5rem; --el-border-color: #e4e4e7; --el-bg-color-page: #f4f4f5; --el-text-color-primary: #18181b; --el-text-color-regular: #3f3f46; --el-text-color-secondary: #71717a; --el-font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "PingFang SC", "Microsoft YaHei", "Noto Sans SC", sans-serif; } * { box-sizing: border-box; } html, body, #app { margin: 0; padding: 0; height: 100%; font-size: 14px; font-family: var(--el-font-family); background: var(--background); color: var(--foreground); } .layout { display: flex; height: 100vh; } .sidebar { width: var(--sidebar-width); flex-shrink: 0; background: var(--sidebar); border-right: 1px solid var(--sidebar-border); backdrop-filter: blur(8px); display: flex; flex-direction: column; overflow-y: auto; } .sidebar-brand { display: flex; align-items: center; gap: var(--space-3); padding: var(--space-5) var(--space-4); } .brand-icon { width: 32px; height: 32px; border-radius: var(--radius); background: var(--primary); color: var(--primary-foreground); display: flex; align-items: center; justify-content: center; } .brand-name { font-size: 1.125rem; font-weight: 600; } .nav-group-label { font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.04em; color: var(--muted-foreground); padding: var(--space-4) var(--space-4) var(--space-2); } .nav-item { display: flex; align-items: center; gap: var(--space-2); padding: var(--space-2) var(--space-3); margin: 0 var(--space-2); border-radius: var(--radius); color: var(--muted-foreground); text-decoration: none; font-size: 0.875rem; font-weight: 500; } .nav-item:hover { background: var(--sidebar-accent); } .nav-item.active { background: var(--sidebar-accent); color: var(--primary); font-weight: 600; } .main-area { flex: 1; display: flex; flex-direction: column; min-width: 0; } .topbar { height: 52px; display: flex; align-items: center; justify-content: space-between; padding: 0 var(--space-6); background: var(--card); border-bottom: 1px solid var(--border); } .topbar-title { font-size: 0.875rem; color: var(--muted-foreground); } .main-content { flex: 1; overflow-y: auto; padding: var(--space-6); } .page { max-width: 1400px; } .page-header { display: flex; align-items: flex-start; justify-content: space-between; margin-bottom: var(--space-6); gap: var(--space-4); } .page-actions { display: flex; align-items: center; gap: var(--space-2); flex-shrink: 0; flex-wrap: wrap; } .page-title { font-size: 1.5rem; font-weight: 600; letter-spacing: -0.02em; margin: 0 0 var(--space-1); } .page-desc { font-size: 0.875rem; color: var(--muted-foreground); margin: 0; } .stat-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--space-4); margin-bottom: var(--space-6); } .stat-card { background: var(--card); border: 1px solid var(--border); border-radius: var(--radius); padding: var(--space-5); } .stat-card__label { font-size: 0.875rem; color: var(--muted-foreground); margin-bottom: var(--space-2); } .stat-card__value { font-size: 1.75rem; font-weight: 600; } .app-card { background: var(--card); border: 1px solid var(--border); border-radius: var(--radius); margin-bottom: var(--space-4); } .page > .app-card:last-child, .page > .content-grid:last-child { margin-bottom: 0; } .app-card__header { padding: var(--space-4) var(--space-5); border-bottom: 1px solid var(--border); } .app-card__title { font-size: 0.9375rem; font-weight: 600; margin: 0; } .app-card__body--padded { padding: var(--space-5); } .content-grid { display: grid; gap: var(--space-4); margin-bottom: var(--space-6); } .content-grid--2-1 { grid-template-columns: 1.4fr 1fr; } .content-grid--2 { grid-template-columns: 1fr 1fr; } .content-grid--3 { grid-template-columns: repeat(3, 1fr); } .card-toolbar { display: flex; align-items: center; gap: var(--space-2); padding: var(--space-4) var(--space-5) 0; margin-bottom: var(--space-4); flex-wrap: wrap; } .card-toolbar--desc { align-items: flex-start; } .card-pager { padding: var(--space-4) var(--space-5); display: flex; justify-content: flex-end; } .card-footer-hint { padding: 0 var(--space-5) var(--space-3); margin: 0; } .card-footer-hint + .card-pager { padding-top: var(--space-3); } .card-inset-block { margin: 0 var(--space-5) var(--space-4); padding: var(--space-3); background: var(--muted); border-radius: var(--radius); font-size: 0.875rem; } .tabs-bar { padding: 0 var(--space-5); border-bottom: 1px solid var(--border); } .tabs-bar .el-tabs__header { margin-bottom: 0; } .page-toolbar { display: flex; align-items: center; gap: var(--space-2); margin-bottom: var(--space-4); flex-wrap: wrap; } .inline-actions { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; } .card-header-row { display: flex; align-items: center; justify-content: space-between; gap: var(--space-4); } .form-hint { margin-top: 6px; font-size: 0.75rem; color: var(--muted-foreground); } .form-hint--flush { margin-top: 0; } .u-mt-3 { margin-top: var(--space-3); } .u-mt-4 { margin-top: var(--space-4); } .u-mb-3 { margin-bottom: var(--space-3); } .u-mb-4 { margin-bottom: var(--space-4); } .u-mb-6 { margin-bottom: var(--space-6); } .text-center { text-align: center; } .w-full { width: 100%; } code.inline-code { font-size: 0.75rem; padding: 0 var(--space-1); background: var(--muted); border-radius: var(--radius); } .section-title { font-size: 0.875rem; font-weight: 600; margin-bottom: var(--space-4); } .section-title:not(:first-child) { margin-top: var(--space-6); } .login-page { min-height: 100vh; display: flex; align-items: center; justify-content: center; background: var(--background); padding: var(--space-6); } .login-wrap { max-width: 400px; width: 100%; text-align: center; } .login-brand-icon { width: 48px; height: 48px; border-radius: 12px; background: var(--primary); color: var(--primary-foreground); display: inline-flex; align-items: center; justify-content: center; margin-bottom: var(--space-4); } .login-card { background: var(--card); border: 1px solid var(--border); border-radius: var(--radius); padding: var(--space-8); margin-top: var(--space-6); text-align: left; } .status-good { color: #047857; } .status-warn { color: #b45309; } .status-bad { color: #dc2626; } .status-muted { color: var(--muted-foreground); } .toolbar-select { width: 120px; } .toolbar-select--country { width: 160px; } .toolbar-select--wide { width: 280px; } .toolbar-input { width: 200px; } .toolbar-input--wide { width: 260px; } .toolbar-input--grow { flex: 1; min-width: 200px; } .table-cell-select { width: 100%; min-width: 200px; } .el-form .el-select { width: 100%; } .app-card .el-table { --el-table-header-bg-color: var(--muted); font-size: 0.875rem; } .app-card .el-table--small .el-table__cell { padding: var(--space-2) var(--space-3); } .app-card .el-table--small th.el-table__cell { font-size: 0.75rem; font-weight: 600; color: var(--muted-foreground); } .app-card .el-table--small td.el-table__cell { color: var(--el-text-color-regular); } .app-card .el-table--small .el-table__row { height: 40px; } .app-card .el-table .el-button.is-link { padding: 0; height: auto; } .app-card .el-table .cell { line-height: 1.4; } .table-scroll--sm { max-height: 320px; } .table-scroll--md { max-height: 360px; } .table-scroll--lg { max-height: 480px; } .card-toolbar .toolbar-input--grow { flex: 1; min-width: 200px; } .card-pager .el-pagination { font-size: 0.875rem; } .kv-block p { margin: 0 0 var(--space-2); } .kv-block p:last-child { margin-bottom: 0; } .kv-block .label { display: inline-block; width: 5rem; color: var(--muted-foreground); } .kv-block code { font-size: 0.8125rem; } .cell-stack { display: flex; flex-direction: column; gap: var(--space-2); width: 100%; } .meta-list { margin: 0; padding: 0; list-style: none; font-size: 0.8125rem; color: var(--muted-foreground); } .meta-list li { display: flex; justify-content: space-between; gap: var(--space-3); padding: var(--space-1) 0; } .meta-list__mono { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; } .meta-list__ok { color: var(--el-color-success); } .meta-list__warn { color: var(--el-color-warning); } .chart-box { height: 280px; } .custom-scrollbar { scrollbar-width: thin; scrollbar-color: #e4e4e7 transparent; } .custom-scrollbar::-webkit-scrollbar { width: 6px; height: 6px; } .custom-scrollbar::-webkit-scrollbar-thumb { background: #e4e4e7; border-radius: 3px; } .custom-scrollbar::-webkit-scrollbar-track { background: transparent; } @media (max-width: 1024px) { .stat-grid { grid-template-columns: repeat(2, 1fr); } .content-grid--2-1, .content-grid--2, .content-grid--3 { grid-template-columns: 1fr; } } @media (max-width: 640px) { .stat-grid { grid-template-columns: 1fr; } .main-content { padding: var(--space-4); } .page-header { flex-direction: column; } } ``` --- *浅色后台管理系统 UI 设计规范*