import { useEffect, useMemo, useState } from 'react'; import { useParams, useNavigate, Outlet, Link, useLocation } from 'react-router-dom'; import { Layout, Menu, Spin, Button, Statistic, Row, Col, Card, Drawer } from 'antd'; import { ArrowLeftOutlined, FileTextOutlined, TeamOutlined, BookOutlined, // ToolOutlined, GlobalOutlined, MenuFoldOutlined, MenuUnfoldOutlined, ApartmentOutlined, BankOutlined, EditOutlined, FundOutlined, HeartOutlined, } from '@ant-design/icons'; import { useStore } from '../store'; import { useCharacterSync, useOutlineSync, useChapterSync } from '../store/hooks'; import { projectApi } from '../services/api'; const { Header, Sider, Content } = Layout; // 判断是否为移动端 const isMobile = () => window.innerWidth <= 768; export default function ProjectDetail() { const { projectId } = useParams<{ projectId: string }>(); const navigate = useNavigate(); const location = useLocation(); const [collapsed, setCollapsed] = useState(false); const [drawerVisible, setDrawerVisible] = useState(false); const [mobile, setMobile] = useState(isMobile()); // 监听窗口大小变化 useEffect(() => { const handleResize = () => { setMobile(isMobile()); if (!isMobile()) { setDrawerVisible(false); } }; window.addEventListener('resize', handleResize); return () => window.removeEventListener('resize', handleResize); }, []); const { currentProject, setCurrentProject, clearProjectData, loading, setLoading, outlines, characters, chapters, } = useStore(); // 使用同步 hooks const { refreshCharacters } = useCharacterSync(); const { refreshOutlines } = useOutlineSync(); const { refreshChapters } = useChapterSync(); useEffect(() => { const loadProjectData = async (id: string) => { try { setLoading(true); // 加载项目基本信息 const project = await projectApi.getProject(id); setCurrentProject(project); // 并行加载其他数据 await Promise.all([ refreshOutlines(id), refreshCharacters(id), refreshChapters(id), ]); } catch (error) { console.error('加载项目数据失败:', error); } finally { setLoading(false); } }; if (projectId) { loadProjectData(projectId); } return () => { clearProjectData(); }; }, [projectId, clearProjectData, setLoading, setCurrentProject, refreshOutlines, refreshCharacters, refreshChapters]); // 移除事件监听,避免无限循环 // Hook 内部已经更新了 store,不需要再次刷新 const menuItems = [ { key: 'world-setting', icon: , label: 世界设定, }, { key: 'characters', icon: , label: 角色管理, }, { key: 'relationships', icon: , label: 关系管理, }, { key: 'organizations', icon: , label: 组织管理, }, { key: 'outline', icon: , label: 大纲管理, }, { key: 'chapters', icon: , label: 章节管理, }, { key: 'chapter-analysis', icon: , label: 剧情分析, }, { key: 'writing-styles', icon: , label: 写作风格, }, // { // key: 'polish', // icon: , // label: AI去味, // }, { key: 'sponsor', icon: , label: 赞助支持, }, ]; // 根据当前路径动态确定选中的菜单项 const selectedKey = useMemo(() => { const path = location.pathname; if (path.includes('/world-setting')) return 'world-setting'; if (path.includes('/relationships')) return 'relationships'; if (path.includes('/organizations')) return 'organizations'; if (path.includes('/outline')) return 'outline'; if (path.includes('/characters')) return 'characters'; if (path.includes('/chapter-analysis')) return 'chapter-analysis'; if (path.includes('/chapters')) return 'chapters'; if (path.includes('/writing-styles')) return 'writing-styles'; if (path.includes('/sponsor')) return 'sponsor'; // if (path.includes('/polish')) return 'polish'; return 'world-setting'; // 默认选中世界设定 }, [location.pathname]); if (loading || !currentProject) { return (
); } // 渲染菜单内容 const renderMenu = () => (
mobile && setDrawerVisible(false)} />
); return (
)}

{currentProject.title}

{mobile && ( )} {!mobile && (
大纲} value={outlines.length} suffix="条" valueStyle={{ fontSize: '16px', fontWeight: 600, color: '#667eea' }} /> 角色} value={characters.length} suffix="个" valueStyle={{ fontSize: '16px', fontWeight: 600, color: '#52c41a' }} /> 章节} value={chapters.length} suffix="章" valueStyle={{ fontSize: '16px', fontWeight: 600, color: '#1890ff' }} /> 已写} value={currentProject.current_words} suffix="字" valueStyle={{ fontSize: '16px', fontWeight: 600, color: '#fa8c16' }} />
)}
{mobile ? ( setDrawerVisible(false)} open={drawerVisible} width={280} styles={{ body: { padding: 0, display: 'flex', flexDirection: 'column' } }} > {renderMenu()} ) : (
{renderMenu()}
)}
); }