- 新增审核管理页面:版本审核列表、下架审核列表、审核详情页 - 完善技能状态机:开发中/已上架/下架审核中/已下架四种状态 - 实现版本审核机制:审核中/通过/拒绝/撤销四种状态 - 更新 README:详细记录技能开发流程与审核机制 - 优化技能详情页:根据状态展示不同操作按钮 - 完善我的技能列表:状态筛选与操作限制 - 新增上传新版本页面:分离版本上传与基本信息编辑 - 更新 openspec 规范:技能审核流程与状态定义
194 lines
7.3 KiB
JavaScript
194 lines
7.3 KiB
JavaScript
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 SidebarBrand from '../components/layout/SidebarBrand.jsx';
|
||
import SidebarUser from '../components/layout/SidebarUser.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 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();
|
||
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 <DevOverviewPage onSkillClick={openSkillEditor} />;
|
||
case 'mySkills':
|
||
return <MySkillsPage onSkillClick={openSkillEditor} />;
|
||
case 'uploadSkill':
|
||
return <UploadSkillPage onBack={() => switchPage('mySkills')} />;
|
||
case 'devDocs':
|
||
return <DevDocsPage />;
|
||
case 'devAccount':
|
||
return <DevAccountPage />;
|
||
case 'skillEditor':
|
||
return <SkillEditorPage
|
||
skillId={currentSkillId}
|
||
onBack={handleBack}
|
||
onUploadNewVersion={openNewVersionPage}
|
||
onUpdateInfo={openUpdateInfoPage}
|
||
/>;
|
||
case 'newVersion':
|
||
return <UploadVersionPage skillName={newVersionSkillName} onBack={handleEditorBack} />;
|
||
case 'updateInfo':
|
||
return <UpdateSkillInfoPage
|
||
skill={api.developer.getSkillById(currentSkillId)}
|
||
onBack={handleEditorBack}
|
||
/>;
|
||
default:
|
||
return <div>Page not found</div>;
|
||
}
|
||
};
|
||
|
||
const getPageTitle = () => {
|
||
return DEVELOPER_PAGES[currentPage]?.title || '';
|
||
};
|
||
|
||
const sidebar = (
|
||
<>
|
||
<div className="chat-sidebar-header">
|
||
<SidebarBrand subtitle="技能开发台" />
|
||
<div className="sidebar-divider"></div>
|
||
<button className="btn btn-primary" style={{ width: '100%' }} onClick={createNewProject}>
|
||
<span style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', gap: '6px' }}>
|
||
<FiPlus /> 创建技能
|
||
</span>
|
||
</button>
|
||
</div>
|
||
<div className="chat-sidebar-content">
|
||
{api.developer.getMySkills().map(skill => (
|
||
<div
|
||
key={skill.id}
|
||
className={`conversation-item ${currentSkillId === skill.id && currentPage === 'skillEditor' ? 'active' : ''}`}
|
||
onClick={() => openSkillEditor(skill.id)}
|
||
>
|
||
<div className="conversation-title">{skill.name}</div>
|
||
<div className="conversation-time">
|
||
<span className={`status ${skillStatusMap[skill.status]?.className || 'status-stopped'}`}>
|
||
{skillStatusMap[skill.status]?.text || skill.status}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
<div className="chat-sidebar-nav">
|
||
<SidebarNavItem
|
||
icon={<FiHome />}
|
||
label="总览"
|
||
active={currentPage === 'overview'}
|
||
onClick={() => switchPage('overview')}
|
||
/>
|
||
<SidebarNavItem
|
||
icon={<FaPuzzlePiece />}
|
||
label="我的技能"
|
||
active={currentPage === 'mySkills'}
|
||
onClick={() => switchPage('mySkills')}
|
||
/>
|
||
<SidebarNavItem
|
||
icon={<FiTerminal />}
|
||
label="开发文档"
|
||
active={currentPage === 'devDocs'}
|
||
onClick={() => switchPage('devDocs')}
|
||
/>
|
||
</div>
|
||
<SidebarUser onClick={() => switchPage('devAccount')} />
|
||
</>
|
||
);
|
||
|
||
return (
|
||
<Layout
|
||
sidebar={sidebar}
|
||
headerTitle={getPageTitle()}
|
||
sidebarClassName="chat-sidebar"
|
||
>
|
||
{renderPage()}
|
||
</Layout>
|
||
);
|
||
}
|
||
|
||
export default DeveloperPage;
|