feat(frontend): Day 7 - frontend complete layout finished

This commit is contained in:
AI Clinical Dev Team
2025-10-10 17:52:47 +08:00
parent df20300190
commit fef8feb9db
11 changed files with 872 additions and 129 deletions

View File

@@ -1,14 +1,18 @@
import { Routes, Route, Navigate } from 'react-router-dom' import { Routes, Route, Navigate } from 'react-router-dom'
import MainLayout from './layouts/MainLayout' import MainLayout from './layouts/MainLayout'
import HomePage from './pages/HomePage' import HomePage from './pages/HomePage'
import AgentPage from './pages/AgentPage' import AgentChatPage from './pages/AgentChatPage'
import KnowledgePage from './pages/KnowledgePage'
import HistoryPage from './pages/HistoryPage'
function App() { function App() {
return ( return (
<Routes> <Routes>
<Route path="/" element={<MainLayout />}> <Route path="/" element={<MainLayout />}>
<Route index element={<HomePage />} /> <Route index element={<HomePage />} />
<Route path="agent/:agentId" element={<AgentPage />} /> <Route path="agent/:agentId" element={<AgentChatPage />} />
<Route path="knowledge" element={<KnowledgePage />} />
<Route path="history" element={<HistoryPage />} />
<Route path="*" element={<Navigate to="/" replace />} /> <Route path="*" element={<Navigate to="/" replace />} />
</Route> </Route>
</Routes> </Routes>

View File

@@ -0,0 +1,96 @@
import { Modal, Form, Input, Radio, message } from 'antd';
import { useProjectStore, Project } from '../stores/useProjectStore';
const { TextArea } = Input;
export const CreateProjectDialog = () => {
const [form] = Form.useForm();
const { showCreateDialog, setShowCreateDialog, addProject } = useProjectStore();
const handleOk = async () => {
try {
const values = await form.validateFields();
// 模拟创建项目后续会调用真实API
const newProject: Project = {
id: `proj-${Date.now()}`,
name: values.name,
background: values.background || '',
researchType: values.researchType,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
};
addProject(newProject);
message.success('项目创建成功');
form.resetFields();
setShowCreateDialog(false);
} catch (error) {
console.error('表单验证失败:', error);
}
};
const handleCancel = () => {
form.resetFields();
setShowCreateDialog(false);
};
return (
<Modal
title="创建新项目"
open={showCreateDialog}
onOk={handleOk}
onCancel={handleCancel}
width={600}
okText="创建"
cancelText="取消"
>
<Form
form={form}
layout="vertical"
initialValues={{ researchType: 'observational' }}
>
<Form.Item
name="name"
label="项目名称"
rules={[
{ required: true, message: '请输入项目名称' },
{ max: 100, message: '项目名称不能超过100个字符' },
]}
>
<Input placeholder="例如:心血管疾病与生活方式关系研究" />
</Form.Item>
<Form.Item
name="researchType"
label="研究类型"
rules={[{ required: true, message: '请选择研究类型' }]}
>
<Radio.Group>
<Radio value="observational"></Radio>
<Radio value="interventional"></Radio>
</Radio.Group>
</Form.Item>
<Form.Item
name="background"
label="项目背景"
tooltip="可以包括:研究背景、目标人群、研究目的等"
>
<TextArea
rows={6}
placeholder="请详细描述项目背景信息AI将基于这些信息提供更精准的建议...
例如:
研究背景:心血管疾病是全球主要死因之一...
目标人群18-65岁成年人
研究目的:探索生活方式与心血管疾病的关系"
maxLength={2000}
showCount
/>
</Form.Item>
</Form>
</Modal>
);
};

View File

@@ -0,0 +1,105 @@
import { Modal, Form, Input, Radio, message } from 'antd';
import { useEffect } from 'react';
import { useProjectStore } from '../stores/useProjectStore';
const { TextArea } = Input;
export const EditProjectDialog = () => {
const [form] = Form.useForm();
const {
currentProject,
showEditDialog,
setShowEditDialog,
updateProject,
} = useProjectStore();
// 当对话框打开时,填充表单
useEffect(() => {
if (showEditDialog && currentProject) {
form.setFieldsValue({
name: currentProject.name,
researchType: currentProject.researchType,
background: currentProject.background,
});
}
}, [showEditDialog, currentProject, form]);
const handleOk = async () => {
if (!currentProject) return;
try {
const values = await form.validateFields();
// 模拟更新项目后续会调用真实API
updateProject(currentProject.id, {
name: values.name,
background: values.background || '',
researchType: values.researchType,
updatedAt: new Date().toISOString(),
});
message.success('项目更新成功');
setShowEditDialog(false);
} catch (error) {
console.error('表单验证失败:', error);
}
};
const handleCancel = () => {
form.resetFields();
setShowEditDialog(false);
};
return (
<Modal
title="编辑项目"
open={showEditDialog}
onOk={handleOk}
onCancel={handleCancel}
width={600}
okText="保存"
cancelText="取消"
>
<Form
form={form}
layout="vertical"
>
<Form.Item
name="name"
label="项目名称"
rules={[
{ required: true, message: '请输入项目名称' },
{ max: 100, message: '项目名称不能超过100个字符' },
]}
>
<Input placeholder="例如:心血管疾病与生活方式关系研究" />
</Form.Item>
<Form.Item
name="researchType"
label="研究类型"
rules={[{ required: true, message: '请选择研究类型' }]}
>
<Radio.Group>
<Radio value="observational"></Radio>
<Radio value="interventional"></Radio>
</Radio.Group>
</Form.Item>
<Form.Item
name="background"
label="项目背景"
tooltip="可以包括:研究背景、目标人群、研究目的等"
>
<TextArea
rows={6}
placeholder="请详细描述项目背景信息AI将基于这些信息提供更精准的建议..."
maxLength={2000}
showCount
/>
</Form.Item>
</Form>
</Modal>
);
};

View File

@@ -0,0 +1,75 @@
import { Select, Button, Space, Tooltip } from 'antd';
import { PlusOutlined, EditOutlined, FolderOpenOutlined } from '@ant-design/icons';
import { useProjectStore } from '../stores/useProjectStore';
export const ProjectSelector = () => {
const {
currentProject,
projects,
setCurrentProject,
setShowCreateDialog,
setShowEditDialog,
} = useProjectStore();
const handleProjectChange = (projectId: string) => {
if (projectId === 'global') {
setCurrentProject(null);
} else {
const project = projects.find((p) => p.id === projectId);
if (project) {
setCurrentProject(project);
}
}
};
return (
<div className="p-4 border-b border-gray-200">
<div className="flex items-center gap-2 mb-2">
<FolderOpenOutlined className="text-gray-400" />
<span className="text-sm font-medium text-gray-700"></span>
</div>
<Space.Compact block>
<Select
value={currentProject?.id || 'global'}
onChange={handleProjectChange}
style={{ width: '100%' }}
placeholder="选择项目"
>
<Select.Option value="global">
<span className="text-blue-600"></span>
</Select.Option>
{projects.map((project) => (
<Select.Option key={project.id} value={project.id}>
{project.name}
</Select.Option>
))}
</Select>
<Tooltip title="创建新项目">
<Button
icon={<PlusOutlined />}
onClick={() => setShowCreateDialog(true)}
/>
</Tooltip>
{currentProject && (
<Tooltip title="编辑项目">
<Button
icon={<EditOutlined />}
onClick={() => setShowEditDialog(true)}
/>
</Tooltip>
)}
</Space.Compact>
{currentProject && (
<div className="mt-2 text-xs text-gray-500 truncate">
{currentProject.background || '未设置项目背景'}
</div>
)}
</div>
);
};

View File

@@ -6,28 +6,38 @@ import {
MenuUnfoldOutlined, MenuUnfoldOutlined,
HomeOutlined, HomeOutlined,
ExperimentOutlined, ExperimentOutlined,
FolderOpenOutlined,
HistoryOutlined,
UserOutlined, UserOutlined,
SettingOutlined, SettingOutlined,
LogoutOutlined, LogoutOutlined,
} from '@ant-design/icons' } from '@ant-design/icons'
import type { MenuProps } from 'antd' import type { MenuProps } from 'antd'
import { ProjectSelector } from '../components/ProjectSelector'
import { CreateProjectDialog } from '../components/CreateProjectDialog'
import { EditProjectDialog } from '../components/EditProjectDialog'
const { Header, Sider, Content } = Layout const { Header, Sider, Content } = Layout
// 12个智能体配置 // 12个智能体配置已按PRD更正
const AGENTS = [ export const AGENTS = [
{ id: 'topic-evaluation', name: '选题评价智能体', icon: '🎯' }, // 选题阶段
{ id: 'scientific-question', name: '科学问题梳理智能体', icon: '🔬' }, { id: 'topic-evaluation', name: '选题评价智能体', icon: '🎯', enabled: true },
{ id: 'picos-construction', name: 'PICOS构建智能体', icon: '📋' }, { id: 'scientific-question', name: '科学问题梳理智能体', icon: '🔬', enabled: false },
{ id: 'observation-design', name: '观察指标设计智能体', icon: '📊' }, { id: 'picos-construction', name: 'PICOS构建智能体', icon: '📋', enabled: false },
{ id: 'crf-development', name: 'CRF制定智能体', icon: '📝' },
{ id: 'sample-size', name: '样本量计算智能体', icon: '🔢' }, // 研究设计阶段
{ id: 'protocol-writing', name: '临床研究方案撰写智能体', icon: '📄' }, { id: 'observation-design', name: '观察指标设计智能体', icon: '📊', enabled: false },
{ id: 'paper-polishing', name: '论文润色智能体', icon: '✨' }, { id: 'crf-development', name: 'CRF制定智能体', icon: '📝', enabled: false },
{ id: 'paper-translation', name: '论文翻译智能体', icon: '🌐' }, { id: 'sample-size', name: '样本量计算智能体', icon: '🔢', enabled: false },
{ id: 'methodology-review', name: '方法学评审智能体', icon: '🔍' }, { id: 'protocol-writing', name: '临床研究方案撰写智能体', icon: '📄', enabled: false },
{ id: 'journal-methodology-review', name: '期刊方法学评审智能体', icon: '📑' },
{ id: 'journal-guidelines-review', name: '期刊稿约评审智能体', icon: '✅' }, // 论文撰写阶段
{ id: 'paper-polishing', name: '论文润色智能体', icon: '✨', enabled: false },
{ id: 'paper-translation', name: '论文翻译智能体', icon: '🌐', enabled: false },
{ id: 'methodology-review', name: '方法学评审智能体', icon: '🔍', enabled: false },
{ id: 'journal-methodology-review', name: '期刊方法学评审智能体', icon: '📑', enabled: false },
{ id: 'journal-guidelines-review', name: '期刊稿约评审智能体', icon: '✅', enabled: false },
] ]
const MainLayout = () => { const MainLayout = () => {
@@ -49,8 +59,19 @@ const MainLayout = () => {
children: AGENTS.map((agent) => ({ children: AGENTS.map((agent) => ({
key: `/agent/${agent.id}`, key: `/agent/${agent.id}`,
label: `${agent.icon} ${agent.name}`, label: `${agent.icon} ${agent.name}`,
disabled: !agent.enabled,
})), })),
}, },
{
key: '/knowledge',
icon: <FolderOpenOutlined />,
label: '知识库管理',
},
{
key: '/history',
icon: <HistoryOutlined />,
label: '历史记录',
},
] ]
// 用户下拉菜单 // 用户下拉菜单
@@ -90,7 +111,15 @@ const MainLayout = () => {
return ( return (
<Layout style={{ height: '100vh' }}> <Layout style={{ height: '100vh' }}>
{/* 侧边栏 */} {/* 侧边栏 */}
<Sider trigger={null} collapsible collapsed={collapsed} theme="light"> <Sider
trigger={null}
collapsible
collapsed={collapsed}
theme="light"
width={280}
style={{ overflow: 'auto' }}
>
{/* Logo区域 */}
<div <div
style={{ style={{
height: 64, height: 64,
@@ -105,6 +134,11 @@ const MainLayout = () => {
> >
{collapsed ? '🏥' : '🏥 AI临床研究'} {collapsed ? '🏥' : '🏥 AI临床研究'}
</div> </div>
{/* 项目选择器 */}
{!collapsed && <ProjectSelector />}
{/* 导航菜单 */}
<Menu <Menu
mode="inline" mode="inline"
selectedKeys={[location.pathname]} selectedKeys={[location.pathname]}
@@ -160,9 +194,12 @@ const MainLayout = () => {
<Outlet /> <Outlet />
</Content> </Content>
</Layout> </Layout>
{/* 对话框组件 */}
<CreateProjectDialog />
<EditProjectDialog />
</Layout> </Layout>
) )
} }
export default MainLayout export default MainLayout

View File

@@ -0,0 +1,167 @@
import { useParams } from 'react-router-dom'
import { Card, Typography, Input, Button, Space, Select, Upload, Tag, Alert, Divider } from 'antd'
import {
SendOutlined,
PaperClipOutlined,
RobotOutlined,
FolderOpenOutlined,
SyncOutlined,
} from '@ant-design/icons'
import { AGENTS } from '../layouts/MainLayout'
const { Title, Paragraph } = Typography
const { TextArea } = Input
const AgentChatPage = () => {
const { agentId } = useParams()
const agent = AGENTS.find((a) => a.id === agentId)
if (!agent) {
return (
<Alert
message="智能体不存在"
description="请从首页选择一个智能体"
type="error"
showIcon
/>
)
}
if (!agent.enabled) {
return (
<Alert
message="该智能体正在开发中"
description="敬请期待后续版本..."
type="warning"
showIcon
/>
)
}
return (
<div style={{ height: '100%', display: 'flex', flexDirection: 'column' }}>
{/* 标题栏 */}
<Card style={{ marginBottom: 16 }}>
<Space align="center">
<span style={{ fontSize: 32 }}>{agent.icon}</span>
<div>
<Title level={3} style={{ marginBottom: 4 }}>
{agent.name}
</Title>
<Paragraph type="secondary" style={{ marginBottom: 0 }}>
DeepSeek-V3
</Paragraph>
</div>
</Space>
</Card>
{/* 聊天区域 */}
<Card
style={{
flex: 1,
display: 'flex',
flexDirection: 'column',
overflow: 'hidden',
}}
bodyStyle={{
flex: 1,
display: 'flex',
flexDirection: 'column',
padding: 0,
}}
>
{/* 消息列表区域(占位符) */}
<div
style={{
flex: 1,
padding: 24,
overflowY: 'auto',
background: '#fafafa',
}}
>
<div style={{ textAlign: 'center', padding: '60px 0', color: '#999' }}>
<RobotOutlined style={{ fontSize: 64, marginBottom: 16 }} />
<div></div>
</div>
{/* 消息示例(占位符) */}
<div style={{ maxWidth: 800, margin: '0 auto' }}>
<div style={{ marginBottom: 24 }}>
<Tag color="blue" style={{ marginBottom: 8 }}>
</Tag>
<Card size="small">
<Paragraph style={{ marginBottom: 0 }}>
...
</Paragraph>
</Card>
</div>
<div style={{ marginBottom: 24 }}>
<Tag color="green" style={{ marginBottom: 8 }}>
AI助手
</Tag>
<Card size="small">
<Paragraph style={{ marginBottom: 0 }}>
AI的回复示例...
</Paragraph>
</Card>
</div>
</div>
</div>
<Divider style={{ margin: 0 }} />
{/* 输入区域 */}
<div style={{ padding: 16, background: '#fff' }}>
{/* 工具栏 */}
<Space style={{ marginBottom: 12, width: '100%', justifyContent: 'space-between' }}>
<Space>
<Upload showUploadList={false}>
<Button icon={<PaperClipOutlined />}></Button>
</Upload>
<Button icon={<FolderOpenOutlined />}>
@
</Button>
<Select
defaultValue="deepseek-v3"
style={{ width: 180 }}
options={[
{ label: 'DeepSeek-V3', value: 'deepseek-v3' },
{ label: 'Qwen3-72B', value: 'qwen3-72b' },
{ label: 'Gemini Pro', value: 'gemini-pro' },
]}
/>
</Space>
<Tag color="orange" icon={<SyncOutlined spin />}>
...
</Tag>
</Space>
{/* 输入框 */}
<Space.Compact style={{ width: '100%' }}>
<TextArea
placeholder="输入您的问题... (功能开发中)"
autoSize={{ minRows: 2, maxRows: 6 }}
disabled
/>
<Button
type="primary"
icon={<SendOutlined />}
style={{ height: 'auto' }}
disabled
>
</Button>
</Space.Compact>
</div>
</Card>
</div>
)
}
export default AgentChatPage

View File

@@ -1,92 +0,0 @@
import { useParams } from 'react-router-dom'
import { Card, Typography, Tag, Button, Space } from 'antd'
import { ArrowLeftOutlined } from '@ant-design/icons'
import { useNavigate } from 'react-router-dom'
const { Title, Paragraph } = Typography
const AGENTS_MAP: Record<string, { name: string; icon: string; desc: string }> = {
'topic-evaluation': { name: '选题评价智能体', icon: '🎯', desc: '从创新性、临床价值、科学性和可行性等维度评估研究选题' },
'scientific-question': { name: '科学问题梳理智能体', icon: '🔬', desc: '将模糊的研究想法提炼成清晰、具体、可验证的科学问题' },
'picos-construction': { name: 'PICOS构建智能体', icon: '📋', desc: '按照PICOS原则结构化定义临床研究的核心要素' },
'observation-design': { name: '观察指标设计智能体', icon: '📊', desc: '推荐合适的主要、次要及安全性观察指标集' },
'crf-development': { name: 'CRF制定智能体', icon: '📝', desc: '自动生成结构化、符合规范的病例报告表CRF框架' },
'sample-size': { name: '样本量计算智能体', icon: '🔢', desc: '根据研究参数提供科学合理的样本量估算结果' },
'protocol-writing': { name: '临床研究方案撰写智能体', icon: '📄', desc: '自动生成结构完整的临床研究设计方案' },
'paper-polishing': { name: '论文润色智能体', icon: '✨', desc: '提供专业级的语言润色,修正语法、拼写和表达方式' },
'paper-translation': { name: '论文翻译智能体', icon: '🌐', desc: '提供专业、精准的中英互译服务' },
'methodology-review': { name: '方法学评审智能体', icon: '🔍', desc: '对研究方案或论文进行全面的方法学评审' },
'journal-methodology-review': { name: '期刊方法学评审智能体', icon: '📑', desc: '模拟期刊审稿人视角,进行方法学挑战' },
'journal-guidelines-review': { name: '期刊稿约评审智能体', icon: '✅', desc: '检查文章格式、字数、参考文献规范等是否符合投稿要求' },
}
const AgentPage = () => {
const { agentId } = useParams<{ agentId: string }>()
const navigate = useNavigate()
const agent = agentId ? AGENTS_MAP[agentId] : null
if (!agent) {
return (
<Card>
<Paragraph></Paragraph>
<Button onClick={() => navigate('/')}></Button>
</Card>
)
}
return (
<div>
{/* 头部 */}
<Space direction="vertical" size="large" style={{ width: '100%' }}>
<Button icon={<ArrowLeftOutlined />} onClick={() => navigate('/')}>
</Button>
<Card>
<Space direction="vertical" size="middle" style={{ width: '100%' }}>
<div>
<span style={{ fontSize: 48, marginRight: 16 }}>{agent.icon}</span>
<Title level={2} style={{ display: 'inline', margin: 0 }}>
{agent.name}
</Title>
<Tag color="blue" style={{ marginLeft: 16 }}>
AI智能体
</Tag>
</div>
<Paragraph type="secondary" style={{ fontSize: 16 }}>
{agent.desc}
</Paragraph>
</Space>
</Card>
{/* 功能区域 - 占位符 */}
<Card title="智能对话区域" extra={<Tag color="orange"></Tag>}>
<div
style={{
height: 400,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
background: '#f5f5f5',
borderRadius: 8,
}}
>
<Space direction="vertical" align="center">
<div style={{ fontSize: 64 }}>💬</div>
<Title level={4} type="secondary">
...
</Title>
<Paragraph type="secondary">
Day 9-10
</Paragraph>
</Space>
</div>
</Card>
</Space>
</div>
)
}
export default AgentPage

View File

@@ -0,0 +1,36 @@
import { Card, Empty, Alert } from 'antd'
import { HistoryOutlined, SyncOutlined } from '@ant-design/icons'
const HistoryPage = () => {
return (
<div>
<Alert
message="功能开发中"
description="历史记录功能正在开发中,您将能够查看和管理所有对话历史..."
type="warning"
showIcon
icon={<SyncOutlined spin />}
style={{ marginBottom: 24 }}
/>
<Card>
<Empty
image={Empty.PRESENTED_IMAGE_SIMPLE}
imageStyle={{ height: 120 }}
description={
<div>
<HistoryOutlined style={{ fontSize: 48, color: '#d9d9d9', marginBottom: 16 }} />
<div style={{ color: '#999' }}>线</div>
<div style={{ color: '#ccc', fontSize: 12, marginTop: 8 }}>
</div>
</div>
}
/>
</Card>
</div>
)
}
export default HistoryPage

View File

@@ -1,27 +1,35 @@
import { Card, Row, Col, Typography, Button } from 'antd' import { Card, Row, Col, Typography, Button, Tag, message } from 'antd'
import { useNavigate } from 'react-router-dom' import { useNavigate } from 'react-router-dom'
import { ExperimentOutlined, RocketOutlined } from '@ant-design/icons' import { ExperimentOutlined, RocketOutlined, LockOutlined } from '@ant-design/icons'
const { Title, Paragraph } = Typography const { Title, Paragraph } = Typography
const AGENTS = [ const AGENTS = [
{ id: 'topic-evaluation', name: '选题评价智能体', icon: '🎯', desc: '从创新性、临床价值、科学性和可行性等维度评估研究选题' }, { id: 'topic-evaluation', name: '选题评价智能体', icon: '🎯', desc: '从创新性、临床价值、科学性和可行性等维度评估研究选题', enabled: true },
{ id: 'scientific-question', name: '科学问题梳理智能体', icon: '🔬', desc: '将模糊的研究想法提炼成清晰、具体、可验证的科学问题' }, { id: 'scientific-question', name: '科学问题梳理智能体', icon: '🔬', desc: '将模糊的研究想法提炼成清晰、具体、可验证的科学问题', enabled: false },
{ id: 'picos-construction', name: 'PICOS构建智能体', icon: '📋', desc: '按照PICOS原则结构化定义临床研究的核心要素' }, { id: 'picos-construction', name: 'PICOS构建智能体', icon: '📋', desc: '按照PICOS原则结构化定义临床研究的核心要素', enabled: false },
{ id: 'observation-design', name: '观察指标设计智能体', icon: '📊', desc: '推荐合适的主要、次要及安全性观察指标集' }, { id: 'observation-design', name: '观察指标设计智能体', icon: '📊', desc: '推荐合适的主要、次要及安全性观察指标集', enabled: false },
{ id: 'crf-development', name: 'CRF制定智能体', icon: '📝', desc: '自动生成结构化、符合规范的病例报告表CRF框架' }, { id: 'crf-development', name: 'CRF制定智能体', icon: '📝', desc: '自动生成结构化、符合规范的病例报告表CRF框架', enabled: false },
{ id: 'sample-size', name: '样本量计算智能体', icon: '🔢', desc: '根据研究参数提供科学合理的样本量估算结果' }, { id: 'sample-size', name: '样本量计算智能体', icon: '🔢', desc: '根据研究参数提供科学合理的样本量估算结果', enabled: false },
{ id: 'protocol-writing', name: '临床研究方案撰写智能体', icon: '📄', desc: '自动生成结构完整的临床研究设计方案' }, { id: 'protocol-writing', name: '临床研究方案撰写智能体', icon: '📄', desc: '自动生成结构完整的临床研究设计方案', enabled: false },
{ id: 'paper-polishing', name: '论文润色智能体', icon: '✨', desc: '提供专业级的语言润色,修正语法、拼写和表达方式' }, { id: 'paper-polishing', name: '论文润色智能体', icon: '✨', desc: '提供专业级的语言润色,修正语法、拼写和表达方式', enabled: false },
{ id: 'paper-translation', name: '论文翻译智能体', icon: '🌐', desc: '提供专业、精准的中英互译服务' }, { id: 'paper-translation', name: '论文翻译智能体', icon: '🌐', desc: '提供专业、精准的中英互译服务', enabled: false },
{ id: 'methodology-review', name: '方法学评审智能体', icon: '🔍', desc: '对研究方案或论文进行全面的方法学评审' }, { id: 'methodology-review', name: '方法学评审智能体', icon: '🔍', desc: '对研究方案或论文进行全面的方法学评审', enabled: false },
{ id: 'journal-methodology-review', name: '期刊方法学评审智能体', icon: '📑', desc: '模拟期刊审稿人视角,进行方法学挑战' }, { id: 'journal-methodology-review', name: '期刊方法学评审智能体', icon: '📑', desc: '模拟期刊审稿人视角,进行方法学挑战', enabled: false },
{ id: 'journal-guidelines-review', name: '期刊稿约评审智能体', icon: '✅', desc: '检查文章格式、字数、参考文献规范等是否符合投稿要求' }, { id: 'journal-guidelines-review', name: '期刊稿约评审智能体', icon: '✅', desc: '检查文章格式、字数、参考文献规范等是否符合投稿要求', enabled: false },
] ]
const HomePage = () => { const HomePage = () => {
const navigate = useNavigate() const navigate = useNavigate()
const handleAgentClick = (agent: typeof AGENTS[0]) => {
if (!agent.enabled) {
message.info('该智能体正在开发中,敬请期待!')
return
}
navigate(`/agent/${agent.id}`)
}
return ( return (
<div> <div>
{/* 欢迎区域 */} {/* 欢迎区域 */}
@@ -44,10 +52,23 @@ const HomePage = () => {
{AGENTS.map((agent) => ( {AGENTS.map((agent) => (
<Col xs={24} sm={12} md={8} lg={6} key={agent.id}> <Col xs={24} sm={12} md={8} lg={6} key={agent.id}>
<Card <Card
hoverable hoverable={agent.enabled}
style={{ height: '100%' }} style={{
onClick={() => navigate(`/agent/${agent.id}`)} height: '100%',
opacity: agent.enabled ? 1 : 0.7,
position: 'relative',
}}
onClick={() => handleAgentClick(agent)}
> >
{!agent.enabled && (
<Tag
color="orange"
icon={<LockOutlined />}
style={{ position: 'absolute', top: 8, right: 8 }}
>
线
</Tag>
)}
<div style={{ textAlign: 'center' }}> <div style={{ textAlign: 'center' }}>
<div style={{ fontSize: 48, marginBottom: 12 }}>{agent.icon}</div> <div style={{ fontSize: 48, marginBottom: 12 }}>{agent.icon}</div>
<Title level={4} style={{ marginBottom: 8 }}> <Title level={4} style={{ marginBottom: 8 }}>
@@ -56,8 +77,8 @@ const HomePage = () => {
<Paragraph type="secondary" style={{ marginBottom: 16, minHeight: 44 }}> <Paragraph type="secondary" style={{ marginBottom: 16, minHeight: 44 }}>
{agent.desc} {agent.desc}
</Paragraph> </Paragraph>
<Button type="primary" block> <Button type={agent.enabled ? 'primary' : 'default'} block disabled={!agent.enabled}>
使 {agent.enabled ? '开始使用' : '敬请期待'}
</Button> </Button>
</div> </div>
</Card> </Card>

View File

@@ -0,0 +1,225 @@
import {
Card,
Button,
Space,
Table,
Tag,
Progress,
Alert,
} from 'antd'
import {
PlusOutlined,
FolderOutlined,
FileTextOutlined,
UploadOutlined,
DeleteOutlined,
SyncOutlined,
} from '@ant-design/icons'
const KnowledgePage = () => {
// 模拟知识库数据
const mockKnowledgeBases = [
{
id: '1',
name: '心血管疾病研究文献库',
fileCount: 15,
totalSize: '45MB',
createdAt: '2025-10-05',
status: 'ready',
},
]
// 模拟文档数据
const mockDocuments = [
{
id: '1',
name: '高血压治疗指南2024.pdf',
size: '3.2MB',
uploadedAt: '2025-10-05 14:30',
status: 'processed',
},
{
id: '2',
name: '心血管疾病流行病学研究.docx',
size: '1.8MB',
uploadedAt: '2025-10-05 15:20',
status: 'processing',
},
]
const kbColumns = [
{
title: '知识库名称',
dataIndex: 'name',
key: 'name',
render: (text: string) => (
<Space>
<FolderOutlined style={{ color: '#1890ff' }} />
<span>{text}</span>
</Space>
),
},
{
title: '文档数量',
dataIndex: 'fileCount',
key: 'fileCount',
render: (count: number) => `${count} / 50`,
},
{
title: '总大小',
dataIndex: 'totalSize',
key: 'totalSize',
},
{
title: '创建时间',
dataIndex: 'createdAt',
key: 'createdAt',
},
{
title: '状态',
dataIndex: 'status',
key: 'status',
render: (status: string) => (
<Tag color={status === 'ready' ? 'green' : 'orange'}>
{status === 'ready' ? '就绪' : '处理中'}
</Tag>
),
},
{
title: '操作',
key: 'actions',
render: () => (
<Space>
<Button size="small" icon={<UploadOutlined />} disabled>
</Button>
<Button size="small" danger icon={<DeleteOutlined />} disabled>
</Button>
</Space>
),
},
]
const docColumns = [
{
title: '文件名',
dataIndex: 'name',
key: 'name',
render: (text: string) => (
<Space>
<FileTextOutlined />
<span>{text}</span>
</Space>
),
},
{
title: '大小',
dataIndex: 'size',
key: 'size',
},
{
title: '上传时间',
dataIndex: 'uploadedAt',
key: 'uploadedAt',
},
{
title: '处理状态',
dataIndex: 'status',
key: 'status',
render: (status: string) => {
if (status === 'processed') {
return <Tag color="green"></Tag>
}
return (
<Space>
<Tag color="processing" icon={<SyncOutlined spin />}>
</Tag>
<Progress percent={65} size="small" style={{ width: 100 }} />
</Space>
)
},
},
{
title: '操作',
key: 'actions',
render: () => (
<Button size="small" danger icon={<DeleteOutlined />} disabled>
</Button>
),
},
]
return (
<div>
{/* 提示信息 */}
<Alert
message="知识库功能说明"
description="每个用户最多可创建3个知识库每个知识库最多上传50个文件支持PDF、DOCX格式。在对话时可以@知识库让AI基于您的文献进行回答。"
type="info"
showIcon
closable
style={{ marginBottom: 16 }}
/>
<Alert
message="功能开发中"
description="知识库管理功能正在开发中,敬请期待..."
type="warning"
showIcon
icon={<SyncOutlined spin />}
style={{ marginBottom: 24 }}
/>
{/* 知识库列表 */}
<Card
title={
<Space>
<FolderOutlined />
<span> (1/3)</span>
</Space>
}
extra={
<Button type="primary" icon={<PlusOutlined />} disabled>
</Button>
}
style={{ marginBottom: 24 }}
>
<Table
columns={kbColumns}
dataSource={mockKnowledgeBases}
rowKey="id"
pagination={false}
/>
</Card>
{/* 文档列表 */}
<Card
title={
<Space>
<FileTextOutlined />
<span></span>
</Space>
}
extra={
<Button icon={<UploadOutlined />} disabled>
</Button>
}
>
<Table
columns={docColumns}
dataSource={mockDocuments}
rowKey="id"
pagination={false}
/>
</Card>
</div>
)
}
export default KnowledgePage

View File

@@ -0,0 +1,69 @@
import { create } from 'zustand';
export interface Project {
id: string;
name: string;
background: string;
researchType: 'observational' | 'interventional';
createdAt: string;
updatedAt: string;
}
interface ProjectState {
// 当前选中的项目
currentProject: Project | null;
// 所有项目列表
projects: Project[];
// 是否显示创建项目对话框
showCreateDialog: boolean;
// 是否显示编辑项目对话框
showEditDialog: boolean;
// Actions
setCurrentProject: (project: Project | null) => void;
setProjects: (projects: Project[]) => void;
addProject: (project: Project) => void;
updateProject: (id: string, updates: Partial<Project>) => void;
deleteProject: (id: string) => void;
setShowCreateDialog: (show: boolean) => void;
setShowEditDialog: (show: boolean) => void;
}
export const useProjectStore = create<ProjectState>((set) => ({
currentProject: null,
projects: [],
showCreateDialog: false,
showEditDialog: false,
setCurrentProject: (project) => set({ currentProject: project }),
setProjects: (projects) => set({ projects }),
addProject: (project) => set((state) => ({
projects: [...state.projects, project],
})),
updateProject: (id, updates) => set((state) => ({
projects: state.projects.map((p) =>
p.id === id ? { ...p, ...updates } : p
),
currentProject:
state.currentProject?.id === id
? { ...state.currentProject, ...updates }
: state.currentProject,
})),
deleteProject: (id) => set((state) => ({
projects: state.projects.filter((p) => p.id !== id),
currentProject:
state.currentProject?.id === id ? null : state.currentProject,
})),
setShowCreateDialog: (show) => set({ showCreateDialog: show }),
setShowEditDialog: (show) => set({ showEditDialog: show }),
}));