Files
opsxsql/SQL平台评估与优化/sql_platform_optimization.agent.final.md
T
logan 746a000e8a feat: 全面UI优化 — 5套主题系统 + 设计体系 + 菜单重组 + 页面优化
1. 主题系统扩展 (5套主题)
   - ocean-deep(深海蓝) / light-professional(明亮白) / midnight-purple(午夜紫)
   - pine-green(松林绿) / orange-vitality(活力橙)
   - useTheme hook 重写支持5主题 + 向后兼容
   - AppearanceSettings 5主题预览卡片

2. 设计体系 (index.css)
   - 按钮系统: dg-btn-primary/secondary/ghost/danger + 尺寸变体
   - 徽章系统: dg-badge-* (8色)
   - 卡片/表格: dg-card-section, dg-table-row, dg-table-header
   - 状态点: dg-status-dot-* (4色)
   - 动效关键帧: slideInLeft, scaleIn

3. 菜单重组 (menu.ts)
   - 5分组: 概览/数据源/工单/权限/系统
   - 6个图标更新: Code2/FileEdit/ShieldCheck/FolderLock/ScrollText/Timer
   - 新增 permission 权限码字段

4. Layout 侧边栏增强
   - 左侧渐变指示条 + 发光效果
   - hover 微动画 + 图标变色
   - 面包屑导航头部
   - 主题切换循环5主题

5. 核心页面优化
   - TicketsPage: 设计系统类 + 颜色对齐
   - QueriesPage: 去Card依赖 + dg-table/dg-btn统一
   - Breadcrumb组件 + useBreadcrumb hook
2026-05-27 01:59:08 +08:00

167 KiB
Raw Blame History

1. 项目概况与技术架构评估

1.1 项目定位与核心功能

1.1.1 平台定位

SQL-Platform 是一款面向企业内部数据操作的自助查询与数据变更审批流平台,其核心定位是为开发、运维、数据分析等角色提供安全可控的数据库操作入口。平台覆盖 MySQL、PostgreSQL、Redis、MongoDB 四种主流数据库类型,通过工单驱动的审批机制,在操作便捷性与数据安全性之间建立平衡。

该平台在生态位上直接对标 Yearning(Go + Vue 技术栈,侧重 MySQL 审计)与 Archery(Python + Vue 技术栈,支持多数据库),同时吸收了 Bytebase 在工单审批流程设计上的先进理念。相较于竞品,本项目采用 React 19 + Go/Gin 的现代化全栈组合,在前后端解耦程度、UI 可定制性以及开发者体验(DX)方面具备差异化竞争力。

1.1.2 核心功能模块

平台功能围绕"数据操作全生命周期管理"展开,可划分为五大核心域:

功能域 核心能力 对应菜单
数据操作 SQL 自助查询(支持 CodeMirror 6 编辑器)、数据变更提交 查询数据、数据变更
审批工作流 工单创建 → 审批节点流转 → 执行/驳回 → 归档 工单管理
数据源治理 多类型数据库实例注册、连接池管理、Schema 浏览 数据源管理
权限管控 RBAC 角色权限模型、权限组划分、用户生命周期管理 权限组、角色权限、用户管理
审计与合规 操作日志全量记录、审批流程自定义、自动任务调度 审计日志、审批流程、自动任务

1.1.3 对标产品分析

维度 SQL-Platform Yearning Archery Bytebase
后端技术 Go/Gin Go/Beego Python/Django Go
前端技术 React 19 + TS Vue 2 Vue 2 Vue 3
支持数据库 MySQL/PgSQL/Redis/MongoDB MySQL 多数据库 全品类
UI 组件体系 shadcn/ui + Tailwind Element UI Element UI 自研组件
编辑器 CodeMirror 6 ACE Editor ACE Editor 自研 Monaco
代码开源度 全开源 开源核心 全开源 开源核心

相较 Yearning,本项目在多数据库支持广度与前端技术栈现代化程度上具有明显优势;相较 Archery,Go 后端的性能表现与部署便捷性更为突出。当前主要差距集中在工单审批流程的可视化设计与**数据库变更的 Schema 版本管理(VCS 集成)**方面,这两项可作为后续版本的重点演进方向。


1.2 技术栈分析

1.2.1 前后端技术选型评估

后端架构

后端采用 Go 1.26 + Gin v1.10.1 的组合,Gin 作为目前 Go 生态中 Stars 数最高(>80k)的 Web 框架,其高性能路由与极简的 Middleware 设计非常适合 API 密集型企业应用。ORM 层选用 GORM v1.26.0,借助其 AutoMigrate、关联预加载与插件扩展能力,可显著降低数据库操作层的样板代码量。元数据库采用 SQLite(通过 gorm.io/driver/sqlite 驱动),这一设计决策使得平台在部署时零依赖外部数据库,极大降低了私有化部署的门槛——这对于中小团队或对运维复杂度敏感的企业极具吸引力。

认证层采用 JWT HS256 + bcrypt 方案,Token 默认 24h 过期,在安全性与用户体验之间取得合理平衡。RBAC 权限中间件的独立设计体现了安全架构的前置思维,建议后续版本考虑引入 Refresh Token 机制以降低长期会话的泄露风险。

前端架构

前端基于 React 19 + TypeScript,构建工具采用 Vite 7,这一组合代表了 2024-2025 年 React 生态的技术前沿。React 19 的 Server Components 支持(尽管当前项目以 CSR 为主)、Compiler 自动优化以及改进的 Suspense 边界处理,为后续性能演进预留了充足空间。Vite 7 相较于 Webpack 在冷启动速度(提升约 10-20 倍)与 HMR 响应速度上具备代际优势,显著改善开发体验。

UI 层采用 shadcn/ui + Tailwind CSS 4 + Radix UI 的三层架构:Radix UI 提供无样式、无障碍(A11y)完备的底层原语;Tailwind CSS 4 提供原子化样式能力;shadcn/ui 则在此基础上封装了一套设计一致、可高度定制的高阶组件。这种"无头组件 + 原子样式 + 组件封装"的分层模式,是当前前端 UI 架构的最佳实践之一。

代码编辑器选用 CodeMirror 6(@codemirror/lang-sql 语言包),相较于 Monaco Editor,CodeMirror 6 在包体积(Tree-shakeable 后约 100KB vs Monaco 的 1MB+)、移动端适配以及主题定制灵活性上表现更优,非常适合本项目这种以 SQL 编辑为核心但非纯 IDE 场景的应用。

图表层采用 Recharts,作为 React 生态中最成熟的 D3 封装库,其与 React 组件模型的深度整合使得在仪表盘页面中构建数据可视化变得直观高效。

1.2.2 技术栈优势分析

  1. 部署零依赖:SQLite 作为元数据库 + Go 单二进制部署,使得整个平台可以编译为单一可执行文件分发,运维成本极低。
  2. 类型安全贯穿全栈:TypeScript(前端)+ Go(后端)的双重静态类型保障,配合 GORM 的模型定义与前端接口类型的共享(可通过 OpenAPI/Swagger 代码生成进一步强化),可显著降低因类型不匹配导致的运行时错误。
  3. 组件体系可扩展性强:shadcn/ui 的组件代码直接存在于项目源码中(非 npm 黑盒),团队可根据业务需求任意修改组件内部实现而不受上游版本锁定。
  4. 性能基线优秀:Go 的 goroutine 并发模型 + Gin 的高性能路由 + Vite 的快速构建 + React.lazy 代码分割,从后端到前端形成了一条完整的性能优化链路。

1.2.3 潜在技术债务

风险点 影响等级 说明与建议
GORM v1.26.0 版本锁定 中 GORM v2 与 v1 存在 API 不兼容变更,长期停留在 v1 会阻碍安全补丁获取。建议规划迁移至 GORM v2。
SQLite 并发写入瓶颈 中 SQLite 在 WAL 模式下可支持多读单写,但在高并发工单场景下可能成为瓶颈。建议评估是否需要支持 PostgreSQL/MySQL 作为可选元数据库。
React 19 生态适配 低-中 React 19 尚属较新版本,部分第三方库可能存在兼容性问题。建议建立依赖版本锁定策略,并持续跟踪关键依赖的 React 19 适配进度。
JWT 无 Refresh Token 中 24h 固定过期时间导致用户需要频繁重新登录,影响体验。建议引入 Refresh Token + Access Token 双 Token 机制。
缺少 API 文档自动化 低 后端 120+ 路由缺乏自动化文档输出。建议集成 Swagger/OpenAPI 自动生成,降低前后端协作成本。

1.3 前端架构现状评估

1.3.1 页面结构与路由设计

项目当前包含 24+ 页面,覆盖平台五大功能域,页面层级设计遵循"功能域 → 功能页 → 操作面板"的三层信息架构原则:

概览
└── 仪表盘(Dashboard)— 运营指标总览

数据源
└── 数据源管理(Instances)— CRUD + 连接测试

工单管理
├── 数据变更(Tickets/Change)— 变更工单列表 + 审批流
└── 查询数据(Tickets/Query)— 查询工单列表 + SQL 执行

权限管理
├── 权限组(PermissionGroups)— 权限集合管理
├── 角色权限(Roles)— RBAC 角色定义
└── 用户管理(Users)— 用户生命周期管理

系统配置
├── 审计日志(AuditLogs)— 操作记录检索
├── 审批流程(ApprovalFlows)— 审批节点自定义
└── 自动任务(ScheduledTasks)— 定时任务管理

配置平台
└── 系统设置(Settings)— 全局参数配置

路由设计层面,项目已采用 React.lazy 进行代码分割,这是一个重要的性能优化信号——通过将各功能域拆分为独立的异步 Chunk,可显著降低首屏加载时间。建议进一步结合 React Router v6 的 loader 与 action API 实现路由级的数据预取,减少页面切换时的感知延迟。

平台提供 5 种登录页主题(SplitView / CenteredCard / SidePanel / FullImage / MinimalDark),这一设计体现了对部署场景多样性的考虑——不同企业可根据自身品牌调性选择最适合的登录体验。从维护性角度,建议将这 5 种主题抽象为统一的 "LoginLayout" 组件,通过配置对象驱动渲染,减少重复逻辑。

1.3.2 组件化程度分析

项目组件体系可分为三个层级,整体呈现良好的分层架构:

层级 组件数量 代表组件 职责说明
基础组件 50+ Button, Input, Dialog, Table, Dropdown shadcn/ui 封装,平台无关,可复用性最高
业务组件 9 Layout, SqlEditor, SchemaSidebar, EditInstanceDialog 领域特定,封装业务逻辑与交互模式
页面组件 24+ Dashboard, Instances, Tickets, Settings 路由级入口,组合业务组件完成特定功能

值得肯定的方面:

  • 自定义组件的命名遵循 PascalCase + 语义化约定(如 SchemaSidebar、EditInstanceDialog),可读性良好。
  • SqlEditor 组件作为核心交互组件被独立封装,基于 CodeMirror 6 提供了 SQL 语法高亮、关键词补全等能力,是可复用性最高的业务资产之一。
  • Layout 组件的统一设计确保了全站导航结构的一致性。

建议改进的方向:

  • 业务组件层当前仅有 9 个,相对于 24+ 页面的规模略显单薄,存在页面间逻辑重复的可能性。建议对高频交互模式(如表单创建/编辑、列表筛选/分页、审批操作确认等)进行进一步抽象。
  • 建议建立组件文档体系(如 Storybook),记录各组件的 Props 接口、使用示例与视觉规格,降低团队协作成本。

1.3.3 状态管理方案

基于代码架构分析,项目当前的状态管理呈现 "React 内置状态 + 局部上下文" 的轻量级模式。对于 SQL-Platform 这类以表单操作和列表管理为核心的应用,这种状态管理策略是合理且务实的:

  • 服务端状态(工单列表、实例配置、用户数据等):通过 API 调用获取,建议引入 TanStack Query (React Query) 进行封装,可自动处理缓存、去重、重试、乐观更新等复杂逻辑。
  • 客户端状态(UI 展开/折叠、编辑器内容、筛选条件):以 useState / useReducer 管理,复杂度可控。
  • 全局共享状态(用户认证信息、主题偏好、权限节点):当前可能通过 React Context 或 Props Drilling 传递,建议评估是否需要引入轻量级全局状态库(如 Zustand)以避免深层嵌套组件的 Props 传递开销。

1.3.4 性能优化措施

目前已实施的优化措施:

优化手段 实施状态 效果评估
React.lazy 代码分割 已实施 降低首屏 JS 体积,提升 FCP/LCP
Vite 7 快速构建 已实施 开发阶段 HMR < 50ms,构建产出高效
Tailwind CSS 原子样式 已实施 无运行时开销,产物体积小

建议补充的优化方向:

  1. 虚拟列表:工单列表与审计日志在数据量较大时可能出现渲染性能瓶颈,建议对长列表引入 @tanstack/react-virtual 或类似方案。
  2. 编辑器按需加载:CodeMirror 6 及其语言包体积不可忽视,建议确保编辑器组件仅在 SQL 相关路由中被异步加载。
  3. Tree Shaking 审查:定期使用 rollup-plugin-visualizer 或类似工具分析产物体积,剔除未使用的依赖代码。
  4. 图片资源优化:如有图表截图、操作指南配图等静态资源,建议采用 WebP/AVIF 格式并配置懒加载。

1.4 现有 UI 设计体系梳理

1.4.1 CSS 变量体系评估

项目已建立一套以 --dg-* 为前缀的 CSS 自定义属性体系,覆盖了 Dark / Light 两种主题模式以及共享的渐变色系统。这一设计决策体现了良好的工程化思维——通过将颜色值抽象为语义化变量,可实现全站一键换肤并降低维护成本。

Dark 主题色板:

变量名 色值 语义角色 评估
--dg-bg-primary #0d1117 页面主背景 GitHub Dark 同款基底,视觉深邃,适合长时间操作
--dg-bg-secondary #161b22 卡片/面板背景 与主背景形成 7% 亮度差,层级感清晰
--dg-bg-tertiary #21262d 输入框/悬停态背景 足够的对比度确保交互反馈可感知

Light 主题色板:

变量名 色值 语义角色 评估
--dg-bg-primary #ffffff 页面主背景 纯白基底,经典稳妥
--dg-bg-secondary #f8f9fb 卡片/面板背景 微冷灰调,避免纯白页面的单调感

共享渐变系统:

变量名 色值 使用场景
--dg-gradient-start #2563eb(蓝色) 品牌主色 → 用于 Logo、主按钮、活跃状态
--dg-gradient-end #9333ea(紫色) 品牌辅色 → 用于渐变过渡、装饰性元素

蓝紫渐变在视觉上传递了科技感与专业性,与数据库管理工具的定位高度契合。该渐变目前被封装为 .dg-gradient-text 与 .dg-gradient-bg 两个工具类,使用便捷。

语义化工具类:

项目还定义了 .dg-card、.dg-input 等高级工具类,将颜色变量、圆角、阴影、边框等属性聚合为语义化的样式单元。这种"变量 + 工具类"的双层体系兼顾了灵活性与开发效率,是可维护性设计的体现。

1.4.2 shadcn/ui 组件库使用情况

项目使用了 50+ shadcn/ui 基础组件,覆盖了表单控件(Input、Select、Checkbox、Radio)、数据展示(Table、Card、Badge、Tooltip)、反馈(Dialog、Toast、Alert、Skeleton)、导航(Tabs、Breadcrumb、Sidebar)等完整类别。

shadcn/ui 的核心优势在于源码级可定制性——组件代码直接存在于项目的 components/ui/ 目录中,团队可以:

  • 自由修改组件的默认样式、动画参数、无障碍属性
  • 根据品牌规范调整颜色、圆角、间距等设计 Token
  • 添加业务特有的 Props 扩展(如在 Button 上增加 isLoading 的统一处理)

当前需要关注的点:50+ 组件的规模意味着维护成本不可忽视,建议在升级 shadcn/ui 版本时建立变更日志审查机制,确保上游的 Bug 修复与安全更新能够及时同步。

1.4.3 主题系统设计

