diff --git a/README.md b/README.md index bb2aaf7..f18167f 100644 --- a/README.md +++ b/README.md @@ -89,12 +89,14 @@ grandclaw-archtype/ │ │ │ ├── AddProjectPage.jsx # 新增/编辑项目 │ │ │ └── AdminLogsPage.jsx # 全局日志查询 │ │ └── developer/ # 开发台子页面 -│ │ ├── MySkillsPage.jsx # 我的技能 +│ │ ├── DevOverviewPage.jsx # 开发者总览 +│ │ ├── MySkillsPage.jsx # 我的技能(筛选+分页) +│ │ ├── SkillEditorPage.jsx # 技能详情(只读+操作) │ │ ├── UploadSkillPage.jsx # 创建技能 -│ │ ├── NewVersionPage.jsx # 上传新版本 +│ │ ├── UpdateSkillInfoPage.jsx # 更新基本信息 +│ │ ├── NewVersionPage.jsx # 上传新版本(仅版本信息) │ │ ├── DevDocsPage.jsx # 开发文档 -│ │ ├── DevAccountPage.jsx # 开发者设置 -│ │ └── SkillEditorPage.jsx # 技能详情/编辑 +│ │ └── DevAccountPage.jsx # 开发者设置 │ └── styles/ # SCSS样式模块 │ ├── _variables.scss # 设计系统变量 │ ├── _mixins.scss # 可复用混入 @@ -165,10 +167,12 @@ pnpm build - **日志查询**:全局系统日志查询,支持多维度筛选(关键词、用户、部门、类型、状态、时间范围) ### 5. 开发台(Developer) -- **我的技能**:已开发的技能列表 -- **创建技能**:上传新技能 -- **上传新版本**:为已发布的技能上传新版本 -- **技能编辑**:编辑技能配置、版本管理 +- **总览**:开发者指标卡片(我的技能总数、已发布、草稿、待审核)、待审核项目列表、最近动态 +- **我的技能**:技能列表,支持关键词搜索、分类筛选、状态筛选、分页,支持上架/下架、删除操作 +- **技能详情**:基本信息只读展示、版本历史管理(启用/下载/删除)、审核拒绝原因展示 +- **创建技能**:基本信息表单 + 技能图标选择 + 技能包上传 +- **更新基本信息**:独立页面编辑技能名称/描述/分类/标签/图标,与版本上传分离 +- **上传新版本**:仅包含版本说明和技能包上传,不含基本信息编辑 - **开发文档**:技能开发相关文档 - **开发者设置**:开发者账号信息 @@ -431,7 +435,7 @@ localStorage.setItem('console_currentScene', 'welcome'); localStorage.setItem('admin_currentPage', 'overview'); // 开发台 -localStorage.setItem('developer_currentPage', 'mySkills'); +localStorage.setItem('developer_currentPage', 'overview'); localStorage.setItem('developer_currentSkillId', '1'); ``` @@ -535,7 +539,7 @@ const members = api.members.list(); - `api.skills` - 技能市场(列表、详情、文件、版本、图标) - `api.conversations` - 聊天场景和对话历史 - `api.logs` - 操作日志(列表、筛选) -- `api.developer` - 开发台数据(技能、分类、模型、文档) +- `api.developer` - 开发台数据(总览、技能、分类、文档) - `api.members` - 项目成员 - `api.tasks` - 定时任务 - `api.admin` - 管理台(总览、部门、用户、项目、全局日志) @@ -547,7 +551,7 @@ const members = api.members.list(); ### 数据文件说明 - `conversations.js`:聊天场景和对话历史 - `skills.js`:技能市场数据,包含技能详情、文件列表、版本历史 -- `developerData.js`:开发台数据,包含我的技能、技能分类、开发文档 +- `developerData.js`:开发台数据,包含我的技能(含图标、版本审核状态)、技能分类、开发者总览、开发文档 - `logs.js`:操作日志数据(成功/失败/警告状态) - `tasks.js`:定时任务数据(包含任务配置和执行日志) - `adminData.js`:管理台数据(部门列表、用户列表、项目列表、总览指标、全局日志、可选项数据) diff --git a/openspec/config.yaml b/openspec/config.yaml index 192391b..a352866 100644 --- a/openspec/config.yaml +++ b/openspec/config.yaml @@ -12,3 +12,4 @@ context: | - 不做安全防御性编程,eval/dangerouslySetInnerHTML等按需使用 - README.md是项目的开发文档,记录代码结构和关键开发模式,优先读取获取上下文 - 涉及页面/路由/组件/功能模块变更或技术栈调整时,同步更新README.md + - Git提交: 仅中文; 格式为"类型: 简短描述",类型可选: feat(新功能)/fix(修复)/refactor(重构)/docs(文档)/style(格式)/test(测试)/chore(构建/工具); 多行描述空行后加详细说明 diff --git a/openspec/specs/admin-list-crud/spec.md b/openspec/specs/admin-list-crud/spec.md index 8d260af..5081a2e 100644 --- a/openspec/specs/admin-list-crud/spec.md +++ b/openspec/specs/admin-list-crud/spec.md @@ -84,3 +84,77 @@ #### Scenario: 取消删除 - **WHEN** 用户在确认弹框中点击取消按钮 - **THEN** 弹框关闭,列表不变 + +### Requirement: 技能列表搜索筛选 +我的技能列表 SHALL 支持按关键词、分类和状态筛选技能数据。 + +#### Scenario: 关键词搜索 +- **WHEN** 用户在筛选卡片的关键词输入框中输入文本并点击查询 +- **THEN** 列表仅显示技能名称或描述中包含该关键词的记录 + +#### Scenario: 分类筛选 +- **WHEN** 用户在筛选卡片的分类下拉框选择某个分类并点击查询 +- **THEN** 列表仅显示该分类的技能记录 + +#### Scenario: 状态筛选 +- **WHEN** 用户在筛选卡片的状态下拉框选择某个状态(已发布/草稿)并点击查询 +- **THEN** 列表仅显示该状态的技能记录 + +#### Scenario: 筛选重置 +- **WHEN** 用户在筛选卡片点击重置按钮 +- **THEN** 筛选条件清空,列表恢复显示全部技能 + +### Requirement: 技能列表分页 +我的技能列表 SHALL 在表格底部展示分页组件。 + +#### Scenario: 分页展示 +- **WHEN** 用户打开我的技能列表页 +- **THEN** 表格底部右侧显示分页组件,包含页码按钮和前后翻页按钮 + +### Requirement: 技能上架下架 +我的技能列表和技能详情页 SHALL 提供技能的上架/下架操作入口。 + +#### Scenario: 列表页下架操作 +- **WHEN** 用户在已发布技能的操作列点击"下架"按钮 +- **THEN** 页面展示成功提示"已下架" + +#### Scenario: 详情页上架/下架切换 +- **WHEN** 用户在技能详情页点击"下架技能"或"上架技能"按钮 +- **THEN** 页面展示对应的成功提示 + +### Requirement: 技能删除确认 +我的技能列表和技能详情页 SHALL 提供技能删除操作,需弹框确认。 + +#### Scenario: 列表页删除确认 +- **WHEN** 用户在技能列表的操作列点击"删除"按钮 +- **THEN** 弹出确认弹框,显示"确定要删除技能"{技能名称}"吗?此操作不可撤销。" + +#### Scenario: 确认删除技能 +- **WHEN** 用户在确认弹框中点击"删除"按钮 +- **THEN** 弹框关闭,页面展示成功提示"已删除" + +#### Scenario: 取消删除技能 +- **WHEN** 用户在确认弹框中点击"取消"按钮 +- **THEN** 弹框关闭,列表不变 + +#### Scenario: 详情页删除确认 +- **WHEN** 用户在技能详情页点击"删除技能"按钮 +- **THEN** 弹出确认弹框,显示"确定要删除技能"{技能名称}"吗?此操作不可撤销。" + +### Requirement: 版本删除确认 +技能详情页版本历史表格 SHALL 为未启用版本提供删除操作,需弹框确认。 + +#### Scenario: 版本删除按钮展示 +- **WHEN** 用户在技能详情页版本历史表格中查看某个未启用的版本 +- **THEN** 该版本操作列显示"删除"按钮 + +#### Scenario: 确认删除版本 +- **WHEN** 用户点击版本的"删除"按钮并在确认弹框中点击"删除" +- **THEN** 弹框关闭,页面展示成功提示"已删除" + +### Requirement: 版本审核拒绝原因展示 +技能详情页版本历史表格 SHALL 为被拒绝的版本展示拒绝原因。 + +#### Scenario: 拒绝原因展示 +- **WHEN** 用户在版本历史表格中查看状态为"审核拒绝"的版本 +- **THEN** 该版本状态标签下方显示红色小字的拒绝原因信息 diff --git a/openspec/specs/developer-basic-info-editing/spec.md b/openspec/specs/developer-basic-info-editing/spec.md new file mode 100644 index 0000000..b31ab4d --- /dev/null +++ b/openspec/specs/developer-basic-info-editing/spec.md @@ -0,0 +1,38 @@ +## ADDED Requirements + +### Requirement: 基本信息编辑表单 +UpdateSkillInfoPage SHALL 提供技能基本信息的编辑表单,预填当前数据。 + +#### Scenario: 表单预填展示 +- **WHEN** 用户从技能详情页点击"更新基本信息"进入 UpdateSkillInfoPage +- **THEN** 表单字段预填当前技能的名称、描述、分类、标签和图标数据 + +#### Scenario: 分类动态生成 +- **WHEN** 用户在基本信息编辑表单中打开分类下拉框 +- **THEN** 下拉选项从数据源动态生成,包含所有可用分类(信息查询、效率工具、开发工具、数据分析、文档处理、业务系统) + +#### Scenario: 提交基本信息修改 +- **WHEN** 用户填写完基本信息后点击"保存修改"按钮 +- **THEN** 页面展示成功提示"保存成功",并返回技能详情页 + +#### Scenario: 取消编辑 +- **WHEN** 用户在基本信息编辑页面点击"取消"按钮 +- **THEN** 返回技能详情页,不保存任何修改 + +### Requirement: 技能图标选择 +UpdateSkillInfoPage 和 UploadSkillPage SHALL 提供技能图标的 emoji 选择器。 + +#### Scenario: 图标选择展示 +- **WHEN** 用户在技能创建或编辑页面看到图标选择区域 +- **THEN** 页面展示 emoji 网格(🌤️📊📝🔧💻📋🔍📈🎯⚡🌐🤖),当前选中项高亮显示 + +#### Scenario: 切换图标 +- **WHEN** 用户点击 emoji 网格中的某个图标 +- **THEN** 该图标高亮选中,之前的选中项取消高亮 + +### Requirement: 技能图标显示 +技能详情页 SHALL 在头部区域展示技能图标。 + +#### Scenario: 图标展示 +- **WHEN** 用户打开技能详情页 +- **THEN** 技能头部区域的图标位置显示该技能选择的 emoji 图标 diff --git a/openspec/specs/developer-overview/spec.md b/openspec/specs/developer-overview/spec.md new file mode 100644 index 0000000..a9979d6 --- /dev/null +++ b/openspec/specs/developer-overview/spec.md @@ -0,0 +1,26 @@ +## ADDED Requirements + +### Requirement: 开发者指标展示 +开发台总览页 SHALL 展示开发者维度的核心指标数据,以卡片形式呈现。 + +#### Scenario: 指标卡片展示 +- **WHEN** 用户打开开发台总览页 +- **THEN** 页面顶部显示4个指标卡片:我的技能总数、已发布数量、草稿数量、待审核版本数量,每个卡片包含数值 + +### Requirement: 待审核项目提醒 +开发台总览页 SHALL 展示待审核的版本项目列表。 + +#### Scenario: 待审核列表展示 +- **WHEN** 用户打开开发台总览页 +- **THEN** 页面左侧区域显示待审核项目列表,每条包含技能名称、版本号、审核状态标签和日期,点击可跳转到对应技能详情页 + +#### Scenario: 审核拒绝项展示 +- **WHEN** 待审核列表中包含被拒绝的版本 +- **THEN** 该项显示拒绝状态标签和"查看原因"链接 + +### Requirement: 最近动态展示 +开发台总览页 SHALL 展示开发者最近的操作动态记录。 + +#### Scenario: 动态列表展示 +- **WHEN** 用户打开开发台总览页 +- **THEN** 页面右侧区域显示最近操作动态列表,每条包含时间、操作描述和状态标签 diff --git a/openspec/specs/feedback-display/spec.md b/openspec/specs/feedback-display/spec.md index a7035a6..e698376 100644 --- a/openspec/specs/feedback-display/spec.md +++ b/openspec/specs/feedback-display/spec.md @@ -37,3 +37,44 @@ #### Scenario: 操作失败提示 - **WHEN** 用户执行操作失败 - **THEN** 页面顶部展示红色错误提示"操作失败,请重试" + +### Requirement: 技能删除确认弹窗 +系统 SHALL 提供 Modal 组件用于技能删除操作的确认。 + +#### Scenario: 技能列表删除确认 +- **WHEN** 用户点击技能列表中某个技能的"删除"按钮 +- **THEN** 页面展示确认弹窗,标题为"确认删除",内容为"确定要删除技能"{技能名称}"吗?此操作不可撤销。" + +#### Scenario: 技能详情页删除确认 +- **WHEN** 用户点击技能详情页的"删除技能"按钮 +- **THEN** 页面展示确认弹窗,标题为"确认删除",内容为"确定要删除技能"{技能名称}"吗?此操作不可撤销。" + +### Requirement: 版本删除确认弹窗 +系统 SHALL 提供 Modal 组件用于版本删除操作的确认。 + +#### Scenario: 版本删除确认 +- **WHEN** 用户点击版本历史表格中某个未启用版本的"删除"按钮 +- **THEN** 页面展示确认弹窗,标题为"确认删除",内容为"确定要删除此版本吗?此操作不可撤销。" + +### Requirement: 开发台操作结果消息提示 +系统 SHALL 提供 Toast 组件用于开发台操作的结果提示。 + +#### Scenario: 上架/下架成功提示 +- **WHEN** 用户执行上架或下架操作 +- **THEN** 页面顶部展示绿色成功提示("已上架"或"已下架") + +#### Scenario: 删除成功提示 +- **WHEN** 用户确认删除技能或版本 +- **THEN** 页面顶部展示绿色成功提示"已删除" + +#### Scenario: 保存成功提示 +- **WHEN** 用户在更新基本信息页面点击"保存修改"按钮 +- **THEN** 页面顶部展示绿色成功提示"保存成功" + +#### Scenario: 提交审核成功提示 +- **WHEN** 用户在上传新版本页面点击"提交审核"按钮 +- **THEN** 页面顶部展示绿色成功提示"已提交审核" + +#### Scenario: 创建技能成功提示 +- **WHEN** 用户在创建技能页面点击"创建技能"按钮 +- **THEN** 页面顶部展示绿色成功提示"创建成功" diff --git a/src/constants/pages.js b/src/constants/pages.js index 4c130ea..d6fa13c 100644 --- a/src/constants/pages.js +++ b/src/constants/pages.js @@ -34,6 +34,7 @@ export const ADMIN_PAGES = { * 开发台页面配置 */ export const DEVELOPER_PAGES = { + overview: { title: '总览', icon: 'FiHome' }, mySkills: { title: '我的技能', icon: 'FaPuzzlePiece' }, uploadSkill: { title: '创建技能', icon: 'FiPlus' }, newVersion: { title: '上传新版本', icon: null }, diff --git a/src/data/developerData.js b/src/data/developerData.js index c381c96..f56f0cf 100644 --- a/src/data/developerData.js +++ b/src/data/developerData.js @@ -3,18 +3,18 @@ export const mySkills = [ id: 1, name: '天气查询助手', desc: '根据城市名称查询当前天气和未来预报,支持全国主要城市', + icon: '🌤️', status: 'published', version: '1.2.0', category: '信息查询', tags: ['天气', '查询', '生活'], - modelSupport: ['Doubao-pro', 'GPT-4', 'Claude-3'], lastModified: '2026-03-18', installs: 156, rating: 4.7, versions: [ - { version: '1.2.0', date: '2026-03-18', desc: '新增支持未来7天预报', current: true, status: 'approved', enabled: true }, - { version: '1.1.0', date: '2026-03-10', desc: '优化响应速度', current: false, status: 'approved', enabled: false }, - { version: '1.0.0', date: '2026-03-01', desc: '初始版本', current: false, status: 'approved', enabled: false } + { 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', @@ -26,16 +26,16 @@ export const mySkills = [ id: 2, name: '待办事项管理', desc: '帮助用户管理日常待办事项,支持添加、完成、删除操作', + icon: '📋', status: 'draft', version: '0.1.0', category: '效率工具', tags: ['待办', '管理', '效率'], - modelSupport: ['Doubao-pro', 'Claude-3'], lastModified: '2026-03-17', installs: 0, rating: 0, versions: [ - { version: '0.1.0', date: '2026-03-17', desc: '开发中版本', current: true, status: 'pending', enabled: false } + { version: '0.1.0', date: '2026-03-17', desc: '开发中版本', current: true, status: 'pending', enabled: false, rejectionReason: '' } ], package: { name: 'todo-manager-v0.1.0.zip', @@ -47,19 +47,19 @@ export const mySkills = [ id: 3, name: '代码审查助手', desc: '自动审查代码质量,提供优化建议和潜在问题检测', + icon: '💻', status: 'published', version: '2.0.1', category: '开发工具', tags: ['代码', '审查', '开发'], - modelSupport: ['Claude-3', 'GPT-4'], lastModified: '2026-03-15', installs: 342, rating: 4.9, versions: [ - { version: '2.0.1', date: '2026-03-15', desc: '修复 Python 代码审查问题', current: true, status: 'approved', enabled: true }, - { version: '2.0.0', date: '2026-03-10', desc: '修复安全问题', current: false, status: 'rejected', enabled: false }, - { version: '2.0.0', date: '2026-03-08', desc: '支持多语言审查', current: false, status: 'approved', enabled: false }, - { version: '1.0.0', date: '2026-02-20', desc: '初始版本', current: false, status: 'approved', enabled: false } + { 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', @@ -71,15 +71,6 @@ export const mySkills = [ export const skillCategories = ['信息查询', '效率工具', '开发工具', '数据分析', '文档处理', '业务系统']; -export const supportedModels = [ - { id: 'doubao-pro', name: 'Doubao-pro', provider: '字节跳动' }, - { id: 'doubao-lite', name: 'Doubao-lite', provider: '字节跳动' }, - { id: 'gpt-4', name: 'GPT-4', provider: 'OpenAI' }, - { id: 'gpt-3.5', name: 'GPT-3.5 Turbo', provider: 'OpenAI' }, - { id: 'claude-3', name: 'Claude-3 Opus', provider: 'Anthropic' }, - { id: 'claude-3-haiku', name: 'Claude-3 Haiku', provider: 'Anthropic' } -]; - export const devDocs = [ { id: 1, title: '快速开始', category: '入门指南', content: '介绍如何开发并上传第一个技能...' }, { id: 2, title: '技能包规范', category: '入门指南', content: '技能包的目录结构和必要文件说明...' }, @@ -89,4 +80,23 @@ export const devDocs = [ { id: 6, title: '工具调用规范', category: 'API参考', content: '定义和使用工具函数的规范...' }, { id: 7, title: '版本管理指南', category: '发布管理', content: '版本号规则和升级策略...' }, { id: 8, title: '发布审核流程', category: '发布管理', content: '技能发布后的审核和上线流程...' } -]; \ No newline at end of file +]; + +export const developerOverview = { + totalSkills: 3, + publishedCount: 2, + draftCount: 1, + pendingReview: 1, + totalInstalls: 498, + pendingItems: [ + { skillId: 2, skillName: '待办事项管理', version: '0.1.0', status: 'pending', date: '2026-03-17' }, + { skillId: 3, skillName: '代码审查助手', version: '2.0.0', status: 'rejected', date: '2026-03-10', rejectionReason: '安全审查未通过,存在潜在的代码注入风险,请修复后重新提交' } + ], + recentActivity: [ + { time: '2026-03-18', action: '发布天气查询助手 v1.2.0', status: '审核中' }, + { time: '2026-03-15', action: '更新代码审查助手 v2.0.1', status: '审核通过' }, + { time: '2026-03-10', action: '代码审查助手 v2.0.0', status: '审核拒绝' }, + { time: '2026-03-08', action: '上传代码审查助手 v2.0.0', status: '审核通过' }, + { time: '2026-03-01', action: '发布天气查询助手 v1.0.0', status: '审核通过' } + ] +}; diff --git a/src/pages/DeveloperPage.jsx b/src/pages/DeveloperPage.jsx index 4ed5b33..4d4d40a 100644 --- a/src/pages/DeveloperPage.jsx +++ b/src/pages/DeveloperPage.jsx @@ -1,6 +1,6 @@ import { useState, useEffect } from 'react'; import { useLocation, useNavigate } from 'react-router-dom'; -import { FiPlus, FiTerminal } from 'react-icons/fi'; +import { FiPlus, FiTerminal, FiHome } from 'react-icons/fi'; import { FaPuzzlePiece } from 'react-icons/fa'; import Layout from '../components/Layout.jsx'; import SidebarBrand from '../components/layout/SidebarBrand.jsx'; @@ -10,12 +10,14 @@ import usePageState from '../hooks/usePageState.js'; import { DEVELOPER_PAGES } from '../constants/pages.js'; import { DEVELOPER_KEYS } from '../constants/storageKeys.js'; import api from '../services/api.js'; +import DevOverviewPage from './developer/DevOverviewPage.jsx'; import MySkillsPage from './developer/MySkillsPage.jsx'; import UploadSkillPage from './developer/UploadSkillPage.jsx'; import NewVersionPage from './developer/NewVersionPage.jsx'; import DevDocsPage from './developer/DevDocsPage.jsx'; import DevAccountPage from './developer/DevAccountPage.jsx'; import SkillEditorPage from './developer/SkillEditorPage.jsx'; +import UpdateSkillInfoPage from './developer/UpdateSkillInfoPage.jsx'; function DeveloperPage() { const location = useLocation(); @@ -24,7 +26,7 @@ function DeveloperPage() { // 使用 usePageState 管理页面状态 const { currentPage, setCurrentPage } = usePageState({ storageKey: DEVELOPER_KEYS.CURRENT_PAGE, - defaultPage: 'mySkills', + defaultPage: 'overview', pageTitles: DEVELOPER_PAGES, }); @@ -37,7 +39,7 @@ function DeveloperPage() { useEffect(() => { if (location.state?.fromHome) { - setCurrentPage('mySkills'); + setCurrentPage('overview'); setCurrentSkillId(null); navigate('.', { replace: true, state: {} }); } @@ -68,30 +70,47 @@ function DeveloperPage() { setCurrentPage('newVersion'); }; + const openUpdateInfoPage = (skillId) => { + setCurrentSkillId(skillId); + setCurrentPage('updateInfo'); + }; + const handleBack = () => { setCurrentPage('mySkills'); setCurrentSkillId(null); }; - const handleNewVersionBack = () => { + const handleEditorBack = () => { setCurrentPage('skillEditor'); setNewVersionSkillName(''); }; const renderPage = () => { switch (currentPage) { + case 'overview': + return ; case 'mySkills': return ; case 'uploadSkill': - return ; + return switchPage('mySkills')} />; case 'devDocs': return ; case 'devAccount': return ; case 'skillEditor': - return ; + return ; case 'newVersion': - return ; + return ; + case 'updateInfo': + return ; default: return
Page not found
; } @@ -125,6 +144,12 @@ function DeveloperPage() { ))}
+ } + label="总览" + active={currentPage === 'overview'} + onClick={() => switchPage('overview')} + /> } label="我的技能" @@ -153,4 +178,4 @@ function DeveloperPage() { ); } -export default DeveloperPage; \ No newline at end of file +export default DeveloperPage; diff --git a/src/pages/developer/DevOverviewPage.jsx b/src/pages/developer/DevOverviewPage.jsx new file mode 100644 index 0000000..003708d --- /dev/null +++ b/src/pages/developer/DevOverviewPage.jsx @@ -0,0 +1,91 @@ +import { FiAlertTriangle, FiInfo } from 'react-icons/fi'; +import { api } from '../../services/api.js'; + +function DevOverviewPage({ onSkillClick }) { + const data = api.developer.getOverview(); + + return ( + <> +
+
+
我的技能
+
{data.totalSkills}
+
+
+
已发布
+
{data.publishedCount}
+
+
+
草稿
+
{data.draftCount}
+
+
+
待审核
+
{data.pendingReview}
+
+
+ +
+
+
+
待审核项目
+
+
+ {data.pendingItems.map((item, index) => ( +
onSkillClick && onSkillClick(item.skillId)} + > + + {item.status === 'rejected' ? : } + + + {item.skillName} {item.version} + {item.date} + +
+ ))} +
+
+ +
+
+
最近动态
+
+
+ + + + + + + + + + {data.recentActivity.map((item, index) => ( + + + + + + ))} + +
时间操作状态
{item.time}{item.action} + + {item.status} + +
+
+
+
+ + ); +} + +export default DevOverviewPage; diff --git a/src/pages/developer/MySkillsPage.jsx b/src/pages/developer/MySkillsPage.jsx index 61eff19..e1248e2 100644 --- a/src/pages/developer/MySkillsPage.jsx +++ b/src/pages/developer/MySkillsPage.jsx @@ -1,50 +1,175 @@ -import { mySkills } from '../../data/developerData.js'; +import { useState } from 'react'; +import { api } from '../../services/api.js'; +import Modal from '../../components/common/Modal.jsx'; +import Toast from '../../components/common/Toast.jsx'; function MySkillsPage({ onSkillClick }) { + const sourceData = api.developer.getMySkills(); + const categories = api.developer.getCategories(); + const [filters, setFilters] = useState({ keyword: '', category: '', status: '' }); + const [deleteTarget, setDeleteTarget] = useState(null); + const [toast, setToast] = useState({ visible: false, type: 'success', message: '' }); + + const handleFilterChange = (key, value) => { + setFilters(prev => ({ ...prev, [key]: value })); + }; + + const handleReset = () => { + setFilters({ keyword: '', category: '', status: '' }); + }; + + const filteredList = sourceData.filter(skill => { + if (filters.keyword && !skill.name.includes(filters.keyword) && !skill.desc.includes(filters.keyword)) { + return false; + } + 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; + return true; + }); + + const handleUnpublish = (e, skill) => { + e.stopPropagation(); + setToast({ visible: true, type: 'success', message: '已下架' }); + }; + + const handleDelete = (e, skill) => { + e.stopPropagation(); + setDeleteTarget(skill); + }; + + const confirmDelete = () => { + setDeleteTarget(null); + setToast({ visible: true, type: 'success', message: '已删除' }); + }; + return ( -
-
-
我的技能
+ <> +
+
+
+
+ + handleFilterChange('keyword', e.target.value)} + /> +
+
+ + +
+
+ + +
+
+
+ + +
+
-
- - - - - - - - - - - - - - {mySkills.map(skill => ( - onSkillClick(skill.id)} style={{ cursor: 'pointer' }}> - - - - - - - - - ))} - -
技能名称分类版本状态安装量评分操作
-
{skill.name}
-
{skill.desc}
-
{skill.category}{skill.version} - {skill.status === 'published' ? '已发布' : '草稿'} - {skill.installs}{skill.rating || '-'} - -
+
+
+
我的技能
+
+
+
+ + + + + + + + + + + + + + {filteredList.map(skill => ( + onSkillClick(skill.id)} style={{ cursor: 'pointer' }}> + + + + + + + + + ))} + +
技能名称分类版本状态安装量评分操作
+
{skill.name}
+
{skill.desc}
+
{skill.category}{skill.version} + + {skill.status === 'published' ? '已发布' : '草稿'} + + {skill.installs}{skill.rating || '-'} +
+ + {skill.status === 'published' && ( + + )} + +
+
+
+
+
+
1
+
+
+
-
+ setDeleteTarget(null)} + confirmText="删除" + > + 确定要删除技能"{deleteTarget?.name}"吗?此操作不可撤销。 + + setToast(prev => ({ ...prev, visible: false }))} + /> + ); } -export default MySkillsPage; \ No newline at end of file +export default MySkillsPage; diff --git a/src/pages/developer/NewVersionPage.jsx b/src/pages/developer/NewVersionPage.jsx index c24a11f..d36f9b2 100644 --- a/src/pages/developer/NewVersionPage.jsx +++ b/src/pages/developer/NewVersionPage.jsx @@ -1,42 +1,56 @@ -import { FiUpload } from 'react-icons/fi'; +import { useState } from 'react'; +import { FiUpload, FiChevronLeft } from 'react-icons/fi'; +import Toast from '../../components/common/Toast.jsx'; function NewVersionPage({ skillName, onBack }) { + const [showToast, setShowToast] = useState(false); + + const handleSubmit = () => { + setShowToast(true); + setTimeout(() => { + onBack(); + }, 1000); + }; + return ( -
-
-
上传新版本
+ <> +
+ 返回技能详情
-
-
- - +
+
+
上传新版本 — {skillName}
-
- - -
-
- - -
-
- -
- -
点击或拖拽文件到此处上传
-
支持 .zip 格式
+
+
+ + +