diff --git a/README.md b/README.md index f18167f..320ed59 100644 --- a/README.md +++ b/README.md @@ -88,13 +88,26 @@ grandclaw-archtype/ │ │ │ ├── AdminProjectsPage.jsx # 项目管理 │ │ │ ├── AddProjectPage.jsx # 新增/编辑项目 │ │ │ └── AdminLogsPage.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 # 创建技能 +│ │ ├── UploadSkillPage.jsx # 创建技能(基本信息) │ │ ├── UpdateSkillInfoPage.jsx # 更新基本信息 -│ │ ├── NewVersionPage.jsx # 上传新版本(仅版本信息) +│ │ ├── UploadVersionPage.jsx # 上传新版本(NEW) │ │ ├── DevDocsPage.jsx # 开发文档 │ │ └── DevAccountPage.jsx # 开发者设置 │ └── styles/ # SCSS样式模块 @@ -153,7 +166,7 @@ pnpm build ### 3. 工作台(Console) - **聊天界面**:支持多种聊天场景(欢迎页、普通对话、技能调用、文件上传) -- **技能市场**:浏览、订阅、查看技能详情 +- **技能市场**:浏览、订阅、查看技能详情(仅展示最新版本) - **日志查询**:支持按用户、类型、状态筛选 - **定时任务**:管理定时任务,支持启用/禁用,查看任务详情 - **项目管理**:成员列表,增加成员 @@ -161,18 +174,19 @@ pnpm build ### 4. 管理台(Admin) - **运营总览**:平台运营指标卡片(用户总数、部门数量、项目数量、今日调用)、异常/待办事项提醒、最近操作日志 +- **审核管理**:版本审核列表与详情、下架审核列表与详情(NEW) - **部门管理**:部门列表,支持搜索筛选、新增、编辑、启用/禁用、删除确认 - **用户管理**:用户列表,支持搜索筛选(关键词/部门/状态)、新增、编辑、启用/禁用、删除确认,角色区分(管理员/开发者/成员) - **项目管理**:项目列表,支持搜索筛选、新增、编辑、启用/禁用、删除确认 - **日志查询**:全局系统日志查询,支持多维度筛选(关键词、用户、部门、类型、状态、时间范围) ### 5. 开发台(Developer) -- **总览**:开发者指标卡片(我的技能总数、已发布、草稿、待审核)、待审核项目列表、最近动态 -- **我的技能**:技能列表,支持关键词搜索、分类筛选、状态筛选、分页,支持上架/下架、删除操作 -- **技能详情**:基本信息只读展示、版本历史管理(启用/下载/删除)、审核拒绝原因展示 -- **创建技能**:基本信息表单 + 技能图标选择 + 技能包上传 +- **总览**:开发者指标卡片(我的技能总数、已上架、开发中、待审核)、待审核项目列表、最近动态 +- **我的技能**:技能列表,支持关键词搜索、分类筛选、状态筛选(开发中/已上架/下架审核中/已下架)、分页,支持下架(需要先撤回审核中的版本)、删除(已上架需要先下架) +- **技能详情**:基本信息只读展示、版本历史管理(根据状态展示操作按钮:审核中-撤回审核+下载、审核通过/拒绝/撤销-仅下载、审核拒绝-显示拒绝理由)、技能操作(上传新版本-有审核中版本时禁用、下架技能、删除技能-已上架时禁用) +- **创建技能**:基本信息表单 + 技能图标选择(移除技能包上传) - **更新基本信息**:独立页面编辑技能名称/描述/分类/标签/图标,与版本上传分离 -- **上传新版本**:仅包含版本说明和技能包上传,不含基本信息编辑 +- **上传新版本**:仅包含版本说明和技能包上传(NEW) - **开发文档**:技能开发相关文档 - **开发者设置**:开发者账号信息 @@ -550,8 +564,8 @@ const members = api.members.list(); ### 数据文件说明 - `conversations.js`:聊天场景和对话历史 -- `skills.js`:技能市场数据,包含技能详情、文件列表、版本历史 -- `developerData.js`:开发台数据,包含我的技能(含图标、版本审核状态)、技能分类、开发者总览、开发文档 +- `skills.js`:技能市场数据,包含技能详情(含状态:dev/published/unlisting/unlisted)、文件列表、版本历史(含状态:reviewing/approved/rejected/withdrawn、拒绝理由)、审核列表(pendingVersionReviews、pendingUnlistReviews) +- `developerData.js`:开发台数据,包含我的技能(含图标、版本审核状态、hasPendingReview标识)、技能分类、开发者总览、开发文档 - `logs.js`:操作日志数据(成功/失败/警告状态) - `tasks.js`:定时任务数据(包含任务配置和执行日志) - `adminData.js`:管理台数据(部门列表、用户列表、项目列表、总览指标、全局日志、可选项数据) @@ -631,4 +645,263 @@ export default defineConfig({ 3. 组件样式添加到 `_components.scss` 4. 页面特定样式添加到 `global.scss` -*最后更新:2026-03-19* +--- + +## 技能开发流程与审核机制 + +### 流程概述 + +技能从创建到上架的完整流程包括:技能创建、版本上传、审核管理、上架销售四个主要环节。 + +### 状态定义 + +#### 技能状态 + +技能有四种状态,表示技能在平台上的可见性和可用性: + +| 状态 | 英文 | 描述 | 图标样式 | +|------|------|------|---------| +| 开发中 | `dev` | 技能已创建但尚未有审核通过的版本 | status-stopped (灰色) | +| 已上架 | `published` | 技能有审核通过的版本,可在技能市场展示和订阅 | status-running (绿色) | +| 下架审核中 | `unlisting` | 开发者申请下架,等待管理员审核 | status-warning (黄色) | +| 已下架 | `unlisted` | 技能已下架,不在技能市场展示,可删除 | status-stopped (灰色) | + +#### 版本状态 + +版本有四种状态,表示版本在审核流程中的位置: + +| 状态 | 英文 | 描述 | 图标样式 | +|------|------|------|---------| +| 审核中 | `reviewing` | 版本已提交,等待管理员审核 | status-warning (黄色) | +| 审核通过 | `approved` | 版本审核通过,自动生效,技能可上架 | status-running (绿色) | +| 审核拒绝 | `rejected` | 版本审核未通过,需修改后重新提交 | status-error (红色) | +| 已撤销 | `withdrawn` | 开发者主动撤回审核,版本废弃 | status-stopped (灰色) | + +### 完整流程 + +``` +┌───────────────────────────────────────────────────────────────────┐ +│ 技能开发流程 │ +└───────────────────────────────────────────────────────────────────┘ + +开发者操作 管理员操作 技能市场 +──────────── ─────────── ───────── +[1. 创建技能] → 技能: 开发中 + │ + ↓ (创建后可立即上传版本) +[2. 上传新版本] → 版本: 审核中 + │ ──────────────────────────▶ [待审核列表] + ↓ │ +[3. 提交审核] ↓ + │ [4. 审核操作] + ├───────────────────────────────────────────────┼───────┐ + │ │ │ + ↓ ↓ ↓ + [审核通过] [审核通过] [审核拒绝] + │ │ + └───────────────────────────────────────────────┘ + ↓ + 技能: 已上架 + 版本: 审核通过(生效) + │ + ───────────────────────────────────────────────────────────▶ [可在技能市场展示] + +后续操作: +- [5. 上传新版本] → (需先撤回审核中的版本,一次只能有一个审核中) +- [6. 下架技能] → (需先撤回审核中的版本,下架需管理员审核) +- [7. 删除技能] → (已上架的技能必须先下架) +``` + +### 关键规则 + +#### 1. 版本上传规则 + +- **版本号由后端自动生成**:前端上传时无需填写版本号 +- **上传即进入审核**:版本上传后状态自动变为"审核中",没有"草稿"状态 +- **一次只能有一个审核中的版本**: + - 如果存在审核中的版本,"上传新版本"按钮禁用 + - 审核通过或撤回后,才能上传新版本 + +#### 2. 审核中版本操作 + +- 可执行操作:撤回审核、下载 +- 撤回审核后,版本状态变为"已撤销"(终态),无法继续操作 +- 如需重新提交,需上传新版本 + +#### 3. 技能操作规则 + +| 技能状态 | 可用操作 | 备注 | +|---------|---------|------| +| 开发中 | 更新基本信息、删除技能 | 未上架,可直接删除 | +| 已上架 | 更新基本信息、下架技能 | 删除按钮禁用(需先下架) | +| 已上架+有审核中版本 | 更新基本信息 | 下架按钮禁用、上传按钮禁用 | +| 下架审核中 | 无(等待管理员审核) | 所有操作按钮禁用 | +| 已下架 | 更新基本信息、删除技能 | 可删除 | + +#### 4. 下架流程 + +- **下架需要管理员审核**: + 1. 开发者点击"下架技能" + 2. 技能状态变为"下架审核中" + 3. 管理台"审核管理" -> "下架审核"列表中显示 + 4. 管理员审核通过/拒绝 + 5. 通过:技能状态变为"已下架",技能市场移除 + 6. 拒绝:技能状态回到"已上架" + +- **下架期间禁止操作**: + - 下架审核中的技能,所有操作按钮禁用 + - 不能上传新版本 + +#### 5. 审核拒绝 + +- 版本审核拒绝后,在版本说明下方显示拒绝理由 +- 被拒绝的版本在技能详情页可见,但无法再次提交审核 +- 开发者需上传新版本重新提交 + +### 数据结构 + +#### 技能数据(skills.js / developerData.js) + +```javascript +{ + id: 1, + name: '代码生成助手', + desc: '根据需求自动生成高质量代码', + tags: ['开发', '代码', 'AI'], + status: 'published', // 技能状态: dev | published | unlisting | unlisted + hasPendingReview: false, // 是否有审核中的版本 + category: '开发工具', + versions: [...] // 版本列表 +} +``` + +#### 版本数据 + +```javascript +{ + version: 'v1.3.0', + date: '2026-03-12', + desc: '新增 Python 3.11 支持', + status: 'approved', // 版本状态: reviewing | approved | rejected | withdrawn + rejectionReason: '' // 审核拒绝理由(仅status为rejected时) +} +``` + +#### 审核列表数据 + +```javascript +// 版本审核列表 +pendingVersionReviews = [{ + id: 1, + skillName: '代码生成助手', + version: 'v1.4.0', + date: '2026-03-20', + developer: '张三' +}] + +// 下架审核列表 +pendingUnlistReviews = [{ + id: 1, + skillName: 'CRM 客户查询', + currentVersion: 'v1.5.0', + date: '2026-03-20', + developer: '赵六' +}] +``` + +### 页面交互细节 + +#### 技能详情页(SkillEditorPage) + +**版本历史表格:** + +| 版本号 | 版本说明 | 状态 | 更新时间 | 操作 | +|-------|---------|------|---------|------| +| v1.3.0 | 新增功能... | 审核通过 | 2026-03-12 | [下载] | +| v1.2.0 | 优化性能... | 审核拒绝 | 2026-03-08 | [下载] 拒绝理由: 测试用例不完整 | +| v1.1.0 | 新增支持... | 审核中 | 2026-02-20 | [撤回审核] [下载] | + +**技能操作按钮:** +- 更新基本信息:始终可用 +- 上传新版本:有审核中版本时禁用,提示"存在审核中的版本,请先撤回后再上传新版本" +- 下架技能:技能状态为已上架且无审核中版本时可用 +- 删除技能:技能状态为开发中或已下架时可用,已上架时禁用并提示"已上架的技能需要先下架才能删除" + +#### 我的技能列表页(MySkillsPage) + +**状态列展示:** +- 开发中:显示"开发中" +- 已上架:显示"已上架" +- 已上架+有审核中版本:显示"已上架 · 审核中" +- 下架审核中:显示"下架审核中" +- 已下架:显示"已下架" + +**操作列:** +- 编辑:始终可用 +- 下架技能:已上架且无审核中版本时可用 +- 删除技能:开发中或已下架时可用 + +#### 管理台审核管理(AdminPage - reviewList) + +**Tab切换:** +- 版本审核:显示待审核的版本列表 +- 下架审核:显示待审核的下架申请列表 + +**版本审核列表:** +| 技能名称 | 版本号 | 提交时间 | 开发者 | 操作 | +|---------|-------|---------|--------|------| +| 代码生成助手 | v1.4.0 | 2026-03-20 | 张三 | [审核] | + +**审核详情页(reviewDetail):** + +**版本审核详情:** +- 基本信息:技能名称、开发者、分类、标签 +- 版本信息:版本号、提交时间、版本说明 +- 文件列表:显示技能包包含的文件 +- 操作:[拒绝] [通过] + +**下架审核详情:** +- 技能信息:技能名称、开发者、当前版本、订阅数、申请时间 +- 操作:[拒绝] [通过] + +#### 技能市场详情页(SkillDetailPage) + +**版本展示:** +- 只显示最新审核通过版本的信息 +- 不展示完整版本历史 +- 示例: + ``` + 当前版本: v1.3.0 + 更新说明: 新增 Python 3.11 支持 + 更新时间: 2026-03-12 + ``` + +### 状态映射关系 + +```javascript +// 技能状态映射 +const skillStatusMap = { + dev: { text: '开发中', className: 'status-stopped' }, + published: { text: '已上架', className: 'status-running' }, + unlisting: { text: '下架审核中', className: 'status-warning' }, + unlisted: { text: '已下架', className: 'status-stopped' } +}; + +// 版本状态映射 +const versionStatusMap = { + reviewing: { text: '审核中', className: 'status-warning' }, + approved: { text: '审核通过', className: 'status-running' }, + rejected: { text: '审核拒绝', className: 'status-error' }, + withdrawn: { text: '已撤销', className: 'status-stopped' } +}; +``` + +### 注意事项 + +1. **原型说明**:本页面为静态原型,所有交互均为前端模拟,未连接真实后端 +2. **状态持久化**:页面刷新后,状态会重置为初始值 +3. **按钮禁用**:使用 HTML 原生 `disabled` 属性,配合 `title` 提示显示禁用原因 +4. **Modal 确认**:删除和下架操作需要二次确认,使用 Modal 组件展示确认对话框 +5. **Toast 提示**:操作完成后显示 Toast 提示,展示操作结果(成功/失败) + +*最后更新:2026-03-20* diff --git a/openspec/specs/developer-overview/spec.md b/openspec/specs/developer-overview/spec.md index a9979d6..6008d62 100644 --- a/openspec/specs/developer-overview/spec.md +++ b/openspec/specs/developer-overview/spec.md @@ -1,11 +1,11 @@ -## ADDED Requirements +## MODIFIED Requirements ### Requirement: 开发者指标展示 开发台总览页 SHALL 展示开发者维度的核心指标数据,以卡片形式呈现。 #### Scenario: 指标卡片展示 - **WHEN** 用户打开开发台总览页 -- **THEN** 页面顶部显示4个指标卡片:我的技能总数、已发布数量、草稿数量、待审核版本数量,每个卡片包含数值 +- **THEN** 页面顶部显示4个指标卡片:我的技能总数、已上架数量、开发中数量、待审核版本数量,每个卡片包含数值 ### Requirement: 待审核项目提醒 开发台总览页 SHALL 展示待审核的版本项目列表。 @@ -18,6 +18,10 @@ - **WHEN** 待审核列表中包含被拒绝的版本 - **THEN** 该项显示拒绝状态标签和"查看原因"链接 +#### Scenario: 下架审核项展示 +- **WHEN** 待审核列表中包含下架审核 +- **THEN** 该项显示"下架审核"状态标签 + ### Requirement: 最近动态展示 开发台总览页 SHALL 展示开发者最近的操作动态记录。 diff --git a/openspec/specs/skill-review-workflow/spec.md b/openspec/specs/skill-review-workflow/spec.md new file mode 100644 index 0000000..ee2ce23 --- /dev/null +++ b/openspec/specs/skill-review-workflow/spec.md @@ -0,0 +1,42 @@ +## ADDED Requirements + +### Requirement: 管理台审核列表 +AdminPage SHALL 提供版本审核和下架审核的列表展示。 + +#### Scenario: 审核管理入口 +- **WHEN** 用户点击管理台侧边栏"审核管理"导航 +- **THEN** 页面显示审核管理列表页面,包含版本审核和下架审核Tab + +#### Scenario: Tab切换展示 +- **WHEN** 用户打开审核管理列表页 +- **THEN** 页面顶部显示"版本审核"和"下架审核"两个Tab,默认选中版本审核 + +#### Scenario: 版本审核列表展示 +- **WHEN** 用户查看版本审核Tab +- **THEN** 页面显示待审核版本列表,每条包含技能名称、版本号、提交时间、开发者、操作按钮 + +#### Scenario: 下架审核列表展示 +- **WHEN** 用户切换到下架审核Tab +- **THEN** 页面显示待审核下架列表,每条包含技能名称、当前版本、申请时间、开发者、操作按钮 + +### Requirement: 管理台版本审核详情 +AdminPage SHALL 提供版本审核的详情展示和操作。 + +#### Scenario: 版本信息展示 +- **WHEN** 用户点击版本审核列表中的"审核"按钮 +- **THEN** 页面显示技能基本信息(名称、开发者、分类、标签)、版本信息(版本号、提交时间、版本说明)、文件列表 + +#### Scenario: 审核操作按钮 +- **WHEN** 用户查看版本审核详情 +- **THEN** 页面底部显示"拒绝"和"通过"两个按钮 + +### Requirement: 管理台下架审核详情 +AdminPage SHALL 提供下架审核的详情展示和操作。 + +#### Scenario: 下架信息展示 +- **WHEN** 用户点击下架审核列表中的"审核"按钮 +- **THEN** 页面显示技能信息(名称、开发者、当前版本、订阅数、申请时间) + +#### Scenario: 下架审核操作 +- **WHEN** 用户查看下架审核详情 +- **THEN** 页面底部显示"拒绝"和"通过"两个按钮 diff --git a/openspec/specs/skill-status-display/spec.md b/openspec/specs/skill-status-display/spec.md new file mode 100644 index 0000000..30fbd76 --- /dev/null +++ b/openspec/specs/skill-status-display/spec.md @@ -0,0 +1,45 @@ +## ADDED Requirements + +### Requirement: 技能状态标签展示 +MySkillsPage SHALL 在技能列表中展示技能状态。 + +#### Scenario: 技能状态列展示 +- **WHEN** 用户查看我的技能列表 +- **THEN** 状态列显示技能的当前状态:开发中、已上架、下架审核中、已下架 + +#### Scenario: 审核中版本提示 +- **WHEN** 技能存在审核中的版本 +- **THEN** 状态列额外显示版本审核状态(如"已上架 · v1.2 审核中") + +### Requirement: 侧边栏技能状态展示 +DeveloperPage SHALL 在侧边栏技能列表中展示状态标签。 + +#### Scenario: 侧边栏状态展示 +- **WHEN** 用户查看开发台侧边栏的技能列表 +- **THEN** 每个技能项显示对应的状态标签(开发中、已上架、下架审核中、已下架) + +### Requirement: 技能操作按钮可用性 +MySkillsPage 和 SkillEditorPage SHALL 根据技能状态控制操作按钮的可用性。 + +#### Scenario: 开发中状态按钮 +- **WHEN** 技能状态为开发中 +- **THEN** 显示"更新基本信息"、"删除技能"按钮,"下架"按钮不显示 + +#### Scenario: 已上架状态按钮 +- **WHEN** 技能状态为已上架 +- **THEN** 显示"更新基本信息"、"下架技能"按钮,"删除技能"按钮禁用并提示"已上架的技能需要先下架才能删除" + +#### Scenario: 已下架状态按钮 +- **WHEN** 技能状态为已下架 +- **THEN** 显示"更新基本信息"、"删除技能"按钮 + +### Requirement: 上传新版本按钮可用性 +SkillEditorPage SHALL 根据版本审核状态控制上传按钮。 + +#### Scenario: 无审核中版本 +- **WHEN** 技能不存在审核中的版本 +- **THEN** "上传新版本"按钮可用 + +#### Scenario: 有审核中版本 +- **WHEN** 技能存在审核中的版本 +- **THEN** "上传新版本"按钮禁用并提示"存在审核中的版本,请先撤回后再上传新版本" diff --git a/openspec/specs/skill-version-management/spec.md b/openspec/specs/skill-version-management/spec.md new file mode 100644 index 0000000..de6582a --- /dev/null +++ b/openspec/specs/skill-version-management/spec.md @@ -0,0 +1,45 @@ +## ADDED Requirements + +### Requirement: 版本上传表单 +UploadVersionPage SHALL 提供版本上传的表单界面。 + +#### Scenario: 版本信息输入 +- **WHEN** 用户打开上传新版本页面 +- **THEN** 页面显示版本说明输入框和文件上传区域 + +#### Scenario: 文件上传展示 +- **WHEN** 用户在上传页面看到文件上传区域 +- **THEN** 页面展示拖拽上传区域,支持 .zip 格式,显示上传图标和提示文字 + +### Requirement: 版本历史展示 +SkillEditorPage SHALL 展示技能的版本历史列表。 + +#### Scenario: 版本列表展示 +- **WHEN** 用户打开技能详情页 +- **THEN** 页面显示版本历史表格,包含版本号、版本说明、状态、更新时间、操作列 + +#### Scenario: 版本状态展示 +- **WHEN** 用户查看版本历史列表 +- **THEN** 每个版本显示对应的状态标签:审核中(warning)、审核通过(running)、审核拒绝(error)、已撤销(stopped) + +#### Scenario: 审核拒绝理由展示 +- **WHEN** 版本状态为审核拒绝 +- **THEN** 版本说明下方显示拒绝理由文本 + +### Requirement: 版本操作按钮 +版本历史表格 SHALL 根据版本状态展示不同的操作按钮。 + +#### Scenario: 审核中版本操作 +- **WHEN** 版本状态为审核中 +- **THEN** 显示"撤回审核"、"下载"按钮 + +#### Scenario: 已完结版本操作 +- **WHEN** 版本状态为审核通过/审核拒绝/已撤销 +- **THEN** 仅显示"下载"按钮 + +### Requirement: 技能市场版本展示 +SkillDetailPage SHALL 展示技能的最新版本信息。 + +#### Scenario: 最新版本展示 +- **WHEN** 用户打开技能市场详情页 +- **THEN** 页面显示当前版本号、更新说明、更新时间(仅展示最新版本,不展示完整历史) diff --git a/src/constants/pages.js b/src/constants/pages.js index d6fa13c..5e7bfc8 100644 --- a/src/constants/pages.js +++ b/src/constants/pages.js @@ -25,6 +25,8 @@ export const ADMIN_PAGES = { users: { title: '用户管理', icon: 'FiUsers' }, projects: { title: '项目管理', icon: 'FiList' }, adminLogs: { title: '日志查询', icon: 'FiActivity' }, + reviewList: { title: '审核管理', icon: 'FiCheckCircle' }, + reviewDetail: { title: '审核详情', icon: null }, addDepartment: { title: '新增部门', icon: null }, addUser: { title: '新增用户', icon: null }, addProject: { title: '新增项目', icon: null }, diff --git a/src/data/developerData.js b/src/data/developerData.js index f56f0cf..0da553a 100644 --- a/src/data/developerData.js +++ b/src/data/developerData.js @@ -5,6 +5,7 @@ export const mySkills = [ desc: '根据城市名称查询当前天气和未来预报,支持全国主要城市', icon: '🌤️', status: 'published', + hasPendingReview: false, version: '1.2.0', category: '信息查询', tags: ['天气', '查询', '生活'], @@ -12,22 +13,18 @@ export const mySkills = [ installs: 156, rating: 4.7, versions: [ - { version: '1.2.0', date: '2026-03-18', desc: '新增支持未来7天预报', current: true, status: 'approved', enabled: true, rejectionReason: '' }, - { version: '1.1.0', date: '2026-03-10', desc: '优化响应速度', current: false, status: 'approved', enabled: false, rejectionReason: '' }, - { version: '1.0.0', date: '2026-03-01', desc: '初始版本', current: false, status: 'approved', enabled: false, rejectionReason: '' } - ], - package: { - name: 'weather-assistant-v1.2.0.zip', - size: '2.4 MB', - uploadDate: '2026-03-18 14:30' - } + { version: '1.2.0', date: '2026-03-18', desc: '新增支持未来7天预报', status: 'approved' }, + { version: '1.1.0', date: '2026-03-10', desc: '优化响应速度', status: 'approved' }, + { version: '1.0.0', date: '2026-03-01', desc: '初始版本', status: 'approved' } + ] }, { id: 2, name: '待办事项管理', desc: '帮助用户管理日常待办事项,支持添加、完成、删除操作', icon: '📋', - status: 'draft', + status: 'dev', + hasPendingReview: false, version: '0.1.0', category: '效率工具', tags: ['待办', '管理', '效率'], @@ -35,13 +32,8 @@ export const mySkills = [ installs: 0, rating: 0, versions: [ - { version: '0.1.0', date: '2026-03-17', desc: '开发中版本', current: true, status: 'pending', enabled: false, rejectionReason: '' } - ], - package: { - name: 'todo-manager-v0.1.0.zip', - size: '1.8 MB', - uploadDate: '2026-03-17 10:15' - } + { version: '0.1.0', date: '2026-03-17', desc: '开发中版本', status: 'reviewing' } + ] }, { id: 3, @@ -49,6 +41,7 @@ export const mySkills = [ desc: '自动审查代码质量,提供优化建议和潜在问题检测', icon: '💻', status: 'published', + hasPendingReview: true, version: '2.0.1', category: '开发工具', tags: ['代码', '审查', '开发'], @@ -56,16 +49,11 @@ export const mySkills = [ installs: 342, rating: 4.9, versions: [ - { version: '2.0.1', date: '2026-03-15', desc: '修复 Python 代码审查问题', current: true, status: 'approved', enabled: true, rejectionReason: '' }, - { version: '2.0.0', date: '2026-03-10', desc: '修复安全问题', current: false, status: 'rejected', enabled: false, rejectionReason: '安全审查未通过,存在潜在的代码注入风险,请修复后重新提交' }, - { version: '2.0.0', date: '2026-03-08', desc: '支持多语言审查', current: false, status: 'approved', enabled: false, rejectionReason: '' }, - { version: '1.0.0', date: '2026-02-20', desc: '初始版本', current: false, status: 'approved', enabled: false, rejectionReason: '' } - ], - package: { - name: 'code-reviewer-v2.0.1.zip', - size: '3.2 MB', - uploadDate: '2026-03-15 16:45' - } + { version: '2.0.1', date: '2026-03-15', desc: '修复 Python 代码审查问题', status: 'approved' }, + { version: '2.0.0', date: '2026-03-10', desc: '修复安全问题', status: 'rejected', rejectionReason: '安全审查未通过,存在潜在的代码注入风险,请修复后重新提交' }, + { version: '2.0.0', date: '2026-03-08', desc: '支持多语言审查', status: 'approved' }, + { version: '1.0.0', date: '2026-02-20', desc: '初始版本', status: 'approved' } + ] } ]; diff --git a/src/data/skills.js b/src/data/skills.js index bb9b9e7..6b309c4 100644 --- a/src/data/skills.js +++ b/src/data/skills.js @@ -1,12 +1,12 @@ // skills data export const skills = [ - { id: 1, name: '代码生成助手', author: 'GrandClaw Team', desc: '根据需求自动生成高质量代码,支持多种编程语言', tags: ['开发', '代码', 'AI'], subs: 1256, rating: 4.8, subscribed: true }, - { id: 2, name: '数据分析专家', author: 'DataLab', desc: '智能分析数据,生成可视化图表和洞察报告', tags: ['数据', '分析', '可视化'], subs: 892, rating: 4.7, subscribed: true }, - { id: 3, name: '文档智能撰写', author: 'DocAI', desc: '帮助撰写各种文档,包括报告、邮件、技术文档等', tags: ['文档', '写作', '办公'], subs: 2103, rating: 4.9, subscribed: true }, - { id: 4, name: 'CRM 客户查询', author: 'Telecom', desc: '对接企业CRM系统,快速查询客户信息和订单状态', tags: ['业务', 'CRM', '客户'], subs: 567, rating: 4.5, subscribed: false }, - { id: 5, name: '财务数据同步', author: 'Finance Team', desc: '自动同步财务系统数据,生成费用报表', tags: ['财务', '报表', '同步'], subs: 432, rating: 4.6, subscribed: false }, - { id: 6, name: '网络故障排查', author: 'NetOps', desc: '智能诊断网络问题,提供故障排除方案', tags: ['运维', '网络', '诊断'], subs: 789, rating: 4.8, subscribed: false } + { id: 1, name: '代码生成助手', author: 'GrandClaw Team', desc: '根据需求自动生成高质量代码,支持多种编程语言', tags: ['开发', '代码', 'AI'], subs: 1256, rating: 4.8, subscribed: true, status: 'published', hasPendingReview: false }, + { id: 2, name: '数据分析专家', author: 'DataLab', desc: '智能分析数据,生成可视化图表和洞察报告', tags: ['数据', '分析', '可视化'], subs: 892, rating: 4.7, subscribed: true, status: 'published', hasPendingReview: true }, + { id: 3, name: '文档智能撰写', author: 'DocAI', desc: '帮助撰写各种文档,包括报告、邮件、技术文档等', tags: ['文档', '写作', '办公'], subs: 2103, rating: 4.9, subscribed: true, status: 'dev', hasPendingReview: false }, + { id: 4, name: 'CRM 客户查询', author: 'Telecom', desc: '对接企业CRM系统,快速查询客户信息和订单状态', tags: ['业务', 'CRM', '客户'], subs: 567, rating: 4.5, subscribed: false, status: 'unlisting', hasPendingReview: false }, + { id: 5, name: '财务数据同步', author: 'Finance Team', desc: '自动同步财务系统数据,生成费用报表', tags: ['财务', '报表', '同步'], subs: 432, rating: 4.6, subscribed: false, status: 'unlisted', hasPendingReview: false }, + { id: 6, name: '网络故障排查', author: 'NetOps', desc: '智能诊断网络问题,提供故障排除方案', tags: ['运维', '网络', '诊断'], subs: 789, rating: 4.8, subscribed: false, status: 'published', hasPendingReview: false } ]; export const skillFiles = [ @@ -17,10 +17,21 @@ export const skillFiles = [ ]; export const skillVersions = [ - { version: 'v1.3.0', date: '2026-03-12', desc: '新增 Python 3.11 支持', current: true }, - { version: 'v1.2.1', date: '2026-03-08', desc: '修复若干已知问题', current: false }, - { version: 'v1.2.0', date: '2026-03-01', desc: '优化性能,提升响应速度 30%', current: false }, - { version: 'v1.1.0', date: '2026-02-15', desc: '新增 JavaScript 支持', current: false } + { version: 'v1.3.0', date: '2026-03-12', desc: '新增 Python 3.11 支持', status: 'approved' }, + { version: 'v1.2.1', date: '2026-03-08', desc: '修复若干已知问题', status: 'rejected', rejectionReason: '测试用例覆盖不完整,请补充单元测试' }, + { version: 'v1.2.0', date: '2026-03-01', desc: '优化性能,提升响应速度 30%', status: 'approved' }, + { version: 'v1.1.5', date: '2026-02-20', desc: '紧急修复安全漏洞', status: 'withdrawn' }, + { version: 'v1.1.0', date: '2026-02-15', desc: '新增 JavaScript 支持', status: 'reviewing' } +]; + +export const pendingVersionReviews = [ + { id: 1, skillName: '代码生成助手', version: 'v1.4.0', date: '2026-03-20', developer: '张三' }, + { id: 2, skillName: '数据分析专家', version: 'v2.0.0', date: '2026-03-19', developer: '李四' }, + { id: 3, skillName: '文档智能撰写', version: 'v1.0.0', date: '2026-03-18', developer: '王五' } +]; + +export const pendingUnlistReviews = [ + { id: 1, skillName: 'CRM 客户查询', currentVersion: 'v1.5.0', date: '2026-03-20', developer: '赵六' } ]; // 技能图标映射 @@ -28,4 +39,4 @@ const skillIcons = ['💻', '📊', '📝', '👥', '📈', '🔧']; export function getSkillIcon(id) { return skillIcons[(id - 1) % skillIcons.length]; -} \ No newline at end of file +} diff --git a/src/pages/AdminPage.jsx b/src/pages/AdminPage.jsx index 4e43637..3a9beed 100644 --- a/src/pages/AdminPage.jsx +++ b/src/pages/AdminPage.jsx @@ -1,6 +1,6 @@ import { useState, useEffect } from 'react'; import { useLocation, useNavigate } from 'react-router-dom'; -import { FiHome, FiBarChart2, FiUsers, FiList, FiActivity } from 'react-icons/fi'; +import { FiHome, FiBarChart2, FiUsers, FiList, FiCheckCircle, FiActivity } from 'react-icons/fi'; import Layout from '../components/Layout.jsx'; import SidebarBrand from '../components/layout/SidebarBrand.jsx'; import SidebarUser from '../components/layout/SidebarUser.jsx'; @@ -16,6 +16,8 @@ import AddDepartmentPage from './admin/AddDepartmentPage.jsx'; import AddUserPage from './admin/AddUserPage.jsx'; import AddProjectPage from './admin/AddProjectPage.jsx'; import AdminLogsPage from './admin/AdminLogsPage.jsx'; +import ConsoleReviewListPage from './console/ConsoleReviewListPage.jsx'; +import ConsoleReviewDetailPage from './console/ConsoleReviewDetailPage.jsx'; function AdminPage() { const location = useLocation(); @@ -28,12 +30,26 @@ function AdminPage() { }); const [editData, setEditData] = useState(null); + const [reviewType, setReviewType] = useState(null); + const [reviewId, setReviewId] = useState(null); const navigateTo = (page, data) => { setEditData(data || null); setCurrentPage(page); }; + const handleReviewClick = (type, id) => { + setReviewType(type); + setReviewId(id); + navigateTo('reviewDetail'); + }; + + const handleReviewBack = () => { + setReviewType(null); + setReviewId(null); + navigateTo('reviewList'); + }; + const renderPage = () => { switch (currentPage) { case 'overview': @@ -55,6 +71,14 @@ function AdminPage() { />; case 'adminLogs': return ; + case 'reviewList': + return ; + case 'reviewDetail': + return ; case 'addDepartment': return navigateTo('departments')} @@ -81,6 +105,9 @@ function AdminPage() { const nameMap = { addDepartment: '部门', addUser: '用户', addProject: '项目' }; return prefix + nameMap[currentPage]; } + if (currentPage === 'reviewDetail') { + return reviewType === 'version' ? '版本审核' : '下架审核'; + } return ADMIN_PAGES[currentPage]?.title || ''; }; @@ -99,6 +126,15 @@ function AdminPage() { iconClassName="admin-nav-icon" textClassName="admin-nav-text" /> + } + label="审核管理" + active={currentPage === 'reviewList' || currentPage === 'reviewDetail'} + onClick={() => navigateTo('reviewList')} + itemClassName="admin-nav-item" + iconClassName="admin-nav-icon" + textClassName="admin-nav-text" + /> } label="部门管理" diff --git a/src/pages/DeveloperPage.jsx b/src/pages/DeveloperPage.jsx index 4d4d40a..51430d9 100644 --- a/src/pages/DeveloperPage.jsx +++ b/src/pages/DeveloperPage.jsx @@ -18,6 +18,14 @@ import DevDocsPage from './developer/DevDocsPage.jsx'; import DevAccountPage from './developer/DevAccountPage.jsx'; import SkillEditorPage from './developer/SkillEditorPage.jsx'; import UpdateSkillInfoPage from './developer/UpdateSkillInfoPage.jsx'; +import UploadVersionPage from './developer/UploadVersionPage.jsx'; + +const skillStatusMap = { + dev: { text: '开发中', className: 'status-stopped' }, + published: { text: '已上架', className: 'status-running' }, + unlisting: { text: '下架审核中', className: 'status-warning' }, + unlisted: { text: '已下架', className: 'status-stopped' } +}; function DeveloperPage() { const location = useLocation(); @@ -105,7 +113,7 @@ function DeveloperPage() { onUpdateInfo={openUpdateInfoPage} />; case 'newVersion': - return ; + return ; case 'updateInfo': return openSkillEditor(skill.id)} >
{skill.name}
-
{skill.status === 'published' ? '已发布' : '草稿'}
+
+ + {skillStatusMap[skill.status]?.text || skill.status} + +
))} diff --git a/src/pages/console/ConsoleReviewDetailPage.jsx b/src/pages/console/ConsoleReviewDetailPage.jsx new file mode 100644 index 0000000..32d3c9f --- /dev/null +++ b/src/pages/console/ConsoleReviewDetailPage.jsx @@ -0,0 +1,146 @@ +import { useState } from 'react'; +import { FiChevronLeft, FiFile } from 'react-icons/fi'; +import { pendingVersionReviews, pendingUnlistReviews, skillFiles } from '../../data/skills.js'; +import Toast from '../../components/common/Toast.jsx'; + +function ConsoleReviewDetailPage({ type, reviewId, onBack }) { + const [showToast, setShowToast] = useState(false); + const [toastMessage, setToastMessage] = useState(''); + + const versionReview = type === 'version' ? pendingVersionReviews.find(r => r.id === reviewId) : null; + const unlistReview = type === 'unlist' ? pendingUnlistReviews.find(r => r.id === reviewId) : null; + + const review = versionReview || unlistReview; + + if (!review) { + return
审核项不存在
; + } + + const handleApprove = () => { + setToastMessage('审核通过'); + setShowToast(true); + setTimeout(() => { + onBack && onBack(); + }, 1000); + }; + + const handleReject = () => { + setToastMessage('已拒绝'); + setShowToast(true); + setTimeout(() => { + onBack && onBack(); + }, 1000); + }; + + return ( + <> +
+ 返回审核列表 +
+ +
+
+
{type === 'version' ? '版本审核' : '下架审核'}
+
+
+ {type === 'version' && ( + <> +
+

基本信息

+
+ 技能名称 + {review.skillName} +
+
+ 开发者 + {review.developer} +
+
+ 分类 + 开发工具 +
+
+ 标签 + + 开发 + 代码 + AI + +
+
+ +
+

版本信息

+
+ 版本号 + {review.version} +
+
+ 提交时间 + {review.date} +
+
+ 版本说明 + 优化性能,提升响应速度 30%,修复若干已知问题 +
+
+ +
+

文件列表

+ {skillFiles.map((file, index) => ( +
+
+
+
{file.name}
+
{file.type} · {file.size}
+
+
+ ))} +
+ + )} + + {type === 'unlist' && ( +
+

技能信息

+
+ 技能名称 + {review.skillName} +
+
+ 开发者 + {review.developer} +
+
+ 当前版本 + {review.currentVersion} +
+
+ 订阅数 + 567 +
+
+ 申请时间 + {review.date} +
+
+ )} + +
+ + +
+
+
+ + setShowToast(false)} + /> + + ); +} + +export default ConsoleReviewDetailPage; diff --git a/src/pages/console/ConsoleReviewListPage.jsx b/src/pages/console/ConsoleReviewListPage.jsx new file mode 100644 index 0000000..4fcb048 --- /dev/null +++ b/src/pages/console/ConsoleReviewListPage.jsx @@ -0,0 +1,110 @@ +import { useState } from 'react'; +import { pendingVersionReviews, pendingUnlistReviews } from '../../data/skills.js'; + +function ConsoleReviewListPage({ onReviewClick }) { + const [activeTab, setActiveTab] = useState('version'); + + return ( + <> +
+
+
审核管理
+
+
+
+
+ + +
+
+ + {activeTab === 'version' && ( +
+ + + + + + + + + + + + {pendingVersionReviews.map(review => ( + + + + + + + + ))} + +
技能名称版本号提交时间开发者操作
{review.skillName}{review.version}{review.date}{review.developer} + +
+
+ )} + + {activeTab === 'unlist' && ( +
+ + + + + + + + + + + + {pendingUnlistReviews.map(review => ( + + + + + + + + ))} + +
技能名称当前版本申请时间开发者操作
{review.skillName}{review.currentVersion}{review.date}{review.developer} + +
+
+ )} + +
+
+
1
+
+
+
+
+ + ); +} + +export default ConsoleReviewListPage; diff --git a/src/pages/console/SkillDetailPage.jsx b/src/pages/console/SkillDetailPage.jsx index 477b042..e883aa1 100644 --- a/src/pages/console/SkillDetailPage.jsx +++ b/src/pages/console/SkillDetailPage.jsx @@ -79,16 +79,21 @@ function SkillDetailPage({ skillId, onBack }) { ))}
-

