diff --git a/README.md b/README.md index 92acd2f..bbdf1d4 100644 --- a/README.md +++ b/README.md @@ -96,13 +96,11 @@ pnpm build # 验证打包(不运行pnpm dev,会挂起流程) src/ ├── components/ # 组件库 │ ├── common/ # 通用组件 (Modal, Toast, EmptyState等) -│ ├── layout/ # 布局组件 (AppHeader, AppLayout, UserDropdown等) +│ ├── layout/ # 布局组件 (AppHeader, AppLayout, UserDropdown, ConsoleLayout, AdminLayout, DeveloperLayout等) │ ├── Layout.jsx # 主布局组件(sidebar + content) │ └── ListSelector.jsx # 列表选择器 │ ├── contexts/ # 全局状态 (UserContext) -├── hooks/ # 自定义Hook (usePageState, useNavigation) -├── constants/ # 常量配置 (pages, storageKeys) ├── services/ # 数据访问层 (api.js) ├── data/ # 模拟数据 │ @@ -279,7 +277,9 @@ export default Example; ## 路由规范 -### 顶层路由 +### 嵌套路由结构 + +所有页面通过正式路由导航,使用 HashRouter + 嵌套路由。 ```jsx // App.jsx @@ -288,9 +288,29 @@ export default Example; } /> }> } /> - } /> - } /> - } /> + + }> + } /> + } /> + } /> + } /> + } /> + {/* ...更多子路由 */} + + + }> + } /> + } /> + } /> + } /> + } /> + {/* ...更多子路由 */} + + + }> + } /> + {/* ...子路由 */} + @@ -299,41 +319,33 @@ export default Example; **说明**: - `AppLayout` 包裹所有需要统一 Header 的页面 - 登录页独立,不使用 `AppLayout` +- 每个模块(Console/Admin/Developer)使用独立的 Layout 组件包裹 `` +- 模块根路径自动重定向到默认子页面 +- 新增/编辑表单通过 URL 参数区分:`/admin/departments/add` vs `/admin/departments/:id/edit` -### 子页面路由 - -每个主页面内部管理子页面: +### 子页面参数获取 ```jsx -// ConsolePage.jsx示例 -const [currentPage, setCurrentPage] = useState('chat'); - -const renderPage = () => { - switch (currentPage) { - case 'chat': return ; - case 'skills': return ; - // ... - } -}; -``` - -### 页面配置 - -```javascript -// constants/pages.js -export const CONSOLE_PAGES = { - chat: { title: '智能助手', icon: 'FiMessageSquare' }, - skills: { title: '技能市场', icon: 'FaPuzzlePiece' }, +// 使用 useParams 获取 URL 参数 +function SkillDetailPage() { + const { skillId } = useParams(); + const skill = api.skills.getById(Number(skillId)); // ... -}; +} + +// 使用 useNavigate 进行导航 +function SkillsPage() { + const navigate = useNavigate(); + return ; +} ``` ### 新增页面流程 -1. 在 `constants/pages.js` 添加页面配置 -2. 在父页面组件导入新页面 -3. 在 `renderPage()` 添加case分支 -4. 添加导航项(如需要) +1. 在 `App.jsx` 添加路由定义 +2. 创建页面组件(使用 `useParams` / `useNavigate`) +3. 在对应 Layout 的 sidebar 添加导航项 +4. 确保页面返回按钮使用固定路径导航 --- @@ -351,28 +363,17 @@ function Component() { } ``` -### 页面状态持久化 +### 页面状态由 URL 驱动 -| 模块 | localStorage键 | 默认值 | -|------|---------------|--------| -| 工作台 | `console_currentPage` | `'chat'` | -| 管理台 | `admin_currentPage` | `'overview'` | -| 开发台 | `developer_currentPage` | `'overview'` | +所有页面状态(当前页面、场景名、实体 ID)通过 URL 参数驱动,不依赖 localStorage。 -### 自定义Hook - -```javascript -// usePageState - 页面状态持久化 -const { currentPage, setCurrentPage } = usePageState({ - storageKey: CONSOLE_KEYS.CURRENT_PAGE, - defaultPage: 'chat', - pageTitles: CONSOLE_PAGES, -}); - -// useNavigation - 导航逻辑 -const { navigateToPage, extraData } = useNavigation(setCurrentPage); -navigateToPage('skillDetail', { skillId: '1' }); -``` +| 状态类型 | 驱动方式 | 示例 | +|---------|---------|------| +| 当前页面 | 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 重新获取 | --- diff --git a/openspec/config.yaml b/openspec/config.yaml index 2c7374b..1ddf838 100644 --- a/openspec/config.yaml +++ b/openspec/config.yaml @@ -14,3 +14,7 @@ context: | - **优先阅读README.md**,README.md文档是项目的开发文档,记录代码结构和关键开发模式,优先读取获取上下文 - 涉及页面/路由/组件/功能模块变更或技术栈调整时,同步更新README.md - Git提交: 仅中文; 格式为"类型: 简短描述",类型可选: feat(新功能)/fix(修复)/refactor(重构)/docs(文档)/style(格式)/test(测试)/chore(构建/工具); 多行描述空行后加详细说明; 禁创建git操作task + +rules: + proposal: + - 仔细审查每一个过往spec判断是否存在Modified Capabilities diff --git a/openspec/specs/unified-state-management/spec.md b/openspec/specs/unified-state-management/spec.md index 27911e7..a06d459 100644 --- a/openspec/specs/unified-state-management/spec.md +++ b/openspec/specs/unified-state-management/spec.md @@ -2,7 +2,7 @@ ## Purpose -提供统一的状态管理方案,包括全局用户信息上下文、页面状态持久化、导航逻辑管理,确保应用状态的一致性和可维护性。 +提供统一的全局用户信息上下文,确保应用状态的一致性和可维护性。页面状态和导航逻辑由 URL 路由驱动。 ## Requirements @@ -20,33 +20,3 @@ #### Scenario: UserContext 提供默认值 - **WHEN** 应用启动且没有提供用户信息 - **THEN** 系统使用默认用户信息(name: '张三', avatar: '张', role: 'AI 产品部') - -### Requirement: 页面状态持久化 Hook -系统 SHALL 提供 usePageState Hook,封装页面状态持久化逻辑,自动处理 localStorage 同步和主页跳转重置。 - -#### Scenario: usePageState 初始化从 localStorage 恢复状态 -- **WHEN** 页面使用 usePageState Hook 并传入 storageKey 和 defaultPage -- **THEN** 系统从 localStorage 读取之前保存的页面状态,若无则使用 defaultPage - -#### Scenario: usePageState 自动同步状态到 localStorage -- **WHEN** 调用 usePageState 返回的 setCurrentPage 函数 -- **THEN** 系统更新状态并自动保存到 localStorage - -#### Scenario: usePageState 处理主页跳转重置 -- **WHEN** 从主页跳转到页面(location.state.fromHome 为 true) -- **THEN** 系统重置页面状态为默认值,并清除路由 state - -#### Scenario: usePageState 提供 getPageTitle 函数 -- **WHEN** 调用 usePageState 返回的 getPageTitle 函数 -- **THEN** 系统根据当前页面 ID 从配置中查找并返回对应的页面标题 - -### Requirement: 导航逻辑 Hook -系统 SHALL 提供 useNavigation Hook,统一处理页面导航和路由状态管理。 - -#### Scenario: useNavigation 提供页面切换函数 -- **WHEN** 调用 useNavigation 返回的 navigateToPage 函数并传入目标页面 ID -- **THEN** 系统更新当前页面状态并执行相应导航逻辑 - -#### Scenario: useNavigation 处理带数据的页面切换 -- **WHEN** 调用 navigateToPage 并传入目标页面 ID 和附加数据(如 skillId) -- **THEN** 系统更新页面状态和附加数据状态 diff --git a/src/App.jsx b/src/App.jsx index 18ec7b0..1245922 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,11 +1,47 @@ -import { HashRouter as Router, Routes, Route } from 'react-router-dom'; +import { HashRouter as Router, Routes, Route, Navigate } from 'react-router-dom'; import { UserProvider } from './contexts/UserContext.jsx'; import AppLayout from './components/layout/AppLayout.jsx'; +import ConsoleLayout from './components/layout/ConsoleLayout.jsx'; +import AdminLayout from './components/layout/AdminLayout.jsx'; +import DeveloperLayout from './components/layout/DeveloperLayout.jsx'; import HomePage from './pages/HomePage.jsx'; import LoginPage from './pages/LoginPage.jsx'; -import ConsolePage from './pages/ConsolePage.jsx'; -import AdminPage from './pages/AdminPage.jsx'; -import DeveloperPage from './pages/DeveloperPage.jsx'; + +// Console 子页面 +import ChatPage from './pages/console/ChatPage.jsx'; +import SkillsPage from './pages/console/SkillsPage.jsx'; +import SkillDetailPage from './pages/console/SkillDetailPage.jsx'; +import ConsoleMySkillsPage from './pages/console/MySkillsPage.jsx'; +import SkillConfigPage from './pages/console/SkillConfigPage.jsx'; +import LogsPage from './pages/console/LogsPage.jsx'; +import TasksPage from './pages/console/TasksPage.jsx'; +import TaskDetailPage from './pages/console/TaskDetailPage.jsx'; +import ProjectsPage from './pages/console/ProjectsPage.jsx'; +import MemberConfigPage from './pages/console/MemberConfigPage.jsx'; +import AddMemberPage from './pages/console/AddMemberPage.jsx'; +import ConsoleReviewListPage from './pages/console/ConsoleReviewListPage.jsx'; +import ConsoleReviewDetailPage from './pages/console/ConsoleReviewDetailPage.jsx'; + +// Admin 子页面 +import OverviewPage from './pages/admin/OverviewPage.jsx'; +import DepartmentsPage from './pages/admin/DepartmentsPage.jsx'; +import AddDepartmentPage from './pages/admin/AddDepartmentPage.jsx'; +import UsersPage from './pages/admin/UsersPage.jsx'; +import AddUserPage from './pages/admin/AddUserPage.jsx'; +import AdminProjectsPage from './pages/admin/AdminProjectsPage.jsx'; +import AddProjectPage from './pages/admin/AddProjectPage.jsx'; +import AdminLogsPage from './pages/admin/AdminLogsPage.jsx'; +import ModelConfigsPage from './pages/admin/ModelConfigsPage.jsx'; +import AddModelConfigPage from './pages/admin/AddModelConfigPage.jsx'; + +// Developer 子页面 +import DevOverviewPage from './pages/developer/DevOverviewPage.jsx'; +import DeveloperMySkillsPage from './pages/developer/MySkillsPage.jsx'; +import UploadSkillPage from './pages/developer/UploadSkillPage.jsx'; +import SkillEditorPage from './pages/developer/SkillEditorPage.jsx'; +import UploadVersionPage from './pages/developer/UploadVersionPage.jsx'; +import UpdateSkillInfoPage from './pages/developer/UpdateSkillInfoPage.jsx'; +import DevDocsPage from './pages/developer/DevDocsPage.jsx'; function App() { return ( @@ -15,9 +51,53 @@ function App() { } /> }> } /> - } /> - } /> - } /> + + }> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + + + }> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + + + }> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + @@ -25,4 +105,4 @@ function App() { ); } -export default App; \ No newline at end of file +export default App; diff --git a/src/components/layout/AdminLayout.jsx b/src/components/layout/AdminLayout.jsx new file mode 100644 index 0000000..b67fc26 --- /dev/null +++ b/src/components/layout/AdminLayout.jsx @@ -0,0 +1,92 @@ +import { Outlet, useLocation, useNavigate } from 'react-router-dom'; +import { FiHome, FiBarChart2, FiUsers, FiList, FiCheckCircle, FiActivity, FiSettings } from 'react-icons/fi'; +import Layout from '../Layout.jsx'; +import SidebarNavItem from './SidebarNavItem.jsx'; + +function AdminLayout() { + const location = useLocation(); + const navigate = useNavigate(); + + const isPathActive = (basePath) => location.pathname.startsWith(basePath); + + const sidebar = ( + <> + + + ); + + return ( + + + + ); +} + +export default AdminLayout; diff --git a/src/components/layout/ConsoleLayout.jsx b/src/components/layout/ConsoleLayout.jsx new file mode 100644 index 0000000..887becd --- /dev/null +++ b/src/components/layout/ConsoleLayout.jsx @@ -0,0 +1,99 @@ +import { Outlet, useLocation, useNavigate } from 'react-router-dom'; +import { FiPlus, FiClock, FiList, FiUsers, FiBox } from 'react-icons/fi'; +import { FaPuzzlePiece } from 'react-icons/fa'; +import Layout from '../Layout.jsx'; +import SidebarNavItem from './SidebarNavItem.jsx'; +import api from '../../services/api.js'; + +function ConsoleLayout() { + const location = useLocation(); + const navigate = useNavigate(); + + // 从 URL 提取当前 scene + const sceneMatch = location.pathname.match(/\/console\/chat\/(.+)$/); + const currentScene = sceneMatch ? sceneMatch[1] : null; + + // 判断是否在 chat 页面(需要全宽布局) + const isChatPage = location.pathname === '/console/chat' || + location.pathname.startsWith('/console/chat/'); + + // sidebar 高亮判断 + const isPathActive = (basePath) => location.pathname.startsWith(basePath); + + const sidebar = ( + <> +
+ +
+
+ {api.conversations.list().map(conv => ( +
navigate(`/console/chat/${conv.scene}`)} + > +
{conv.title}
+
{conv.time}
+
+ ))} +
+
+ + +
+
+ } + label="技能市场" + active={isPathActive('/console/skills')} + onClick={() => navigate('/console/skills')} + /> + } + label="我的技能" + active={isPathActive('/console/my-skills')} + onClick={() => navigate('/console/my-skills')} + /> + } + label="定时任务" + active={isPathActive('/console/tasks')} + onClick={() => navigate('/console/tasks')} + /> + } + label="日志查询" + active={isPathActive('/console/logs')} + onClick={() => navigate('/console/logs')} + /> + } + label="项目管理" + active={isPathActive('/console/projects')} + onClick={() => navigate('/console/projects')} + /> +
+ + ); + + return ( + + + + ); +} + +export default ConsoleLayout; diff --git a/src/components/layout/DeveloperLayout.jsx b/src/components/layout/DeveloperLayout.jsx new file mode 100644 index 0000000..7e3a306 --- /dev/null +++ b/src/components/layout/DeveloperLayout.jsx @@ -0,0 +1,84 @@ +import { Outlet, useLocation, useNavigate } from 'react-router-dom'; +import { FiPlus, FiTerminal, FiHome } from 'react-icons/fi'; +import { FaPuzzlePiece } from 'react-icons/fa'; +import Layout from '../Layout.jsx'; +import SidebarNavItem from './SidebarNavItem.jsx'; +import api from '../../services/api.js'; + +const skillStatusMap = { + dev: { text: '开发中', className: 'status-stopped' }, + published: { text: '已上架', className: 'status-running' }, + unlisting: { text: '下架审核中', className: 'status-warning' }, + unlisted: { text: '已下架', className: 'status-stopped' } +}; + +function DeveloperLayout() { + const location = useLocation(); + const navigate = useNavigate(); + + const isPathActive = (basePath) => location.pathname.startsWith(basePath); + + // 获取当前技能编辑器中的 skillId + const editorMatch = location.pathname.match(/\/developer\/my-skills\/(\d+)\/editor$/); + const activeSkillId = editorMatch ? parseInt(editorMatch[1]) : null; + + const sidebar = ( + <> +
+ +
+
+ {api.developer.getMySkills().map(skill => ( +
navigate(`/developer/my-skills/${skill.id}/editor`)} + > +
{skill.name}
+
+ + {skillStatusMap[skill.status]?.text || skill.status} + +
+
+ ))} +
+
+ } + label="总览" + active={location.pathname === '/developer/overview'} + onClick={() => navigate('/developer/overview')} + /> + } + label="我的技能" + active={isPathActive('/developer/my-skills')} + onClick={() => navigate('/developer/my-skills')} + /> + } + label="开发文档" + active={location.pathname === '/developer/docs'} + onClick={() => navigate('/developer/docs')} + /> +
+ + ); + + return ( + + + + ); +} + +export default DeveloperLayout; diff --git a/src/constants/pages.js b/src/constants/pages.js deleted file mode 100644 index 61e0c31..0000000 --- a/src/constants/pages.js +++ /dev/null @@ -1,61 +0,0 @@ -// 页面配置常量 - -/** - * 工作台页面配置 - */ -export const CONSOLE_PAGES = { - chat: { title: '智能助手', icon: 'FiMessageSquare' }, - skills: { title: '技能市场', icon: 'FaPuzzlePiece' }, - skillDetail: { title: '技能详情', icon: null }, - mySkills: { title: '我的技能', icon: 'FiBox' }, - skillConfig: { title: '技能配置', icon: null }, - logs: { title: '日志查询', icon: 'FiList' }, - scheduledTasks: { title: '定时任务', icon: 'FiClock' }, - taskDetail: { title: '任务详情', icon: null }, - account: { title: '账号管理', icon: 'FiUser' }, - projects: { title: '项目管理', icon: 'FiUsers' }, - memberConfig: { title: '成员配置', icon: null }, - addMember: { title: '增加成员', icon: null }, -}; - -/** - * 管理台页面配置 - */ -export const ADMIN_PAGES = { - overview: { title: '总览', icon: 'FiHome' }, - departments: { title: '部门管理', icon: 'FiBarChart2' }, - users: { title: '用户管理', icon: 'FiUsers' }, - projects: { title: '项目管理', icon: 'FiList' }, - modelConfigs: { title: '模型配置', icon: 'FiSettings' }, - adminLogs: { title: '日志查询', icon: 'FiActivity' }, - reviewList: { title: '审核管理', icon: 'FiCheckCircle' }, - reviewDetail: { title: '审核详情', icon: null }, - addDepartment: { title: '新增部门', icon: null }, - addUser: { title: '新增用户', icon: null }, - addProject: { title: '新增项目', icon: null }, - addModelConfig: { title: '新增配置', icon: null }, - account: { title: '账号管理', icon: 'FiUser' }, -}; - -/** - * 开发台页面配置 - */ -export const DEVELOPER_PAGES = { - overview: { title: '总览', icon: 'FiHome' }, - mySkills: { title: '我的技能', icon: 'FaPuzzlePiece' }, - uploadSkill: { title: '创建技能', icon: 'FiPlus' }, - newVersion: { title: '上传新版本', icon: null }, - devDocs: { title: '开发文档', icon: 'FiTerminal' }, - devAccount: { title: '账号管理', icon: 'FiSettings' }, - skillEditor: { title: '技能详情', icon: null }, -}; - -/** - * 获取页面标题 - * @param {string} pageId - 页面ID - * @param {Object} pagesConfig - 页面配置对象 - * @returns {string} 页面标题 - */ -export function getPageTitle(pageId, pagesConfig) { - return pagesConfig[pageId]?.title || ''; -} diff --git a/src/constants/storageKeys.js b/src/constants/storageKeys.js deleted file mode 100644 index 2aa4b1b..0000000 --- a/src/constants/storageKeys.js +++ /dev/null @@ -1,25 +0,0 @@ -// localStorage 键名常量 - -/** - * 工作台相关键名 - */ -export const CONSOLE_KEYS = { - CURRENT_PAGE: 'console_currentPage', - CURRENT_SCENE: 'console_currentScene', -}; - -/** - * 管理台相关键名 - */ -export const ADMIN_KEYS = { - CURRENT_PAGE: 'admin_currentPage', - MODEL_CONFIG_EDIT_DATA: 'admin_modelConfigEditData', -}; - -/** - * 开发台相关键名 - */ -export const DEVELOPER_KEYS = { - CURRENT_PAGE: 'developer_currentPage', - CURRENT_SKILL_ID: 'developer_currentSkillId', -}; diff --git a/src/hooks/useLocalStorage.js b/src/hooks/useLocalStorage.js deleted file mode 100644 index 7c717c4..0000000 --- a/src/hooks/useLocalStorage.js +++ /dev/null @@ -1,25 +0,0 @@ -import { useState, useEffect } from 'react'; - -function useLocalStorage(key, initialValue) { - const [value, setValue] = useState(() => { - try { - const saved = localStorage.getItem(key); - return saved ? JSON.parse(saved) : initialValue; - } catch (error) { - console.warn(`Error reading localStorage key "${key}":`, error); - return initialValue; - } - }); - - useEffect(() => { - try { - localStorage.setItem(key, JSON.stringify(value)); - } catch (error) { - console.warn(`Error setting localStorage key "${key}":`, error); - } - }, [key, value]); - - return [value, setValue]; -} - -export default useLocalStorage; \ No newline at end of file diff --git a/src/hooks/useNavigation.js b/src/hooks/useNavigation.js deleted file mode 100644 index aa1da25..0000000 --- a/src/hooks/useNavigation.js +++ /dev/null @@ -1,50 +0,0 @@ -import { useState, useCallback } from 'react'; - -/** - * 导航逻辑 Hook - * 统一处理页面导航和附加数据状态管理 - * - * @param {Function} setPageCallback - 设置当前页面的回调函数 - * @returns {Object} 导航操作函数 - */ -function useNavigation(setPageCallback) { - const [extraData, setExtraData] = useState({}); - - /** - * 导航到指定页面 - * @param {string} pageId - 目标页面 ID - * @param {Object} data - 附加数据(如 skillId、taskId 等) - */ - const navigateToPage = useCallback((pageId, data = {}) => { - setPageCallback(pageId); - - // 如果有附加数据,更新 extraData - if (Object.keys(data).length > 0) { - setExtraData(data); - } - }, [setPageCallback]); - - /** - * 设置附加数据 - * @param {Object} data - 附加数据对象 - */ - const setExtraDataValue = useCallback((data) => { - setExtraData(prev => ({ ...prev, ...data })); - }, []); - - /** - * 清除附加数据 - */ - const clearExtraData = useCallback(() => { - setExtraData({}); - }, []); - - return { - extraData, - navigateToPage, - setExtraData: setExtraDataValue, - clearExtraData, - }; -} - -export default useNavigation; diff --git a/src/hooks/usePageState.js b/src/hooks/usePageState.js deleted file mode 100644 index 8294bc5..0000000 --- a/src/hooks/usePageState.js +++ /dev/null @@ -1,58 +0,0 @@ -import { useState, useEffect } from 'react'; -import { useLocation, useNavigate } from 'react-router-dom'; - -/** - * 页面状态持久化 Hook - * 封装页面状态管理、localStorage 同步和主页跳转重置逻辑 - * - * @param {Object} options - 配置选项 - * @param {string} options.storageKey - localStorage 存储键名 - * @param {string} options.defaultPage - 默认页面 ID - * @param {Object} options.pageTitles - 页面标题映射对象 - * @param {Function} options.getPageTitle - 自定义获取页面标题函数(可选) - * @returns {Object} 状态和操作函数 - */ -function usePageState({ - storageKey, - defaultPage, - pageTitles, - getPageTitle: customGetPageTitle, -}) { - const location = useLocation(); - const navigate = useNavigate(); - - // 从 localStorage 恢复或使用默认值 - const [currentPage, setCurrentPage] = useState(() => { - const saved = localStorage.getItem(`${storageKey}_currentPage`); - return saved || defaultPage; - }); - - // 处理主页跳转重置 - useEffect(() => { - if (location.state?.fromHome) { - setCurrentPage(defaultPage); - navigate('.', { replace: true, state: {} }); - } - }, [location.state, navigate, defaultPage]); - - // 同步到 localStorage - useEffect(() => { - localStorage.setItem(`${storageKey}_currentPage`, currentPage); - }, [storageKey, currentPage]); - - // 获取页面标题 - const getPageTitle = (pageId = currentPage) => { - if (customGetPageTitle) { - return customGetPageTitle(pageId, currentPage); - } - return pageTitles[pageId] || ''; - }; - - return { - currentPage, - setCurrentPage, - getPageTitle, - }; -} - -export default usePageState; diff --git a/src/pages/AdminPage.jsx b/src/pages/AdminPage.jsx deleted file mode 100644 index d0bc7f0..0000000 --- a/src/pages/AdminPage.jsx +++ /dev/null @@ -1,192 +0,0 @@ -import { useState, useEffect } from 'react'; -import { useLocation, useNavigate } from 'react-router-dom'; -import { FiHome, FiBarChart2, FiUsers, FiList, FiCheckCircle, FiActivity, FiSettings } from 'react-icons/fi'; -import Layout from '../components/Layout.jsx'; -import SidebarNavItem from '../components/layout/SidebarNavItem.jsx'; -import usePageState from '../hooks/usePageState.js'; -import { ADMIN_PAGES } from '../constants/pages.js'; -import { ADMIN_KEYS } from '../constants/storageKeys.js'; -import OverviewPage from './admin/OverviewPage.jsx'; -import DepartmentsPage from './admin/DepartmentsPage.jsx'; -import UsersPage from './admin/UsersPage.jsx'; -import AdminProjectsPage from './admin/AdminProjectsPage.jsx'; -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'; -import ModelConfigsPage from './admin/ModelConfigsPage.jsx'; -import AddModelConfigPage from './admin/AddModelConfigPage.jsx'; - -function AdminPage() { - const location = useLocation(); - const navigate = useNavigate(); - - const { currentPage, setCurrentPage } = usePageState({ - storageKey: ADMIN_KEYS.CURRENT_PAGE, - defaultPage: 'overview', - pageTitles: ADMIN_PAGES, - }); - - 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': - return ; - case 'departments': - return navigateTo('addDepartment')} - onEdit={(dept) => navigateTo('addDepartment', dept)} - />; - case 'users': - return navigateTo('addUser')} - onEdit={(user) => navigateTo('addUser', user)} - />; - case 'projects': - return navigateTo('addProject')} - onEdit={(project) => navigateTo('addProject', project)} - />; - case 'adminLogs': - return ; - case 'reviewList': - return ; - case 'reviewDetail': - return ; - case 'addDepartment': - return navigateTo('departments')} - editData={editData} - />; - case 'addUser': - return navigateTo('users')} - editData={editData} - />; - case 'addProject': - return navigateTo('projects')} - editData={editData} - />; - case 'modelConfigs': - return navigateTo('addModelConfig')} - onEdit={(config) => navigateTo('addModelConfig', config)} - />; - case 'addModelConfig': - return navigateTo('modelConfigs')} - editData={editData} - />; - default: - return
Page not found
; - } - }; - - const sidebar = ( - <> - - - ); - - return ( - - {renderPage()} - - ); -} - -export default AdminPage; diff --git a/src/pages/ConsolePage.jsx b/src/pages/ConsolePage.jsx deleted file mode 100644 index ef664a4..0000000 --- a/src/pages/ConsolePage.jsx +++ /dev/null @@ -1,219 +0,0 @@ -import { useState, useEffect } from 'react'; -import { useLocation, useNavigate } from 'react-router-dom'; -import { FiPlus, FiClock, FiList, FiUsers, FiBox } from 'react-icons/fi'; -import { FaPuzzlePiece } from 'react-icons/fa'; -import Layout from '../components/Layout.jsx'; -import SidebarNavItem from '../components/layout/SidebarNavItem.jsx'; -import usePageState from '../hooks/usePageState.js'; -import { CONSOLE_PAGES } from '../constants/pages.js'; -import { CONSOLE_KEYS } from '../constants/storageKeys.js'; -import api from '../services/api.js'; -import ChatPage from './console/ChatPage.jsx'; -import SkillsPage from './console/SkillsPage.jsx'; -import SkillDetailPage from './console/SkillDetailPage.jsx'; -import MySkillsPage from './console/MySkillsPage.jsx'; -import SkillConfigPage from './console/SkillConfigPage.jsx'; -import LogsPage from './console/LogsPage.jsx'; -import TasksPage from './console/TasksPage.jsx'; -import TaskDetailPage from './console/TaskDetailPage.jsx'; -import ProjectsPage from './console/ProjectsPage.jsx'; -import MemberConfigPage from './console/MemberConfigPage.jsx'; -import AddMemberPage from './console/AddMemberPage.jsx'; - -function ConsolePage() { - const location = useLocation(); - const navigate = useNavigate(); - - // 使用 usePageState 管理 currentPage(不使用其返回的 getPageTitle,因为需要访问组件局部变量) - const { currentPage, setCurrentPage } = usePageState({ - storageKey: CONSOLE_KEYS.CURRENT_PAGE, - defaultPage: 'chat', - pageTitles: CONSOLE_PAGES, - }); - - // 保留额外的状态(scene 和 skillId 等需要特殊处理) - const [currentScene, setCurrentScene] = useState(() => { - return localStorage.getItem(CONSOLE_KEYS.CURRENT_SCENE) || 'welcome'; - }); - const [currentSkillId, setCurrentSkillId] = useState(null); - const [currentTaskId, setCurrentTaskId] = useState(null); - const [currentSubscriptionId, setCurrentSubscriptionId] = useState(null); - - // 处理主页跳转重置 - useEffect(() => { - if (location.state?.fromHome) { - setCurrentPage('chat'); - setCurrentScene('welcome'); - navigate('.', { replace: true, state: {} }); - } - }, [location.state, navigate, setCurrentPage, setCurrentScene]); - - // 同步 currentScene 到 localStorage - useEffect(() => { - localStorage.setItem(CONSOLE_KEYS.CURRENT_SCENE, currentScene); - }, [currentScene]); - - const switchPage = (pageId, data = {}) => { - setCurrentPage(pageId); - if (data.skillId !== undefined) { - setCurrentSkillId(data.skillId); - } - if (data.subscriptionId !== undefined) { - setCurrentSubscriptionId(data.subscriptionId); - } - }; - - const handleSkillClick = (skillId) => { - switchPage('skillDetail', { skillId }); - }; - - const handleBack = () => { - switchPage('skills'); - }; - - const switchChatScene = (scene) => { - setCurrentScene(scene); - if (currentPage !== 'chat') { - setCurrentPage('chat'); - } - }; - - const createNewChat = () => { - setCurrentScene('welcome'); - setCurrentPage('chat'); - }; - - const activeScene = currentPage === 'chat' ? currentScene : null; - - const renderPage = () => { - switch (currentPage) { - case 'chat': - return ; - case 'skills': - return ; - case 'skillDetail': - return ; - case 'mySkills': - return switchPage('skillConfig', { subscriptionId })} - onBack={() => switchPage('skills')} - />; - case 'skillConfig': - return switchPage('mySkills')} - />; - case 'logs': - return ; - case 'scheduledTasks': - return { - setCurrentTaskId(taskId); - switchPage('taskDetail'); - }} - />; - case 'taskDetail': - return switchPage('scheduledTasks')} - />; - case 'projects': - return switchPage('addMember')} />; - case 'memberConfig': - return switchPage('projects')} />; - case 'addMember': - return switchPage('projects')} />; - default: - return
Page not found
; - } - }; - - const getPageTitle = () => { - let title = CONSOLE_PAGES[currentPage]?.title || ''; - if (currentPage === 'chat') { - const conv = api.conversations.list().find(c => c.scene === currentScene); - title = conv?.title || '智能助手'; - } - if (currentPage === 'skillDetail' && currentSkillId) { - const skill = api.skills.getById(currentSkillId); - title = skill?.name || '技能详情'; - } - return title; - }; - - const sidebar = ( - <> -
- -
-
- {api.conversations.list().map(conv => ( -
switchChatScene(conv.scene)} - > -
{conv.title}
-
{conv.time}
-
- ))} -
-
- - -
-
- } - label="技能市场" - active={currentPage === 'skills'} - onClick={() => switchPage('skills')} - /> - } - label="我的技能" - active={currentPage === 'mySkills'} - onClick={() => switchPage('mySkills')} - /> - } - label="定时任务" - active={currentPage === 'scheduledTasks'} - onClick={() => switchPage('scheduledTasks')} - /> - } - label="日志查询" - active={currentPage === 'logs'} - onClick={() => switchPage('logs')} - /> - } - label="项目管理" - active={currentPage === 'projects'} - onClick={() => switchPage('projects')} - /> -
- - ); - - return ( - - {renderPage()} - - ); -} - -export default ConsolePage; \ No newline at end of file diff --git a/src/pages/DeveloperPage.jsx b/src/pages/DeveloperPage.jsx deleted file mode 100644 index 2c0ebfd..0000000 --- a/src/pages/DeveloperPage.jsx +++ /dev/null @@ -1,180 +0,0 @@ -import { useState, useEffect } from 'react'; -import { useLocation, useNavigate } from 'react-router-dom'; -import { FiPlus, FiTerminal, FiHome } from 'react-icons/fi'; -import { FaPuzzlePiece } from 'react-icons/fa'; -import Layout from '../components/Layout.jsx'; -import SidebarNavItem from '../components/layout/SidebarNavItem.jsx'; -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 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(); - const navigate = useNavigate(); - - // 使用 usePageState 管理页面状态 - const { currentPage, setCurrentPage } = usePageState({ - storageKey: DEVELOPER_KEYS.CURRENT_PAGE, - defaultPage: 'overview', - pageTitles: DEVELOPER_PAGES, - }); - - // 保留额外的状态(currentSkillId 需要持久化到 localStorage) - const [currentSkillId, setCurrentSkillId] = useState(() => { - const saved = localStorage.getItem(DEVELOPER_KEYS.CURRENT_SKILL_ID); - return saved ? JSON.parse(saved) : null; - }); - const [newVersionSkillName, setNewVersionSkillName] = useState(''); - - useEffect(() => { - if (location.state?.fromHome) { - setCurrentPage('overview'); - setCurrentSkillId(null); - navigate('.', { replace: true, state: {} }); - } - }, [location.state, navigate, setCurrentPage, setCurrentSkillId]); - - useEffect(() => { - localStorage.setItem(DEVELOPER_KEYS.CURRENT_SKILL_ID, JSON.stringify(currentSkillId)); - }, [DEVELOPER_KEYS.CURRENT_SKILL_ID, currentSkillId]); - - const switchPage = (pageId, data = {}) => { - setCurrentPage(pageId); - if (data.skillId !== undefined) { - setCurrentSkillId(data.skillId); - } - }; - - const openSkillEditor = (skillId) => { - setCurrentSkillId(skillId); - setCurrentPage('skillEditor'); - }; - - const createNewProject = () => { - setCurrentPage('uploadSkill'); - }; - - const openNewVersionPage = (skillName) => { - setNewVersionSkillName(skillName); - setCurrentPage('newVersion'); - }; - - const openUpdateInfoPage = (skillId) => { - setCurrentSkillId(skillId); - setCurrentPage('updateInfo'); - }; - - const handleBack = () => { - setCurrentPage('mySkills'); - setCurrentSkillId(null); - }; - - const handleEditorBack = () => { - setCurrentPage('skillEditor'); - setNewVersionSkillName(''); - }; - - const renderPage = () => { - switch (currentPage) { - case 'overview': - return ; - case 'mySkills': - return ; - case 'uploadSkill': - return switchPage('mySkills')} />; - case 'devDocs': - return ; - case 'skillEditor': - return ; - case 'newVersion': - return ; - case 'updateInfo': - return ; - default: - return
Page not found
; - } - }; - - const sidebar = ( - <> -
- -
-
- {api.developer.getMySkills().map(skill => ( -
openSkillEditor(skill.id)} - > -
{skill.name}
-
- - {skillStatusMap[skill.status]?.text || skill.status} - -
-
- ))} -
-
- } - label="总览" - active={currentPage === 'overview'} - onClick={() => switchPage('overview')} - /> - } - label="我的技能" - active={currentPage === 'mySkills'} - onClick={() => switchPage('mySkills')} - /> - } - label="开发文档" - active={currentPage === 'devDocs'} - onClick={() => switchPage('devDocs')} - /> -
- - ); - - return ( - - {renderPage()} - - ); -} - -export default DeveloperPage; diff --git a/src/pages/HomePage.jsx b/src/pages/HomePage.jsx index c82f71b..01d451a 100644 --- a/src/pages/HomePage.jsx +++ b/src/pages/HomePage.jsx @@ -15,7 +15,7 @@ function HomePage() { 基于容器化实例的 智能助手平台,提供租户隔离、技能市场、安全审计等核心能力

- + 进入工作台
diff --git a/src/pages/LoginPage.jsx b/src/pages/LoginPage.jsx index 13bb589..bb7c59a 100644 --- a/src/pages/LoginPage.jsx +++ b/src/pages/LoginPage.jsx @@ -112,7 +112,7 @@ function LoginPage() { diff --git a/src/pages/admin/AddDepartmentPage.jsx b/src/pages/admin/AddDepartmentPage.jsx index 4293dc3..2533fe6 100644 --- a/src/pages/admin/AddDepartmentPage.jsx +++ b/src/pages/admin/AddDepartmentPage.jsx @@ -1,8 +1,13 @@ import { useState } from 'react'; +import { useParams, useNavigate } from 'react-router-dom'; import ListSelector from '../../components/ListSelector.jsx'; import { availableLeaders } from '../../data/adminData.js'; +import { api } from '../../services/api.js'; -function AddDepartmentPage({ onBack, editData }) { +function AddDepartmentPage() { + const { id } = useParams(); + const navigate = useNavigate(); + const editData = id ? api.admin.departments.getById(Number(id)) : null; const isEdit = !!editData; const [name, setName] = useState(editData?.name || ''); const [description, setDescription] = useState(editData?.description || ''); @@ -22,7 +27,7 @@ function AddDepartmentPage({ onBack, editData }) { return ( <> -
+
navigate('/admin/departments')}> 返回部门列表
@@ -53,7 +58,7 @@ function AddDepartmentPage({ onBack, editData }) { />
- +
diff --git a/src/pages/admin/AddModelConfigPage.jsx b/src/pages/admin/AddModelConfigPage.jsx index f33077e..5f894da 100644 --- a/src/pages/admin/AddModelConfigPage.jsx +++ b/src/pages/admin/AddModelConfigPage.jsx @@ -1,9 +1,13 @@ import { useState, useEffect } from 'react'; +import { useParams, useNavigate } from 'react-router-dom'; import { FiEye, FiEyeOff } from 'react-icons/fi'; import { api } from '../../services/api.js'; import { MODEL_CONFIG_TYPES, getConfigFields, getConfigTypeList } from '../../data/configTypes.js'; -function AddModelConfigPage({ onBack, editData }) { +function AddModelConfigPage() { + const { id } = useParams(); + const navigate = useNavigate(); + const editData = id ? api.admin.modelConfigs.getById(id) : null; const isEdit = !!editData; // 基础信息 @@ -98,7 +102,7 @@ function AddModelConfigPage({ onBack, editData }) { api.admin.modelConfigs.create(configData); } - onBack(); + navigate('/admin/models'); }; // 获取当前类型的字段定义 @@ -107,7 +111,7 @@ function AddModelConfigPage({ onBack, editData }) { return ( <> -
+
navigate('/admin/models')}> 返回配置列表
@@ -215,7 +219,7 @@ function AddModelConfigPage({ onBack, editData }) { {/* 操作按钮 */}
- +
diff --git a/src/pages/admin/AddProjectPage.jsx b/src/pages/admin/AddProjectPage.jsx index 64507b8..c38359a 100644 --- a/src/pages/admin/AddProjectPage.jsx +++ b/src/pages/admin/AddProjectPage.jsx @@ -1,8 +1,13 @@ import { useState } from 'react'; +import { useParams, useNavigate } from 'react-router-dom'; import ListSelector from '../../components/ListSelector.jsx'; import { availableLeaders } from '../../data/adminData.js'; +import { api } from '../../services/api.js'; -function AddProjectPage({ onBack, editData }) { +function AddProjectPage() { + const { id } = useParams(); + const navigate = useNavigate(); + const editData = id ? api.admin.projects.getById(Number(id)) : null; const isEdit = !!editData; const [name, setName] = useState(editData?.name || ''); const [description, setDescription] = useState(editData?.description || ''); @@ -22,7 +27,7 @@ function AddProjectPage({ onBack, editData }) { return ( <> -
+
navigate('/admin/projects')}> 返回项目列表
@@ -53,7 +58,7 @@ function AddProjectPage({ onBack, editData }) { />
- +
diff --git a/src/pages/admin/AddUserPage.jsx b/src/pages/admin/AddUserPage.jsx index 66ef137..9f06d74 100644 --- a/src/pages/admin/AddUserPage.jsx +++ b/src/pages/admin/AddUserPage.jsx @@ -1,8 +1,13 @@ import { useState } from 'react'; +import { useParams, useNavigate } from 'react-router-dom'; import ListSelector from '../../components/ListSelector.jsx'; import { availableDepartments } from '../../data/adminData.js'; +import { api } from '../../services/api.js'; -function AddUserPage({ onBack, editData }) { +function AddUserPage() { + const { id } = useParams(); + const navigate = useNavigate(); + const editData = id ? api.admin.users.getById(Number(id)) : null; const isEdit = !!editData; const [name, setName] = useState(editData?.name || ''); const [role, setRole] = useState(editData?.role || '成员'); @@ -24,7 +29,7 @@ function AddUserPage({ onBack, editData }) { return ( <> -
+
navigate('/admin/users')}> 返回用户列表
@@ -67,7 +72,7 @@ function AddUserPage({ onBack, editData }) { setPhone(e.target.value)} />
- +
diff --git a/src/pages/admin/AdminProjectsPage.jsx b/src/pages/admin/AdminProjectsPage.jsx index 5f83a21..4c5c273 100644 --- a/src/pages/admin/AdminProjectsPage.jsx +++ b/src/pages/admin/AdminProjectsPage.jsx @@ -1,4 +1,5 @@ import { useState } from 'react'; +import { useNavigate } from 'react-router-dom'; import { api } from '../../services/api.js'; import Modal from '../../components/common/Modal.jsx'; @@ -7,7 +8,8 @@ function StatusTag({ status }) { return {status}; } -function AdminProjectsPage({ onAdd, onEdit }) { +function AdminProjectsPage() { + const navigate = useNavigate(); const sourceData = api.admin.projects.list(); const [filters, setFilters] = useState({ keyword: '', status: '' }); const [deleteTarget, setDeleteTarget] = useState(null); @@ -71,7 +73,7 @@ function AdminProjectsPage({ onAdd, onEdit }) {
项目列表
- +
@@ -99,7 +101,7 @@ function AdminProjectsPage({ onAdd, onEdit }) {
- +
diff --git a/src/pages/admin/DepartmentsPage.jsx b/src/pages/admin/DepartmentsPage.jsx index bc879e3..b905715 100644 --- a/src/pages/admin/DepartmentsPage.jsx +++ b/src/pages/admin/DepartmentsPage.jsx @@ -1,4 +1,5 @@ import { useState } from 'react'; +import { useNavigate } from 'react-router-dom'; import { api } from '../../services/api.js'; import Modal from '../../components/common/Modal.jsx'; @@ -7,7 +8,8 @@ function StatusTag({ status }) { return {status}; } -function DepartmentsPage({ onAdd, onEdit }) { +function DepartmentsPage() { + const navigate = useNavigate(); const sourceData = api.admin.departments.list(); const [filters, setFilters] = useState({ keyword: '', status: '' }); const [deleteTarget, setDeleteTarget] = useState(null); @@ -71,7 +73,7 @@ function DepartmentsPage({ onAdd, onEdit }) {
部门列表
- +
@@ -99,7 +101,7 @@ function DepartmentsPage({ onAdd, onEdit }) {
- +
diff --git a/src/pages/admin/ModelConfigsPage.jsx b/src/pages/admin/ModelConfigsPage.jsx index a12e583..95b1205 100644 --- a/src/pages/admin/ModelConfigsPage.jsx +++ b/src/pages/admin/ModelConfigsPage.jsx @@ -1,10 +1,12 @@ import { useState } from 'react'; +import { useNavigate } from 'react-router-dom'; import { FiPlus } from 'react-icons/fi'; import { api } from '../../services/api.js'; import { MODEL_CONFIG_TYPES, getConfigSummary } from '../../data/configTypes.js'; import Modal from '../../components/common/Modal.jsx'; -function ModelConfigsPage({ onAdd, onEdit }) { +function ModelConfigsPage() { + const navigate = useNavigate(); const [configs, setConfigs] = useState(api.admin.modelConfigs.list()); const [showSetActiveModal, setShowSetActiveModal] = useState(false); const [showDeleteModal, setShowDeleteModal] = useState(false); @@ -44,7 +46,7 @@ function ModelConfigsPage({ onAdd, onEdit }) {
配置列表
-
@@ -85,7 +87,7 @@ function ModelConfigsPage({ onAdd, onEdit }) { )} +
@@ -124,7 +126,7 @@ function UsersPage({ onAdd, onEdit }) {
- +
diff --git a/src/pages/console/AddMemberPage.jsx b/src/pages/console/AddMemberPage.jsx index 0e578c9..70d005c 100644 --- a/src/pages/console/AddMemberPage.jsx +++ b/src/pages/console/AddMemberPage.jsx @@ -1,4 +1,5 @@ import { useState } from 'react'; +import { useNavigate } from 'react-router-dom'; import ListSelector from '../../components/ListSelector.jsx'; const availableMembers = [ @@ -12,7 +13,8 @@ const availableMembers = [ { id: 8, name: '杨十八', department: '数据分析部', email: 'yangshiba@example.com' } ]; -function AddMemberPage({ onBack }) { +function AddMemberPage() { + const navigate = useNavigate(); const [selectedMembers, setSelectedMembers] = useState([]); const memberColumns = [ @@ -31,7 +33,7 @@ function AddMemberPage({ onBack }) { return ( <> -
+
navigate('/console/projects')}> 返回成员列表
@@ -51,7 +53,7 @@ function AddMemberPage({ onBack }) { onClearSelected={() => setSelectedMembers([])} />
- + diff --git a/src/pages/console/ChatPage.jsx b/src/pages/console/ChatPage.jsx index 9c34c7a..48c74ca 100644 --- a/src/pages/console/ChatPage.jsx +++ b/src/pages/console/ChatPage.jsx @@ -1,10 +1,13 @@ import { useEffect, useRef } from 'react'; +import { useParams } from 'react-router-dom'; import { getChatScenes } from '../../data/conversations.js'; import { FiPaperclip, FiCode, FiSend } from 'react-icons/fi'; -function ChatPage({ scene }) { +function ChatPage() { + const { scene } = useParams(); + const currentScene = scene || 'welcome'; const chatScenes = getChatScenes(); - const html = chatScenes[scene] || ''; + const html = chatScenes[currentScene] || ''; const chatMessagesRef = useRef(null); useEffect(() => { diff --git a/src/pages/console/ConsoleReviewDetailPage.jsx b/src/pages/console/ConsoleReviewDetailPage.jsx index 624fc7f..efecb4c 100644 --- a/src/pages/console/ConsoleReviewDetailPage.jsx +++ b/src/pages/console/ConsoleReviewDetailPage.jsx @@ -1,14 +1,17 @@ import { useState } from 'react'; +import { useParams, useNavigate } from 'react-router-dom'; import { 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 }) { +function ConsoleReviewDetailPage() { + const { type, reviewId } = useParams(); + const navigate = useNavigate(); 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 versionReview = type === 'version' ? pendingVersionReviews.find(r => r.id === Number(reviewId)) : null; + const unlistReview = type === 'unlist' ? pendingUnlistReviews.find(r => r.id === Number(reviewId)) : null; const review = versionReview || unlistReview; @@ -20,7 +23,7 @@ function ConsoleReviewDetailPage({ type, reviewId, onBack }) { setToastMessage('审核通过'); setShowToast(true); setTimeout(() => { - onBack && onBack(); + onBack && navigate('/admin/reviews'); }, 1000); }; @@ -28,13 +31,13 @@ function ConsoleReviewDetailPage({ type, reviewId, onBack }) { setToastMessage('已拒绝'); setShowToast(true); setTimeout(() => { - onBack && onBack(); + onBack && navigate('/admin/reviews'); }, 1000); }; return ( <> -
+
navigate('/admin/reviews')}> 返回审核列表
diff --git a/src/pages/console/ConsoleReviewListPage.jsx b/src/pages/console/ConsoleReviewListPage.jsx index 761b9fa..40e4877 100644 --- a/src/pages/console/ConsoleReviewListPage.jsx +++ b/src/pages/console/ConsoleReviewListPage.jsx @@ -1,7 +1,9 @@ import { useState } from 'react'; +import { useNavigate } from 'react-router-dom'; import { pendingVersionReviews, pendingUnlistReviews } from '../../data/skills.js'; -function ConsoleReviewListPage({ onReviewClick }) { +function ConsoleReviewListPage() { + const navigate = useNavigate(); const [activeTab, setActiveTab] = useState('version'); return ( @@ -50,7 +52,7 @@ function ConsoleReviewListPage({ onReviewClick }) { @@ -84,7 +86,7 @@ function ConsoleReviewListPage({ onReviewClick }) { diff --git a/src/pages/console/MemberConfigPage.jsx b/src/pages/console/MemberConfigPage.jsx index 5f04b88..7eb276f 100644 --- a/src/pages/console/MemberConfigPage.jsx +++ b/src/pages/console/MemberConfigPage.jsx @@ -1,7 +1,12 @@ -function MemberConfigPage({ onBack }) { +import { useNavigate, useParams } from 'react-router-dom'; + +function MemberConfigPage() { + const { memberId } = useParams(); + const navigate = useNavigate(); + return ( <> -
+
navigate('/console/projects')}> 返回成员列表
@@ -10,11 +15,11 @@ function MemberConfigPage({ onBack }) {
成员配置
-

成员配置页面内容

+

成员配置页面内容 (成员 ID: {memberId})

); } -export default MemberConfigPage; \ No newline at end of file +export default MemberConfigPage; diff --git a/src/pages/console/MySkillsPage.jsx b/src/pages/console/MySkillsPage.jsx index 8d9c629..be5cdc0 100644 --- a/src/pages/console/MySkillsPage.jsx +++ b/src/pages/console/MySkillsPage.jsx @@ -1,4 +1,5 @@ import { useState } from 'react'; +import { useNavigate } from 'react-router-dom'; import { FiSearch } from 'react-icons/fi'; import { FaBoxOpen } from 'react-icons/fa'; import { skills, userSubscriptions } from '../../data/skills.js'; @@ -6,7 +7,8 @@ import EmptyState from '../../components/common/EmptyState.jsx'; import Modal from '../../components/common/Modal.jsx'; import Toast from '../../components/common/Toast.jsx'; -function MySkillsPage({ onConfig, onBack }) { +function MySkillsPage() { + const navigate = useNavigate(); const [filters, setFilters] = useState({ keyword: '', category: '', status: '' }); const [subscriptions, setSubscriptions] = useState(userSubscriptions); const [actionTarget, setActionTarget] = useState(null); @@ -249,7 +251,7 @@ function MySkillsPage({ onConfig, onBack }) { )} diff --git a/src/pages/console/ProjectsPage.jsx b/src/pages/console/ProjectsPage.jsx index a041ac3..850a18c 100644 --- a/src/pages/console/ProjectsPage.jsx +++ b/src/pages/console/ProjectsPage.jsx @@ -1,10 +1,12 @@ import { useState } from 'react'; +import { useNavigate } from 'react-router-dom'; import { FiUsers, FiSearch } from 'react-icons/fi'; import { projectMembers } from '../../data/members.js'; import EmptyState from '../../components/common/EmptyState.jsx'; import Modal from '../../components/common/Modal.jsx'; -function ProjectsPage({ onAddMember }) { +function ProjectsPage() { + const navigate = useNavigate(); const [members, setMembers] = useState(projectMembers); const [removeTarget, setRemoveTarget] = useState(null); const [filters, setFilters] = useState({ @@ -82,7 +84,7 @@ function ProjectsPage({ onAddMember }) {
成员列表
- +
{filteredMembers.length > 0 ? ( @@ -110,7 +112,7 @@ function ProjectsPage({ onAddMember }) { {member.role}
- +
diff --git a/src/pages/console/SkillConfigPage.jsx b/src/pages/console/SkillConfigPage.jsx index 9b1d303..3758128 100644 --- a/src/pages/console/SkillConfigPage.jsx +++ b/src/pages/console/SkillConfigPage.jsx @@ -1,9 +1,12 @@ import { useState, useEffect } from 'react'; +import { useParams, useNavigate } from 'react-router-dom'; import { FiPlus, FiX, FiUsers, FiStar, FiPackage } from 'react-icons/fi'; import { skills, userSubscriptions } from '../../data/skills.js'; import Toast from '../../components/common/Toast.jsx'; -function SkillConfigPage({ subscriptionId, onBack }) { +function SkillConfigPage() { + const { subscriptionId } = useParams(); + const navigate = useNavigate(); const [subscriptions, setSubscriptions] = useState(userSubscriptions); const [subscription, setSubscription] = useState(null); const [skill, setSkill] = useState(null); @@ -12,7 +15,7 @@ function SkillConfigPage({ subscriptionId, onBack }) { const [toast, setToast] = useState({ visible: false, type: 'success', message: '' }); useEffect(() => { - const sub = subscriptions.find(s => s.id === subscriptionId); + const sub = subscriptions.find(s => s.id === Number(subscriptionId)); if (sub) { setSubscription(sub); const skillData = skills.find(s => s.id === sub.skillId); @@ -82,7 +85,7 @@ function SkillConfigPage({ subscriptionId, onBack }) { // 延迟返回 setTimeout(() => { - onBack(); + navigate('/console/my-skills'); }, 500); }; @@ -94,7 +97,7 @@ function SkillConfigPage({ subscriptionId, onBack }) { return ( <> -
+
navigate('/console/my-skills')}> 返回我的技能
@@ -207,7 +210,7 @@ function SkillConfigPage({ subscriptionId, onBack }) {
)}
- - + diff --git a/src/pages/developer/DevOverviewPage.jsx b/src/pages/developer/DevOverviewPage.jsx index 003708d..adb6ef2 100644 --- a/src/pages/developer/DevOverviewPage.jsx +++ b/src/pages/developer/DevOverviewPage.jsx @@ -1,7 +1,9 @@ +import { useNavigate } from 'react-router-dom'; import { FiAlertTriangle, FiInfo } from 'react-icons/fi'; import { api } from '../../services/api.js'; -function DevOverviewPage({ onSkillClick }) { +function DevOverviewPage() { + const navigate = useNavigate(); const data = api.developer.getOverview(); return ( @@ -36,7 +38,7 @@ function DevOverviewPage({ onSkillClick }) { key={index} className={`anomaly-item ${item.status === 'rejected' ? 'anomaly-warning' : 'anomaly-info'}`} style={{ cursor: 'pointer' }} - onClick={() => onSkillClick && onSkillClick(item.skillId)} + onClick={() => navigate(`/developer/my-skills/${item.skillId}/editor`)} > {item.status === 'rejected' ? : } diff --git a/src/pages/developer/MySkillsPage.jsx b/src/pages/developer/MySkillsPage.jsx index df569a9..38a1eb5 100644 --- a/src/pages/developer/MySkillsPage.jsx +++ b/src/pages/developer/MySkillsPage.jsx @@ -1,4 +1,5 @@ import { useState } from 'react'; +import { useNavigate } from 'react-router-dom'; import { api } from '../../services/api.js'; import Modal from '../../components/common/Modal.jsx'; import Toast from '../../components/common/Toast.jsx'; @@ -10,7 +11,8 @@ const skillStatusMap = { unlisted: { text: '已下架', className: 'status-stopped' } }; -function MySkillsPage({ onSkillClick }) { +function MySkillsPage() { + const navigate = useNavigate(); const sourceData = api.developer.getMySkills(); const [filters, setFilters] = useState({ keyword: '', status: '' }); const [deleteTarget, setDeleteTarget] = useState(null); @@ -100,7 +102,7 @@ function MySkillsPage({ onSkillClick }) { {filteredList.map(skill => ( - onSkillClick(skill.id)}> + navigate(`/developer/my-skills/${skill.id}/editor`)}> {skill.name} {skill.desc} @@ -110,7 +112,7 @@ function MySkillsPage({ onSkillClick }) {
- {skill.status === 'published' && ( diff --git a/src/pages/developer/NewVersionPage.jsx b/src/pages/developer/NewVersionPage.jsx deleted file mode 100644 index c272130..0000000 --- a/src/pages/developer/NewVersionPage.jsx +++ /dev/null @@ -1,58 +0,0 @@ -import { useState } from 'react'; -import { FiUpload } 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}
-
-
-
- -