- 建立 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>
GrandClaw 原型项目
项目概述
GrandClaw 是一个企业级AI智能助手平台的前端原型项目,主要用于展示平台的主要页面布局、交互流程和视觉设计。项目采用现代化的前端技术栈,实现了四大核心模块:
- 首页:平台入口,包含登录功能
- 工作台:用户与AI助手交互的主要界面,包含聊天、技能市场、日志查询、定时任务等功能
- 管理台:运营管理界面,包含总览、部门管理、用户管理、项目管理
- 开发台:技能开发界面,包含我的技能、技能编辑、开发文档等
技术栈
核心框架
- React 19.2.4:UI组件库
- React Router 7.13.1:路由管理(使用HashRouter)
- Vite 8.0.1:构建工具和开发服务器
UI与样式
- react-icons 5.5.0:图标库(Feather + FontAwesome图标集)
- Sass 1.98.0:CSS预处理器
- 五层分层架构:tokens → core → layouts → components → pages
- BEM 命名规范:所有组件类名遵循
.block__element--modifier格式
构建优化
- vite-plugin-singlefile 2.3.2:单文件打包,解决CORS问题
- 相对路径配置:支持直接打开HTML文件运行
- 注意:构建时会显示弃用警告
WARN inlineDynamicImports option is deprecated, please use codeSplitting: false instead.,这是由vite-plugin-singlefile插件内部使用弃用选项导致的,不影响功能,可以忽略。
包管理
- pnpm:高效的包管理器
项目结构
grandclaw-archtype/
├── dist/ # 构建输出目录(单个index.html)
├── public/ # 静态资源
├── src/ # 源代码
│ ├── App.jsx # 主路由配置
│ ├── main.jsx # 应用入口
│ ├── components/ # 组件
│ │ ├── layout/ # 布局组件(SidebarBrand、SidebarUser、SidebarNavItem)
│ │ ├── common/ # 通用UI组件(EmptyState、StatusBadge、TagInput、SearchBar)
│ │ ├── Layout.jsx # 通用布局组件(侧边栏+主内容)
│ │ └── ListSelector.jsx # 列表选择器组件(支持单选/多选)
│ ├── contexts/ # 全局状态管理
│ │ └── UserContext.jsx # 用户信息上下文
│ ├── hooks/ # 自定义Hook
│ │ ├── useLocalStorage.js # localStorage状态管理Hook
│ │ ├── usePageState.js # 页面状态持久化Hook
│ │ └── useNavigation.js # 导航逻辑Hook
│ ├── constants/ # 常量定义
│ │ ├── pages.js # 页面配置(路由、标题、图标)
│ │ └── storageKeys.js # localStorage键名常量
│ ├── services/ # 数据访问层
│ │ └── api.js # 统一数据访问接口
│ ├── data/ # 模拟数据
│ │ ├── conversations.js # 聊天场景数据
│ │ ├── developerData.js # 开发台数据
│ │ ├── logs.js # 日志数据
│ │ ├── members.js # 成员数据
│ │ ├── skills.js # 技能数据
│ │ ├── tasks.js # 定时任务数据
│ │ ├── adminData.js # 管理台数据(部门/用户/项目/总览/日志/模型配置)
│ │ └── configTypes.js # 模型配置类型注册表
│ ├── pages/ # 页面组件
│ │ ├── HomePage.jsx # 首页
│ │ ├── LoginPage.jsx # 登录页面
│ │ ├── ConsolePage.jsx # 工作台主页面
│ │ ├── AdminPage.jsx # 管理台主页面
│ │ ├── DeveloperPage.jsx # 开发台主页面
│ │ ├── console/ # 工作台子页面
│ │ ├── admin/ # 管理台子页面
│ │ └── developer/ # 开发台子页面
│ └── 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配置
└── pnpm-lock.yaml # 依赖锁定文件
开发指南
环境要求
- Node.js 18+
- pnpm(推荐)或 npm
安装依赖
pnpm install
开发模式
pnpm dev
# 访问 http://localhost:5173
构建生产版本
pnpm build
# 生成 dist/index.html(单个HTML文件)
预览生产构建
直接双击 dist/index.html 文件,或在浏览器中打开。
核心功能
1. 首页
- 平台品牌展示
- 导航入口(工作台、开发台、管理台)
- 登录入口
2. 登录页面
- 用户名/邮箱登录
- 密码输入
- 验证码(防爆破)
- 记住我功能
- 忘记密码链接
3. 工作台(Console)
- 聊天界面:支持多种聊天场景(欢迎页、普通对话、技能调用、文件上传)
- 技能市场:浏览已上架技能、订阅技能(仅展示已上架技能)
- 我的技能:管理已订阅技能,支持启用/禁用、配置变量、取消订阅(NEW)
- 技能配置:为已订阅技能配置 key-value 变量(NEW)
- 日志查询:支持按用户、类型、状态筛选
- 定时任务:管理定时任务,支持启用/禁用,查看任务详情
- 项目管理:成员列表,增加成员
- 账号管理:个人信息和密码修改
4. 管理台(Admin)
- 运营总览:平台运营指标卡片(用户总数、部门数量、项目数量、今日调用)、异常/待办事项提醒、最近操作日志
- 审核管理:版本审核列表与详情、下架审核列表与详情
- 部门管理:部门列表,支持搜索筛选、新增、编辑、启用/禁用、删除确认
- 用户管理:用户列表,支持搜索筛选(关键词/部门/状态)、新增、编辑、启用/禁用、删除确认,角色区分(管理员/开发者/成员)
- 项目管理:项目列表,支持搜索筛选、新增、编辑、启用/禁用、删除确认
- 模型配置:管理平台的默认模型接入配置,支持多组配置(OpenAI兼容接口、智算管理平台等类型),可切换生效配置,生效中配置不可编辑/删除,配置类型创建后不可修改
- 日志查询:全局系统日志查询,支持多维度筛选(关键词、用户、部门、类型、状态、时间范围)
5. 开发台(Developer)
- 总览:开发者指标卡片(我的技能总数、已上架、开发中、待审核)、待审核项目列表、最近动态
- 我的技能:技能列表,支持关键词搜索(内部名称/内部描述)、状态筛选(开发中/已上架/下架审核中/已下架)、分页,支持下架(需要先撤回审核中的版本)、删除(已上架需要先下架),仅展示开发者内部信息
- 技能详情:四段式布局 - 1) 概览卡片(内部信息:内部名称、状态、编辑按钮);2) 当前生效版本卡片(商店展示效果预览,分类作为第一个标签显示);3) 版本历史卡片(普通卡片布局,展示版本号、状态、日期、版本说明、发布信息预览、操作按钮,无下载按钮);4) 管理操作卡片
- 创建技能:简化表单 - 仅内部技能名称、内部技能描述(明确标注仅供开发者管理使用)
- 编辑内部信息:独立页面编辑内部技能名称/内部技能描述(明确标注不影响商店展示)
- 上传新版本:增强表单 - 版本说明区域 + 发布信息区域(技能发布名称、技能发布描述、分类、标签、图标),非首版本默认继承当前生效版本的值
- 开发文档:技能开发相关文档
- 开发者设置:开发者账号信息
重要数据结构变更说明
- 开发者内部信息:内部名称、内部描述 - 仅供开发者管理,与商店展示完全无关,可随时修改无需审核
- 版本发布信息:发布名称、发布描述、分类、标签、图标 - 存储在版本中,随版本审核通过后生效,修改必须发布新版本
- 技能商店展示:完全从当前生效版本取发布信息,确保任何商店内容变更都经过版本审核
- 分类与标签展示:分类始终作为第一个标签显示,与普通标签一起展示
按钮禁用规则
基于 hasPendingReview 标志和技能状态控制操作按钮可用性:
- 上传新版本按钮:
status === 'unlisting' || status === 'unlisted' || hasPendingReview === true时禁用 - 下架技能按钮:
hasPendingReview === true时禁用 - 删除技能按钮:
status === 'published' || status === 'unlisting' || hasPendingReview === true时禁用
撤回审核按钮样式
- 按钮类型:警告按钮(橙色)
- 按钮类名:
btn btn-warning btn-sm - 按钮图标:逆时针旋转图标(FiRotateCcw)
- 按钮文案:"撤回审核"
- 展示位置:版本历史卡片中审核中版本的操作区域
路由结构
项目使用 HashRouter,所有路由基于哈希路径,支持直接打开HTML文件运行。
主要路由
/ # 首页
/login # 登录页面
/console # 工作台
/admin # 管理台
/developer # 开发台
路由配置(App.jsx)
import { HashRouter as Router, Routes, Route } from 'react-router-dom';
<Router>
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="/login" element={<LoginPage />} />
<Route path="/console" element={<ConsolePage />} />
<Route path="/admin" element={<AdminPage />} />
<Route path="/developer" element={<DeveloperPage />} />
</Routes>
</Router>
通用组件
布局组件
SidebarBrand 品牌区域
侧边栏顶部的品牌展示组件。
import { SidebarBrand } from '../components/layout/SidebarBrand.jsx';
<SidebarBrand subtitle="企业级AI平台" />
SidebarUser 用户信息
侧边栏底部的用户信息展示组件,使用全局用户上下文。
import { SidebarUser } from '../components/layout/SidebarUser.jsx';
<SidebarUser
onClick={() => navigate('/account')}
wrapperClassName="sidebar-user"
infoClassName="sidebar-user-info"
nameClassName="sidebar-user-name"
roleClassName="sidebar-user-role"
/>
SidebarNavItem 导航项
侧边栏导航菜单项组件。
import { SidebarNavItem } from '../components/layout/SidebarNavItem.jsx';
<SidebarNavItem
icon={<FiMessageSquare />}
label="智能助手"
active={currentPage === 'chat'}
onClick={() => setCurrentPage('chat')}
itemClassName="sidebar-nav-item"
iconClassName="sidebar-nav-icon"
textClassName="sidebar-nav-text"
/>
通用UI组件
EmptyState 空状态
用于展示空列表或无数据状态的组件。
import EmptyState from '../components/common/EmptyState.jsx';
<EmptyState
icon={<FiInbox size={48} />}
title="暂无数据"
description="当前没有可显示的内容"
/>
Modal 弹窗
用于展示确认操作的弹窗组件,支持自定义标题和内容。
import Modal from '../components/common/Modal.jsx';
<Modal
visible={showModal}
title="确认删除"
onConfirm={handleConfirm}
onCancel={handleCancel}
confirmText="删除"
>
确定要删除这个任务吗?
</Modal>
Toast 消息提示
用于展示操作结果的消息提示组件,支持成功、错误、警告、信息四种类型。
import Toast from '../components/common/Toast.jsx';
<Toast
visible={showToast}
type="success"
message="操作成功"
onClose={() => setShowToast(false)}
/>
支持的类型:
success- 成功(绿色)error- 错误(红色)warning- 警告(黄色)info- 信息(蓝色)
StatusBadge 状态标签
用于显示状态(成功、失败、警告等)的标签组件。
import StatusBadge from '../components/common/StatusBadge.jsx';
<StatusBadge status="success" text="运行中" />
<StatusBadge status="error" text="失败" />
<StatusBadge status="warning" text="警告" />
<StatusBadge status="stopped" text="已停止" />
TagInput 标签输入
支持输入标签的输入框组件。
import TagInput from '../components/common/TagInput.jsx';
<TagInput
tags={tags}
onChange={setTags}
placeholder="输入标签后按回车添加"
/>
SearchBar 搜索框
通用的搜索输入框组件。
import SearchBar from '../components/common/SearchBar.jsx';
<SearchBar
value={searchQuery}
onChange={setSearchQuery}
placeholder="搜索..."
/>
列表组件
ListSelector 列表选择器
通用的列表选择器组件,支持单选和多选模式。
import ListSelector from '../components/ListSelector.jsx';
<ListSelector
data={dataList} // 数据数组
selectedIds={selectedIds} // 已选项
onChange={handleChange} // 选择变化回调
searchPlaceholder="搜索..." // 搜索框占位符
columns={columns} // 列配置 [{ key, label }]
multiSelect={false} // 是否多选
selectedLabel="已选标签" // 已选标签文字
onClearSelected={() => {}} // 清除已选回调
/>
状态管理
全局状态
UserContext 用户信息上下文
全局用户信息状态,通过 UserProvider 提供给整个应用。
import { UserProvider, useUserContext } from '../contexts/UserContext.jsx';
// 在 App.jsx 中包裹
<UserProvider user={{ name: '张三', avatar: '张', role: 'AI 产品部' }}>
<App />
</UserProvider>
// 在组件中使用
function Component() {
const { user } = useUserContext();
return <div>{user.name}</div>;
}
自定义 Hooks
usePageState 页面状态持久化
处理页面切换状态的 Hook,支持 localStorage 持久化和主页跳转重置。
import { usePageState } from '../hooks/usePageState.js';
import { CONSOLE_PAGES } from '../constants/pages.js';
import { CONSOLE_KEYS } from '../constants/storageKeys.js';
const { currentPage, setCurrentPage } = usePageState({
storageKey: CONSOLE_KEYS.CURRENT_PAGE,
defaultPage: 'chat',
pageTitles: CONSOLE_PAGES,
});
useNavigation 导航逻辑
封装页面导航逻辑的 Hook,支持携带额外数据。
import { useNavigation } from '../hooks/useNavigation.js';
const { navigateToPage, extraData } = useNavigation(setCurrentPage);
// 导航到指定页面
navigateToPage('skills', { skillId: '1' });
// 获取导航传递的数据
const skillId = extraData.skillId;
useLocalStorage localStorage 状态管理
同步组件状态到 localStorage 的 Hook。
import { useLocalStorage } from '../hooks/useLocalStorage.js';
const [value, setValue] = useLocalStorage('myKey', 'defaultValue');
setValue('newValue'); // 自动同步到 localStorage
导航状态持久化策略
每个主要页面(工作台、管理台、开发台)都有独立的localStorage键:
// 工作台
localStorage.setItem('console_currentPage', 'chat');
localStorage.setItem('console_currentScene', 'welcome');
// 管理台
localStorage.setItem('admin_currentPage', 'overview');
// 开发台
localStorage.setItem('developer_currentPage', 'overview');
localStorage.setItem('developer_currentSkillId', '1');
主页跳转 vs 刷新浏览器
通过location.state.fromHome区分两种导航来源:
// 从主页跳转:显示默认页面
useEffect(() => {
if (location.state?.fromHome) {
setCurrentPage('chat'); // 默认页面
navigate('.', { replace: true, state: {} }); // 清除state
}
}, [location.state]);
// 刷新浏览器:从localStorage恢复
const [currentPage, setCurrentPage] = useState(() => {
return localStorage.getItem('console_currentPage') || 'chat';
});
样式系统
分层架构
样式采用五层架构,依赖方向自上而下:tokens → core → layouts → components → pages
┌─────────────────────────────────────────┐
│ pages/ 页面特有组件,仅覆盖模式 │ ← 最上层
├─────────────────────────────────────────┤
│ components/ 可复用UI组件库,BEM命名 │
├─────────────────────────────────────────┤
│ layouts/ 布局系统(shell/chat/admin) │
├─────────────────────────────────────────┤
│ core/ 重置、CSS变量、body样式 │
├─────────────────────────────────────────┤
│ tokens/ 设计令牌(颜色/间距/阴影...) │ ← 最底层
└─────────────────────────────────────────┘
第1层:Tokens(设计令牌)
所有设计决策集中定义,禁止在组件或页面中硬编码值。
// src/styles/tokens/_colors.scss
$primary: #3B82F6;
$primary-dark: #2563EB;
$success: #10B981;
$danger: #EF4444;
$text-1: #1E293B;
$text-3: #94A3B8;
// 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;
使用方式: 通过 @use 引入,直接使用变量名。
@use '../../tokens' as *;
.my-component {
color: $text-1;
padding: $spacing-4;
font-weight: $font-weight-semibold;
}
同时提供 CSS 变量(core 层定义),供运行时和 JSX 内联场景使用:
// 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 语句,无任何直接样式定义:
// 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
// Block — 组件根节点
.card { }
// Element — 组件内部元素,用双下划线连接
.card__header { }
.card__body { }
.card__footer { }
// Modifier — 变体/状态,用双连字符连接
.card--flat { }
.card--elevated { }
.btn--primary { }
.btn--danger { }
.tag--running { }
.tag--admin { }
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 按以下顺序组织:
// 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; }
新增组件开发流程
- 确认组件层级:属于 tokens/core/layouts/components/pages 哪一层
- 创建目录:
src/styles/components/{name}/ - 创建
_index.scss:按 BEM 结构编写样式 - 引入 tokens:
@use '../../tokens' as *;使用设计令牌 - 在
_index.scss中注册:components/_index.scss添加@forward '{name}'; - 在 JSX 中使用:
className="block__element--modifier" - 验证构建:
pnpm build
新增页面样式开发流程
- 优先复用组件库:先检查
components/是否已有可用组件 - 页面特有组件:在
pages/_{page}.scss中定义 - 仅放页面独有的样式:可复用的应提取到
components/ - 不硬编码值:使用 tokens 中的变量或 CSS 变量
内联样式规则
禁止使用内联 style 的场景:
- 可复用的组件样式(按钮、卡片、表格操作列等)
- 使用 tokens 中已有变量的值
- 多个页面/组件中重复出现的模式
允许使用内联 style 的场景:
- 各表特有的内容列宽(如
style={{ width: '180px' }}) - 动态计算值(如进度条宽度
width: 65%) - 聊天消息内容中的排版(通过
dangerouslySetInnerHTML渲染)
表格操作列规范
表格操作列统一使用以下类名,禁止写内联样式:
// 表头 — 操作列宽度
<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 |
橙色实心 |
状态标签规范
// 运行状态
<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 的方式
// 在组件或页面文件中
@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);
}
样式文件引用规则
// 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 调用:
@use '../../tokens' as *;
@include mobile {
// <= 768px
.my-component {
flex-direction: column;
}
}
@include tablet {
// 769px ~ 1024px
}
@include desktop {
// >= 1025px
}
数据访问层
项目使用统一的数据访问接口 src/services/api.js,所有数据获取都通过 API 层进行,便于未来对接后端服务。
API 使用示例
import { api } from '../services/api.js';
// 获取技能列表
const skills = api.skills.list();
// 获取单个技能详情
const skill = api.skills.getById('1');
// 获取日志列表
const logs = api.logs.list();
// 按条件筛选日志
const filteredLogs = api.logs.filter({ user, type, status });
// 获取开发者技能
const mySkills = api.developer.getMySkills();
// 获取成员列表
const members = api.members.list();
API 模块结构
api.user- 用户信息api.skills- 技能市场(列表、详情、文件、版本、图标)api.conversations- 聊天场景和对话历史api.logs- 操作日志(列表、筛选)api.developer- 开发台数据(总览、技能、分类、文档)api.members- 项目成员api.tasks- 定时任务api.admin- 管理台(总览、部门、用户、项目、模型配置、全局日志)
数据模拟
所有数据都存储在 src/data/ 目录下的JavaScript文件中,作为静态模拟数据。API 服务层统一从这些文件读取数据。
数据文件说明
conversations.js:聊天场景和对话历史skills.js:技能市场数据,包含技能详情(含状态:dev/published/unlisting/unlisted)、文件列表、版本历史(含状态:reviewing/approved/rejected/withdrawn、拒绝理由)、审核列表(pendingVersionReviews、pendingUnlistReviews)developerData.js:开发台数据,包含我的技能(含图标、版本审核状态、hasPendingReview标识)、技能分类、开发者总览、开发文档logs.js:操作日志数据(成功/失败/警告状态)tasks.js:定时任务数据(包含任务配置和执行日志)adminData.js:管理台数据(部门列表、用户列表、项目列表、模型配置列表、总览指标、全局日志、可选项数据)configTypes.js:模型配置类型注册表(OpenAI兼容接口、智算管理平台等类型定义)members.js:项目成员数据
构建和部署
构建配置(vite.config.js)
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { viteSingleFile } from 'vite-plugin-singlefile';
export default defineConfig({
base: './', // 相对路径,支持直接打开HTML文件
plugins: [
react(),
viteSingleFile() // 单文件打包
],
});
构建产物
运行 pnpm build 后生成:
dist/index.html:包含所有JavaScript和CSS的单个HTML文件- 支持直接双击打开,无需服务器
- 使用HashRouter,路由正常工作
部署方式
- 本地运行:直接双击
dist/index.html - 静态服务器:将
dist/目录部署到任何静态文件服务器 - CDN部署:上传单个HTML文件到CDN即可
已知问题和注意事项
1. 聊天场景渲染
- 聊天内容使用
dangerouslySetInnerHTML渲染HTML字符串 - 深度思考区域支持点击展开/折叠
- 聊天输入框仅为展示,无实际功能
2. 浏览器兼容性
- 需要现代浏览器(Chrome、Firefox、Safari、Edge)
- 不支持IE
- file://协议下可能存在某些限制
3. 状态持久化
- 使用localStorage存储子页面状态
- 清除浏览器数据会丢失导航状态
- 不同浏览器标签页共享localStorage
4. 性能考虑
- 单个HTML文件较大,首次加载可能稍慢
- 所有图标已通过react-icons优化,按需加载
- SCSS样式已编译为CSS,无运行时开销
5. 构建警告
- 构建时会显示弃用警告:
WARN inlineDynamicImports option is deprecated, please use codeSplitting: false instead. - 这个警告来自
vite-plugin-singlefile插件内部,无法通过配置消除 - 警告不影响功能,可以安全忽略
开发建议
添加新页面
- 在
src/pages/目录下创建页面组件 - 在父页面组件(如 ConsolePage、AdminPage)中添加路由逻辑
- 如果需要持久化状态,添加 localStorage 逻辑
- 页面特有样式添加到
src/styles/pages/_{module}.scss(优先复用组件库)
添加新组件
- 在
src/components/目录下创建组件 - 在
src/styles/components/{name}/下创建_index.scss - 使用
@use '../../tokens' as *;引入设计令牌 - 使用 BEM 命名:
.block__element--modifier - 在
src/styles/components/_index.scss中添加@forward '{name}';
修改样式
- 全局调整:修改
src/styles/tokens/中的令牌变量 - 组件调整:修改
src/styles/components/{name}/_index.scss - 页面调整:修改
src/styles/pages/_{module}.scss - 禁止直接修改
global.scss(它是纯入口文件)
样式验证清单
- 类名是否遵循 BEM 规范
- 颜色/间距/字号是否使用 tokens 变量
- 是否存在重复定义的样式
- 可复用样式是否放在
components/而非pages/ - 表格操作列是否使用
.col-actions+.table-actions pnpm build是否通过
更多文档
审核审批流程的详细说明请查看:docs/审核流程.md
最后更新:2026-03-26