Files
GrandClaw-prototype/README.md
lanyuanxiaoyao 3f815db0b2 feat: 添加三层级模型配置管理系统原型
新增三个层级的模型配置管理功能:
- 平台级模型配置(管理台):配置列表、新增/编辑、删除(默认模型不允许删除)、设为默认(仅页面状态)
- 项目级模型配置(工作台):配置列表、新增/编辑、删除(允许删除默认)、设为默认(仅页面状态)
- 个人模型配置(工作台):配置列表、新增/编辑、删除(允许删除默认)、设为默认(仅页面状态)
- 融合式模型选择器:在聊天输入框顶部集成,按层级分组展示模型列表(平台/项目/个人)

技术实现:
- 新增项目级和个人级配置数据文件
- 扩展 api.js 数据访问层,添加 consoleModels.project 和 consoleModels.user 对象
- 新增 4 个页面组件(ProjectModelConfigsPage、AddProjectModelConfigPage、UserModelConfigsPage、AddUserModelConfigPage)
- 修改 2 个现有页面(ModelConfigsPage、ChatPage、ConsoleLayout)
- 修改 Modal 组件支持 cancelText 为空时隐藏取消按钮
- 在 App.jsx 中添加 6 条新路由
- 新增模型选择器样式文件(融合式设计、分组展示、响应式)
- 更新 README.md 项目结构

样式特点:
- 融合式模型选择器与输入框风格一致
- 下拉列表按层级分组(平台/项目/个人)
- 默认标记使用渐变背景色
- 选中状态高亮(浅蓝色背景 + 左侧边框 + 右侧勾选)
- 响应式设计(移动端适配)

数据示例:
- 项目级:3 个示例配置(不同类型和状态)
- 个人级:2 个示例配置(不同类型和状态)
2026-04-10 13:43:19 +08:00