版本历史

- {skillVersions.map(ver => ( -
-
- {ver.version} - {ver.desc} +

当前版本

+ {(() => { + const approvedVersion = skillVersions.find(v => v.status === 'approved'); + return approvedVersion ? ( +
+
+ {approvedVersion.version} + {approvedVersion.desc} +
+
{approvedVersion.date}
-
{ver.date}
-
- ))} + ) : ( +
暂无版本信息
+ ); + })()}
diff --git a/src/pages/developer/MySkillsPage.jsx b/src/pages/developer/MySkillsPage.jsx index e1248e2..6f79b14 100644 --- a/src/pages/developer/MySkillsPage.jsx +++ b/src/pages/developer/MySkillsPage.jsx @@ -3,6 +3,13 @@ import { api } from '../../services/api.js'; import Modal from '../../components/common/Modal.jsx'; import Toast from '../../components/common/Toast.jsx'; +const skillStatusMap = { + dev: { text: '开发中', className: 'status-stopped' }, + published: { text: '已上架', className: 'status-running' }, + unlisting: { text: '下架审核中', className: 'status-warning' }, + unlisted: { text: '已下架', className: 'status-stopped' } +}; + function MySkillsPage({ onSkillClick }) { const sourceData = api.developer.getMySkills(); const categories = api.developer.getCategories(); @@ -25,8 +32,7 @@ function MySkillsPage({ onSkillClick }) { if (filters.category && skill.category !== filters.category) { return false; } - if (filters.status === 'published' && skill.status !== 'published') return false; - if (filters.status === 'draft' && skill.status !== 'draft') return false; + if (filters.status && skill.status !== filters.status) return false; return true; }); @@ -81,8 +87,10 @@ function MySkillsPage({ onSkillClick }) { onChange={e => handleFilterChange('status', e.target.value)} > - - + + + + @@ -120,9 +128,12 @@ function MySkillsPage({ onSkillClick }) { {skill.category} {skill.version} - - {skill.status === 'published' ? '已发布' : '草稿'} + + {skillStatusMap[skill.status]?.text || skill.status} + {skill.hasPendingReview && ( + 审核中 + )} {skill.installs} {skill.rating || '-'} @@ -132,11 +143,21 @@ function MySkillsPage({ onSkillClick }) { 编辑 {skill.status === 'published' && ( - )} - diff --git a/src/pages/developer/SkillEditorPage.jsx b/src/pages/developer/SkillEditorPage.jsx index d225c39..e98d5d3 100644 --- a/src/pages/developer/SkillEditorPage.jsx +++ b/src/pages/developer/SkillEditorPage.jsx @@ -4,6 +4,20 @@ import { api } from '../../services/api.js'; import Modal from '../../components/common/Modal.jsx'; import Toast from '../../components/common/Toast.jsx'; +const versionStatusMap = { + reviewing: { text: '审核中', className: 'status-warning' }, + approved: { text: '审核通过', className: 'status-running' }, + rejected: { text: '审核拒绝', className: 'status-error' }, + withdrawn: { text: '已撤销', className: 'status-stopped' } +}; + +const skillStatusMap = { + dev: { text: '开发中', className: 'status-stopped' }, + published: { text: '已上架', className: 'status-running' }, + unlisting: { text: '下架审核中', className: 'status-warning' }, + unlisted: { text: '已下架', className: 'status-stopped' } +}; + function SkillEditorPage({ skillId, onBack, onUploadNewVersion, onUpdateInfo }) { const skill = api.developer.getSkillById(skillId); const [deleteSkillModal, setDeleteSkillModal] = useState(false); @@ -50,9 +64,7 @@ function SkillEditorPage({ skillId, onBack, onUploadNewVersion, onUpdateInfo }) ))}
- 版本: {skill.version} - 安装量: {skill.installs} - 评分: {skill.rating || '-'} + 当前版本: {skill.version}
@@ -78,13 +90,20 @@ function SkillEditorPage({ skillId, onBack, onUploadNewVersion, onUpdateInfo }) ))}
-
- - - -
+
+ + {skill.status === 'published' && ( + + )} + +
@@ -94,7 +113,14 @@ function SkillEditorPage({ skillId, onBack, onUploadNewVersion, onUpdateInfo })
- +