平台当前的主题系统具备以下特征:

  1. 双主题覆盖:Dark / Light 两种模式,通过 CSS 变量切换实现,切换成本极低(仅需切换顶层 class)。
  2. 渐变色作为品牌标识:蓝紫渐变(#2563eb → #9333ea)贯穿两种主题,形成了强烈的品牌视觉记忆点。
  3. CSS 变量驱动:所有主题相关属性均通过 --dg-* 变量管理,无硬编码色值散落在组件代码中。

建议完善的方面:

  • 当前主题切换可能依赖 CSS 的 prefers-color-scheme 媒体查询或手动切换按钮,建议增加主题偏好持久化(localStorage)与系统主题自动跟随的双重机制。
  • 建议补充 High Contrast(高对比度) 模式,满足无障碍合规要求(WCAG 2.1 AA 级)。
  • 考虑引入 主题 Token 文档,明确定义每种语义色值的使用场景(如 --dg-bg-primary 仅用于页面级背景,不可用于卡片),防止变量滥用导致视觉不一致。

1.4.4 当前设计体系的优势与不足

优势:

维度 表现
工程化成熟度 CSS 变量 + 工具类的双层体系,shadcn/ui 的源码级可控性
品牌一致性 蓝紫渐变标识贯穿全站,Dark/Light 双主题覆盖完整
开发效率 Tailwind 原子类 + 语义化工具类,编写样式快速直观
扩展能力 新增主题仅需定义一组 CSS 变量,无需修改组件代码

不足:

维度 问题描述 改进优先级
设计 Token 粒度 当前变量主要集中在颜色维度,缺少间距、字体、阴影、动效的 Token 化定义 高
动效体系缺失 页面转场、组件出现/消失、状态切换等交互缺乏统一的动效规范 高
无障碍支持 缺少 ARIA 属性系统化检查、键盘导航测试、色彩对比度合规验证 中
响应式设计 平台以桌面端操作为主,但缺少不同分辨率下的布局适配策略 中
图标体系 未明确图标库选型(如 Lucide、Radix Icons)与使用规范 低

综上,SQL-Platform 的 UI 设计体系已具备良好的基础骨架,特别是在 CSS 变量体系与组件化封装方面展现了专业水准。后续优化的核心方向应聚焦于设计 Token 的维度扩展(补充间距、动效、阴影 Token)、交互动效体系的建立以及无障碍合规性的系统性提升,这些内容将在后续章节中展开详细方案。

2. 现状问题诊断

经过对 SQL-Platform 前端代码库的全面审阅与走查,我们共识别出 5 大维度、23 项具体问题。这些问题贯穿登录认证、主题系统、核心功能页面、菜单导航以及整体设计体系,严重程度从"视觉瑕疵"到"架构性债务"不等。以下按模块逐项展开诊断。


2.1 登录页面五大问题分析

平台提供了 5 种登录页面布局(SplitView、CenteredCard、SidePanel、FullImage、MinimalDark),位于 /frontend/src/components/login/ 目录下。尽管数量丰富,但 5 个组件共享大量共性问题,且各布局存在特有的设计缺陷。以下从视觉层次、交互体验、品牌感知、组件化程度和动效反馈五个维度进行诊断。

2.1.1 视觉层次薄弱

共性问题:5 个登录页均缺乏清晰的视觉焦点层级,背景、装饰元素与核心表单区域之间的对比关系处理随意。

布局组件 具体视觉问题 严重程度
SplitView.tsx 左侧特性列表采用简单 icon+文字网格排列,无卡片包裹、无分割线,各 feature 项视觉权重完全一致,用户难以快速扫描核心卖点 中
CenteredCard.tsx 渐变背景仅为单色线性过渡,无纹理或光晕层次;卡片阴影硬编码为 0 8px 32px rgba(0,0,0,0.3),缺乏环境光感,呈现"浮空贴图"效果 中
SidePanel.tsx 装饰性圆圈使用 borderRadius: 50% 的 div 硬编码,无渐变填充、无模糊处理,视觉上如同"开发占位符";品牌区域仅放置 Database 图标,无品牌图形标识 高
FullImage.tsx 电路板网格背景使用 CSS repeating-linear-gradient 实现,线条粗细一致、无辉光效果,整体呈现"半成品 sketch"观感;毛玻璃效果 backdrop-filter: blur(16px) 未配合饱和度降低( saturate)与边缘高光,通透感不足 高
MinimalDark.tsx 几乎无任何装饰性设计元素,纯黑背景+白字表单的极简走向极端,缺乏辅助视觉引导线或光晕点缀,整体显得空旷冷清 中

根因分析:各登录页的开发重心放在"布局差异化"上,却忽视了"视觉深度营造"。装饰元素的实现手段停留在 CSS 基础属性层面,未利用渐变、噪点纹理、多层阴影、边缘发光等现代视觉技法来建立前景/中景/背景的三层空间关系。

2.1.2 交互体验不足

登录作为用户进入平台的第一触点,其交互体验直接影响用户对平台专业度的初始判断。当前 5 个登录组件在表单交互方面存在系统性缺陷:

  1. 表单验证视觉反馈缺失:全部使用原生 <input> 元素,未采用 shadcn/ui 的 <Input> 组件。输入框在 focus、error、success 三种状态下均无边框颜色变化、无图标提示、无 shake 动画。用户提交错误表单后,仅能看到简单的红色文字提示,错误字段本身没有任何视觉变化。

  2. "记住我"功能缺位:5 个登录页均未提供"记住账号/保持登录"选项。对于企业级工具而言,频繁的会话过期与重复登录是显著的效率损耗点。

  3. "忘记密码"入口缺失:没有提供密码找回或重置的链接入口,用户一旦遗忘密码将无从操作。

  4. 安全提示文字缺失:未展示"建议使用强密码"、"登录即表示同意服务条款"等合规性文案,降低了平台的安全可信度。

  5. 版本号显示缺失:登录页未展示当前平台版本号(如 v2.4.1),不利于运维排查与用户反馈时的版本确认。

2.1.3 品牌感缺失

品牌名在所有 5 个登录页中统一硬编码为"SQL审核平台",名称过于直白、缺乏品牌辨识度与情感连接。对比业界标杆产品(如 Bytebase 的科技感命名、Archery 的品牌化 Logo),当前的命名策略停留在"功能描述"层面,未形成品牌记忆点。

更关键的是,5 个登录页均未展示品牌标识(Logo、品牌图形、吉祥物或标志性配色)。SidePanel 仅用 Database 通用图标代替品牌图形,CenteredCard 完全没有任何品牌标识。登录页作为品牌视觉锤的核心触点,未能承担品牌认知建设的职责。

2.1.4 组件化程度低

从代码实现层面审视,5 个登录组件存在显著的工程化债务:

  • 未使用 shadcn/ui <Input> 组件:全部直接使用原生 <input>,导致表单样式无法继承主题系统的 CSS 变量,切换主题时可能出现颜色断层。
  • 内联样式泛滥:表单样式大量使用内联 style 属性(如 style={{ padding: '24px', borderRadius: '8px' }}),而非 Tailwind CSS 工具类。这破坏了项目的样式一致性,也增加了主题切换的维护成本。
  • 按钮样式不统一:有的登录按钮使用渐变背景,有的使用纯色,有的使用 outline 样式,5 个组件之间没有共享按钮规范。
  • 登录按钮 loading 状态简陋:loading 状态仅通过文字变化(如"登录中...")体现,无 spinner 图标、无按钮宽度保持、无禁用状态视觉变化,用户可能因按钮"变形"而产生疑惑。

改进方向:应将登录表单抽象为可复用的 <LoginForm> 组件,统一使用 shadcn/ui 的 Input、Button、Checkbox、Label 等原子组件,并通过 Tailwind class 管理所有样式。

2.1.5 动效与反馈匮乏

当前登录页的动效设计几乎处于空白状态:

交互场景 当前表现 期望表现
登录成功 直接跳转,无过渡动画 显示成功 checkmark 动画,延迟 400ms 后优雅跳转
登录失败 简单红色文字提示 表单 shake 动画 + 错误字段高亮 + 带图标的错误消息
输入框 focus 无特殊效果 边框颜色过渡 + 柔和外发光(ring)
按钮 hover/click 无反馈或仅有透明度变化 微缩放 + 阴影加深 + 涟漪效果
页面加载 静态呈现 表单区域的 fade-in + translateY 入场动画

根因分析:开发过程中未引入动画库(如 framer-motion),所有交互反馈停留在浏览器默认行为层面。登录页作为情感化设计的最佳载体,动效的缺失使得整体体验显得"机械"与"冰冷"。


2.2 主题系统局限性

当前主题系统仅支持 dark 与 light 两种模式,通过 data-theme HTML 属性进行切换,主题选择入口位于 AppearanceSettings 页面,以简单的按钮组形式呈现。

核心局限点:

  1. 色彩方案单一:缺乏紫色系、绿色系、暖色系、高对比度等多元色彩方案。现代 SaaS 平台(如 Vercel、Notion、Linear)通常提供 4-8 种预设主题,以满足不同用户的审美偏好与无障碍需求。

  2. 主题预览简陋:预览区域仅以 4:3 比例的缩略图展示,用户无法直观感受主题在真实数据密度下的表现。建议提供可交互的实时预览(如展示表格、按钮、标签等组件的组合效果)。

  3. 主题切换过渡生硬:切换主题时无过渡动画,页面瞬间"闪切",视觉体验断裂。应加入 200-300ms 的颜色过渡动画(transition: color, background-color)。

  4. 自定义能力缺失:高级用户无法调整主色调、圆角大小、字体密度等参数。建议提供"自定义主题"模式,允许用户通过色盘选择器定义个性化的品牌色。

  5. CSS 变量覆盖范围不足:部分组件仍使用硬编码颜色值,未完全纳入 CSS 变量体系,导致主题切换时存在"漏色"现象。


2.3 核心功能页面问题

2.3.1 审批变更工单系统

工单系统包含 3 个核心页面,总计 2742 行代码,存在显著的 UX 债务:

审批变更页(TicketsPage.tsx,742 行):

  • 表格使用原生 <table> 元素,未使用 shadcn/ui 的 Table 组件,导致列头排序、列宽调整、行悬停效果等功能全部缺失。
  • 状态标签样式极其简单(如 bg-blue-500/15 text-blue-400),仅为色块+文字,无圆角规范、无图标辅助、无二态(solid/soft)区分。
  • 操作按钮采用简单文字链接形式("查看""编辑""删除"),无图标按钮、无操作菜单(dropdown menu),在窄屏下易折行。
  • 缺少批量操作功能:用户无法对多条工单进行批量审批或批量关闭,效率低下。
  • 搜索框为原生 <input>,无搜索图标、无清除按钮、无搜索建议。
  • 分页组件为手写实现,未使用 shadcn/ui Pagination,样式与平台其他分页区域不统一。

工单创建页(TicketCreatePage.tsx,1192 行):

  • 布局采用上下堆叠的线性结构,左侧 SQL 编辑器与右侧库表浏览面板的空间配比固定,未利用响应式网格最大化编辑区域。
  • SQL 编辑器下方的信息条(行数、字符数、语法状态)样式过于简单,无图标、无色彩状态区分。
  • 审核结果展示为扁平列表,缺乏视觉层次:通过/失败的规则未使用不同颜色或图标区分,高亮行与错误行的视觉权重相近。
  • 库表浏览面板的 tab 切换样式为基础样式,无活跃 tab 的指示器动画。
  • 无自动保存草稿功能:用户误刷新页面后,已编写的 SQL 将全部丢失,这是企业级工具的高风险体验缺口。

工单详情页(TicketDetailPage.tsx,808 行):

  • 审批流程可视化使用简单圆形+直线实现,无节点状态图标(如 Check、Clock、X)、无连线动画、无进度百分比指示。
  • 信息网格采用 2 列 flex 布局直接裸露呈现,无卡片包裹、无分割线、无标签-值的对齐规范。
  • 执行结果直接展示原始 JSON 字符串,无格式化、无语法高亮、无可折叠区域,运维人员阅读极为困难。
  • 关联工单区域仅为简单的工单编号链接列表,无状态徽章、无优先级标识。

2.3.2 查询数据页面

QueriesPage.tsx(896 行)作为数据查询的核心工作区,存在以下 UX 问题:

  • 查询结果表格同样使用原生 <table>,列宽不可调整(无拖拽调整功能),对于宽表(20+ 列)的浏览体验极差。
  • 缺少查询收藏夹功能:用户无法保存常用的查询语句,每次需重新输入或从历史中翻找。
  • 历史记录面板为简单的文字列表,无查询耗时显示、无行数结果、无收藏/删除操作。
  • SQL 编辑器与结果表格之间的区域无明确的分割线或可调整拖拽条(resizer),用户无法按需求分配编辑区与结果区的屏幕空间。

2.3.3 系统设置页面

SettingsPage.tsx(56 行)作为设置入口,问题尤为突出:

  • Tab 图标使用 Emoji(🎨 外观、⚙️ 平台、🛡️ 安全、🔔 通知、🔐 密码),与平台其他页面统一使用的 lucide-react 图标体系严重冲突,视觉风格割裂。Emoji 在不同操作系统下的渲染差异也会导致界面不统一。
  • Tab 切换无动画过渡,内容区域的切换为瞬间跳转,体验生硬。
  • 各设置子页面的表单设计不一致:有的使用 shadcn/ui 组件,有的(如 PlatformConfigPage)直接使用原生 <input>,标签对齐方式、间距、按钮位置均不统一。
  • 缺少设置变更确认/撤销机制:用户在修改敏感配置(如数据库连接参数)后,没有二次确认对话框;也没有"重置为默认值"或"撤销本次更改"的功能,误操作风险高。

2.4 菜单体系问题

平台的侧边栏导航菜单存在以下结构性问题:

  1. 图标体系不统一:Settings 页面使用 Emoji 图标,而其他所有页面使用 lucide-react 图标库。这种混用破坏了导航的视觉一致性,也降低了专业感。

  2. 菜单分组合理性不足:当前菜单分为 6 个 section,但部分分类逻辑存在问题。例如"自动任务"被归类在"系统配置"分组下,而从用户心智模型来看,自动任务属于"运维操作"范畴,与"系统配置"(偏静态参数)的认知类别不同。建议重新审视信息架构,采用"按用户角色分组"或"按操作频率分组"的策略。

  3. 面包屑导航缺失:页面层级超过 2 层后(如 设置 > 平台配置 > 数据库参数),用户无法通过面包屑快速定位当前位置或返回上级,只能依赖浏览器后退按钮。

  4. Hover/Active 状态过渡动画缺失:菜单项的悬停和激活状态切换为瞬间变化,无 150-200ms 的颜色/背景过渡动画,整体感觉"卡顿"而非"响应"。

  5. 当前页面指示不够醒目:激活菜单项仅通过轻微的颜色变化区分,缺乏左侧边框高亮(border-left accent)或背景色块等强指示手段。


2.5 设计体系不一致性

从代码层面审视,平台在整体 design system 的贯彻执行上存在严重的碎片化现象:

卡片组件混用:部分页面使用 shadcn/ui 的 <Card> 组件(如 Dashboard),部分页面使用 <div> 手写卡片样式(如工单详情页的信息网格)。手写卡片的圆角、阴影、边框、背景色与 shadcn/ui Card 的默认参数存在细微差异,积累后形成显著的视觉噪音。

按钮样式分裂:平台内同时存在 3 种按钮风格——渐变按钮(部分登录页)、纯色填充按钮(shadcn/ui 默认)、outline 描边按钮(部分操作栏)。三种风格的使用没有明确的场景规范,同一页面内也可能混用,破坏了操作隐喻的一致性。

表单输入框样式差异:有的页面使用 shadcn/ui <Input>(带统一的 focus ring、圆角、高度),有的页面使用原生 <input>(样式依赖浏览器默认),有的页面使用内联样式自定义的输入框。三种实现的视觉高度、边框粗细、焦点反馈均不相同。

颜色管理混乱:部分组件使用硬编码颜色值(如 rgba(0,0,0,0.3)、#3b82f6),部分组件使用 CSS 变量(如 hsl(var(--primary))),部分组件使用 Tailwind 的语义化 class(如 bg-primary)。当主题切换或品牌色调整时,硬编码的颜色值不会跟随变化,导致"漏色"与视觉断层。

间距系统缺失:各页面的 padding 和 margin 取值随意,无统一的 spacing scale。例如有的卡片内边距为 16px,有的为 20px,有的为 24px;页面级的外边距也无统一规范(pagePadding token 缺失)。这种不一致使得页面之间的"呼吸感"差异明显,整体缺乏精致感。

根因总结:以上不一致性的根本原因在于缺乏一个被严格执行的 Design Token 体系与组件使用规范。开发人员在实现新页面时,没有明确的"应该使用哪个组件、哪个 token"的指引,导致各页面"各自为政"。建议引入一套完整的 Design Token(颜色、间距、字体、阴影、圆角)并配合 ESLint 规则强制在组件层面统一规范。


本章小结:SQL-Platform 的现状问题可归纳为"功能可用但体验粗糙"。登录页面作为品牌门面,在视觉层次、交互反馈、品牌感知上均有显著短板;核心功能页面(工单系统、查询页面、设置页面)在组件选型、布局效率、信息架构上存在系统性债务;主题系统与菜单体系的局限性则反映了设计体系在规划层面的不足。这些问题并非孤立存在,而是相互交织:设计体系的碎片化导致了各页面实现的不一致,组件化程度低导致了交互体验的简陋,主题系统的局限又加剧了视觉风格的单一。后续章节将针对这些问题提出具体的优化方案。

3. 五套专业登录页设计方案

针对现有五个登录页组件(SplitView.tsx、CenteredCard.tsx、SidePanel.tsx、FullImage.tsx、MinimalDark.tsx)"不专业、太 low"的用户反馈,本章提供五套完整的专业级登录页重设计方案。每套方案均基于 React 19 + TypeScript + Tailwind CSS 4 + shadcn/ui + Lucide Icons 技术栈,保留现有的 useLogin 与 useTheme hook 架构,在视觉表现、交互体验与动效层次上实现全面升级。

五套方案分别对应五种截然不同的企业气质:科技互联、金融安全、云端 SaaS、极客代码、创意自然。设计方案遵循"一次一屏"(Single Screen)原则,确保登录流程简洁高效,同时在第一眼传递出强烈的品牌专业感。


3.1 方案一「极光科技」— 动态粒子科技风

设计概念与灵感来源

「极光科技」方案的灵感源自现代科技公司的数据可视化美学——深空般的背景中,粒子如数据节点般流动、连接,暗示着数据库节点之间的网络拓扑关系。设计参考了 Vercel、PlanetScale 等现代 DevTools 产品的暗黑登录页风格,将"数据流动"这一抽象概念转化为可感知的视觉语言。深色背景配合冷色调粒子动画,营造出深邃、专注、充满科技张力的第一印象。

适用场景

适合科技互联网公司、数据库工具产品、DevOps 平台、技术驱动型 SaaS 企业。这类企业的用户群体(开发者、DBA、技术人员)对科技感视觉有天然的好感度与信任预期。

视觉规范

元素 规范值 说明
背景渐变 #0a0e27 → #1a1f4b 深邃的深蓝到暗蓝,比纯黑更有空间感
粒子数量 80 个 足够形成视觉密度,不过度抢占注意力
粒子颜色 #3b82f680(半透蓝) 与品牌蓝色系一致,带透明度避免喧宾夺主
连线距离 120px 控制连线密度,确保画面不过于繁杂
卡片背景 rgba(255,255,255,0.03) 极微弱的白色,配合毛玻璃效果
卡片毛玻璃 backdrop-blur: 20px iOS 风格毛玻璃,现代感强烈
卡片边框 1px solid rgba(255,255,255,0.08) 微妙的发光边框勾勒卡片轮廓
卡片圆角 16px 现代感的较大圆角
卡片内边距 40px 32px 宽裕的内部空间
Logo 底圆 56px,渐变 from #2563eb to #9333ea 渐变圆形底托提升品牌质感
Logo 图标 Shield(Lucide),白色,24px 安全感的盾形图标
品牌名 "DataGuard",20px,白色,字重 600 清晰的品牌露出
输入框高度 44px 符合现代表单的最佳点击高度
输入框圆角 10px 与卡片圆角协调
输入框聚焦 边框 #3b82f6,外发光 0 0 0 3px rgba(59,130,246,0.15) 蓝色光晕聚焦指示
按钮渐变 from #2563eb to #7c3aed 蓝紫渐变,与现有品牌渐变一致
按钮高度 44px 与输入框等高,视觉对齐
按钮圆角 10px 与输入框统一
辅助链接色 #8b949e,hover #58a6ff GitHub 风格的灰色链接

动效规范

粒子系统动画:使用 Canvas 2D 实现 60fps 粒子系统。每个粒子以随机速度在画布内移动(速度范围 0.3~0.8px/frame),碰到边界反弹。当两个粒子距离小于 120px 时,绘制连线(线条透明度随距离衰减:opacity = 1 - distance/120 * 0.7)。鼠标位置额外产生一个"引力点",靠近鼠标的粒子连线距离阈值提升至 180px,形成交互反馈。

卡片入场动画:

@keyframes cardEnter {
  from { opacity: 0; transform: translateY(20px); }
  to   { opacity: 1; transform: translateY(0); }
}
/* duration: 600ms, easing: cubic-bezier(0.16, 1, 0.3, 1) */

使用 cubic-bezier(0.16, 1, 0.3, 1) 缓动函数——初始阶段快速响应,结尾柔和减速,营造"丝滑吸附"的质感。

输入框聚焦:边框颜色过渡 200ms ease,外发光同步渐显,给用户明确的焦点反馈。

按钮 Hover:transform: translateY(-1px) 微上移,box-shadow 从 0 4px 12px rgba(37,99,235,0.25) 增强为 0 8px 24px rgba(37,99,235,0.35),过渡 200ms ease。

错误状态 Shake 动画:

@keyframes shake {
  0%, 100% { transform: translateX(0); }
  25% { transform: translateX(-5px); }
  50% { transform: translateX(5px); }
  75% { transform: translateX(-5px); }
}
/* duration: 300ms, 执行 1 次,同时边框变为 #ef4444 */

代码实现要点

  1. 粒子系统:使用 useRef<HTMLCanvasElement> 获取 Canvas 上下文,在 useEffect 中初始化粒子数组并启动 requestAnimationFrame 动画循环。粒子系统封装为独立的 ParticleBackground.tsx 组件,通过 props 接收 particleCount、connectionDistance、color 等参数。

  2. 卡片结构:使用 Card(shadcn/ui)作为容器,外层包裹 div 设置 backdrop-blur-xl bg-white/[0.03] border border-white/[0.08] 实现毛玻璃效果。

  3. 表单组件:Input 和 Button 直接使用 shadcn/ui 组件,通过 Tailwind 的 className 覆盖默认样式。输入框聚焦外发光使用 focus:ring-[3px] focus:ring-blue-500/15 focus:border-blue-500。

  4. 状态管理:继续使用现有的 useLogin hook(username / password / loading / error),错误触发时通过条件类名绑定 shake 动画。


3.2 方案二「企业金融」— 银行级安全可信风

设计概念与灵感来源

金融类产品的登录页第一要务是传递"安全感"。「企业金融」方案借鉴了招商银行企业网银、Stripe Dashboard 以及 Bloomberg Terminal 的视觉语言——通过深沉的蓝色调、严谨的版式分割、明确的信任标识,在瞬间建立起用户对系统的信任。左右分屏的布局本身就是一种力量感的表达:左侧沉稳的品牌展示,右侧聚焦的表单操作,信息层级清晰,符合金融用户"高效、安全、专业"的心理预期。

适用场景

金融、银行、保险、证券、支付等强监管行业企业。用户群体包括金融从业者、企业财务人员、风控人员,他们对安全标识(SSL、加密、审计)高度敏感。

视觉规范

元素 规范值 说明
左侧背景 #0c1a3c → #1a365d 深蓝渐变,传递权威与稳重
左侧宽度 60% 大面积品牌展示区
右侧背景 #f8fafc 极浅灰白,与左侧形成清晰分割
右侧宽度 40% 聚焦的表单操作区
品牌 Logo 白色 Shield 图标 + "DataGuard" 居中偏上布局
Slogan "安全的数据库变更管理",18px,#94a3b8 简洁定位标语
信任指标 3 个:SSL 加密、权限隔离、审计追踪 带 CheckCircle 图标的列表
信任指标图标 CheckCircle2(Lucide),#22c55e 绿色对勾传递"已验证"
信任指标文字 #cbd5e1,14px 柔和的说明文字
右侧卡片 白色背景,圆角 16px 干净的表单容器
卡片阴影 0 4px 24px rgba(0,0,0,0.08) 微妙的浮起感
卡片内边距 48px 40px 金融级宽裕间距
登录标题 "欢迎回来",24px,#0f172a,字重 700 亲切的问候
输入框样式 底部边框(Underline Input) 类似 Material Design 的底线输入
输入框高度 48px 稍大以凸显重要感
输入框边框 默认 border-b-2 border-slate-200,聚焦 border-[#1a365d] 底线聚焦变深蓝
按钮背景 #1a365d 沉稳深蓝,呼应左侧
按钮高度 48px 与输入框等高
按钮圆角 8px 相对克制的圆角,体现严谨
安全提示 "🔒 您的连接已加密保护",12px,#64748b 明确的安全信号

动效规范

左右面板载入:页面加载时,左侧面板从左侧滑入(translateX(-40px) → 0,400ms ease-out),右侧面板从右侧同步滑入(translateX(40px) → 0,400ms ease-out,延迟 50ms)。两侧同时汇聚中间,隐喻"安全通道的建立"。

信任指标依次淡入:3 个信任指标在左侧面板就位后依次显示,每个间隔 150ms:

.trust-item:nth-child(1) { animation-delay: 200ms; }
.trust-item:nth-child(2) { animation-delay: 350ms; }
.trust-item:nth-child(3) { animation-delay: 500ms; }
/* 统一动画: opacity 0→1, translateX(-10px)→0, 300ms ease-out */

按钮 Hover:背景色从 #1a365d 平滑过渡为 #122a4a(加深 10%),同时增加 box-shadow: 0 4px 16px rgba(12,26,60,0.2),过渡 200ms ease。

输入框聚焦:底部边框从 #e2e8f0 变为 #1a365d,宽度有微妙的"延展"效果(使用 scaleX 从 0→1 配合 transform-origin: left),200ms ease。

代码实现要点

  1. 分屏布局:使用 CSS Grid grid-cols-[60%_40%] 或 Flex flex-[60] / flex-[40] 实现左右分割。移动端(< 1024px)切换为上下布局,左侧折叠为顶部横幅。

  2. 底线输入框:不使用 shadcn/ui 默认的边框输入框,而是自定义 div 包裹 <input>,通过 border-b-2 + bg-transparent 实现底线样式,聚焦时切换 border-color。

  3. 信任指标列表:封装为 TrustIndicators 子组件,接收指标数组渲染,配合 setTimeout 或 CSS animation-delay 实现依次淡入。

  4. 安全提示:底部固定位置,使用 flex items-center gap-2 布局,锁图标使用 Lucide Lock 组件。


3.3 方案三「云端数据」— 云计算 SaaS 风

设计概念与灵感来源

云计算和 SaaS 产品的登录页追求的是"轻盈、开放、无门槛"。「云端数据」方案汲取了 Snowflake、Figma、Notion 等现代 SaaS 产品的登录体验——明亮的浅色调、友好的圆角语言、丰富的 SSO 集成选项,向用户传递"即开即用"的产品气质。右上角的装饰性云朵图案是对"云端"概念的直接呼应,但处理为低对比度的纹理,避免过度具象化。

适用场景

云服务商、SaaS 产品、互联网协作工具、中台系统、B2B 软件公司。用户群体包括产品经理、技术团队、普通企业用户,他们期望登录流程简单快捷,SSO 支持是加分项。

视觉规范

元素 规范值 说明
背景渐变 #f0f4f8 → #e2e8f0 柔和的天空感浅灰蓝渐变
装饰云朵 SVG 路径,右上角,opacity: 0.3 微妙的云端意象,不干扰主体
卡片背景 #ffffff 纯白 干净、明亮的中心焦点
卡片圆角 20px SaaS 产品偏爱的友好大圆角
卡片阴影 0 8px 32px rgba(148,163,184,0.15) 柔和的悬浮阴影
卡片宽度 420px 适中宽度,聚焦表单
卡片内边距 48px 40px 宽裕的内部空间
顶部品牌区 40px 圆角矩形图标 + "SQL-Platform" 清晰的身份标识
品牌图标背景 from #0ea5e9 to #2563eb 天蓝到蓝的渐变,呼应云端主题
副标题 "数据库变更管理平台",14px,#64748b 产品定位说明
输入框背景 #f8fafc 极浅灰底,区别于卡片白
输入框圆角 12px 圆角输入,友好感
输入框边框 默认无边框,聚焦 1px solid #3b82f6 最小化视觉噪音
输入框高度 46px 略大于标准,易于点击
按钮渐变 from #0ea5e9 to #2563eb 天空蓝到标准蓝
按钮圆角 12px 与输入框统一
按钮高度 46px 与输入框等高对齐
分隔线 "或使用以下方式登录",13px,#94a3b8 经典的 SSO 分隔
SSO 图标 企业微信/钉钉/飞书,40px 圆形按钮 图标化登录选项
SSO 按钮背景 #f1f5f9,hover #e2e8f0 灰色底,不与主按钮竞争

动效规范

卡片入场:从上方轻柔落下,同时伴随微妙的缩放——opacity 0→1、translateY(-16px)→0、scale(0.98)→1,500ms cubic-bezier(0.34, 1.56, 0.64, 1)。使用弹性缓动函数(overshoot easing),营造 SaaS 产品特有的"活泼弹动感"。

输入框聚焦:无边框输入框在聚焦时,背景色从 #f8fafc 微妙提亮至 #ffffff,同时蓝色边框从 opacity 0 渐显至 opacity 1,过渡 150ms ease。

按钮 Hover:translateY(-2px) 上浮,box-shadow 增强,同时渐变产生微妙的位置偏移(使用 background-position 动画),200ms ease。

SSO 图标 Hover:圆形背景从 #f1f5f9 变为 #e2e8f0,图标 scale(1.1),150ms ease。

装饰云朵:缓慢的水平飘移动画(translateX(0) → translateX(20px) → translateX(0),20s ease-in-out infinite),赋予页面呼吸感。

代码实现要点

  1. 卡片弹性动画:使用 Tailwind 的 animate-in 配合自定义 CSS keyframe 实现弹性入场。或使用 Framer Motion(如项目已引入)的 initial/animate props 配合 type: "spring", stiffness: 200, damping: 15 实现物理弹性效果。

  2. 无边框输入框:shadcn/ui 的 Input 组件默认有边框,需通过 border-0 bg-slate-50 focus:bg-white focus:ring-1 focus:ring-blue-500 覆盖样式。

  3. SSO 按钮组:使用 Flex 布局 flex gap-3 justify-center,每个 SSO 按钮为 button 元素包裹品牌 SVG 图标。考虑到不同企业的 SSO 需求,SSO 区域应封装为 SSOProviders 组件,通过配置数组动态渲染。

  4. 云朵装饰:将云朵 SVG 设为 position: absolute; top: -50px; right: -30px; pointer-events: none;,确保不影响交互。


3.4 方案四「极简矩阵」— 开发者极客风

设计概念与灵感来源

对于面向开发者的工具平台,过于" polished "的设计反而可能显得不够硬核。「极简矩阵」方案致敬了《黑客帝国》的数字雨(Matrix Rain)以及终端命令行的纯粹美学,将登录页转化为一个"进入系统"的仪式。纯黑背景上绿色字符如雨般落下,输入框如同终端命令行,每一个交互细节都在告诉用户:"这是一个为技术人员打造的严肃工具。"

适用场景

技术团队、开发者工具、DevOps 平台、开源项目、CLI/GUI 混合产品。用户群体是开发者、SRE、运维工程师,他们对终端界面有亲切感,极客风格的 UI 反而能唤起"同道中人"的认同感。

视觉规范

元素 规范值 说明
背景色 #000000 纯黑 终端/命令行的经典底色
字符雨颜色 #22c55e(绿色),#16a34a(深绿) 双色调绿色增加层次
字符集 大小写字母 + 数字 + 日文片假名 Matrix 经典字符集
字符密度 比原版 Matrix 减少 40% 不过度干扰登录表单的可读性
字符下落速度 2~6 px/frame 随机 自然的不规则雨效
卡片背景 rgba(0,0,0,0.85) 高透明度黑底,字符隐约可见
卡片边框 1px solid rgba(34,197,94,0.3) 绿色发光边框,终端窗口感
卡片圆角 4px 极小圆角,锐利的技术感
标题字体 JetBrains Mono,22px,#22c55e 等宽字体是终端的灵魂
标题文字 > sql-platform login 命令行风格的标题
副标题 "请输入您的凭据以继续",13px,#4ade80 浅绿说明文字
输入框背景 #0a0a0a 深黑,区别于卡片
输入框边框 1px solid rgba(34,197,94,0.25) 微弱绿边
输入框圆角 4px 锐利
输入框字体 JetBrains Mono,14px 打字机般的输入体验
输入框文字色 #22c55e 绿色输入文字
Placeholder #374151(深灰) 低调提示
输入框聚焦 边框变为 rgba(34,197,94,0.6) 明亮的绿色边框
按钮背景 #22c55e 醒目绿色
按钮文字 #000000,JetBrains Mono,字重 600 黑底绿字,高对比
按钮圆角 4px 锐利风格统一
光标 自定义绿色闪烁光标 2px 宽竖线,每 530ms 闪烁
底部版本号 "v2.4.1 — build 20240815",11px,#374151 开发者熟悉的版本信息

动效规范

字符雨动画:使用 Canvas 2D 实现。初始化多列字符,每列以随机速度向下滚动。每个字符有随机的生命周期——新字符高亮(#4ade80),旧字符渐暗(#15803d → #166534),超出画布底部后重置到顶部。字符雨在登录卡片后方运行,使用 z-index 分层。

卡片入场:模拟终端启动——先显示 > sql-platform login_(带闪烁光标),停顿 300ms 后表单元素逐行"打印"出现。每个元素使用 opacity 0→1,间隔 80ms,模拟打字机效果。

输入框聚焦:边框绿色亮度提升(opacity 0.25 → 0.6),同时产生微弱的绿色外发光 0 0 8px rgba(34,197,94,0.15),150ms ease。

按钮 Hover:背景色从 #22c55e 变为 #4ade80(更亮),同时按钮产生 box-shadow: 0 0 16px rgba(34,197,94,0.3) 绿光晕染,150ms ease。

光标闪烁:

@keyframes blink {
  0%, 49% { opacity: 1; }
  50%, 100% { opacity: 0; }
}
/* animation: blink 1.06s step-end infinite */

使用 step-end 时序函数实现硬切闪烁,更接近真实终端光标。

错误状态:终端风格的错误提示——输入框边框变为 #ef4444,下方显示红色文字 [ERROR] 用户名或密码不正确,字体 JetBrains Mono,12px。无 shake 动画,用冷静的错误信息替代。

代码实现要点

  1. 字符雨 Canvas:独立的 MatrixRain.tsx 组件。核心逻辑:创建 columns 数组,每列跟踪当前字符和 y 坐标。在 requestAnimationFrame 循环中,以半透明黑色 (rgba(0,0,0,0.05)) 覆盖画布而非完全清除,形成字符拖尾效果。字符从预定义字符集中随机选取。

  2. 等宽字体:通过 Google Fonts 或本地引入 JetBrains Mono。Tailwind 配置中添加 fontFamily: { mono: ['JetBrains Mono', 'monospace'] }。

  3. 打字机入场:使用自定义 hook useTypewriterEffect,控制子元素依次显示。或直接使用 setTimeout 链实现简单的延迟序列。

  4. 终端光标:使用一个 span 元素(w-[2px] h-[1.2em] bg-green-500 inline-block)配合 CSS 动画实现闪烁。可封装为 TerminalCursor 小组件。

  5. 密码输入:密码框仍使用 type="password",但自定义样式使圆点/星号也呈现绿色(通过 color: #22c55e 和 caret-color: #22c55e)。


3.5 方案五「自然流动」— 有机渐变自然风

设计概念与灵感来源

并非所有企业都喜欢冷峻的科技风。「自然流动」方案面向创意公司、设计团队和文化企业,从自然界的极光、晨曦、水波中汲取配色灵感。三个巨大的模糊色块在背景中缓慢漂移、交叠,产生不断变化的有机渐变——这种"活着的"背景打破了登录页的单调,将等待时间转化为一种视觉享受。设计参考了 Linear、Arc Browser 等产品的有机渐变美学,以及 Blobs 设计趋势。

适用场景

创意设计公司、数字营销机构、文化传媒企业、生活方式品牌、内部创意工具。用户群体包括设计师、创意总监、市场人员,他们对视觉美感有较高要求,偏好温暖、有情感的界面。

视觉规范

元素 规范值 说明
背景底色 #0f0e1a 深紫黑,衬托前景彩色光晕
渐变圆 1 #f472b6(粉色),直径 600px,blur 120px 左上区域
渐变圆 2 #a78bfa(紫色),直径 500px,blur 120px 右侧中部
渐变圆 3 #60a5fa(蓝色),直径 550px,blur 120px 底部中央
模糊方式 CSS filter: blur(120px) 或 SVG feGaussianBlur 产生柔和的色块融合
卡片背景 rgba(255,255,255,0.1) 半透明白
卡片毛玻璃 backdrop-blur: 24px 更强的模糊,突出背后的色彩流动
卡片圆角 24px 有机的大圆角
卡片边框 1px solid rgba(255,255,255,0.15) 半透明白色边框
卡片内边距 48px 40px 宽裕的空间
品牌图标 64px 大圆角矩形,border-radius: 20px 渐变背景 from #f472b6 to #a78bfa
品牌名 "DataGuard",22px,白色,字重 600 居中展示
品牌标语 "数据之美,流动不息",14px,rgba(255,255,255,0.6) 诗意化标语
输入框背景 rgba(255,255,255,0.08) 毛玻璃输入
输入框边框 1px solid rgba(255,255,255,0.12) 微妙的边框
输入框圆角 14px 圆润
输入框高度 46px 舒适点击高度
输入框文字 白色,rgba(255,255,255,0.9) 高对比度
输入框聚焦 边框变为 rgba(255,255,255,0.4) 白色高亮
按钮背景 #ffffff 白色主按钮
按钮文字 #0f0e1a,字重 600 深色文字
按钮圆角 12px 现代圆角
按钮高度 46px 与输入框等高
卡片外光晕 box-shadow: 0 0 80px rgba(167,139,250,0.15) 微妙的紫色光晕包裹卡片

动效规范

渐变圆漂移:三个彩色模糊圆以不同的速度和轨迹缓慢移动,形成不断变化的背景:

  • 粉圆:translate 在 (0,0) → (30px,-20px) → (-20px,30px) → (0,0) 之间循环,15s ease-in-out infinite
  • 紫圆:translate 在 (0,0) → (-25px,25px) → (20px,-15px) → (0,0),18s ease-in-out infinite
  • 蓝圆:translate 在 (0,0) → (20px,20px) → (-30px,-10px) → (0,0),12s ease-in-out infinite

三个圆的运动周期互质(15s/18s/12s),确保组合图案永不重复。

卡片入场:opacity 0→1、translateY(24px)→0、scale(0.96)→1,700ms cubic-bezier(0.22, 1, 0.36, 1)。较长的时间和柔和的缓动,配合背景的流动感,营造"浮出水面"的诗意体验。

输入框聚焦:边框透明度提升,同时产生微弱的白色外发光 0 0 0 3px rgba(255,255,255,0.08),200ms ease。

按钮 Hover:scale(1.02) 微放大(而非上移,更柔和),box-shadow 增强为 0 8px 24px rgba(255,255,255,0.2),过渡 200ms ease。白色按钮在暗色背景下产生"发光"效果。

光晕脉动:卡片外层的紫色光晕以 4s ease-in-out infinite 的节奏在 opacity 0.1 ↔ 0.2 之间呼吸,赋予页面生命力。

代码实现要点

  1. SVG 渐变网格:使用绝对定位的 div 元素包裹三个 div(每个设置 border-radius: 50%、filter: blur(120px)、对应背景色),通过 CSS @keyframes 分别控制 transform 动画。或使用 SVG <circle> 配合 <filter><feGaussianBlur> 实现同样的模糊色块效果。

  2. 强毛玻璃卡片:backdrop-blur-2xl bg-white/10 border border-white/15 rounded-3xl shadow-[0_0_80px_rgba(167,139,250,0.15)]。注意 backdrop-blur 需要浏览器支持,可考虑添加 -webkit-backdrop-blur 前缀。

  3. 白色主按钮:在深色背景下,白色按钮天然具有高对比度。Hover 效果避免使用 translateY(上移动效偏科技感),改用 scale(1.02)(更柔和、有机)。

  4. 性能优化:blur(120px) 大模糊是 GPU 密集型操作。建议使用 will-change: transform 提示浏览器优化,同时确保动画元素使用 transform 而非 top/left 属性。在低性能设备上可通过 @media (prefers-reduced-motion: reduce) 关闭背景动画。


3.6 各方案组件设计要点(对比表)

设计维度 方案一「极光科技」 方案二「企业金融」 方案三「云端数据」 方案四「极简矩阵」 方案五「自然流动」
布局结构 居中卡片 + 全屏粒子背景 左右分屏 60:40 居中卡片 + 浅色背景 居中卡片 + 全屏字符雨 居中卡片 + 动态渐变背景
背景实现 Canvas 2D 粒子系统 CSS 渐变 + 纯色分割 CSS 渐变 + SVG 装饰 Canvas 2D Matrix Rain SVG/CSS 模糊渐变圆
卡片材质 毛玻璃 backdrop-blur: 20px 纯白实色 纯白实色 半透明黑 rgba(0,0,0,0.85) 强毛玻璃 backdrop-blur: 24px
卡片圆角 16px 16px 20px 4px(锐利) 24px(最大)
卡片边框 1px solid rgba(255,255,255,0.08) 无 无 1px solid rgba(34,197,94,0.3) 1px solid rgba(255,255,255,0.15)
主色调 蓝 #2563eb + 紫 #7c3aed 深蓝 #1a365d 天蓝 #0ea5e9 绿 #22c55e 粉 #f472b6 + 紫 #a78bfa + 蓝 #60a5fa
背景色 #0a0e27 → #1a1f4b 左 #0c1a3c 右 #f8fafc #f0f4f8 → #e2e8f0 #000000 #0f0e1a
输入框样式 标准边框式 底部底线式 无边框 + 灰底 等宽字体 + 绿边框 毛玻璃半透明
按钮样式 渐变蓝紫 纯色深蓝 渐变天蓝 纯色绿 纯白
按钮圆角 10px 8px 12px 4px 12px
字体选择 系统默认 sans-serif 系统默认 sans-serif 系统默认 sans-serif JetBrains Mono 等宽 系统默认 sans-serif
SSO 支持 无 无 有(企业微信/钉钉/飞书) 无 无
安全标识 Shield 图标暗示 SSL/权限/审计 明确列出 无 版本号信息 无
品牌展示 渐变圆 + Shield 图标 左侧大面积品牌区 顶部品牌图标 终端命令行风格 大圆角渐变图标
移动端适配 卡片全宽 + 边距 上下布局折叠 卡片全宽 卡片全宽 卡片全宽

3.7 各方案动效设计规范(汇总表)

动效维度 方案一「极光科技」 方案二「企业金融」 方案三「云端数据」 方案四「极简矩阵」 方案五「自然流动」
背景动画 粒子连线 60fps 无 云朵缓慢水平漂移 字符雨下落 三圆渐变漂移
入场动画 卡片淡入+上移 600ms 左右滑入 400ms 弹性下落 500ms 打字机逐行 80ms/行 柔和浮现 700ms
入场缓动 cubic-bezier(0.16,1,0.3,1) ease-out cubic-bezier(0.34,1.56,0.64,1) step-end 风格 cubic-bezier(0.22,1,0.36,1)
输入框聚焦 蓝边框+光晕 200ms 底线延展 200ms 背景提亮+边框渐显 150ms 绿边框亮度提升 150ms 白边框透明度提升 200ms
按钮 Hover 上移 1px + 阴影增强 背景加深 + 阴影 上移 2px + 渐变偏移 绿光晕染 + 背景变亮 缩放 1.02 + 白影
错误反馈 Shake 抖动 300ms 边框变红 + 提示文字 边框变红 + 弹性抖动 红色终端错误信息 边框变红 + 光晕转红
特殊交互 鼠标粒子引力 信任指标依次淡入 SSO 图标放大 光标闪烁 光晕呼吸脉动
动画性能 requestAnimationFrame CSS 过渡 CSS 过渡/Framer Motion requestAnimationFrame CSS transform + will-change
减弱动效 保留静态粒子 无需处理 保留静态布局 保留静态字符背景 保留静态模糊色块

方案选型建议

五套方案并非"五选一"的关系,而是为不同的部署场景提供针对性选择:

  • 对外技术产品(如数据库管理工具、开发者平台)→ 方案一「极光科技」或方案四「极简矩阵」
  • 金融/政企客户私有化部署 → 方案二「企业金融」
  • SaaS 订阅产品/云端服务 → 方案三「云端数据」
  • 创意团队/设计公司内部系统 → 方案五「自然流动」

建议在系统设置中提供登录页主题切换功能,允许管理员根据企业 VI 选择合适的方案,或在不同的子域名/入口自动匹配不同风格(如 dev.dataguard.com 使用极客风,finance.dataguard.com 使用金融风)。所有方案共享 useLogin 和 useTheme hook,切换主题仅涉及 UI 层组件的替换,业务逻辑零改动。

4. 五套登录后主题设计方案

为满足不同用户群体在不同使用场景下的视觉偏好,本章为 SQL-Platform 设计五套完整的登录后主题方案。每套主题均包含完整的 CSS 变量规范、色彩心理学依据及典型应用场景说明。五套主题涵盖从专业沉稳到活力温暖的全方位视觉谱系,确保平台在各类企业环境中都能提供最佳的用户体验。


4.1 主题一「深蓝专业」— 默认深色增强版

设计定位:深蓝专业主题是在现有深色主题基础上的全面增强版本,定位为平台的默认主题。设计目标是在保持深色模式优势(降低眼部疲劳、突出内容)的同时,通过更纯正深邃的蓝色调提升整体品质感,并强化视觉层次与可读性。

色彩设计理念:该主题摒弃了原主题中偏灰的杂色倾向,将背景色收敛到更纯正的深蓝谱系。主背景色 #070d19 接近纯黑但带有微弱的蓝色相,营造出深邃而稳定的基底。次背景色 #0f172a 参考了 Slate 色板中最深沉的蓝色调,既保证了与主背景的层次区分,又维持了蓝色谱系的纯粹性。文字色彩采用 #f8fafc(极亮蓝白)作为主文字色,相比纯白色更具温润感,长时间阅读不易产生刺眼感。次文字色 #94a3b8 提供了充分的对比度,确保辅助信息的可读性。

强调色体系:蓝色系强调色选用 #3b82f6(Tailwind Blue 500),这是一个饱和度适中、辨识度高的蓝色,在深色背景上呈现明亮但不刺眼的视觉效果。状态色(成功、错误、警告)均经过深色背景适配,确保各色彩在深蓝底上都有足够的明度对比。

适用场景:作为默认主题,适用于绝大多数使用场景,特别适合技术团队日常操作、数据库管理、权限配置等需要长时间专注的工作界面。

/* === 主题一:深蓝专业 ocean-deep === */
[data-theme="ocean-deep"] {
  /* shadcn/ui 基础变量 */
  --background: 222 47% 4%;
  --foreground: 210 40% 98%;
  --card: 222 47% 7%;
  --card-foreground: 210 40% 98%;
  --popover: 222 47% 7%;
  --popover-foreground: 210 40% 98%;
  --primary: 217 91% 60%;
  --primary-foreground: 0 0% 100%;
  --secondary: 217 33% 17%;
  --secondary-foreground: 210 40% 98%;
  --muted: 217 33% 17%;
  --muted-foreground: 215 20% 65%;
  --accent: 217 33% 17%;
  --accent-foreground: 210 40% 98%;
  --destructive: 0 84% 60%;
  --destructive-foreground: 0 0% 100%;
  --border: 217 33% 17%;
  --input: 217 33% 17%;
  --ring: 217 91% 60%;
  --radius: 0.5rem;

  /* DataGuard 自定义变量 */
  --dg-bg-primary: #070d19;
  --dg-bg-secondary: #0f172a;
  --dg-bg-tertiary: #172033;
  --dg-bg-hover: #233049;
  --dg-border: #1e293b;
  --dg-border-light: #172033;
  --dg-text-primary: #f8fafc;
  --dg-text-secondary: #94a3b8;
  --dg-text-muted: #64748b;
  --dg-blue: #3b82f6;
  --dg-green: #22c55e;
  --dg-red: #ef4444;
  --dg-orange: #f97316;
  --dg-purple: #8b5cf6;
  --dg-cyan: #06b6d4;
  --dg-yellow: #eab308;
  --dg-gradient-start: #3b82f6;
  --dg-gradient-end: #2563eb;
}

4.2 主题二「浅色商务」— 企业办公风

设计定位:浅色商务主题面向白天办公环境优化,采用温暖的灰白色调替代常见的冷白背景,营造舒适、专业且不刺眼的工作界面。该主题适合不习惯深色模式的用户,或在明亮办公室环境中使用。

色彩设计理念:背景色摒弃了纯白的冷峻感,主背景色 #fafaf9 选用带有微弱暖色相的" zinc white",在显示器上呈现柔和的纸质感。次背景色 #f5f5f4 和三级背景色 #e7e5e4 构成自然的层次递进,避免了传统浅色主题中背景层次过于平淡的问题。边框色 #d6d3d1 同样带有暖色调,使界面分割更加自然和谐。文字色彩方面,主文字色 #1c1917 是带有微弱棕色相的深黑色,相比纯黑在暖白背景上对比度更舒适,避免了"黑字白纸"的刺眼感。

强调色体系:强调色选用稳重的 #2563eb(Tailwind Blue 600),这是一个偏深的标准蓝色,传达专业、可信赖的企业形象。状态色均调整为适合浅色背景的版本,成功绿 #16a34a、错误红 #dc2626、警告橙 #ca8a04 在浅色背景上均保持良好的可读性。

适用场景:适合白天办公环境、管理人员日常审批查看、客户演示投屏等场景。浅色界面在投影设备上的显示效果通常优于深色界面,因此该主题也是会议室演示的首选。

/* === 主题二:浅色商务 light-professional === */
[data-theme="light-professional"] {
  /* shadcn/ui 基础变量 */
  --background: 40 9% 98%;
  --foreground: 24 10% 10%;
  --card: 40 9% 96%;
  --card-foreground: 24 10% 10%;
  --popover: 40 9% 96%;
  --popover-foreground: 24 10% 10%;
  --primary: 221 83% 53%;
  --primary-foreground: 0 0% 100%;
  --secondary: 40 6% 90%;
  --secondary-foreground: 24 10% 10%;
  --muted: 40 6% 90%;
  --muted-foreground: 25 5% 45%;
  --accent: 40 6% 90%;
  --accent-foreground: 24 10% 10%;
  --destructive: 0 72% 51%;
  --destructive-foreground: 0 0% 100%;
  --border: 24 6% 83%;
  --input: 24 6% 83%;
  --ring: 221 83% 53%;
  --radius: 0.5rem;

  /* DataGuard 自定义变量 */
  --dg-bg-primary: #fafaf9;
  --dg-bg-secondary: #f5f5f4;
  --dg-bg-tertiary: #e7e5e4;
  --dg-bg-hover: #d6d3d1;
  --dg-border: #d6d3d1;
  --dg-border-light: #e7e5e4;
  --dg-text-primary: #1c1917;
  --dg-text-secondary: #78716c;
  --dg-text-muted: #a8a29e;
  --dg-blue: #2563eb;
  --dg-green: #16a34a;
  --dg-red: #dc2626;
  --dg-orange: #ea580c;
  --dg-purple: #7c3aed;
  --dg-cyan: #0891b2;
  --dg-yellow: #ca8a04;
  --dg-gradient-start: #2563eb;
  --dg-gradient-end: #4f46e5;
}

4.3 主题三「午夜紫韵」— 高端紫色调

设计定位:午夜紫韵主题以深紫黑色为基底,搭配明亮的紫色渐变强调色,营造高端、神秘且富有科技感的视觉氛围。该主题适合作为给客户演示时的展示主题,能够传达产品的技术领先性和品质感。

色彩设计理念:主背景色 #0a0612 是带有明显紫色相的极深色,接近"午夜黑"的概念,为整个界面奠定了神秘而高贵的基调。次背景色 #130c21 和三级背景色 #1c1330 沿紫色谱系逐步提亮,形成清晰的层次结构。悬停背景色 #2d1f4a 是一个中等深度的紫色,在交互时能够产生明显的视觉反馈,同时保持整体色调的一致性。文字色彩方面,主文字色 #faf5ff 带有微弱的紫色调,使文字在深色背景上更加柔和雅致。

强调色体系:该主题的核心亮点在于紫色渐变强调体系。渐变起点 #9333ea(Tailwind Purple 600)到渐变终点 #c084fc(Tailwind Purple 400)构成了一段明亮且富有活力的紫色渐变,用于品牌标识、主要按钮和重要数据高亮。蓝色调整为偏紫的靛蓝色 #818cf8,绿色调整为翠绿色 #34d399,红色调整为玫红色 #fb7185,所有状态色都经过紫色调环境的协调适配。

适用场景:产品发布会演示、客户汇报展示、高端企业部署、技术峰会展台等需要彰显产品品质的正式场合。

/* === 主题三:午夜紫韵 midnight-purple === */
[data-theme="midnight-purple"] {
  /* shadcn/ui 基础变量 */
  --background: 264 56% 4%;
  --foreground: 280 100% 98%;
  --card: 264 48% 8%;
  --card-foreground: 280 100% 98%;
  --popover: 264 48% 8%;
  --popover-foreground: 280 100% 98%;
  --primary: 270 91% 65%;
  --primary-foreground: 0 0% 100%;
  --secondary: 264 40% 20%;
  --secondary-foreground: 280 100% 98%;
  --muted: 264 40% 20%;
  --muted-foreground: 280 30% 60%;
  --accent: 264 40% 20%;
  --accent-foreground: 280 100% 98%;
  --destructive: 348 100% 72%;
  --destructive-foreground: 264 56% 4%;
  --border: 264 40% 20%;
  --input: 264 40% 20%;
  --ring: 270 91% 65%;
  --radius: 0.5rem;

  /* DataGuard 自定义变量 */
  --dg-bg-primary: #0a0612;
  --dg-bg-secondary: #130c21;
  --dg-bg-tertiary: #1c1330;
  --dg-bg-hover: #2d1f4a;
  --dg-border: #2d1f4a;
  --dg-border-light: #1c1330;
  --dg-text-primary: #faf5ff;
  --dg-text-secondary: #b794c7;
  --dg-text-muted: #7c5a8a;
  --dg-blue: #818cf8;
  --dg-green: #34d399;
  --dg-red: #fb7185;
  --dg-orange: #fb923c;
  --dg-purple: #c084fc;
  --dg-cyan: #22d3ee;
  --dg-yellow: #facc15;
  --dg-gradient-start: #9333ea;
  --dg-gradient-end: #c084fc;
}

4.4 主题四「松绿自然」— 护眼绿调

设计定位:松绿自然主题面向长时间使用平台的开发团队设计,以墨绿色为基调,利用绿色光谱对人眼最为友好的特性,最大程度降低长时间注视屏幕带来的视觉疲劳。

色彩设计理念:该主题的核心设计原则是"护眼优先"。主背景色 #0a1210 是带有明显绿色相的极深色,类似深夜松林深处的颜色,极低明度确保了基础视觉负荷的最小化。次背景色 #111f1a(深墨绿)和三级背景色 #192c24(墨绿灰)沿绿色谱系递进,在保持低明度的同时构建清晰的视觉层次。悬停背景色 #253d32 选用了中等深度的松绿色,交互时呈现自然、柔和的状态变化。边框色 #253d32 与悬停色共用,确保交互反馈与界面分割的视觉统一。主文字色 #f0fdf4 是极淡的薄荷白,在墨绿背景上明亮而不刺眼,具有极佳的阅读舒适度。

强调色体系:绿色系强调色选用 #4ade80(Tailwind Green 400),这是一个明亮、清新的绿色,在墨绿暗底上如同松针上的晨露般醒目。信息色 #2dd4bf 偏向青绿色,与主色调完美协调。蓝色调整为天蓝色 #38bdf8,在绿调环境中起到色彩平衡的作用,避免界面过于单调。

适用场景:开发团队日常长时间使用、DBA 持续监控数据库状态、运维人员驻屏值班等需要长时间面对屏幕的工作场景。

/* === 主题四:松绿自然 pine-green === */
[data-theme="pine-green"] {
  /* shadcn/ui 基础变量 */
  --background: 150 25% 5%;
  --foreground: 140 60% 97%;
  --card: 150 25% 9%;
  --card-foreground: 140 60% 97%;
  --popover: 150 25% 9%;
  --popover-foreground: 140 60% 97%;
  --primary: 142 71% 45%;
  --primary-foreground: 150 25% 5%;
  --secondary: 150 20% 18%;
  --secondary-foreground: 140 60% 97%;
  --muted: 150 20% 18%;
  --muted-foreground: 150 20% 55%;
  --accent: 150 20% 18%;
  --accent-foreground: 140 60% 97%;
  --destructive: 0 90% 72%;
  --destructive-foreground: 150 25% 5%;
  --border: 150 20% 18%;
  --input: 150 20% 18%;
  --ring: 142 71% 45%;
  --radius: 0.5rem;

  /* DataGuard 自定义变量 */
  --dg-bg-primary: #0a1210;
  --dg-bg-secondary: #111f1a;
  --dg-bg-tertiary: #192c24;
  --dg-bg-hover: #253d32;
  --dg-border: #253d32;
  --dg-border-light: #192c24;
  --dg-text-primary: #f0fdf4;
  --dg-text-secondary: #86b8a0;
  --dg-text-muted: #5a7c6b;
  --dg-blue: #38bdf8;
  --dg-green: #4ade80;
  --dg-red: #f87171;
  --dg-orange: #fb923c;
  --dg-purple: #c084fc;
  --dg-cyan: #2dd4bf;
  --dg-yellow: #facc15;
  --dg-gradient-start: #059669;
  --dg-gradient-end: #10b981;
}

4.5 主题五「橙光活力」— 活力暖色调

设计定位:橙光活力主题以深棕暗橙为基底,搭配明亮的橙色渐变,传递温暖、积极、富有活力的品牌情绪。该主题面向年轻化团队和创新型企业,打破传统数据安全平台冰冷刻板的印象。

色彩设计理念:主背景色 #110d08 是带有明显棕色相的极深色,类似深烘焙咖啡豆的颜色,温暖而沉稳。次背景色 #1d1610 和三级背景色 #2b1f16 沿棕色谱系递进,营造出如同暖色调咖啡馆般的舒适感。悬停背景色 #3d2d20 是一个中等深度的暖棕色,交互反馈如同木材被温暖光线照亮。文字色彩方面,主文字色 #fef7ed 是极淡的暖白色,如同烛光下的纸张,在暗棕底上既明亮又温暖。

强调色体系:核心渐变从 #ea580c(Tailwind Orange 600)到 #f59e0b(Tailwind Amber 500),这是一段充满能量感的橙黄渐变,用于重要按钮、品牌标识和数据高亮时极具视觉冲击力。蓝色调整为柔和的 #60a5fa,在暖色调环境中起到中和平衡的作用。紫色调整为偏暖的 #a78bfa,与整体暖调和谐共存。

适用场景:互联网创业公司、年轻化的技术团队、创意型企业、产品内部分享会等追求活力与创新氛围的使用场景。

/* === 主题五:橙光活力 orange-vitality === */
[data-theme="orange-vitality"] {
  /* shadcn/ui 基础变量 */
  --background: 30 35% 5%;
  --foreground: 30 100% 97%;
  --card: 30 30% 9%;
  --card-foreground: 30 100% 97%;
  --popover: 30 30% 9%;
  --popover-foreground: 30 100% 97%;
  --primary: 25 95% 53%;
  --primary-foreground: 0 0% 100%;
  --secondary: 30 25% 18%;
  --secondary-foreground: 30 100% 97%;
  --muted: 30 25% 18%;
  --muted-foreground: 30 25% 55%;
  --accent: 30 25% 18%;
  --accent-foreground: 30 100% 97%;
  --destructive: 0 80% 70%;
  --destructive-foreground: 30 35% 5%;
  --border: 30 25% 18%;
  --input: 30 25% 18%;
  --ring: 25 95% 53%;
  --radius: 0.5rem;

  /* DataGuard 自定义变量 */
  --dg-bg-primary: #110d08;
  --dg-bg-secondary: #1d1610;
  --dg-bg-tertiary: #2b1f16;
  --dg-bg-hover: #3d2d20;
  --dg-border: #3d2d20;
  --dg-border-light: #2b1f16;
  --dg-text-primary: #fef7ed;
  --dg-text-secondary: #d4a574;
  --dg-text-muted: #9c7a54;
  --dg-blue: #60a5fa;
  --dg-green: #4ade80;
  --dg-red: #f87171;
  --dg-orange: #fb923c;
  --dg-purple: #a78bfa;
  --dg-cyan: #2dd4bf;
  --dg-yellow: #fcd34d;
  --dg-gradient-start: #ea580c;
  --dg-gradient-end: #f59e0b;
}

4.6 各主题 CSS 变量规范对比表

下表汇总了五套主题的全部 DataGuard 自定义 CSS 变量值,便于开发实现时快速查阅与对比。

CSS 变量 深蓝专业 浅色商务 午夜紫韵 松绿自然 橙光活力
--dg-bg-primary #070d19 #fafaf9 #0a0612 #0a1210 #110d08
--dg-bg-secondary #0f172a #f5f5f4 #130c21 #111f1a #1d1610
--dg-bg-tertiary #172033 #e7e5e4 #1c1330 #192c24 #2b1f16
--dg-bg-hover #233049 #d6d3d1 #2d1f4a #253d32 #3d2d20
--dg-border #1e293b #d6d3d1 #2d1f4a #253d32 #3d2d20
--dg-border-light #172033 #e7e5e4 #1c1330 #192c24 #2b1f16
--dg-text-primary #f8fafc #1c1917 #faf5ff #f0fdf4 #fef7ed
--dg-text-secondary #94a3b8 #78716c #b794c7 #86b8a0 #d4a574
--dg-text-muted #64748b #a8a29e #7c5a8a #5a7c6b #9c7a54
--dg-blue #3b82f6 #2563eb #818cf8 #38bdf8 #60a5fa
--dg-green #22c55e #16a34a #34d399 #4ade80 #4ade80
--dg-red #ef4444 #dc2626 #fb7185 #f87171 #f87171
--dg-orange #f97316 #ea580c #fb923c #fb923c #fb923c
--dg-purple #8b5cf6 #7c3aed #c084fc #c084fc #a78bfa
--dg-cyan #06b6d4 #0891b2 #22d3ee #2dd4bf #2dd4bf
--dg-yellow #eab308 #ca8a04 #facc15 #facc15 #fcd34d
--dg-gradient-start #3b82f6 #2563eb #9333ea #059669 #ea580c
--dg-gradient-end #2563eb #4f46e5 #c084fc #10b981 #f59e0b

shadcn/ui 基础变量对比表

HSL 变量 深蓝专业 浅色商务 午夜紫韵 松绿自然 橙光活力
--background 222 47% 4% 40 9% 98% 264 56% 4% 150 25% 5% 30 35% 5%
--foreground 210 40% 98% 24 10% 10% 280 100% 98% 140 60% 97% 30 100% 97%
--card 222 47% 7% 40 9% 96% 264 48% 8% 150 25% 9% 30 30% 9%
--primary 217 91% 60% 221 83% 53% 270 91% 65% 142 71% 45% 25 95% 53%
--secondary 217 33% 17% 40 6% 90% 264 40% 20% 150 20% 18% 30 25% 18%
--muted 217 33% 17% 40 6% 90% 264 40% 20% 150 20% 18% 30 25% 18%
--muted-foreground 215 20% 65% 25 5% 45% 280 30% 60% 150 20% 55% 30 25% 55%
--destructive 0 84% 60% 0 72% 51% 348 100% 72% 0 90% 72% 0 80% 70%
--border 217 33% 17% 24 6% 83% 264 40% 20% 150 20% 18% 30 25% 18%
--ring 217 91% 60% 221 83% 53% 270 91% 65% 142 71% 45% 25 95% 53%

4.7 主题切换实现方案

4.7.1 CSS 变量组织方式

五套主题的 CSS 变量采用 "分散定义、统一命名空间" 的组织策略。每套主题的所有变量定义在一个独立的 data-theme 属性选择器下,避免变量冲突和覆盖混乱。

文件结构建议:

frontend/src/styles/
├── themes/
│   ├── index.css          /* 主题入口文件,导入所有主题 */
│   ├── ocean-deep.css     /* 主题一:深蓝专业 */
│   ├── light-professional.css  /* 主题二:浅色商务 */
│   ├── midnight-purple.css     /* 主题三:午夜紫韵 */
│   ├── pine-green.css          /* 主题四:松绿自然 */
│   └── orange-vitality.css     /* 主题五:橙光活力 */
└── index.css              /* 现有主样式文件 */

入口文件 themes/index.css 内容:

/* ===== SQL-Platform 主题系统入口 ===== */
/* 按加载顺序:默认主题(深蓝专业)无需 data-theme 属性 */

@import "./ocean-deep.css";
@import "./light-professional.css";
@import "./midnight-purple.css";
@import "./pine-green.css";
@import "./orange-vitality.css";

实现说明:ocean-deep.css 同时提供 :root 级别的默认变量(确保无 data-theme 属性时回退到默认主题)和 [data-theme="ocean-deep"] 选择器下的显式定义,保证其他主题切换回来时变量能正确覆盖。

4.7.2 useTheme Hook 增强实现

现有的 useTheme hook 需要从双值切换(dark/light)扩展为多主题选择。以下是增强后的实现方案:

// frontend/src/hooks/useTheme.ts
import { useState, useEffect, useCallback } from 'react';

/** 支持的主题标识列表 */
export const THEMES = [
  { id: 'ocean-deep', label: '深蓝专业', description: '默认深色增强版' },
  { id: 'light-professional', label: '浅色商务', description: '企业办公风' },
  { id: 'midnight-purple', label: '午夜紫韵', description: '高端紫色调' },
  { id: 'pine-green', label: '松绿自然', description: '护眼绿调' },
  { id: 'orange-vitality', label: '橙光活力', description: '活力暖色调' },
] as const;

export type ThemeId = typeof THEMES[number]['id'];

const STORAGE_KEY = 'sqlp-theme-id';
const DEFAULT_THEME: ThemeId = 'ocean-deep';

/**
 * 从 localStorage 读取保存的主题
 */
function getSavedTheme(): ThemeId {
  try {
    const saved = localStorage.getItem(STORAGE_KEY);
    if (saved && THEMES.some(t => t.id === saved)) {
      return saved as ThemeId;
    }
  } catch {
    // localStorage 不可用时忽略
  }
  return DEFAULT_THEME;
}

/**
 * 检测系统色彩偏好
 */
function getSystemPreference(): 'dark' | 'light' {
  return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
}

/**
 * 根据系统偏好映射默认主题
 */
function getDefaultBySystem(): ThemeId {
  return getSystemPreference() === 'dark' ? 'ocean-deep' : 'light-professional';
}

export function useTheme() {
  const [theme, setThemeState] = useState<ThemeId>(() => {
    const saved = getSavedTheme();
    return saved || getDefaultBySystem();
  });

  /**
   * 切换主题的核心方法
   */
  const setTheme = useCallback((newTheme: ThemeId) => {
    setThemeState(newTheme);
    document.documentElement.setAttribute('data-theme', newTheme);
    try {
      localStorage.setItem(STORAGE_KEY, newTheme);
    } catch {
      // localStorage 不可用时静默处理
    }
  }, []);

  /**
   * 初始化主题:首次加载时应用
   */
  useEffect(() => {
    const saved = getSavedTheme();
    const initial = saved || getDefaultBySystem();
    document.documentElement.setAttribute('data-theme', initial);
    setThemeState(initial);
  }, []);

  /**
   * 监听系统主题变化,仅在用户未手动选择主题时自动切换
   */
  useEffect(() => {
    const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
    const handler = (e: MediaQueryListEvent) => {
      // 检查用户是否已手动设置过主题
      const hasUserPreference = localStorage.getItem(STORAGE_KEY) !== null;
      if (!hasUserPreference) {
        const newTheme = e.matches ? 'ocean-deep' : 'light-professional';
        setTheme(newTheme);
      }
    };
    mediaQuery.addEventListener('change', handler);
    return () => mediaQuery.removeEventListener('change', handler);
  }, [setTheme]);

  return { theme, setTheme, themes: THEMES };
}

4.7.3 主题预览组件设计

AppearanceSettings 页面需新增主题选择区域,包含五张主题预览卡片。每张卡片展示主题的核心色彩样本和简要描述。

// frontend/src/components/settings/ThemePreviewCard.tsx
import { Check } from 'lucide-react';
import { cn } from '@/lib/utils';
import type { ThemeId } from '@/hooks/useTheme';

interface ThemePreviewCardProps {
  themeId: ThemeId;
  label: string;
  description: string;
  colors: {
    bg: string;
    secondary: string;
    accent: string;
    text: string;
  };
  isActive: boolean;
  onClick: () => void;
}

export function ThemePreviewCard({
  label,
  description,
  colors,
  isActive,
  onClick,
}: ThemePreviewCardProps) {
  return (
    <button
      onClick={onClick}
      className={cn(
        "relative flex flex-col gap-2 rounded-lg border-2 p-3 transition-all duration-200",
        "hover:shadow-md hover:-translate-y-0.5",
        isActive
          ? "border-dg-blue ring-2 ring-dg-blue/20"
          : "border-dg-border hover:border-dg-border-light"
      )}
      aria-pressed={isActive}
    >
      {/* 预览色块区域 */}
      <div
        className="h-16 w-full rounded-md flex items-end justify-end p-1.5 gap-1"
        style={{ backgroundColor: colors.bg }}
      >
        <div
          className="w-4 h-4 rounded-sm"
          style={{ backgroundColor: colors.secondary }}
        />
        <div
          className="w-4 h-4 rounded-sm"
          style={{ backgroundColor: colors.accent }}
        />
        <div
          className="w-4 h-4 rounded-sm"
          style={{ backgroundColor: colors.text }}
        />
      </div>

      {/* 文字信息 */}
      <div className="text-left">
        <p className="text-sm font-medium text-dg-text-primary">{label}</p>
        <p className="text-xs text-dg-text-muted">{description}</p>
      </div>

      {/* 选中标记 */}
      {isActive && (
        <div className="absolute -top-1.5 -right-1.5 w-5 h-5 bg-dg-blue rounded-full flex items-center justify-center">
          <Check className="w-3 h-3 text-white" />
        </div>
      )}
    </button>
  );
}

AppearanceSettings 页面中的主题选择区域布局:

// 在 AppearanceSettings 页面中使用
import { useTheme, THEMES } from '@/hooks/useTheme';
import { ThemePreviewCard } from '@/components/settings/ThemePreviewCard';

// 各主题预览色值配置
const THEME_PREVIEW_COLORS: Record<string, { bg: string; secondary: string; accent: string; text: string }> = {
  'ocean-deep':       { bg: '#070d19', secondary: '#0f172a', accent: '#3b82f6', text: '#f8fafc' },
  'light-professional': { bg: '#fafaf9', secondary: '#f5f5f4', accent: '#2563eb', text: '#1c1917' },
  'midnight-purple':  { bg: '#0a0612', secondary: '#130c21', accent: '#c084fc', text: '#faf5ff' },
  'pine-green':       { bg: '#0a1210', secondary: '#111f1a', accent: '#4ade80', text: '#f0fdf4' },
  'orange-vitality':  { bg: '#110d08', secondary: '#1d1610', accent: '#fb923c', text: '#fef7ed' },
};

function ThemeSelector() {
  const { theme: activeTheme, setTheme } = useTheme();

  return (
    <div className="space-y-3">
      <h3 className="text-sm font-medium text-dg-text-primary">界面主题</h3>
      <div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-5 gap-3">
        {THEMES.map((t) => (
          <ThemePreviewCard
            key={t.id}
            themeId={t.id}
            label={t.label}
            description={t.description}
            colors={THEME_PREVIEW_COLORS[t.id]}
            isActive={activeTheme === t.id}
            onClick={() => setTheme(t.id)}
          />
        ))}
      </div>
    </div>
  );
}

4.7.4 localStorage 持久化方案

主题持久化遵循以下策略:

策略项 说明
存储键名 sqlp-theme-id,与现有 theme 键分离,避免升级冲突
存储值 主题标识字符串,如 ocean-deep、light-professional
回退逻辑 读取不到有效值时,检测 prefers-color-scheme 系统偏好,暗色默认 ocean-deep,亮色默认 light-professional
版本兼容 首次升级时,若检测到旧版 theme: "dark" 键,自动迁移至 ocean-deep;theme: "light" 迁移至 light-professional,随后删除旧键
异常处理 localStorage 不可用时(隐私模式、存储已满),降级为仅内存状态,不抛出错误
跨设备同步 通过 storage 事件监听同源其他标签页的主题变更,实现多标签页同步:
/**
 * 跨标签页主题同步
 */
useEffect(() => {
  const handler = (e: StorageEvent) => {
    if (e.key === STORAGE_KEY && e.newValue) {
      const newTheme = e.newValue as ThemeId;
      if (THEMES.some(t => t.id === newTheme)) {
        setThemeState(newTheme);
        document.documentElement.setAttribute('data-theme', newTheme);
      }
    }
  };
  window.addEventListener('storage', handler);
  return () => window.removeEventListener('storage', handler);
}, []);

4.7.5 系统主题偏好检测

平台需响应操作系统的 prefers-color-scheme 媒体查询,提供智能化的默认主题推荐。

检测逻辑:

// 检测系统暗色/浅色偏好
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
const prefersLight = window.matchMedia('(prefers-color-scheme: light)').matches;

偏好映射规则:

系统偏好 默认推荐主题 推荐逻辑
prefers-color-scheme: dark 深蓝专业 (ocean-deep) 暗色系统偏好用户通常偏好深色界面
prefers-color-scheme: light 浅色商务 (light-professional) 亮色系统偏好用户通常偏好浅色界面
未指定(no-preference) 深蓝专业 (ocean-deep) 默认采用平台默认主题

动态响应机制:

/**
 * 系统主题偏好动态响应
 * 仅在用户未手动设置主题时自动跟随系统
 */
useEffect(() => {
  const darkQuery = window.matchMedia('(prefers-color-scheme: dark)');
  const lightQuery = window.matchMedia('(prefers-color-scheme: light)');

  const handleChange = () => {
    const hasUserPreference = localStorage.getItem(STORAGE_KEY) !== null;
    if (hasUserPreference) return; // 用户已手动选择,不自动切换

    if (darkQuery.matches) {
      setTheme('ocean-deep');
    } else if (lightQuery.matches) {
      setTheme('light-professional');
    }
  };

  darkQuery.addEventListener('change', handleChange);
  lightQuery.addEventListener('change', handleChange);

  return () => {
    darkQuery.removeEventListener('change', handleChange);
    lightQuery.removeEventListener('change', handleChange);
  };
}, [setTheme]);

重要原则:系统偏好仅影响首次访问或清除 localStorage 后的默认主题选择。一旦用户手动选择了主题,该选择将被持久化并优先于系统偏好,避免在用户主动选择后因系统时间变化(如 macOS 自动切换暗色模式)导致界面意外变更。

4.7.6 切换过渡动效

主题切换时应提供平滑的视觉过渡,避免色彩突变带来的不适感。

/* 全局主题过渡效果 —— 添加到 index.css */
html {
  /* 对所有 CSS 变量变化应用 200ms 缓动过渡 */
  transition: background-color 200ms ease, color 200ms ease;
}

html *, html *::before, html *::after {
  /* 谨慎使用:仅对关键属性过渡,避免性能问题 */
  transition: background-color 200ms ease,
              border-color 200ms ease,
              color 150ms ease;
}

/* 可选:切换瞬间的淡入效果 */
[data-theme] {
  animation: themeFadeIn 200ms ease;
}

@keyframes themeFadeIn {
  from { opacity: 0.92; }
  to   { opacity: 1; }
}

性能优化建议:全局 * 过渡规则可能影响大型列表渲染性能。生产环境中建议仅对需要过渡的元素添加过渡规则,或通过 JavaScript 在切换主题时临时添加过渡 class,切换完成后再移除:

/**
 * 带过渡动画的主题切换
 */
const setThemeWithTransition = useCallback((newTheme: ThemeId) => {
  const root = document.documentElement;
  root.classList.add('theme-transitioning');
  setTheme(newTheme);
  // 300ms 后移除过渡 class
  setTimeout(() => {
    root.classList.remove('theme-transitioning');
  }, 300);
}, [setTheme]);
/* 仅在切换时启用过渡 */
html.theme-transitioning,
html.theme-transitioning * {
  transition: background-color 200ms ease,
              border-color 200ms ease,
              color 150ms ease !important;
}

以上实现方案确保五套主题能够稳定、高效、优雅地在平台中运行,为用户提供丰富的个性化选择。

5. 核心功能页面优化方案

本章针对 SQL-Platform 三个核心功能模块——审批变更(工单系统)、查询数据、系统设置——进行逐页拆解分析,提出可落地的 UI/UX 优化方案。每个方案均包含具体的组件选型、布局参数、交互细节与视觉规范,确保开发团队能够直接转化为实现任务。


5.1 审批变更工单系统优化

审批变更是平台的核心业务流,涉及工单列表、工单创建、工单详情三个主要页面。当前实现存在布局松散、信息层级不清、操作流程割裂等问题。以下按页面逐一给出优化方案。

5.1.1 工单列表页优化

组件选型与布局重构

将原生 <table> 全面替换为 @tanstack/react-table + shadcn/ui DataTable 组合。DataTable 内置列排序、列过滤、行选择、分页等能力,可显著减少 742 行中的手写逻辑。表格容器采用 rounded-xl border border-border/60 bg-card 样式,阴影参数为 shadow-sm。

状态卡片升级

原状态卡片(全部/待审批/执行中/完成/失败)改为 Stats Cards。每张卡片包含:状态图标(Lucide)、工单数量(text-2xl font-bold)、环比趋势箭头(绿色上升/红色下降)、环比百分比(text-xs text-muted-foreground)。卡片使用 cn() 动态绑定激活状态的边框色——激活时边框为 ring-2 ring-primary/40 border-primary,未激活为 border-transparent hover:border-border/80。卡片网格采用 grid grid-cols-5 gap-4,在 < 1024px 下退化为 grid-cols-3 + grid-cols-2。

高级筛选栏

在状态卡片下方新增折叠式筛选栏,包含以下控件:

筛选项 组件 说明
实例 Select(多选) 从实例列表异步加载
数据库类型 Select(多选) MySQL/PostgreSQL/MongoDB 等
提交人 Input + Avatar 支持用户名/邮箱搜索
时间范围 DateRangePicker shadcn/ui 官方组件
优先级 ToggleGroup 高/中/低三态切换

筛选栏默认折叠,点击"高级筛选"按钮展开,展开高度动画 duration-300 ease-in-out。所有筛选条件通过 URL query params 同步,支持刷新后状态保持。

表格交互增强

  • 优先级标识:每行左侧增加 4px 宽色条,高=bg-red-500、中=bg-amber-500、低=bg-slate-400,使用绝对定位贴左边框。
  • 行 Hover 效果:hover:bg-muted/60,同时左侧出现 border-l-2 border-primary 指示条,过渡 transition-colors duration-150。
  • 操作列优化:将分散的查看/通过/拒绝/执行/撤销按钮收缩为图标按钮组(Eye, CheckCircle, XCircle, Play, RotateCcw)。当操作超过 3 个时,溢出部分收入 DropdownMenu。按钮使用 variant="ghost" size="icon"。
  • 批量操作:顶部新增批量操作栏,选中行后出现(AnimatePresence 滑入),包含"批量审批"、"批量拒绝"、"导出选中"三个主操作,危险操作使用 variant="destructive"。
  • 列自定义:列头右键菜单支持"隐藏此列"、"重置列宽",列宽拖拽使用 react-resizable 实现,拖拽手柄 4px 宽,hover 时显示 bg-primary/30。

搜索与分页

搜索框使用 Input + Search 图标,支持 Cmd+K 快捷键聚焦。分页组件采用 shadcn/ui Pagination,页码按钮尺寸 h-9 w-9,当前页 bg-primary text-primary-foreground。每页条数可选 10/25/50/100。


5.1.2 工单创建页优化

布局重构:左右分栏

将当前 1192 行的单栏布局改为左右分栏:左侧编辑器区占 w-3/5(60%),右侧辅助信息面板占 w-2/5(40%),中间分隔线 border-r border-border/60。两栏均独立滚动,避免编辑器滚动时右侧信息丢失。分栏在 < 1280px 下退化为上下堆叠布局。

步骤指示器(Step Indicator)

在分栏顶部增加横向步骤条,共三步:

① 填写信息  ──→  ② SQL 审核  ──→  ③ 提交审批

步骤条使用 shadcn/ui 风格的自定义组件。已完成步骤圆圈 bg-primary text-primary-foreground,当前步骤 bg-primary ring-4 ring-primary/20,未到达步骤 bg-muted text-muted-foreground。连线使用 h-0.5 w-16,已完成段 bg-primary,未完成段 bg-border。步骤切换允许回退(已填写内容保留),但前进需通过当前步骤的验证(如 SQL 审核必须通过才能到第三步)。

左侧编辑器区

  • 顶部工具栏:实例选择 + 数据库选择使用 Popover + Command 组合(支持搜索筛选),定时执行使用 Switch + DateTimePicker,格式化按钮(AlignLeft 图标)使用 variant="outline" size="sm"。工具栏使用 flex items-center gap-3 p-3 border-b border-border/60 bg-muted/30。
  • SQL 编辑器:保留 CodeMirror 6,新增配置:行号显示(lineNumbers())、迷你地图(@codemirror/view showMinimap 或自定义 SVG 缩略图)、当前行高亮(highlightActiveLineGutter())。编辑器高度 h-[480px],边框 border-0(融入容器)。
  • SQL 模板快捷插入:编辑器左下角增加"模板"按钮,点击弹出 Popover,内含分类模板列表(DDL/DML/查询),点击后 editor.dispatch() 插入模板 SQL。

右侧辅助面板

  • 审核结果卡片:使用三个独立卡片分别展示 Error(bg-red-50 border-red-200)、Warning(bg-amber-50 border-amber-200)、Info(bg-blue-50 border-blue-200)。每张卡片包含:数量 badge(rounded-full px-2 py-0.5 text-xs font-medium)、发现项列表(每项包含行号、规则名、描述)、展开/折叠按钮。Error 卡片默认展开,其他折叠。
  • 变更原因输入:将 <textarea> 替换为轻量级 Markdown 编辑器(react-simplemde-editor 或自定义实现),支持粗体、斜体、代码块、列表快捷按钮。高度 min-h-[120px],占位符文本"请描述本次变更的业务原因和影响范围..."
  • 工单预览:在提交按钮左侧增加"预览"按钮,点击弹出 Dialog(宽 max-w-3xl),展示完整工单信息的只读视图,包括 SQL 摘要、变更原因、元数据。
  • 自动保存草稿:每 30 秒将表单状态序列化到 localStorage,键名为 ticket_draft_${userId}。页面加载时检测草稿,若有则提示"检测到未提交的草稿,是否恢复?"(Toast + 操作按钮)。提交成功后清除草稿。

5.1.3 工单详情页优化

头部 Hero Section

将原头部改为 Hero Section 风格:

  • 背景:bg-gradient-to-r from-primary/5 via-primary/3 to-transparent,底部边框 border-b border-border/60。
  • 标题:text-3xl font-bold tracking-tight,状态 badge 紧跟标题右侧,badge 尺寸加大(text-sm px-3 py-1)。
  • 元信息行:flex gap-6 text-sm text-muted-foreground,包含提交人(Avatar + 用户名)、提交时间(Calendar 图标)、实例名(Database 图标)、数据库(Table 图标)。
  • 操作按钮组:头部右侧放置主操作按钮,审批通过用 variant="default"(CheckCircle 图标),拒绝用 variant="destructive"(XCircle 图标),执行用 variant="outline"(Play 图标)。操作按钮根据当前状态条件渲染(如已完成的工单不显示"通过"按钮)。

审批流程 Timeline

将原圆形+连线流程改为横向 Timeline:

[创建]──→[待审批]──→[已通过]──→[执行中]──→[已完成]
  张三      李四       王五       系统        系统
 10:23    14:05      16:30      16:31      16:33
  • 节点形状:操作步骤使用圆形(w-10 h-10 rounded-full),判断步骤(如"审核通过?")使用菱形(CSS rotate(45deg) 实现,内部图标反向旋转)。
  • 当前节点:ring-4 ring-primary/30 bg-primary text-white,带脉冲动画(animate-pulse 或自定义 CSS keyframes scale 1→1.05→1)。
  • 已完成连线:h-0.5 bg-primary。
  • 未完成连线:h-0.5 bg-border border-dashed。
  • 鼠标悬停节点时,弹出 Tooltip 显示步骤详情(操作人、时间、备注)。

内容区域

  • SQL 内容区:只读编辑器上方增加工具栏,包含"复制"(Copy 图标,点击后图标变为 Check 持续 2 秒)、"下载 SQL"(Download 图标)、"全屏查看"(Maximize 图标)。
  • 执行结果 Dashboard:将原本平铺的执行结果改为 Dashboard 风格,使用 grid grid-cols-4 gap-4 布局:
    • 执行状态:大图标 + 状态文字,成功 text-green-600、失败 text-red-600。
    • 影响行数:text-3xl font-bold + "行" 单位。
    • 执行耗时:text-3xl font-bold + "秒" 单位。
    • 警告数:如有则显示 badge,无则显示 "-" 并置灰。
  • 回滚 SQL:使用 Accordion 包裹,默认折叠,类型为 type="single" collapsible。展开后显示只读编辑器 + 复制按钮。
  • 审批意见输入:在审批流程下方新增意见输入区,使用 Textarea + 提交按钮。支持 @ 提及功能(输入 @ 后弹出用户列表 Popover,使用 Command 组件筛选)。已提交的审批意见以评论卡片形式展示,包含 Avatar、用户名、时间、意见内容。
  • 关联工单 Carousel:将关联工单列表改为横向滚动卡片组,每张卡片显示工单标题、状态 badge、最后更新时间。使用 scroll-snap-type: x mandatory 实现平滑滚动,左右箭头按钮控制滚动。
  • 操作日志时间轴:左侧竖线 w-px bg-border absolute left-4 top-0 bottom-0,每个日志节点包含:圆点(操作类型颜色编码)、时间(text-xs text-muted-foreground)、操作描述、操作人 Avatar。

5.1.4 审批流程可视化优化

流程图实现方案

推荐采用自定义 SVG 方案(相比 Mermaid.js 更轻量、更易定制样式)。流程图容器使用 aspect-[16/9] max-h-[360px],SVG 内部使用 <defs> 定义箭头标记和渐变色。

节点设计

节点类型 形状 尺寸 颜色
开始/结束 圆角矩形(rx=20) 100×48px fill: hsl(var(--primary))
操作步骤 圆角矩形(rx=8) 140×56px fill: hsl(var(--card)), stroke: hsl(var(--border))
判断节点 菱形(polygon) 112×80px fill: hsl(var(--amber-50)), stroke: hsl(var(--amber-400))
当前步骤 任意形状 + 脉冲 基础尺寸 ring + animate-pulse

节点文本居中显示,使用 text-sm font-medium fill-foreground。当前步骤节点的边框使用 stroke: hsl(var(--primary)),线宽 stroke-width: 2.5,并附加 SVG <animate> 脉冲效果(opacity 0.6→1→0.6,周期 2s)。

连线设计

  • 正常路径:stroke: hsl(var(--primary)),stroke-width: 2,带箭头标记。
  • 回退路径(如"拒绝"分支):stroke: hsl(var(--destructive)),stroke-width: 2,stroke-dasharray: 6 3,箭头标记颜色同步。
  • 条件分支标签:连线中点放置文本标签,如"通过"/"拒绝",使用 text-xs fill-muted-foreground,背景白色矩形 fill: hsl(var(--background)) 防止线穿过文字。
  • 鼠标悬停连线时,线宽动画增至 stroke-width: 3,同时显示 Tooltip 说明该路径的触发条件。

交互增强

  • 点击任意节点,右侧(或下方)弹出详情面板,显示该步骤的完整信息(操作人、操作时间、耗时、备注、相关规则)。
  • 流程图支持缩放(鼠标滚轮,范围 0.5x-2x)和平移(拖拽),使用 transform 属性实现。
  • 提供"重置视图"按钮,点击后平滑动画回归初始缩放和位置。
  • 当前正在进行的步骤,节点右上角显示旋转的 Loader2 图标(animate-spin)。

5.2 查询数据页面优化

查询数据页面(QueriesPage.tsx,896 行)是开发人员最高频使用的功能模块。当前页面在编辑器体验、结果展示、导航效率方面均有较大提升空间。

5.2.1 SQL 编辑器优化

主题同步

CodeMirror 6 编辑器主题通过 Compartment 动态切换,监听 theme 状态变化:

const themeCompartment = new Compartment();
// 深色主题使用 oneDark,浅色使用 lightTheme
const themeExtension = theme === 'dark' ? oneDark : lightTheme;
view.dispatch({ effects: themeCompartment.reconfigure(themeExtension) });

编辑器背景色使用 bg-card,确保与页面主题无缝融合。光标颜色使用 caret-color: hsl(var(--primary))。

全屏编辑模式

监听 F11 按键(preventDefault 阻止浏览器默认全屏),触发自定义全屏:编辑器容器使用 fixed inset-0 z-50 bg-background 定位,动画 animate-in zoom-in-95 duration-200。全屏模式下隐藏侧边栏,工具栏收缩为单行。退出方式:再次按 F11 或点击右上角 Minimize 图标按钮。使用 useHotkeys hook 管理快捷键。

代码折叠

启用 CodeMirror 6 内置的 @codemirror/language foldGutter 扩展,折叠指示器使用 &bull; 或自定义图标(ChevronDown/ChevronRight)。折叠区域背景色使用 bg-muted/30 以视觉区分。

多标签页支持

在编辑器顶部增加标签页栏,高度 h-10,样式类似 VS Code:

  • 活动标签:bg-card border-t-2 border-primary text-foreground。
  • 非活动标签:bg-muted/50 border-t-2 border-transparent text-muted-foreground hover:bg-muted。
  • 标签内容:查询前 20 个字符 + 未保存指示点(bg-primary rounded-full w-2 h-2)。
  • 关闭按钮:标签 hover 时显示 X 图标,size="sm" variant="ghost"。
  • 新建标签:右侧 + 按钮,点击后创建空查询标签。
  • 标签状态存储在 zustand store 中,切换标签时通过 editor.dispatch 替换文档内容。

查询参数化

支持 :paramName 语法,编辑器通过自定义 MatchDecorator 高亮参数(background: hsl(var(--primary)/0.15), border-radius: 3px)。编辑器下方增加参数输入区:自动解析 SQL 中的参数,为每个参数生成 Input 字段(标签 = 参数名,占位符 = 数据类型推断)。参数值随查询一起提交到后端。


5.2.2 结果展示优化

DataTable 替换

将原生结果表格替换为 shadcn/ui DataTable(@tanstack/react-table)。关键配置:

  • enableColumnResizing: true:列宽可拖拽调整,拖拽手柄在列头右侧边缘,hover 时显示 w-1 h-full bg-primary/30。
  • enableSorting: true:点击列头排序,排序状态循环:无 → 升序 → 降序 → 无。排序图标使用 ArrowUpDown,激活时根据方向切换为 ArrowUp 或 ArrowDown。
  • enableRowSelection: true:支持行选择,批量导出。

列头固定(Sticky Header)

结果表格容器设置 max-h-[480px] overflow-auto,表头使用 sticky top-0 z-10 bg-card border-b 固定,确保纵向滚动时列头始终可见。

大数据量虚拟滚动

当结果行数超过 1000 行时,启用 @tanstack/react-virtual 虚拟滚动。虚拟滚动配置:

  • estimateSize: () => 40:每行预估高度 40px。
  • overscan: 10:上下各预渲染 10 行。
  • 行元素使用绝对定位 position: absolute; top: 0; transform: translateY(${virtualRow.start}px)。

单元格内容预览

  • 文本/数字列:超出列宽时显示 truncate,hover 时弹出 Popover 展示完整内容,Popover 最大宽度 max-w-sm,支持文本选择。
  • JSON 列:使用 lucide-react 的 Braces 图标标识,内容折叠显示 {...} 或 [...](包含元素个数 badge)。点击展开后使用 react-json-view 或自定义树形组件格式化展示,支持层级折叠。
  • NULL 值:特殊样式 text-muted-foreground italic 显示 "NULL"。

结果导出

工具栏增加导出 DropdownMenu,包含:

格式 说明
CSV 逗号分隔,包含 BOM 解决中文乱码
JSON 对象数组格式,字段名作为 key
Excel 通过 xlsx 库生成 .xlsx 文件

导出范围支持:当前页 / 全部结果 / 选中行。大数据量导出(>10,000 行)时显示进度条 Dialog。

结果图表视图

在结果表格上方增加视图切换 ToggleGroup:Table / BarChart / LineChart。

  • 柱状图:使用 recharts 的 <BarChart>,选择两列(X 轴 + Y 轴)后渲染。X 轴标签旋转 45° 防止重叠。
  • 折线图:使用 <LineChart>,适合时间序列数据。支持多系列(选择多个数值列)。
  • 图表容器高度 h-[360px],空状态显示 BarChart3 图标 + "请选择数据列生成图表" 提示。
  • 图表配置面板:X 轴选择、Y 轴选择(可多选)、图表标题输入。

5.2.3 侧边栏导航优化

Schema 树搜索过滤

Schema 侧边栏顶部增加搜索输入框(Input size="sm" + Search 图标),实时过滤数据库/表/字段名。过滤逻辑:

  • 匹配数据库名:展示该数据库下所有表。
  • 匹配表名:展示该表及其所在数据库(其他数据库折叠)。
  • 匹配字段名:展示该字段及其所属表和数据库。

使用 fuse.js 实现模糊搜索,配置 threshold: 0.3, keys: ['name']。

表节点增强

每个表节点左侧显示 Table2 图标(w-4 h-4 text-muted-foreground),右侧显示行数 badge(rounded-full bg-muted px-1.5 py-0 text-[10px] text-muted-foreground)。行数从 INFORMATION_SCHEMA 或后端缓存获取,显示 "~" 前缀表示近似值。

收藏表功能

表节点 hover 时显示星标按钮(Star 图标,size="sm" variant="ghost"),点击切换收藏状态。已收藏的表显示实心 Star 图标(fill: hsl(var(--amber-400)))。收藏表固定在侧边栏顶部"收藏"分组下,分组标题使用 text-xs font-semibold text-muted-foreground uppercase tracking-wider。

最近访问表

在"收藏"分组下方增加"最近访问"分组,最多展示 10 个最近点击过的表,按访问时间倒序。每个表项显示访问时间相对文本(2分钟前)。使用 lru-cache 或简单的 Map 实现客户端缓存,持久化到 localStorage。

拖拽插入 SQL

实现从 Schema 树拖拽表名/字段名到编辑器:

  • 拖拽开始:设置 dataTransfer.setData('text/plain', tableName),拖拽图像为半透明标签显示表名。
  • 拖拽经过编辑器:显示插入位置指示线。
  • 拖拽释放:根据拖拽对象类型插入不同内容:
    • 表名:插入 `tableName`
    • 字段名:插入 `tableName`.`columnName`
    • 数据库名:插入 `databaseName`

使用 CodeMirror 6 的 dropCursor 扩展显示插入位置。


5.2.4 历史记录优化

分类标签页

将历史记录抽屉中的列表改为 shadcn/ui Tabs 分类:

  • 全部(History 图标):所有查询记录。
  • 成功(CheckCircle 图标,绿色):执行成功的查询。
  • 失败(XCircle 图标,红色):执行失败的查询。
  • 收藏(Star 图标,琥珀色):用户收藏的查询。

每个标签显示记录数量 badge。标签切换使用 AnimatePresence 实现内容淡入淡出。

记录项增强

每条历史记录显示:

  • 左侧:状态圆点(成功 bg-green-500、失败 bg-red-500),直径 8px。
  • 标题行:查询前 40 个字符 + 执行时间 badge(rounded-md bg-muted px-1.5 py-0.5 text-xs,如 "23ms"、"1.2s")。
  • 展开/折叠:点击记录行展开 SQL 预览(ChevronDown / ChevronRight 图标指示),展开的 SQL 使用只读 CodeMirror 实例(高度 max-h-[120px])或 <pre> 标签 + 语法高亮。
  • 操作按钮组(hover 显示):"运行"(Play 图标,将 SQL 插入编辑器并执行)、"插入"(CornerDownLeft 图标,仅插入编辑器)、"收藏"(Star 图标)、"删除"(Trash2 图标,variant="ghost" 且 hover 变红)。

一键对比功能

在历史记录操作菜单中增加"对比"选项。选择两条记录后,弹出对比 Dialog(宽 max-w-5xl),使用左右分栏展示两条 SQL 的差异。差异高亮使用 diff-match-patch 库或自定义实现:

  • 相同行:正常显示。
  • 删除行:背景 bg-red-50,左侧红色竖线标记。
  • 新增行:背景 bg-green-50,左侧绿色竖线标记。
  • 修改行:字符级差异高亮,删除字符 bg-red-200 line-through,新增字符 bg-green-200。

文件夹/标签组织

允许用户创建自定义文件夹("日常巡检"、"业务报表"等)来组织历史查询。文件夹使用侧边栏树形结构展示,支持:

  • 新建文件夹:输入名称,使用 Dialog + Input。
  • 拖拽排序:文件夹和历史记录均支持拖拽重新排序。
  • 颜色标签:每个文件夹可选颜色标签(8 种预设色),用于快速视觉识别。
  • 搜索:文件夹和历史记录支持全文搜索(SQL 内容 + 备注)。

5.3 系统设置页面优化

当前设置页面(SettingsPage.tsx 仅 56 行)结构过于简单,PlatformConfigPage.tsx 和 AppearanceSettings.tsx 也存在分类不清晰、交互不一致的问题。

5.3.1 设置分类重组

新分类结构

将原有分类重新组织为 7 个一级分类,每个分类分配独立路由(/settings/:category):

分类 路由 包含内容 图标
外观设置 /settings/appearance 主题/语言/密度/字体/圆角/自定义CSS Palette
安全设置 /settings/security 密码策略/登录限制/2FA/IP白名单 Shield
通知设置 /settings/notifications 渠道配置/模板管理/订阅规则 Bell
执行设置 /settings/execution 超时/LIMIT/并发/执行器配置 PlayCircle
审计设置 /settings/audit 保留策略/敏感词/审计级别 ClipboardList
集成设置 /settings/integrations LDAP/Webhook/API密钥 Plug
高级设置 /settings/advanced 清理/备份/日志/导入导出 Cog

导航布局

设置页面采用左右布局:左侧导航栏宽 w-64(固定),右侧内容区 flex-1。左侧导航使用 shadcn/ui 的 ScrollArea 包裹,每个导航项包含图标(w-4 h-4)+ 分类名 + 设置项数量 badge(如"12 项")。当前激活项使用 bg-primary/10 text-primary border-r-2 border-primary。

导航栏底部放置"危险区域"入口(AlertTriangle 图标,红色文字),包含恢复默认设置、清除所有数据等操作。


5.3.2 表单交互优化

统一表单架构

所有设置表单统一使用 shadcn/ui Form + react-hook-form + zod 验证。每个设置项包裹在卡片组件中:

┌─────────────────────────────────────┐
│ 设置项标题                           │  ← text-base font-medium
│ 设置项描述,说明此配置的作用和影响范围    │  ← text-sm text-muted-foreground
│                                     │
│ [输入控件]                    [重置] │  ← 控件右对齐,重置按钮变灰
└─────────────────────────────────────┘

卡片样式:rounded-xl border border-border/60 bg-card p-5 shadow-sm,hover 时 shadow-md transition-shadow duration-200。卡片间距 space-y-4。

设置项搜索

页面顶部增加全局搜索栏(Input + Search 图标 + Command+K 快捷键),支持跨分类搜索设置项。搜索范围包括:设置项标题、描述、关键词标签。搜索结果按分类分组展示,点击后滚动定位到对应设置项并高亮(bg-primary/10 闪烁 2 秒后消退)。使用 fuse.js 实现,threshold: 0.25。

变更指示与保存

  • 变更指示:每个设置卡片在值发生变化时,底部显示 "已修改" 文字(text-xs text-amber-600)+ "撤销" 链接,同时卡片顶部边框变为 border-t-2 border-amber-400。
  • 保存策略:每个分类页右下角固定悬浮保存栏(sticky bottom-6),包含"保存更改"(variant="default")和"重置"(variant="outline")按钮。保存栏使用 bg-card border shadow-lg rounded-xl px-6 py-3。
  • 自动保存:可选开启,每 5 秒自动保存已变更的设置(防抖处理)。
  • 保存成功:顶部显示 Toast("设置已保存",CheckCircle 图标),保存按钮短暂变为成功状态(文字 "已保存" + Check 图标,2 秒后恢复)。

变更历史

在设置页面右上角增加"变更历史"按钮(History 图标),点击后弹出 Sheet(右侧滑出,宽 w-[480px])。历史记录展示最近 50 条设置变更,每条包含:

  • 时间(相对格式,如"3 分钟前")
  • 分类名
  • 设置项标题
  • 旧值 → 新值(差异高亮)
  • 操作人 Avatar + 用户名

历史记录数据由后端提供,前端使用 useSWR 轮询获取(间隔 30 秒)。

危险设置二次确认

对于高风险设置项(如"允许删除生产环境数据"、"关闭审计日志"),使用以下交互设计:

  • 卡片边框:border-red-200。
  • 卡片顶部:bg-red-50 px-5 py-2 -mx-5 -mt-5 mb-3 rounded-t-xl border-b border-red-100,内含 AlertTriangle 图标 + "危险设置" 警告文字。
  • 开关/输入控件旁增加确认 Dialog,用户点击变更时弹出,要求输入操作密码或二次确认文字(如"请输入 DELETE 确认")。

设置导入/导出

在高级设置分类中增加导入/导出功能:

  • 导出:将所有设置序列化为 JSON 文件下载,包含元数据(导出时间、版本、操作人)。
  • 导入:拖拽或选择 JSON 文件,预览变更内容(使用 diff 视图展示与当前设置的差异),确认后应用。导入前自动创建备份点。

5.3.3 安全设置增强

密码策略配置

新增密码策略卡片,包含以下设置项:

设置项 控件 默认值 验证规则
最小密码长度 Slider (6-32) + Input 8 zod min(6).max(32)
必须包含大写字母 Switch true —
必须包含小写字母 Switch true —
必须包含数字 Switch true —
必须包含特殊字符 Switch false —
密码过期时间(天) Input (数字) 90 min(1).max(365)
历史密码禁止 reuse 次数 Slider (0-10) 3 min(0).max(10)

密码强度实时预览:下方显示密码示例框,输入测试密码后动态显示强度条(红/黄/绿三段)和文字评价("弱"/"中"/"强")。

登录失败锁定策略

  • 启用开关:Switch,默认开启。
  • 最大尝试次数:Slider (3-10),默认 5 次。
  • 锁定时间:Select(5分钟/15分钟/30分钟/1小时/手动解锁),默认 15 分钟。
  • 通知管理员:Switch,锁定事件发生时通知管理员。

IP 白名单配置

使用 Textarea 输入,每行一个 IP 或 CIDR 段(如 192.168.1.0/24)。实时验证输入格式,非法格式行显示红色下划线 + Tooltip 说明错误原因。支持从文件导入(.txt 或 .csv 格式)。

白名单列表以标签云形式展示,每个 IP 标签可单独删除(X 图标)。空状态时显示 Globe 图标 + "未配置 IP 白名单,允许所有 IP 访问" 提示。

Session 超时设置

  • 超时时间:Select(15分钟/30分钟/1小时/2小时/4小时/8小时/永不),默认 30 分钟。
  • 超时前警告:Switch,在超时前 2 分钟弹出警告 Dialog,提供"保持登录"按钮。
  • 多设备登录限制:Select(不限制/1台/2台/3台),默认不限制。

操作审计级别配置

使用 RadioGroup 三选一:

  • 基础:仅记录登录/登出和审批操作。
  • 标准(默认):额外记录查询执行和设置变更。
  • 完整:记录所有操作包括 Schema 浏览和导出行为。

每个级别旁显示预估日志存储量(基于当前使用量计算)。


5.3.4 外观设置增强

主题实时预览

主题模式切换(深色/浅色/跟随系统)改为实时预览:用户点击选项后立即应用主题(document.documentElement.classList),但此时不保存到后端。页面显示"主题已临时切换,保存以永久生效"的提示条(bg-primary/10 border border-primary/20 rounded-lg px-4 py-2 mb-4),包含"保存"和"恢复"按钮。用户刷新页面后未保存的主题恢复为已保存值。

界面密度选择

新增三档密度选择,使用 ToggleGroup 展示:

密度 效果 适用场景
紧凑 (compact) 表格行高 32px,卡片内边距 16px,字体 13px 数据密集型用户
默认 (default) 表格行高 40px,卡片内边距 20px,字体 14px 平衡
舒适 (comfortable) 表格行高 48px,卡片内边距 24px,字体 14px 触控设备或视力辅助

选择后立即生效,通过 CSS 变量控制:--density-factor: 0.8 | 1.0 | 1.2,各组件使用 calc() 基于该变量计算尺寸。

侧边栏宽度可调

在"布局"子分类中增加侧边栏宽度设置:

  • 控件:Slider (200px-400px),默认 280px。
  • 实时预览:拖动滑块时侧边栏宽度实时变化。
  • 重置按钮:点击恢复默认 280px。

字体大小设置

  • 基础字体大小:Select(12px/13px/14px/15px/16px),默认 14px。
  • 代码字体大小:Select(12px/13px/14px/15px/16px),默认 14px。
  • 代码字体族:Select('JetBrains Mono', 'Fira Code', 'Source Code Pro', 'monospace'),默认 'JetBrains Mono'。

字体族选择时,预览文字实时展示该字体的渲染效果。

圆角风格设置

使用三个可视化选项卡片:

风格 CSS 变量 视觉描述
锐利 (sharp) --radius: 0px 直角,无圆角
默认 (default) --radius: 0.5rem 中等圆角,卡片 8px
圆润 (rounded) --radius: 0.75rem 大圆角,卡片 12px

每个选项卡片展示缩略图:一个矩形 + 一个按钮 + 一个 badge,使用对应圆角值渲染。选中状态 ring-2 ring-primary。

自定义 CSS(高级用户)

在"高级"子分类中增加自定义 CSS 输入区:

  • 使用 CodeMirror 6 编辑器(只开启基础高亮,无语法检查),高度 min-h-[200px]。
  • 占位符:"/* 在此输入自定义 CSS,将覆盖默认样式 /\n\n.custom-sidebar {\n / your styles */\n}"。
  • 实时预览开关:Switch,开启时 CSS 立即应用到页面。
  • 安全提示:输入区上方显示 Info 图标 + "自定义 CSS 仅影响当前用户,错误样式可能导致页面显示异常" 警告。
  • CSS 作用域:通过 Shadow DOM 或 style 标签 + 特定 class 前缀隔离,避免影响全局样式。

登录页风格选择

将原有的 5 个缩略图卡片优化为:

  • 卡片尺寸:aspect-[16/10] rounded-lg overflow-hidden border-2。
  • 选中状态:border-primary ring-2 ring-primary/20。
  • 缩略图内容:使用实际登录页的静态截图或实时 iframe 预览。
  • 卡片底部:风格名称 + 简短描述。
  • 新增"上传自定义背景"按钮:支持上传图片(JPG/PNG,最大 5MB),上传后显示预览和"删除"按钮。

以上优化方案覆盖了审批变更、查询数据、系统设置三大核心模块的全部关键页面。每个方案均提供了具体的组件选型、布局参数、交互细节和视觉规范,可直接转化为前端开发任务。建议按照"查询数据(高频使用)→ 审批变更(核心流程)→ 系统设置(低频但影响全局)"的优先级顺序逐步实施。

6. 菜单体系全面优化

菜单导航是SQL-Platform最核心的交互骨架,直接影响用户的工作流效率和平台整体体验。当前菜单体系存在分组逻辑不够清晰、图标体系不统一、权限控制粒度较粗、缺少面包屑导航等结构性问题。本章将从菜单结构重组、图标规范化、权限映射优化和面包屑导航四个维度进行系统性重构。


6.1 菜单结构重组方案

6.1.1 重组设计思路

当前菜单的六个分组存在三个核心问题:其一,"配置平台"分组仅包含"系统设置"一项,空间利用率极低;其二,"自动任务"归类于"系统配置"不够合理,它与运维监控的关联度远高于系统配置;其三,"工单管理"的命名偏向内部管理视角,对实际执行数据操作的开发者而言不够直观。

重组方案遵循"以用户角色为中心、以任务流为导向"的原则,将原有6个分组精简为6个更具语义清晰度的分组。核心调整包括:将"概览"升级为"工作台"并增加快捷入口;将"工单管理"重命名为"数据操作";将"自动任务"移至"运维中心";将孤立的"系统设置"与新增"个人中心"组成"平台设置"。

6.1.2 优化后的完整菜单定义

// /frontend/src/constants/menu.ts

import type { LucideIcon } from 'lucide-react';

/** 权限码类型 —— 替代粗粒度的 roles 数组 */
export type PermissionCode =
  | 'dashboard:view'
  | 'todo:view'
  | 'query:quick_access'
  | 'ticket:view'
  | 'ticket:create'
  | 'query:view'
  | 'sql_assistant:use'
  | 'datasource:view'
  | 'datasource:manage'
  | 'data_dict:view'
  | 'user:view'
  | 'user:manage'
  | 'role:view'
  | 'role:manage'
  | 'perm_group:view'
  | 'perm_group:manage'
  | 'workflow:view'
  | 'workflow:manage'
  | 'audit_log:view'
  | 'auto_task:view'
  | 'auto_task:manage'
  | 'announcement:view'
  | 'settings:system'
  | 'settings:personal';

/** 菜单分组定义 */
export interface MenuSection {
  id: string;
  label: string;
  icon?: LucideIcon;
  order: number;
}

export const menuSections: MenuSection[] = [
  { id: 'workspace',   label: '工作台',  order: 10 },
  { id: 'dataops',     label: '数据操作', order: 20 },
  { id: 'datasource',  label: '数据源',  order: 30 },
  { id: 'permission',  label: '权限中心', order: 40 },
  { id: 'ops',         label: '运维中心', order: 50 },
  { id: 'platform',    label: '平台设置', order: 60 },
];

/** Badge 数据源类型 */
export type BadgeSource = 'pending_count' | 'unread_count' | 'task_fail_count' | 'announcement_count' | none;

/** 菜单项定义 */
export interface MenuItem {
  id: string;
  label: string;
  icon: string;                 // lucide-react 图标名称
  section: string;              // 对应 menuSections.id
  permissions: PermissionCode[]; // 细粒度权限码(满足任一即显示)
  badge?: BadgeSource;          // 动态角标数据源
  shortcut?: string;            // 快捷键(如 Ctrl+1)
  order: number;                // 组内排序
}

export const menuItems: MenuItem[] = [
  // ─── 工作台 ───
  {
    id: 'dashboard',
    label: '仪表盘',
    icon: 'LayoutDashboard',
    section: 'workspace',
    permissions: ['dashboard:view'],
    order: 10,
  },
  {
    id: 'my-todo',
    label: '我的待办',
    icon: 'CheckCircle2',
    section: 'workspace',
    permissions: ['ticket:view', 'workflow:view'],
    badge: 'pending_count',
    order: 20,
  },
  {
    id: 'quick-queries',
    label: '快速查询',
    icon: 'Zap',
    section: 'workspace',
    permissions: ['query:view', 'query:quick_access'],
    order: 30,
  },

  // ─── 数据操作 ───
  {
    id: 'tickets',
    label: '数据变更',
    icon: 'ClipboardList',
    section: 'dataops',
    permissions: ['ticket:view'],
    badge: 'pending_count',
    order: 10,
  },
  {
    id: 'queries',
    label: '查询数据',
    icon: 'Search',
    section: 'dataops',
    permissions: ['query:view'],
    order: 20,
  },
  {
    id: 'sql-assistant',
    label: 'SQL助手',
    icon: 'Wand2',
    section: 'dataops',
    permissions: ['sql_assistant:use', 'query:view'],
    order: 30,
  },

  // ─── 数据源 ───
  {
    id: 'instances',
    label: '数据源管理',
    icon: 'Database',
    section: 'datasource',
    permissions: ['datasource:view'],
    order: 10,
  },
  {
    id: 'data-dict',
    label: '数据字典',
    icon: 'BookOpen',
    section: 'datasource',
    permissions: ['data_dict:view', 'datasource:view'],
    order: 20,
  },

  // ─── 权限中心 ───
  {
    id: 'users',
    label: '用户管理',
    icon: 'Users',
    section: 'permission',
    permissions: ['user:view'],
    order: 10,
  },
  {
    id: 'roles',
    label: '角色权限',
    icon: 'UserCog',
    section: 'permission',
    permissions: ['role:view'],
    order: 20,
  },
  {
    id: 'instance-groups',
    label: '权限组',
    icon: 'Shield',
    section: 'permission',
    permissions: ['perm_group:view'],
    order: 30,
  },
  {
    id: 'workflow',
    label: '审批流程',
    icon: 'GitBranch',
    section: 'permission',
    permissions: ['workflow:view'],
    order: 40,
  },

  // ─── 运维中心 ───
  {
    id: 'audit-logs',
    label: '审计日志',
    icon: 'FileText',
    section: 'ops',
    permissions: ['audit_log:view'],
    order: 10,
  },
  {
    id: 'auto-task',
    label: '自动任务',
    icon: 'Clock',
    section: 'ops',
    permissions: ['auto_task:view'],
    badge: 'task_fail_count',
    order: 20,
  },
  {
    id: 'announcements',
    label: '系统公告',
    icon: 'Megaphone',
    section: 'ops',
    permissions: ['announcement:view'],
    badge: 'announcement_count',
    order: 30,
  },

  // ─── 平台设置 ───
  {
    id: 'settings',
    label: '系统设置',
    icon: 'Settings',
    section: 'platform',
    permissions: ['settings:system'],
    order: 10,
  },
  {
    id: 'profile',
    label: '个人中心',
    icon: 'UserCircle',
    section: 'platform',
    permissions: ['settings:personal'],
    order: 20,
  },
];

/** 根据分组 ID 获取菜单项 */
export const getMenuItemsBySection = (sectionId: string): MenuItem[] => {
  return menuItems
    .filter((item) => item.section === sectionId)
    .sort((a, b) => a.order - b.order);
};

/** 根据权限码过滤可见菜单项 */
export const filterMenuByPermissions = (
  items: MenuItem[],
  userPermissions: PermissionCode[]
): MenuItem[] => {
  return items.filter((item) =>
    item.permissions.some((perm) => userPermissions.includes(perm))
  );
};

6.1.3 重组收益

维度 调整前 调整后 收益
分组数量 6个(含1个单项目分组) 6个(每个分组2-4项) 分布均匀,视觉平衡
语义清晰度 "工单管理"偏管理视角 "数据操作"以操作为导向 降低认知成本
功能关联性 自动任务与系统配置弱关联 自动任务归入运维中心 逻辑一致性提升
快捷入口 无 工作台聚合待办+快速查询 减少用户操作路径
可扩展性 新增功能无处安放 各分组预留扩展空间 支持未来功能迭代

6.2 图标体系规范化

6.2.1 现状问题分析

当前系统存在两套图标体系并行的问题:侧边栏使用 lucide-react 矢量图标,而设置页面使用 emoji 图标(🎨⚙️🛡️🔔🔐)。这种不一致带来三个负面影响:视觉风格割裂,emoji 在不同操作系统渲染差异大;专业感下降,emoji 偏向社交/休闲场景;无障碍支持差,emoji 的屏幕阅读器朗读行为不可控。

6.2.2 图标映射表

以下是重组后全部菜单项的图标映射,统一使用 lucide-react 图标库:

菜单项 ID 菜单标签 图标名称 (Lucide) 选用理由
dashboard 仪表盘 LayoutDashboard 网格布局隐喻,dashboard 标准语义
my-todo 我的待办 CheckCircle2 待办完成状态的圆形勾选隐喻
quick-queries 快速查询 Zap 闪电符号表达"快速"语义
tickets 数据变更 ClipboardList 工单列表的标准图标,保持不变
queries 查询数据 Search 搜索查询的标准语义
sql-assistant SQL助手 Wand2 魔杖隐喻"智能辅助/魔法般的体验"
instances 数据源管理 Database 数据库标准图标,保持不变
data-dict 数据字典 BookOpen 打开的书籍隐喻"字典/参考"
users 用户管理 Users 用户群像标准图标,保持不变
roles 角色权限 UserCog 用户+齿轮表达"用户配置"
instance-groups 权限组 Shield 盾牌隐喻"权限保护",替代原 Lock
workflow 审批流程 GitBranch 分支流程图标,保持不变
audit-logs 审计日志 FileText 文件记录标准图标,保持不变
auto-task 自动任务 Clock 时钟隐喻"定时/自动化",保持不变
announcements 系统公告 Megaphone 喇叭隐喻"广播/公告"
settings 系统设置 Settings 齿轮标准图标,保持不变
profile 个人中心 UserCircle 圆形用户头像隐喻"个人信息"

6.2.3 图标动态导入实现

为避免一次性导入全部图标导致的包体积膨胀,采用字符串名称 + 动态导入方案:

// /frontend/src/constants/menu.ts

import * as Icons from 'lucide-react';
import type { LucideProps } from 'lucide-react';
import React from 'react';

/** 图标名称到组件的动态映射 */
export const getMenuIcon = (
  iconName: string
): React.ComponentType<LucideProps> | null => {
  const Icon = (Icons as Record<string, React.ComponentType<LucideProps>>)[iconName];
  return Icon || null;
};

/** 在 SidebarMenuItem 组件中的使用方式 */
// const IconComponent = getMenuIcon(item.icon);
// {IconComponent && <IconComponent className={iconClassNames} size={18} />}

6.2.4 图标颜色与尺寸规范

/* /frontend/src/styles/menu-icons.css */

/* ─── 基础尺寸 ─── */
.menu-icon {
  width: 18px;
  height: 18px;
  flex-shrink: 0;
  transition: color 150ms ease, transform 150ms ease;
}

/* ─── 颜色状态 ─── */
.menu-icon--default {
  color: var(--dg-text-secondary);   /* #9ca3af */
}

.menu-item:hover .menu-icon--default {
  color: var(--dg-text-primary);     /* #e5e7eb */
  transform: scale(1.05);
}

.menu-icon--active {
  color: rgb(96 165 250);            /* text-blue-400 */
}

/* ─── 分组标题图标(如使用)─── */
.menu-section-icon {
  color: var(--dg-text-muted);       /* #6b7280 */
  width: 16px;
  height: 16px;
}

/* ─── 角标容器 ─── */
.menu-badge {
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  border-radius: 9px;
  font-size: 11px;
  font-weight: 600;
  background-color: rgb(239 68 68);   /* red-500 */
  color: white;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  margin-left: auto;
  animation: badge-pulse 2s ease-in-out infinite;
}

@keyframes badge-pulse {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.08); }
}

6.2.5 设置页 emoji 替换方案

原设置页面中使用了以下 emoji 图标,需全部替换为 lucide-react 对应图标:

设置项 原 emoji 替换为 Lucide 图标 图标名称
外观主题 🎨 Palette Palette
平台设置 ⚙️ SlidersHorizontal SlidersHorizontal
安全设置 🛡️ ShieldCheck ShieldCheck
通知设置 🔔 Bell Bell
密码修改 🔐 KeyRound KeyRound

替换后设置页面的图标导入:

// /frontend/src/pages/settings/SettingsPage.tsx

import {
  Palette,
  SlidersHorizontal,
  ShieldCheck,
  Bell,
  KeyRound,
} from 'lucide-react';

const settingsGroups = [
  { id: 'appearance', label: '外观主题', icon: Palette },
  { id: 'platform',   label: '平台设置', icon: SlidersHorizontal },
  { id: 'security',   label: '安全设置', icon: ShieldCheck },
  { id: 'notification', label: '通知设置', icon: Bell },
  { id: 'password',   label: '密码修改', icon: KeyRound },
];

6.3 权限映射优化

6.3.1 从角色到权限的范式迁移

原方案使用 roles: ['admin', 'dba', 'developer', 'auditor'] 这种粗粒度的角色数组控制菜单可见性,存在三个结构性缺陷:角色名称硬编码,新增角色需修改多处代码;同一角色内不同用户的菜单可见性无法进一步细分;无法支持基于RBAC的动态权限变更。

新方案引入 permissions: PermissionCode[] 数组,每个菜单项声明所需的细粒度权限码。用户登录时后端返回该用户的权限码列表,前端通过权限交集运算判断菜单可见性。此设计遵循"后端控制权限,前端控制展示"的分层原则。

6.3.2 权限码命名规范

权限码采用 resource:action 的标准格式:

// /frontend/src/constants/permissions.ts

/** 权限码完整定义 */
export const PERMISSIONS = {
  // 工作台
  DASHBOARD_VIEW:    'dashboard:view'       as const,
  TODO_VIEW:         'todo:view'            as const,
  QUERY_QUICK_ACCESS:'query:quick_access'   as const,

  // 数据操作
  TICKET_VIEW:       'ticket:view'          as const,
  TICKET_CREATE:     'ticket:create'        as const,
  TICKET_APPROVE:    'ticket:approve'       as const,
  QUERY_VIEW:        'query:view'           as const,
  QUERY_CREATE:      'query:create'         as const,
  SQL_ASSISTANT_USE: 'sql_assistant:use'    as const,

  // 数据源
  DATASOURCE_VIEW:   'datasource:view'      as const,
  DATASOURCE_MANAGE: 'datasource:manage'    as const,
  DATA_DICT_VIEW:    'data_dict:view'       as const,

  // 权限中心
  USER_VIEW:         'user:view'            as const,
  USER_MANAGE:       'user:manage'          as const,
  ROLE_VIEW:         'role:view'            as const,
  ROLE_MANAGE:       'role:manage'          as const,
  PERM_GROUP_VIEW:   'perm_group:view'      as const,
  PERM_GROUP_MANAGE: 'perm_group:manage'    as const,
  WORKFLOW_VIEW:     'workflow:view'        as const,
  WORKFLOW_MANAGE:   'workflow:manage'      as const,

  // 运维中心
  AUDIT_LOG_VIEW:    'audit_log:view'       as const,
  AUTO_TASK_VIEW:    'auto_task:view'       as const,
  AUTO_TASK_MANAGE:  'auto_task:manage'     as const,
  ANNOUNCEMENT_VIEW: 'announcement:view'    as const,

  // 平台设置
  SETTINGS_SYSTEM:   'settings:system'      as const,
  SETTINGS_PERSONAL: 'settings:personal'    as const,
} as const;

/** 权限码联合类型 —— 从常量对象推导 */
export type PermissionCode =
  typeof PERMISSIONS[keyof typeof PERMISSIONS];

/** 角色到权限的映射模板 —— 后端可动态调整 */
export const ROLE_PERMISSION_TEMPLATES: Record<string, PermissionCode[]> = {
  admin: Object.values(PERMISSIONS),
  dba: [
    PERMISSIONS.DASHBOARD_VIEW,
    PERMISSIONS.TODO_VIEW,
    PERMISSIONS.TICKET_VIEW,
    PERMISSIONS.TICKET_APPROVE,
    PERMISSIONS.QUERY_VIEW,
    PERMISSIONS.QUERY_CREATE,
    PERMISSIONS.SQL_ASSISTANT_USE,
    PERMISSIONS.DATASOURCE_VIEW,
    PERMISSIONS.DATASOURCE_MANAGE,
    PERMISSIONS.DATA_DICT_VIEW,
    PERMISSIONS.WORKFLOW_VIEW,
    PERMISSIONS.AUDIT_LOG_VIEW,
    PERMISSIONS.AUTO_TASK_VIEW,
    PERMISSIONS.ANNOUNCEMENT_VIEW,
    PERMISSIONS.SETTINGS_PERSONAL,
  ],
  developer: [
    PERMISSIONS.DASHBOARD_VIEW,
    PERMISSIONS.TICKET_VIEW,
    PERMISSIONS.TICKET_CREATE,
    PERMISSIONS.QUERY_VIEW,
    PERMISSIONS.QUERY_CREATE,
    PERMISSIONS.SQL_ASSISTANT_USE,
    PERMISSIONS.DATASOURCE_VIEW,
    PERMISSIONS.DATA_DICT_VIEW,
    PERMISSIONS.WORKFLOW_VIEW,
    PERMISSIONS.ANNOUNCEMENT_VIEW,
    PERMISSIONS.SETTINGS_PERSONAL,
  ],
  auditor: [
    PERMISSIONS.DASHBOARD_VIEW,
    PERMISSIONS.TICKET_VIEW,
    PERMISSIONS.QUERY_VIEW,
    PERMISSIONS.AUDIT_LOG_VIEW,
    PERMISSIONS.ANNOUNCEMENT_VIEW,
    PERMISSIONS.SETTINGS_PERSONAL,
  ],
};

6.3.3 菜单可见性判断 Hook

// /frontend/src/hooks/useMenuPermissions.ts

import { useMemo } from 'react';
import { useAuth } from '@/contexts/AuthContext';
import {
  menuSections,
  menuItems,
  filterMenuByPermissions,
  getMenuItemsBySection,
} from '@/constants/menu';
import type { MenuItem, MenuSection } from '@/constants/menu';

export interface VisibleMenuGroup {
  section: MenuSection;
  items: MenuItem[];
}

/** 获取当前用户可见的菜单结构 */
export const useVisibleMenu = (): VisibleMenuGroup[] => {
  const { userPermissions } = useAuth(); // 从 AuthContext 获取用户权限码数组

  return useMemo(() => {
    // 按 order 排序分组
    const sortedSections = [...menuSections].sort((a, b) => a.order - b.order);

    return sortedSections
      .map((section) => {
        const sectionItems = getMenuItemsBySection(section.id);
        const visibleItems = filterMenuByPermissions(sectionItems, userPermissions);
        return { section, items: visibleItems };
      })
      .filter((group) => group.items.length > 0); // 过滤空分组
  }, [userPermissions]);
};

/** 判断用户是否拥有某个菜单项的访问权限 */
export const useHasMenuPermission = (menuItemId: string): boolean => {
  const { userPermissions } = useAuth();
  const item = menuItems.find((m) => m.id === menuItemId);
  if (!item) return false;
  return item.permissions.some((perm) => userPermissions.includes(perm));
};

/** 获取动态角标数据 */
export const useMenuBadges = (): Record<string, number> => {
  const { badgeData } = useAuth(); // 从 AuthContext 获取角标数据

  return useMemo(() => {
    const result: Record<string, number> = {};
    menuItems.forEach((item) => {
      if (item.badge && badgeData[item.badge] !== undefined) {
        result[item.id] = badgeData[item.badge];
      }
    });
    return result;
  }, [badgeData]);
};

6.3.4 动态角标设计

菜单角标实时展示待处理事项数量,提醒用户关注。角标数据通过 WebSocket 或轮询接口从后端获取:

// /frontend/src/types/badge.ts

/** 角标数据源类型 */
export interface BadgeData {
  pending_count: number;        // 待审批工单数
  unread_count: number;         // 未读消息数
  task_fail_count: number;      // 失败自动任务数
  announcement_count: number;   // 未读公告数
}

// /frontend/src/hooks/useBadgePolling.ts

import { useQuery } from '@tanstack/react-query';
import type { BadgeData } from '@/types/badge';

const POLLING_INTERVAL = 60_000; // 60秒轮询

export const useBadgePolling = () => {
  return useQuery<BadgeData>({
    queryKey: ['menu-badges'],
    queryFn: async () => {
      const res = await fetch('/api/v1/badges');
      if (!res.ok) throw new Error('Failed to fetch badges');
      return res.json();
    },
    refetchInterval: POLLING_INTERVAL,
    staleTime: 30_000,
  });
};

角标在侧边栏的渲染逻辑:

// 角标渲染(嵌入 SidebarMenuItem 组件中)
{badgeCount !== undefined && badgeCount > 0 && (
  <span className={`
    ml-auto inline-flex items-center justify-center
    min-w-[18px] h-[18px] px-[5px] rounded-full
    text-[11px] font-semibold
    ${badgeCount > 99 ? 'bg-red-600' : 'bg-red-500'}
    text-white
    animate-[badge-pulse_2s_ease-in-out_infinite]
  `}>
    {badgeCount > 99 ? '99+' : badgeCount}
  </span>
)}

6.3.5 权限迁移路径

阶段 操作 时间窗口
第一阶段(并行期) 同时保留 roles 和 permissions 字段,以 roles 为 fallback 1-2 个迭代
第二阶段(切换期) 后端全面返回 permissions,前端以 permissions 为主 1 个迭代
第三阶段(清理期) 移除 roles 字段,权限判断完全基于 permissions 1 个迭代

6.4 面包屑导航补充

6.4.1 设计目标与定位

面包屑导航填补当前平台在"位置感知"维度的空白。当用户通过深层链接(如审批流程详情页)进入页面时,面包屑提供清晰的路径回溯能力。设计遵循三个原则:路径语义化(显示可读标签而非路由路径)、可交互(支持点击跳转)、与侧边栏激活状态联动。

6.4.2 路径映射规则

面包屑的数据源不是路由配置本身,而是一个独立的路径映射表。这种解耦设计使面包屑标签与 URL 路径分离,便于国际化和文案调整。

// /frontend/src/constants/breadcrumb.ts

/** 面包屑路径映射项 */
export interface BreadcrumbMapping {
  path: string;                    // 路由路径(支持参数,如 /tickets/:id)
  label: string;                   // 面包屑显示文本
  parent?: string;                 // 父级路径(用于构建完整面包屑链)
  icon?: string;                   // 可选的 lucide 图标名称
}

/** 面包屑路径映射表 —— 按深度从浅到深排列 */
export const breadcrumbMappings: BreadcrumbMapping[] = [
  // 工作台
  { path: '/dashboard',    label: '仪表盘',   icon: 'LayoutDashboard' },
  { path: '/my-todo',      label: '我的待办',  icon: 'CheckCircle2' },
  { path: '/quick-queries',label: '快速查询',  icon: 'Zap' },

  // 数据操作
  { path: '/tickets',      label: '数据变更',  icon: 'ClipboardList' },
  { path: '/tickets/:id',  label: '工单详情',  parent: '/tickets' },
  { path: '/queries',      label: '查询数据',  icon: 'Search' },
  { path: '/queries/:id',  label: '查询详情',  parent: '/queries' },
  { path: '/sql-assistant',label: 'SQL助手',   icon: 'Wand2' },

  // 数据源
  { path: '/instances',    label: '数据源管理', icon: 'Database' },
  { path: '/instances/:id',label: '数据源详情', parent: '/instances' },
  { path: '/data-dict',    label: '数据字典',  icon: 'BookOpen' },

  // 权限中心
  { path: '/users',        label: '用户管理',  icon: 'Users' },
  { path: '/users/:id',    label: '用户详情',  parent: '/users' },
  { path: '/roles',        label: '角色权限',  icon: 'UserCog' },
  { path: '/instance-groups', label: '权限组', icon: 'Shield' },
  { path: '/workflow',     label: '审批流程',  icon: 'GitBranch' },

  // 运维中心
  { path: '/audit-logs',   label: '审计日志',  icon: 'FileText' },
  { path: '/auto-task',    label: '自动任务',  icon: 'Clock' },
  { path: '/announcements',label: '系统公告',  icon: 'Megaphone' },

  // 平台设置
  { path: '/settings',     label: '系统设置',  icon: 'Settings' },
  { path: '/profile',      label: '个人中心',  icon: 'UserCircle' },
];

6.4.3 面包屑构建工具函数

// /frontend/src/utils/breadcrumb.ts

import { breadcrumbMappings } from '@/constants/breadcrumb';
import type { BreadcrumbMapping } from '@/constants/breadcrumb';

export interface BreadcrumbItem {
  label: string;
  path: string;
  icon?: string;
  isActive: boolean;  // 是否为当前页(不可点击)
}

/** 将路径模板匹配到实际路径 */
const matchPath = (template: string, actualPath: string): boolean => {
  const templateParts = template.split('/');
  const actualParts = actualPath.split('/');
  if (templateParts.length !== actualParts.length) return false;

  return templateParts.every((part, i) => {
    if (part.startsWith(':')) return true; // 参数段通配
    return part === actualParts[i];
  });
};

/** 根据当前路径构建面包屑链 */
export const buildBreadcrumbs = (currentPath: string): BreadcrumbItem[] => {
  const chain: BreadcrumbItem[] = [];
  const visited = new Set<string>();

  // 查找当前路径的映射
  let currentMapping = breadcrumbMappings.find((m) =>
    matchPath(m.path, currentPath)
  );

  // 逐层向上追溯父级
  while (currentMapping && !visited.has(currentMapping.path)) {
    visited.add(currentMapping.path);
    chain.unshift({
      label: currentMapping.label,
      path: currentPath, // 当前页使用实际路径
      icon: currentMapping.icon,
      isActive: chain.length === 0,
    });

    if (currentMapping.parent) {
      currentMapping = breadcrumbMappings.find(
        (m) => m.path === currentMapping!.parent
      );
    } else {
      break;
    }
  }

  return chain;
};

/** 获取面包屑项的目标路径(处理参数化路径) */
export const resolveBreadcrumbPath = (
  mapping: BreadcrumbMapping,
  currentPath: string
): string => {
  if (!mapping.path.includes(':')) return mapping.path;

  // 从当前路径提取参数值
  const templateParts = mapping.path.split('/');
  const currentParts = currentPath.split('/');

  const resolvedParts = templateParts.map((part, i) => {
    if (part.startsWith(':')) return currentParts[i] || part;
    return part;
  });

  return resolvedParts.join('/');
};

6.4.4 面包屑 React 组件

// /frontend/src/components/layout/Breadcrumb.tsx

import React from 'react';
import { Link, useLocation } from 'react-router-dom';
import { ChevronRight, Home } from 'lucide-react';
import { buildBreadcrumbs, resolveBreadcrumbPath } from '@/utils/breadcrumb';
import { breadcrumbMappings } from '@/constants/breadcrumb';
import { getMenuIcon } from '@/constants/menu';

export const Breadcrumb: React.FC = () => {
  const location = useLocation();
  const items = buildBreadcrumbs(location.pathname);

  if (items.length <= 1) {
    // 顶层页面不显示面包屑
    return null;
  }

  return (
    <nav
      aria-label="面包屑导航"
      className="flex items-center gap-1 px-6 py-3 text-sm"
    >
      {/* 首页快捷入口 */}
      <Link
        to="/dashboard"
        className="
          flex items-center gap-1
          text-[var(--dg-text-muted)]
          hover:text-[var(--dg-text-primary)]
          transition-colors duration-150
        "
        title="返回仪表盘"
      >
        <Home size={14} />
      </Link>

      {items.length > 0 && (
        <ChevronRight
          size={14}
          className="text-[var(--dg-text-muted)] mx-0.5"
        />
      )}

      {/* 面包屑项 */}
      {items.map((item, index) => {
        const isLast = index === items.length - 1;
        const IconComponent = item.icon ? getMenuIcon(item.icon) : null;

        return (
          <React.Fragment key={`${item.path}-${index}`}>
            <div className="flex items-center gap-1.5">
              {IconComponent && (
                <IconComponent
                  size={14}
                  className={`
                    ${isLast ? 'text-blue-400' : 'text-[var(--dg-text-muted)]'}
                  `}
                />
              )}
              {isLast ? (
                // 当前页:不可点击,高亮显示
                <span
                  className="font-medium text-[var(--dg-text-primary)]"
                  aria-current="page"
                >
                  {item.label}
                </span>
              ) : (
                // 父级:可点击跳转
                <Link
                  to={item.path}
                  className="
                    text-[var(--dg-text-secondary)]
                    hover:text-[var(--dg-text-primary)]
                    hover:underline underline-offset-2
                    transition-colors duration-150
                  "
                >
                  {item.label}
                </Link>
              )}
            </div>

            {!isLast && (
              <ChevronRight
                size={14}
                className="text-[var(--dg-text-muted)] mx-0.5"
              />
            )}
          </React.Fragment>
        );
      })}
    </nav>
  );
};

6.4.5 面包屑与布局的集成

// /frontend/src/components/layout/AppLayout.tsx —— 集成面包屑后的布局

import React from 'react';
import { Sidebar } from './Sidebar';
import { Breadcrumb } from './Breadcrumb';
import { TopBar } from './TopBar';

export const AppLayout: React.FC<{ children: React.ReactNode }> = ({
  children,
}) => {
  return (
    <div className="flex h-screen bg-[var(--dg-bg-primary)]">
      {/* 侧边栏 */}
      <aside className="w-[220px] flex-shrink-0 border-r border-[var(--dg-border)]">
        <Sidebar />
      </aside>

      {/* 主内容区 */}
      <main className="flex-1 flex flex-col min-w-0 overflow-hidden">
        {/* 顶部栏 */}
        <TopBar />

        {/* 面包屑导航 */}
        <Breadcrumb />

        {/* 页面内容 */}
        <div
          className="
            flex-1 overflow-auto
            px-6 py-4
            scroll-smooth
          "
        >
          {children}
        </div>
      </main>
    </div>
  );
};

6.4.6 面包屑设计规范

设计要素 规范值 说明
字体大小 text-sm (14px) 小于页面标题,大于辅助文本
字体颜色(默认) var(--dg-text-secondary) 非当前页
字体颜色(当前页) var(--dg-text-primary) 加粗高亮
图标尺寸 14px 小于侧边栏图标,弱化层级
分隔符 ChevronRight 14px 统一右箭头
垂直间距 py-3 (12px) 紧凑布局
水平间距 px-6 (24px) 与页面内容对齐
最大显示层级 4 级 超过则折叠中间层级
悬停交互 下划线 + 颜色过渡 transition-colors duration-150

面包屑的显示逻辑遵循"至少两级才展示"原则——当用户位于仪表盘等顶层页面时面包屑自动隐藏,避免冗余信息干扰。对于超过四级的深层路径,中间层级折叠为"..."下拉菜单,保持导航栏的简洁可读性。


6.5 菜单 Hover/Active 动效设计

6.5.1 动画参数规范

/* /frontend/src/styles/menu-animations.css */

/* ─── 菜单项基础 ─── */
.menu-item {
  position: relative;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 14px;
  margin: 2px 8px;
  border-radius: 8px;
  font-size: 13px;
  font-weight: 450;
  color: var(--dg-text-secondary);
  cursor: pointer;
  transition:
    background-color 180ms cubic-bezier(0.4, 0, 0.2, 1),
    color 150ms cubic-bezier(0.4, 0, 0.2, 1),
    transform 120ms cubic-bezier(0.4, 0, 0.2, 1);
  overflow: hidden;
}

/* ─── Hover 状态 ─── */
.menu-item:hover {
  background-color: rgba(255, 255, 255, 0.04);
  color: var(--dg-text-primary);
  transform: translateX(2px);
}

/* ─── Active 状态 ─── */
.menu-item--active {
  background-color: rgb(59 130 246 / 0.10); /* blue-500/10 */
  color: rgb(96 165 250);                    /* blue-400 */
  font-weight: 550;
}

/* 左侧指示条 */
.menu-item--active::before {
  content: '';
  position: absolute;
  left: 0;
  top: 50%;
  transform: translateY(-50%);
  width: 3px;
  height: 18px;
  background-color: rgb(96 165 250); /* blue-400 */
  border-radius: 0 2px 2px 0;
  animation: indicator-slide-in 200ms cubic-bezier(0.4, 0, 0.2, 1);
}

@keyframes indicator-slide-in {
  from {
    height: 0;
    opacity: 0;
  }
  to {
    height: 18px;
    opacity: 1;
  }
}

/* ─── 点击反馈 ─── */
.menu-item:active {
  transform: scale(0.98);
  transition-duration: 80ms;
}

/* ─── 菜单分组标题 ─── */
.menu-section-title {
  padding: 16px 14px 6px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--dg-text-muted);
  user-select: none;
}

6.5.2 动画设计决策说明

动画 时长 缓动函数 设计意图
Hover 背景色 180ms cubic-bezier(0.4, 0, 0.2, 1) Material Design 标准缓动,自然流畅
Hover 位移 120ms 同上 轻微右移引导视觉向右,暗示可点击
文字颜色 150ms 同上 略快于背景色,形成层次
指示条展开 200ms 同上 从0到18px高度,带有生长感
点击缩放 80ms 线性 快速反馈,确认操作被接收

所有动画均采用 GPU 加速属性(transform、opacity、background-color),确保在 60fps 下流畅运行,不会造成布局抖动。


6.6 收藏/快捷访问功能

6.6.1 功能设计

"快速查询"菜单项(位于工作台分组)承载收藏/快捷访问功能。用户可在任何页面点击顶部栏的星形图标将当前页面加入收藏,收藏项会出现在快速查询的下拉面板中。

6.6.2 数据模型与存储

// /frontend/src/types/favorite.ts

export interface FavoriteItem {
  id: string;          // 唯一标识
  label: string;       // 显示名称(用户可编辑)
  path: string;        // 路由路径
  icon: string;        // lucide 图标名称
  createdAt: string;   // ISO 时间戳
  order: number;       // 自定义排序
}

/** 收藏数据持久化 —— localStorage + 云端同步 */
const FAVORITES_STORAGE_KEY = 'sql_platform_favorites';

export const favoriteStorage = {
  getAll(): FavoriteItem[] {
    try {
      const raw = localStorage.getItem(FAVORITES_STORAGE_KEY);
      return raw ? JSON.parse(raw) : [];
    } catch {
      return [];
    }
  },

  save(items: FavoriteItem[]): void {
    localStorage.setItem(FAVORITES_STORAGE_KEY, JSON.stringify(items));
  },

  add(item: Omit<FavoriteItem, 'order'>): FavoriteItem[] {
    const items = this.getAll();
    if (items.some((f) => f.path === item.path)) return items; // 去重
    const newItem: FavoriteItem = {
      ...item,
      order: items.length,
      createdAt: new Date().toISOString(),
    };
    const updated = [...items, newItem];
    this.save(updated);
    return updated;
  },

  remove(id: string): FavoriteItem[] {
    const items = this.getAll().filter((f) => f.id !== id);
    this.save(items);
    return items;
  },

  reorder(id: string, newOrder: number): FavoriteItem[] {
    const items = this.getAll();
    const item = items.find((f) => f.id === id);
    if (!item) return items;
    item.order = newOrder;
    this.save(items.sort((a, b) => a.order - b.order));
    return items;
  },
};

6.7 实施优先级与验收标准

6.7.1 实施优先级

优先级 模块 预估工时 依赖项
P0 6.1 菜单结构重组 4h 无
P0 6.2 图标体系规范化 3h 6.1
P1 6.3 权限映射优化 6h 后端接口配合
P1 6.4 面包屑导航 4h 6.1
P2 6.5 菜单动效 2h 6.1, 6.2
P2 6.6 收藏功能 4h 无

6.7.2 验收标准

  • 侧边栏展示重组后的6个分组,无空分组显示
  • 全部菜单项使用 lucide-react 图标,系统无 emoji 图标残留
  • 菜单可见性基于 permissions 数组判断,roles 字段已移除
  • 面包屑导航在二级及以上页面正确显示路径链
  • 菜单项 Hover/Active 动效流畅,无掉帧
  • 动态角标正确显示待处理数量,超过99显示"99+"
  • 设置页面 emoji 已全部替换为 lucide 图标

6.8 本章小结

本章从菜单结构重组、图标规范化、权限映射优化和面包屑导航四个维度对 SQL-Platform 的导航体系进行了系统性重构。核心改进包括:将原有6个分组重组为语义更清晰的工作台/数据操作/数据源/权限中心/运维中心/平台设置分组;建立了统一的 lucide-react 图标体系,彻底移除了 emoji 图标;引入 PermissionCode 细粒度权限码替代粗粒度的角色数组;补充了面包屑导航完善位置感知能力。这些改进使导航体系从"可用"迈向"好用",为平台的长期功能扩展奠定了坚实的交互基础。

7. 设计体系统一方案

7.1 色彩体系规范

主色板定义

角色 色值 用途
Primary #58a6ff (--dg-blue) 主要操作、链接、高亮
Secondary #8957e5 (--dg-purple) 次级强调、标签、分类
Accent #39c5cf (--dg-cyan) 提示点、新功能标记、数据高亮

功能色定义

角色 色值 使用场景
Success #3fb950 成功状态、通过标识、正向数据
Warning #f0883e 警告提示、需谨慎操作
Error #f85149 错误状态、删除操作、阻断性提示
Info #58a6ff 信息提示、帮助引导

中性色(Gray Scale)

以现有变量为锚点扩展完整灰阶:#f0f6fc (50) → #c9d1d9 (200) → #8b949e (400) → #6e7681 (500) → #484f58 (700) → #30363d (800) → #21262d (850) → #161b22 (900) → #0d1117 (950)。分别对应主文本、次级文本、弱化文本、悬浮背景、边框、深边框、卡片背景、次级背景、主背景。

渐变规范

  • 品牌渐变(.dg-gradient-bg):linear-gradient(135deg, #2563eb, #9333ea),用于Hero区域、登录页背景、Banner
  • 文字渐变(.dg-gradient-text):linear-gradient(90deg, #60a5fa, #c084fc),仅用于大标题强调
  • 边框渐变(.dg-gradient-br):linear-gradient(135deg, #2563eb, #9333ea) 作为 border-image,用于特色卡片边框
  • 使用规则:同一页面渐变不超过2种,文字渐变仅限H1/H2级别

Tailwind配置扩展

colors: {
  dg: {
    bg: { primary: '#0d1117', secondary: '#161b22', tertiary: '#21262d', hover: '#30363d' },
    border: { DEFAULT: '#30363d', light: '#21262d' },
    text: { primary: '#f0f6fc', secondary: '#8b949e', muted: '#6e7681' },
    blue: '#58a6ff', green: '#3fb950', red: '#f85149',
    orange: '#f0883e', purple: '#8957e5', cyan: '#39c5cf', yellow: '#d29922',
  }
}

7.2 字体排版规范

字体栈:font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans SC", "PingFang SC", "Microsoft YaHei", sans-serif; 系统字体优先,中文回退依次为Noto Sans SC、PingFang SC、Microsoft YaHei。

字号Scale(基于1.25倍率):

Token 尺寸 行高 用途
text-xs 12px 18px 辅助文字、时间戳、徽章内文字
text-sm 14px 20px 表单标签、表格内容、按钮文字
text-base 16px 24px 正文、菜单项、对话框内容
text-lg 18px 28px 卡片标题、区块小标题
text-xl 20px 30px 页面次级标题
text-2xl 24px 32px 页面标题
text-3xl 30px 38px 模块大标题
text-4xl 36px 44px Hero标题、登录页品牌名

字重规范:normal (400) 用于正文,medium (500) 用于菜单和按钮,semibold (600) 用于标题,bold (700) 用于强调数据。H1-H6统一semibold,避免过粗导致的中文笔画粘连。

等宽字体:代码块与数据展示使用 font-family: "JetBrains Mono", "Fira Code", "SF Mono", monospace;,字号14px,行高1.6。

7.3 间距体系规范

基于4px的Spacing Scale:0(0px)、1(4px)、2(8px)、3(12px)、4(16px)、5(20px)、6(24px)、8(32px)、10(40px)、12(48px)、16(64px)、20(80px)、24(96px)。

页面内边距:内容区水平 px-6(24px),最大宽度1440px居中;垂直方向页面级 py-8(32px),模块级 py-6(24px)。

卡片内边距:标准卡片 p-6(24px),紧凑卡片 p-4(16px),数据卡片 px-5 py-4(20px/16px)。

表单间距:标签与输入框间距 mb-1.5(6px),表单项之间 mb-5(20px),表单组之间 mb-8(32px)。

表格间距:表头内边距 px-4 py-3(16px/12px),行内边距 px-4 py-3(16px/12px),行间距0(以边框分隔)。

常见布局模板:页面标题区(mb-8)→ 统计卡片区(grid gap-4, mb-8)→ 主内容卡片区(gap-6)。

7.4 组件交互规范

按钮体系

变体 背景 文字 圆角 内边距 使用场景
Primary #58a6ff #0d1117 6px px-4 py-2 主要提交、确认操作
Secondary #21262d #f0f6fc 6px px-4 py-2 取消、返回操作
Ghost transparent #8b949e 6px px-4 py-2 次要操作、工具栏按钮
Danger #f85149 #fff 6px px-4 py-2 删除、阻断性操作
Link transparent #58a6ff 0 px-2 py-1 导航跳转、文字链接

尺寸:sm (h-8, px-3, text-xs),md (h-10, px-4, text-sm),lg (h-12, px-6, text-base)。圆角统一6px,hover时亮度提升10%,transition 150ms。

输入框体系:默认边框#30363d,focus边框#58a6ff并附加ring-2 ring-blue-500/20,error边框#f85149带红色ring,disabled背景#161b22文字#6e7681。统一圆角6px,内边距px-3 py-2,高度40px。

卡片体系:默认卡片(bg #161b22 border #30363d 圆角8px),可悬浮卡片(hover时border-color过渡为#484f58、translateY(-1px)、shadow-lg),可选中卡片(选中态border #58a6ff、bg #58a6ff/5%)。

表格体系:表头bg #161b22 文字#8b949e font-semibold,行hover bg #21262d,操作列按钮间距gap-2。边框1px #21262d。

弹窗体系:确认弹窗(宽400px),表单弹窗(宽560px),抽屉(宽480px/640px两档)。统一圆角12px,阴影shadow-2xl,入场动画scaleIn 200ms。

徽章体系:状态徽章(圆角全角,px-2 py-0.5,text-xs),标签徽章(圆角4px,带边框),计数徽章(圆形,min-w-5,h-5)。

7.5 动效体系规范

缓动函数定义

名称 值 用途
ease-default cubic-bezier(0.4, 0, 0.2, 1) 常规过渡
ease-in cubic-bezier(0.4, 0, 1, 1) 元素退出
ease-out cubic-bezier(0, 0, 0.2, 1) 元素入场
ease-spring cubic-bezier(0.34, 1.56, 0.64, 1) 弹性交互

时长Scale:fast 150ms(hover、focus环、颜色过渡),normal 250ms(弹窗、展开/收起),slow 350ms(页面转场、大型组件)。

入场动画:fadeIn(opacity 0→1, 200ms ease-out)、slideUp(translateY 8px→0 + opacity, 250ms ease-out)、scaleIn(scale 0.95→1 + opacity, 200ms ease-out)。弹窗统一scaleIn,列表项使用slideUp带stagger 30ms。

交互动画:hover过渡统一150ms,按钮active时scale 0.98,卡片hover时translateY(-1px) + border-color过渡,输入框focus时ring 150ms。所有交互元素必须有hover反馈。

Loading动画:旋转器(border-2 border-t-blue-500,animate-spin,20px),骨架屏(shimmer动画,bg-gradient横向扫光,1.5s linear infinite)。

CSS Keyframes

@keyframes fadeIn { from { opacity: 0 } to { opacity: 1 } }
@keyframes slideUp { from { opacity: 0; transform: translateY(8px) } to { opacity: 1; transform: translateY(0) } }
@keyframes scaleIn { from { opacity: 0; transform: scale(0.95) } to { opacity: 1; transform: scale(1) } }

8. 实施路线图

8.1 优先级划分

优先级 范围 目标
P0 关键 登录页改造、设计体系统一 解决当前最大用户体验短板,建立可复用的设计基础
P1 重要 主题系统扩展、核心功能优化 提升产品专业感,优化高频使用路径
P2 优化 菜单体系、动效体系 增强导航效率与交互质感
P3 增强 高级功能 数据可视化、个性化等增值能力

8.2 阶段一:登录页改造(2周)

Week 1:组件重构

  • Day 1-2:开发粒子动画背景组件(Canvas实现,可配置粒子密度与连线颜色)
  • Day 3:品牌展示区(渐变文字、系统名称、副标题、版本号)
  • Day 4:登录表单重构(统一输入框规范、密码可见性切换、记住登录态)
  • Day 5:安全提示区块(SSL标识、多因子认证引导、安全公告轮播)

Week 2:动效与联调

  • Day 1-2:入场动效串联(粒子→标题→表单依次fadeIn+slideUp,stagger 150ms)
  • Day 3:表单验证增强(实时校验、错误动画抖动、聚焦引导)
  • Day 4-5:联调测试、响应式适配、浏览器兼容性验证

交付物:全新登录页、可复用粒子背景组件、登录表单组件。验收标准:Lighthouse性能分≥90,首屏加载<1.5s,动画帧率≥55fps。

8.3 阶段二:主题系统扩展(1周)

  • Day 1-2:CSS变量体系扩展(将7.1节色彩规范完整植入,新增主题变量层 --theme-*)
  • Day 3-4:主题切换UI(设置面板主题选择器、实时预览、本地持久化)
  • Day 5:测试优化(变量回退机制、主题切换无闪烁、50+ shadcn/ui组件全覆盖)

交付物:扩展后的CSS变量体系、主题切换组件、Light/Dark/High-Contrast三主题。验收标准:主题切换<100ms无闪烁,所有组件颜色100%变量驱动。

8.4 阶段三:核心功能优化(3周)

Week 1:审批变更工单系统

  • 工单列表卡片化重构(统一.dg-card规范)
  • 审批流可视化(状态徽章体系、进度条组件)
  • 批量操作工具栏(按钮体系统一)

Week 2:查询数据页面

  • SQL编辑器区域优化(等宽字体规范、行号样式)
  • 结果表格重构(表格体系统一、列宽记忆)
  • 查询历史面板(间距体系应用、空状态设计)

Week 3:系统设置页面

  • 设置项卡片分组(间距模板应用)
  • 表单交互优化(输入框规范、保存反馈动效)
  • 权限矩阵表格重构(表格规范全面落地)

交付物:三个核心页面重构完成、对应组件更新。验收标准:无硬编码颜色,间距100%符合scale,交互动效完整。

8.5 阶段四:菜单与体系统一(1周)

  • Day 1-2:菜单结构重组(按用户角色分组、图标体系统一、激活态样式)
  • Day 3-4:全局设计体系统一(替换所有硬编码颜色为CSS变量,按钮/卡片/输入框标准化)
  • Day 5:动效体系实现(全局加载动画、页面转场、交互反馈动效)

交付物:重组后的菜单组件、全局样式清理报告、动效配置文件。验收标准:代码库零硬编码色值(除渐变色定义外),全局交互动效一致性100%。

8.6 预期效果

定性效果:登录页从功能型升级为品牌展示窗口;核心操作流程视觉层级清晰,用户认知负担降低;全产品交互反馈一致,专业感显著提升。

定量效果:

指标 当前预估 目标
首屏加载时间 ~2.5s <1.5s
Lighthouse性能分 ~70 ≥90
全局样式不一致点 35+处 0处
CSS变量覆盖率 ~60% 100%

风险控制:每阶段独立分支开发,P0/P1阶段完成后可独立上线;所有样式变更基于现有变量扩展,保持向后兼容;关键页面保留回滚开关,确保24小时内可回退至原版本。