refactor: 重构样式系统为五层分层架构

- 建立 tokens/core/layouts/components/pages 五层样式架构
- 所有组件采用 BEM 命名规范(.block__element--modifier)
- 16 个组件目录,每个组件独立 _index.scss
- 清理表格操作列内联样式,统一使用 .col-actions/.table-actions
- 更新 README 样式开发规范文档
- 同步 3 个 delta spec 到主 specs

Co-Authored-By: opencode <noreply@opencode.ai>
This commit is contained in:
2026-03-26 14:14:52 +08:00
parent b00d75de8a
commit f1d5e77285
61 changed files with 4796 additions and 4277 deletions

508
README.md
View File

@@ -19,7 +19,8 @@ GrandClaw 是一个企业级AI智能助手平台的前端原型项目主要
### UI与样式
- **react-icons 5.5.0**图标库Feather + FontAwesome图标集
- **Sass 1.98.0**CSS预处理器
- **SCSS模块化**:变量、混入、组件、布局、页面分离
- **五层分层架构**tokens → core → layouts → components → pages
- **BEM 命名规范**:所有组件类名遵循 `.block__element--modifier` 格式
### 构建优化
- **vite-plugin-singlefile 2.3.2**单文件打包解决CORS问题
@@ -70,59 +71,55 @@ grandclaw-archtype/
│ │ ├── AdminPage.jsx # 管理台主页面
│ │ ├── DeveloperPage.jsx # 开发台主页面
│ │ ├── console/ # 工作台子页面
│ │ │ ├── ChatPage.jsx # 聊天页面
│ │ │ ├── SkillsPage.jsx # 技能市场
│ │ │ ├── SkillDetailPage.jsx # 技能详情
│ │ │ ├── MySkillsPage.jsx # 我的技能管理NEW
│ │ │ ├── SkillConfigPage.jsx # 技能配置NEW
│ │ │ ├── LogsPage.jsx # 日志查询
│ │ │ ├── TasksPage.jsx # 定时任务
│ │ │ ├── TaskDetailPage.jsx # 任务详情
│ │ │ ├── AccountPage.jsx # 账号管理
│ │ │ ├── ProjectsPage.jsx # 项目管理
│ │ │ ├── MemberConfigPage.jsx # 成员配置
│ │ │ └── AddMemberPage.jsx # 增加成员
│ │ ├── admin/ # 管理台子页面
│ │ │ ├── OverviewPage.jsx # 运营总览
│ │ │ ├── DepartmentsPage.jsx # 部门管理
│ │ │ ├── AddDepartmentPage.jsx # 新增/编辑部门
│ │ │ ├── UsersPage.jsx # 用户管理
│ │ │ ├── AddUserPage.jsx # 新增/编辑用户
│ │ │ ├── AdminProjectsPage.jsx # 项目管理
│ │ │ ├── AddProjectPage.jsx # 新增/编辑项目
│ │ │ ├── AdminLogsPage.jsx # 全局日志查询
│ │ │ ├── ModelConfigsPage.jsx # 模型配置列表
│ │ │ └── AddModelConfigPage.jsx # 新增/编辑模型配置
│ │ ├── console/ # 工作台子页面
│ │ │ ├── ChatPage.jsx # 聊天页面
│ │ │ ├── SkillsPage.jsx # 技能市场
│ │ │ ├── SkillDetailPage.jsx # 技能详情
│ │ │ ├── LogsPage.jsx # 日志查询
│ │ │ ├── TasksPage.jsx # 定时任务
│ │ │ ├── TaskDetailPage.jsx # 任务详情
│ │ │ ├── AccountPage.jsx # 账号管理
│ │ │ ├── ProjectsPage.jsx # 项目管理
│ │ │ ├── MemberConfigPage.jsx # 成员配置
│ │ │ ├── AddMemberPage.jsx # 增加成员
│ │ │ ├── ConsoleReviewListPage.jsx # 审核管理列表NEW
│ │ │ └── ConsoleReviewDetailPage.jsx # 审核详情NEW
│ │ └── developer/ # 开发台子页面
│ ├── DevOverviewPage.jsx # 开发者总览
│ ├── MySkillsPage.jsx # 我的技能(筛选+分页)
├── SkillEditorPage.jsx # 技能详情(只读+操作)
├── UploadSkillPage.jsx # 创建技能(基本信息
├── UpdateSkillInfoPage.jsx # 更新基本信息
├── UploadVersionPage.jsx # 上传新版本NEW
├── DevDocsPage.jsx # 开发文档
── DevAccountPage.jsx # 开发者设置
└── styles/ # SCSS样式模块
│ ├── _variables.scss # 设计系统变量
│ ├── _mixins.scss # 可复用混入
├── _base.scss # 基础重置和全局样式
│ ├── _components.scss # 通用组件样式
│ ├── _layout.scss # 布局相关样式
│ ├── _pages.scss # 页面特定样式
└── global.scss # 主样式文件,导入所有模块
└── styles/ # SCSS样式系统分层架构
│ ├── tokens/ # 第1层设计令牌
├── _colors.scss # 品牌色、功能色、中性色
├── _spacing.scss # 间距系统4px基数
├── _shadows.scss # 阴影层级
├── _radius.scss # 圆角系统
├── _typography.scss # 字体、字号、字重
── _z-index.scss # 层级系统
├── _transitions.scss # 过渡动画参数
├── _breakpoints.scss # 响应式断点
├── _mixins.scss # 可复用混入
│ └── _index.scss # 统一导出
│ ├── core/ # 第2层核心样式
├── _reset.scss # CSS重置
├── _css-variables.scss # :root CSS变量
│ ├── _base.scss # body全局样式
│ │ └── _index.scss
│ ├── layouts/ # 第3层布局系统
│ │ ├── _app-shell.scss # 主框架sidebar+header+main
│ │ ├── _chat-layout.scss # 聊天页面布局
│ │ ├── _admin-layout.scss # 管理台布局
│ │ └── _index.scss
│ ├── components/ # 第4层组件库
│ │ ├── button/ # 按钮(.btn, .btn--primary, .btn--ghost
│ │ ├── card/ # 卡片(.card, .card__header, .card__body
│ │ ├── table/ # 表格(.table, .table-actions, .col-actions
│ │ ├── form/ # 表单(.form-group, .form__input, .form__label
│ │ ├── tag/ # 标签(.tag--running, .tag--admin
│ │ ├── modal/ # 弹窗(.modal, .modal__overlay
│ │ ├── toast/ # 提示(.toast--success, .toast--error
│ │ ├── pagination/ # 分页
│ │ ├── empty-state/ # 空状态
│ │ ├── switch/ # 开关
│ │ ├── skill-card/ # 技能卡片
│ │ ├── nav/ # 导航项
│ │ ├── detail/ # 详情页组件
│ │ ├── password-input/ # 密码输入框
│ │ ├── search-bar/ # 搜索栏
│ │ ├── stat-card/ # 统计卡片
│ │ └── _index.scss # 统一导出
│ ├── pages/ # 第5层页面特有样式
│ │ ├── _console.scss # 工作台特有组件
│ │ ├── _admin.scss # 管理台特有组件
│ │ ├── _developer.scss # 开发台特有组件
│ │ ├── _home.scss # 首页特有组件
│ │ └── _index.scss
│ └── global.scss # 主入口文件(仅 @use无直接样式
├── index.html # HTML入口文件
├── package.json # 项目配置和依赖
├── vite.config.js # Vite配置
@@ -500,52 +497,367 @@ const [currentPage, setCurrentPage] = useState(() => {
## 样式系统
### SCSS模块化结
### 分层架
样式采用五层架构,依赖方向自上而下:`tokens → core → layouts → components → pages`
```
src/styles/
├── _variables.scss # 设计系统变量(颜色、间距、阴影等)
├── _mixins.scss # 可复用混入(媒体查询、弹性布局等)
├── _base.scss # 基础重置、CSS变量定义、body样式
├── _components.scss # 通用组件(按钮、卡片、表单、状态标签)
├── _layout.scss # 布局相关(侧边栏、主内容区)
├── _pages.scss # 页面特定样式(首页、管理台等)
└── global.scss # 主文件,导入所有模块
┌─────────────────────────────────────────┐
│ pages/ 页面特有组件,仅覆盖模式 │ ← 最上层
├─────────────────────────────────────────┤
│ components/ 可复用UI组件库BEM命名 │
├─────────────────────────────────────────┤
layouts/ 布局系统shell/chat/admin
├─────────────────────────────────────────┤
│ core/ 重置、CSS变量、body样式 │
├─────────────────────────────────────────┤
│ tokens/ 设计令牌(颜色/间距/阴影... │ ← 最底层
└─────────────────────────────────────────┘
```
### 设计系统变量_variables.scss
### 第1层Tokens设计令牌
所有设计决策集中定义,禁止在组件或页面中硬编码值。
```scss
// 品牌主色
// src/styles/tokens/_colors.scss
$primary: #3B82F6;
$primary-light: #EFF6FF;
$primary-dark: #2563EB;
// 功能色
$success: #10B981;
$warning: #F59E0B;
$danger: #EF4444;
// 中性色
$text-1: #1E293B;
$text-2: #475569;
$text-3: #94A3B8;
// 布局尺寸
$sidebar-width: 240px;
$header-height: 60px;
$radius-md: 8px;
// src/styles/tokens/_spacing.scss
$spacing-1: 4px;
$spacing-2: 8px;
$spacing-4: 16px;
// src/styles/tokens/_typography.scss
$font-size-base: 14px;
$font-weight-semibold: 600;
```
### 状态标签样式
支持多种状态标签:
- `status-running` - 成功/运行中(绿色)
- `status-stopped` - 停止/禁用(灰色)
- `status-warning` - 警告(黄色)
- `status-error` - 失败/错误(红色)
**使用方式:** 通过 `@use` 引入,直接使用变量名。
### 角色标签样式
- `role-admin` - 管理员(蓝色)
- `role-member` - 成员(灰色)
- `role-developer` - 开发者(橙色)
```scss
@use '../../tokens' as *;
.my-component {
color: $text-1;
padding: $spacing-4;
font-weight: $font-weight-semibold;
}
```
**同时提供 CSS 变量**core 层定义),供运行时和 JSX 内联场景使用:
```scss
// core/_css-variables.scss 自动从 tokens 生成 :root 变量
:root {
--color-primary: #{$primary};
--color-text-1: #{$text-1};
}
// JSX 中可通过 style 使用
<div style={{ color: 'var(--color-text-1)' }}>
```
### 第2层Core核心样式
包含 CSS 重置、`:root` CSS 变量定义、body 全局样式。无业务逻辑。
```
core/
├── _reset.scss # * { margin:0; padding:0; box-sizing:border-box; }
├── _css-variables.scss # :root { --color-primary: ...; }
├── _base.scss # body { font-family, color, background }
└── _index.scss # @forward 所有 core 模块
```
### 第3层Layouts布局系统
页面骨架布局不包含具体UI组件样式。
| 文件 | 职责 |
|------|------|
| `_app-shell.scss` | 主框架sidebar + header + main-content + 响应式 |
| `_chat-layout.scss` | 聊天页chat-sidebar + chat-content + conversation-list |
| `_admin-layout.scss` | 管理台admin-sidebar + admin-nav + member-selection |
**关键类名:**
- `.layout` / `.app-shell` — 主布局容器
- `.sidebar` — 侧边栏
- `.main-content` — 主内容区
- `.header` — 顶部栏
- `.page-content` — 页面内容区
### 第4层Components组件库
每个组件一个目录,内部 `_index.scss` 包含该组件的完整样式。
```
components/
├── button/_index.scss
├── card/_index.scss
├── table/_index.scss
├── form/_index.scss
├── tag/_index.scss
├── modal/_index.scss
├── toast/_index.scss
└── _index.scss # @forward 所有组件
```
**组件目录创建规则:**
```
components/
└── {component-name}/
└── _index.scss # 必须存在,包含组件全部样式
```
### 第5层Pages页面特有样式
仅包含**页面独有的组件样式**,不放可复用组件。
```
pages/
├── _console.scss # 聊天消息、欢迎区、输入框等
├── _admin.scss # 总览统计、异常列表等
├── _developer.scss # 技能概览卡片、版本历史卡片等
├── _home.scss # 首页英雄区、特性卡片等
└── _index.scss
```
### 主入口文件
`global.scss` 是纯入口文件,**仅包含 `@use` 语句**,无任何直接样式定义:
```scss
// src/styles/global.scss
@use 'tokens' as *;
@use 'core' as *;
@use 'layouts' as *;
@use 'components' as *;
@use 'pages/console' as *;
@use 'pages/admin' as *;
@use 'pages/developer' as *;
@use 'pages/home' as *;
```
---
## 样式开发规范
### BEM 命名规范
所有组件类名必须遵循 BEM 格式:`.block__element--modifier`
```scss
// Block — 组件根节点
.card { }
// Element — 组件内部元素,用双下划线连接
.card__header { }
.card__body { }
.card__footer { }
// Modifier — 变体/状态,用双连字符连接
.card--flat { }
.card--elevated { }
.btn--primary { }
.btn--danger { }
.tag--running { }
.tag--admin { }
```
**JSX 对应写法:**
```jsx
<div className="card">
<div className="card__header">
<span className="card__title">标题</span>
</div>
<div className="card__body">
内容
</div>
</div>
<button className="btn btn--primary">确认</button>
<button className="btn btn--danger btn--sm">删除</button>
<span className="tag tag--running">运行中</span>
```
### 组件文件内部结构
每个组件的 `_index.scss` 按以下顺序组织:
```scss
// 1. 依赖引入
@use '../../tokens' as *;
// 2. Block — 组件根节点
.btn { }
// 3. Elements — 内部元素
.btn__icon { }
// 4. Modifiers — 变体(按语义分组)
// 颜色变体
.btn--primary { }
.btn--danger { }
// 尺寸变体
.btn--sm { }
.btn--lg { }
// 状态变体
.btn--loading { }
// 5. Legacy 兼容别名(过渡期)
.text-btn { @extend .btn--ghost; }
```
### 新增组件开发流程
1. **确认组件层级**:属于 tokens/core/layouts/components/pages 哪一层
2. **创建目录**`src/styles/components/{name}/`
3. **创建 `_index.scss`**:按 BEM 结构编写样式
4. **引入 tokens**`@use '../../tokens' as *;` 使用设计令牌
5. **在 `_index.scss` 中注册**`components/_index.scss` 添加 `@forward '{name}';`
6. **在 JSX 中使用**`className="block__element--modifier"`
7. **验证构建**`pnpm build`
### 新增页面样式开发流程
1. **优先复用组件库**:先检查 `components/` 是否已有可用组件
2. **页面特有组件**:在 `pages/_{page}.scss` 中定义
3. **仅放页面独有的样式**:可复用的应提取到 `components/`
4. **不硬编码值**:使用 tokens 中的变量或 CSS 变量
### 内联样式规则
**禁止使用内联 style 的场景:**
- 可复用的组件样式(按钮、卡片、表格操作列等)
- 使用 tokens 中已有变量的值
- 多个页面/组件中重复出现的模式
**允许使用内联 style 的场景:**
- 各表特有的内容列宽(如 `style={{ width: '180px' }}`
- 动态计算值(如进度条宽度 `width: 65%`
- 聊天消息内容中的排版(通过 `dangerouslySetInnerHTML` 渲染)
### 表格操作列规范
表格操作列统一使用以下类名,禁止写内联样式:
```jsx
// 表头 — 操作列宽度
<th className="col-actions">操作</th> // 200px
<th className="col-actions--narrow">操作</th> // 120px
<th className="col-actions--tiny">操作</th> // 80px
// 单元格 — 操作按钮容器
<td className="col-actions">
<div className="table-actions">
<button className="text-btn text-btn-primary">编辑</button>
<button className="text-btn text-btn-danger">删除</button>
</div>
</td>
// 可点击行
<tr className="tr-clickable" onClick={...}>
```
### 按钮使用规范
| 场景 | 类名 | 颜色 |
|------|------|------|
| 主操作(确认、提交) | `btn btn--primary` | 蓝色实心 |
| 次要操作(取消、重置) | `btn` | 灰色边框 |
| 表格内编辑 | `text-btn text-btn-primary` | 蓝色文字 |
| 表格内删除/禁用 | `text-btn text-btn-danger` | 红色文字 |
| 危险操作确认 | `btn btn--danger` | 红色实心 |
| 警告操作 | `btn btn--warning` | 橙色实心 |
### 状态标签规范
```jsx
// 运行状态
<span className="tag tag--running">运行中</span>
<span className="tag tag--stopped">已停止</span>
<span className="tag tag--error">失败</span>
<span className="tag tag--warning">警告</span>
// 用户角色
<span className="tag tag--admin">管理员</span>
<span className="tag tag--member">成员</span>
<span className="tag tag--developer">开发者</span>
```
### 引用 Tokens 的方式
```scss
// 在组件或页面文件中
@use '../../tokens' as *;
// 直接使用 SCSS 变量
.my-class {
color: $primary;
padding: $spacing-4;
border-radius: $radius-md;
font-weight: $font-weight-semibold;
box-shadow: $shadow-card;
}
// 使用 CSS 变量(适合需要运行时切换的场景)
.my-class {
color: var(--color-primary);
background: var(--color-bg-1);
}
```
### 样式文件引用规则
```scss
// tokens 层:无依赖
@use 'colors' as *;
// core 层:依赖 tokens
@use '../tokens' as *;
// layouts 层:依赖 tokens
@use '../tokens' as *;
// components 层:依赖 tokens
@use '../../tokens' as *;
// pages 层:依赖 tokens
@use '../tokens' as *;
```
**禁止**:跨层引用(如 components 直接引用 pages
### 响应式开发
使用 tokens 中定义的断点,通过 mixins 调用:
```scss
@use '../../tokens' as *;
@include mobile {
// <= 768px
.my-component {
flex-direction: column;
}
}
@include tablet {
// 769px ~ 1024px
}
@include desktop {
// >= 1025px
}
```
## 数据访问层
@@ -658,20 +970,30 @@ export default defineConfig({
### 添加新页面
1.`src/pages/` 目录下创建页面组件
2. 在父页面组件如ConsolePage、AdminPage中添加路由逻辑
3. 如果需要持久化状态添加localStorage逻辑
4. `src/styles/global.scss` 中添加页面特定样式
2. 在父页面组件(如 ConsolePage、AdminPage中添加路由逻辑
3. 如果需要持久化状态,添加 localStorage 逻辑
4. 页面特有样式添加到 `src/styles/pages/_{module}.scss`(优先复用组件库)
### 添加新组件
1.`src/components/` 目录下创建组件
2.`src/styles/global.scss` 中添加组件样式
3. 使用设计系统变量保持一致性
2.`src/styles/components/{name}/` 下创建 `_index.scss`
3. 使用 `@use '../../tokens' as *;` 引入设计令牌
4. 使用 BEM 命名:`.block__element--modifier`
5.`src/styles/components/_index.scss` 中添加 `@forward '{name}';`
### 修改样式
1. 优先修改 `src/styles/_variables.scss` 中的变量
2. 添加新的混入到 `src/styles/_mixins.scss`
3. 组件样式添加到 `_components.scss`
4. 页面特定样式添加到 `global.scss`
1. **全局调整**修改 `src/styles/tokens/` 中的令牌变量
2. **组件调整**:修改 `src/styles/components/{name}/_index.scss`
3. **页面调整**:修改 `src/styles/pages/_{module}.scss`
4. 禁止直接修改 `global.scss`(它是纯入口文件)
### 样式验证清单
- [ ] 类名是否遵循 BEM 规范
- [ ] 颜色/间距/字号是否使用 tokens 变量
- [ ] 是否存在重复定义的样式
- [ ] 可复用样式是否放在 `components/` 而非 `pages/`
- [ ] 表格操作列是否使用 `.col-actions` + `.table-actions`
- [ ] `pnpm build` 是否通过
---