版本历史

@@ -104,8 +130,7 @@ function SkillEditorPage({ skillId, onBack, onUploadNewVersion, onUpdateInfo }) - - + @@ -113,7 +138,6 @@ function SkillEditorPage({ skillId, onBack, onUploadNewVersion, onUpdateInfo }) 版本说明 状态 更新时间 - 是否启用 操作 @@ -128,30 +152,21 @@ function SkillEditorPage({ skillId, onBack, onUploadNewVersion, onUpdateInfo }) )} - {ver.status === 'pending' ? ( - 审核中 - ) : ver.status === 'rejected' ? ( - 审核拒绝 - ) : ( - 审核通过 - )} + + {versionStatusMap[ver.status]?.text || ver.status} + {ver.date} - - {ver.enabled ? ( - 已启用 - ) : ( - 未启用 - )} -
- {!ver.enabled && } - - {!ver.enabled && ( - + {ver.status === 'reviewing' && ( + <> + + + + )} + {(ver.status === 'approved' || ver.status === 'rejected' || ver.status === 'withdrawn') && ( + )}
diff --git a/src/pages/developer/UploadSkillPage.jsx b/src/pages/developer/UploadSkillPage.jsx index 75bce74..7055ebc 100644 --- a/src/pages/developer/UploadSkillPage.jsx +++ b/src/pages/developer/UploadSkillPage.jsx @@ -1,4 +1,4 @@ -import { FiUpload, FiX } from 'react-icons/fi'; +import { FiX } from 'react-icons/fi'; import { useState } from 'react'; import { api } from '../../services/api.js'; import Toast from '../../components/common/Toast.jsx'; @@ -87,14 +87,6 @@ function UploadSkillPage({ onBack }) {
当前选择: {icon}
-
- -
- -
点击或拖拽文件到此处上传
-
支持 .zip 格式
-
-
diff --git a/src/pages/developer/UploadVersionPage.jsx b/src/pages/developer/UploadVersionPage.jsx new file mode 100644 index 0000000..86b274e --- /dev/null +++ b/src/pages/developer/UploadVersionPage.jsx @@ -0,0 +1,63 @@ +import { FiUpload, FiChevronLeft } from 'react-icons/fi'; +import { useState } from 'react'; +import Toast from '../../components/common/Toast.jsx'; + +function UploadVersionPage({ skillName, onBack }) { + const [showToast, setShowToast] = useState(false); + + const handleSubmit = () => { + setShowToast(true); + setTimeout(() => { + onBack(); + }, 1000); + }; + + return ( + <> +
+ 返回技能详情 +
+
+
+
上传新版本
+
+
+
+ 技能: {skillName} +
+
+ +