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
2026-03-20 09:42:29 +08:00
2026-03-20 09:42:29 +08:00

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: 添加技能详情页面

- 新增技能详情布局
- 集成版本历史展示

快速开始

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命名规范

// 格式: .block__element--modifier
.card { }
.card__header { }
.card__body { }
.btn--primary { }
.tag--running { }
// JSX对应
<div className="card">
    <div className="card__header">标题</div>
</div>
<button className="btn btn--primary">确认</button>
<span className="tag tag--running">运行中</span>

引用规则

// 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

表格操作列规范

// 表头
<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}';

组件模板

// 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;
// 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 + 嵌套路由。

// 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

子页面参数获取

// 使用 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

// 使用
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 访问:

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

Description
No description provided
Readme 706 KiB
Languages
JavaScript 79.9%
SCSS 20%