lanyuanxiaoyao f507032d98 style: 统一侧边栏菜单图标和顺序
- 工作台"个人模型"改名为"我的模型",并移至"我的技能"之后
- 管理台"模型配置"图标统一为 FiCpu
- 工作台"技能配置"图标改为 FiBox,与"我的技能"保持一致
2026-04-13 10:08:54 +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 使用现代浏览器

对话输入框布局

布局结构

对话输入框采用响应式水平布局,根据屏幕宽度自动调整组件排列方式。

桌面端(>= 768px

  • 水平布局:模型选择器 → 输入区域 → 工具按钮 → 发送按钮
  • 模型选择器:标准模式(显示完整模型名称,宽度 160px
  • 分隔方式:右侧垂直边框

平板端480-768px

  • 水平布局:模型选择器 → 输入区域 → 工具按钮 → 发送按钮
  • 模型选择器:紧凑模式(仅显示图标,宽度 100px
  • 工具按钮:上传文件、代码块
  • 分隔方式:右侧垂直边框

移动端(< 480px

  • 垂直布局:模型选择器在上,输入区域在下
  • 模型选择器:标准模式(显示完整模型名称,宽度 160px
  • 分隔方式:底部水平边框

ModelSelector 组件

ModelSelector 组件支持 variant 属性,控制显示模式:

<ModelSelector
  selectedModel={selectedModel}
  onSelectModel={setSelectedModel}
  variant="standard"  //  "compact"
/>

variant 属性值

  • standard:标准模式,显示完整模型名称
  • compact:紧凑模式,仅显示图标

响应式断点

断点名称 屏幕宽度 布局模式 模型选择器 宽度
桌面端 >= 768px 水平 标准 160px
平板端 480-768px 水平 紧凑 100px
移动端 < 480px 垂直 标准 160px

工具按钮

  • 工具按钮横向排列在输入区域右侧(水平布局)或下方(垂直布局)
  • 当前工具按钮:上传文件、代码块
  • 工具按钮右侧显示分隔边框(水平布局)

下拉菜单方向

  • 默认向下展开
  • 如果下方空间不足,自动向上展开

最后更新2026-04-10

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