450 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# GrandClaw 原型项目
企业级AI智能助手平台前端原型展示主要页面布局、交互流程和视觉设计。
---
## ⚠️ 核心约束(必读)
> 以下约束必须严格遵守,不得违反
### 项目性质
- **纯前端展示原型**无后端交互供内部开发人员参考UI界面
- **目标**:展示页面布局、样式和组件能力
- **交互限制**:允许轻量级交互展示(表单验证、弹框),重叠/覆盖类状态(弹框、下拉)允许简单交互切换
### 语言规范
| 场景 | 语言 |
|------|------|
| 交流、文档、注释、提交信息 | **中文** |
| 代码命名(变量、函数、类、文件) | **英文** |
### 技术约束
| 约束项 | 规则 |
|--------|------|
| UI库 | **禁止引入**使用当前SCSS样式方案 |
| TypeScript | **禁止引入**使用JavaScript |
| ESLint | **禁止引入** |
| 包管理器 | **pnpm**不允许npm、yarn |
| 测试 | **不构建**,使用`pnpm build`验证打包即可 |
| 性能优化 | **不做**保持vite-plugin-singlefile单文件打包 |
| 安全防御 | **不做**eval/dangerouslySetInnerHTML按需使用 |
### 复用优先原则
```
新增代码时优先级:
1. 复用已有样式src/styles
2. 复用已有组件src/components
3. 复用已有页面布局模式
4. 最后才创建新代码
```
### Git提交规范
```
格式: 类型: 简短描述
类型可选:
- feat: 新功能
- fix: 修复
- refactor: 重构
- docs: 文档
- style: 格式
- test: 测试
- chore: 构建/工具
多行描述: 空行后加详细说明
示例:
feat: 添加技能详情页面
- 新增技能详情布局
- 集成版本历史展示
```
---
## 快速开始
```bash
pnpm install # 安装依赖
pnpm build # 验证打包不运行pnpm dev会挂起流程
```
---
## 技术栈
| 技术 | 版本 | 用途 |
|------|------|------|
| React | 19.2.4 | UI框架 |
| React Router (HashRouter) | 7.13.1 | 路由管理 |
| Vite | 8.0.1 | 构建工具 |
| react-icons | 5.5.0 | 图标库 |
| Sass | 1.98.0 | 样式预处理 |
| vite-plugin-singlefile | 2.3.2 | 单文件打包 |
---
## 项目结构
```
src/
├── components/ # 组件库
│ ├── common/ # 通用组件 (Modal, Toast, EmptyState等)
│ ├── layout/ # 布局组件 (AppHeader, AppLayout, UserDropdown, ConsoleLayout, AdminLayout, DeveloperLayout等)
│ ├── Layout.jsx # 主布局组件sidebar + content
│ └── ListSelector.jsx # 列表选择器
├── contexts/ # 全局状态 (UserContext)
├── services/ # 数据访问层 (api.js)
├── data/ # 模拟数据
│ ├── adminData.js # 管理台数据(含平台级模型配置)
│ ├── projectModelConfigs.js # 项目级模型配置数据
│ └── userModelConfigs.js # 个人模型配置数据
├── pages/ # 页面组件
│ ├── console/ # 工作台子页面
│ ├── admin/ # 管理台子页面
│ └── developer/ # 开发台子页面
├── styles/ # 样式系统(五层架构)
│ ├── tokens/ # 设计令牌(颜色、间距等)
│ ├── core/ # 核心样式重置、CSS变量
│ ├── layouts/ # 布局系统
│ ├── components/ # 组件样式
│ ├── pages/ # 页面样式
│ │ ├── model-selector/ # 模型选择器样式
│ │ └── ...
│ └── global.scss # 主入口
├── App.jsx # 路由配置
└── main.jsx # 应用入口
```
---
## 核心模块
| 模块 | 路由 | 功能 |
|------|------|------|
| 首页 | `/` | 品牌展示、登录入口 |
| 工作台 | `/console` | 聊天、技能市场、定时任务、项目管理(成员/权限/技能/模型配置)、个人模型配置 |
| 管理台 | `/admin` | 部门/用户/项目管理、模型配置 |
| 开发台 | `/developer` | 技能开发、版本管理 |
---
## 样式规范
### 五层架构
```
tokens → core → layouts → components → pages
```
| 层级 | 职责 | 规则 |
|------|------|------|
| tokens/ | 设计令牌 | **禁止硬编码**颜色、间距、字号 |
| core/ | 核心样式 | CSS重置、全局变量 |
| layouts/ | 布局系统 | 页面骨架,不含具体组件 |
| components/ | 组件样式 | 可复用,**BEM命名** |
| pages/ | 页面样式 | 页面独有,不放置可复用样式 |
### BEM命名规范
```scss
// 格式: .block__element--modifier
.card { }
.card__header { }
.card__body { }
.btn--primary { }
.tag--running { }
```
```jsx
// JSX对应
<div className="card">
<div className="card__header">标题</div>
</div>
<button className="btn btn--primary">确认</button>
<span className="tag tag--running">运行中</span>
```
### 引用规则
```scss
// tokens层无依赖
@use 'colors' as *;
// 其他层依赖tokens
@use '../tokens' as *; // core、layouts
@use '../../tokens' as *; // components
@use '../tokens' as *; // pages
```
**禁止跨层引用**如components引用pages
### 按钮规范
| 场景 | 类名 |
|------|------|
| 主操作 | `btn btn--primary` |
| 次要操作 | `btn` |
| 表格内编辑 | `text-btn text-btn-primary` |
| 表格内删除 | `text-btn text-btn-danger` |
| 危险确认 | `btn btn--danger` |
| 警告操作 | `btn btn--warning` |
### 表格操作列规范
```jsx
// 表头
<th className="col-actions">操作</th> // 200px
<th className="col-actions--narrow">操作</th> // 120px
// 单元格
<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>
```
**禁止内联样式定义操作列**
---
## 组件规范
### 新增组件流程
1. 创建 `src/components/{category}/ComponentName.jsx`
2. 创建 `src/styles/components/{name}/_index.scss`
3. 使用 `@use '../../tokens' as *;`
4. 遵循BEM命名
5.`components/_index.scss` 添加 `@forward '{name}';`
### 组件模板
```jsx
// components/common/Example.jsx
function Example({ title, children }) {
return (
<div className="example">
<div className="example__header">{title}</div>
<div className="example__body">{children}</div>
</div>
);
}
export default Example;
```
```scss
// styles/components/example/_index.scss
@use '../../tokens' as *;
.example {
padding: $spacing-4;
&__header {
font-weight: $font-weight-semibold;
color: $text-1;
}
&__body {
color: $text-2;
}
}
```
### 可复用组件
| 组件 | 路径 | 用途 |
|------|------|------|
| Layout | `components/Layout.jsx` | 主布局sidebar + content |
| AppLayout | `components/layout/AppLayout.jsx` | 全局布局header + main |
| AppHeader | `components/layout/AppHeader.jsx` | 统一导航头部 |
| UserDropdown | `components/layout/UserDropdown.jsx` | 用户下拉菜单 |
| Modal | `components/common/Modal.jsx` | 确认弹窗 |
| Toast | `components/common/Toast.jsx` | 消息提示 |
| EmptyState | `components/common/EmptyState.jsx` | 空状态展示 |
| SearchBar | `components/common/SearchBar.jsx` | 搜索框 |
| StatusBadge | `components/common/StatusBadge.jsx` | 状态标签 |
| SidebarNavItem | `components/layout/SidebarNavItem.jsx` | 侧边栏导航项 |
| SidebarNavGroup | `components/layout/SidebarNavGroup.jsx` | 可展开侧边栏导航组 |
---
## 路由规范
### 嵌套路由结构
所有页面通过正式路由导航,使用 HashRouter + 嵌套路由。
```jsx
// App.jsx
<HashRouter>
<Routes>
<Route path="/login" element={<LoginPage />} />
<Route element={<AppLayout />}>
<Route path="/" element={<HomePage />} />
<Route path="/console" element={<ConsoleLayout />}>
<Route index element={<Navigate to="chat/welcome" replace />} />
<Route path="chat" element={<ChatPage />} />
<Route path="chat/:scene" element={<ChatPage />} />
<Route path="skills" element={<SkillsPage />} />
<Route path="skills/:skillId" element={<SkillDetailPage />} />
<Route path="project" element={<Navigate to="members" replace />} />
<Route path="project/members" element={<MembersPage />} />
<Route path="project/members/add" element={<AddMemberPage />} />
<Route path="project/members/:memberId/config" element={<MemberConfigPage />} />
<Route path="project/permissions" element={<PermissionsPage />} />
<Route path="project/skills" element={<SkillsConfigPage />} />
{/* ...更多子路由 */}
</Route>
<Route path="/admin" element={<AdminLayout />}>
<Route index element={<Navigate to="overview" replace />} />
<Route path="overview" element={<OverviewPage />} />
<Route path="departments" element={<DepartmentsPage />} />
<Route path="departments/add" element={<AddDepartmentPage />} />
<Route path="departments/:id/edit" element={<AddDepartmentPage />} />
{/* ...更多子路由 */}
</Route>
<Route path="/developer" element={<DeveloperLayout />}>
<Route index element={<Navigate to="overview" replace />} />
{/* ...子路由 */}
</Route>
</Route>
</Routes>
</HashRouter>
```
**说明**
- `AppLayout` 包裹所有需要统一 Header 的页面
- 登录页独立,不使用 `AppLayout`
- 每个模块Console/Admin/Developer使用独立的 Layout 组件包裹 `<Outlet />`
- 模块根路径自动重定向到默认子页面
- 新增/编辑表单通过 URL 参数区分:`/admin/departments/add` vs `/admin/departments/:id/edit`
### 子页面参数获取
```jsx
// 使用 useParams 获取 URL 参数
function SkillDetailPage() {
const { skillId } = useParams();
const skill = api.skills.getById(Number(skillId));
// ...
}
// 使用 useNavigate 进行导航
function SkillsPage() {
const navigate = useNavigate();
return <button onClick={() => navigate('/console/skills/1')}>查看</button>;
}
```
### 新增页面流程
1.`App.jsx` 添加路由定义
2. 创建页面组件(使用 `useParams` / `useNavigate`
3. 在对应 Layout 的 sidebar 添加导航项
4. 确保页面返回按钮使用固定路径导航
---
## 状态管理
### 全局状态UserContext
```jsx
// 使用
import { useUserContext } from '../contexts/UserContext.jsx';
function Component() {
const { user } = useUserContext();
return <div>{user.name}</div>;
}
```
### 页面状态由 URL 驱动
所有页面状态(当前页面、场景名、实体 ID通过 URL 参数驱动,不依赖 localStorage。
| 状态类型 | 驱动方式 | 示例 |
|---------|---------|------|
| 当前页面 | URL 路径 | `/console/skills` |
| 实体 ID | URL 参数 | `/console/skills/:skillId` |
| 场景名 | URL 参数 | `/console/chat/:scene` |
| 新增/编辑模式 | URL 参数有无 | `/admin/users/add` vs `/admin/users/:id/edit` |
| 编辑数据 | `api.getById(Number(id))` | 通过 ID 重新获取 |
---
## 数据访问
所有数据通过 `src/services/api.js` 访问:
```javascript
import api from '../services/api.js';
// 用户
api.user.getInfo();
// 技能
api.skills.list();
api.skills.getById(id);
// 开发台
api.developer.getMySkills();
api.developer.getOverview();
// 管理台
api.admin.departments.list();
api.admin.users.list();
api.admin.modelConfigs.create(data);
// 日志筛选
api.logs.filter({ user, type, status });
```
---
## 开发清单
### 新增功能前检查
- [ ] 是否可复用已有样式?
- [ ] 是否可复用已有组件?
- [ ] 是否可复用已有页面布局模式?
### 代码提交前检查
- [ ] 类名是否遵循BEM
- [ ] 颜色/间距是否使用tokens变量
- [ ] 表格操作列是否使用规范类名?
- [ ] `pnpm build` 是否通过?
- [ ] 文档注释是否使用中文?
- [ ] 变量/函数命名是否使用英文?
---
## 已知问题
| 问题 | 说明 | 处理 |
|------|------|------|
| 构建警告 | `inlineDynamicImports is deprecated` | 来自vite-plugin-singlefile可忽略 |
| 浏览器兼容 | 不支持IE | 使用现代浏览器 |
---
*最后更新2026-03-30*