feat(frontend): Day 6 - frontend basic architecture completed
This commit is contained in:
29
frontend/.gitignore
vendored
Normal file
29
frontend/.gitignore
vendored
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
dist-ssr
|
||||||
|
*.local
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea
|
||||||
|
.DS_Store
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
||||||
|
|
||||||
|
# Environment variables
|
||||||
|
.env.local
|
||||||
|
.env.production
|
||||||
|
|
||||||
146
frontend/README.md
Normal file
146
frontend/README.md
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
# AI临床研究平台 - 前端
|
||||||
|
|
||||||
|
基于 Vite + React + TypeScript + Ant Design 构建
|
||||||
|
|
||||||
|
## 🚀 快速开始
|
||||||
|
|
||||||
|
### 1. 安装依赖
|
||||||
|
```bash
|
||||||
|
npm install
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 启动开发服务器
|
||||||
|
```bash
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
访问:http://localhost:3000
|
||||||
|
|
||||||
|
### 3. 构建生产版本
|
||||||
|
```bash
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📦 技术栈
|
||||||
|
|
||||||
|
- **框架**: React 18 + TypeScript
|
||||||
|
- **构建工具**: Vite 6
|
||||||
|
- **UI组件库**: Ant Design 5
|
||||||
|
- **CSS框架**: Tailwind CSS 3
|
||||||
|
- **路由**: React Router 6
|
||||||
|
- **HTTP客户端**: Axios
|
||||||
|
- **状态管理**: React Hooks(Day 7+可能引入Zustand)
|
||||||
|
|
||||||
|
## 📁 项目结构
|
||||||
|
|
||||||
|
```
|
||||||
|
frontend/
|
||||||
|
├── src/
|
||||||
|
│ ├── api/ # API请求
|
||||||
|
│ │ ├── request.ts # Axios配置
|
||||||
|
│ │ └── index.ts # API接口
|
||||||
|
│ ├── assets/ # 静态资源
|
||||||
|
│ ├── components/ # 公共组件
|
||||||
|
│ ├── layouts/ # 布局组件
|
||||||
|
│ │ └── MainLayout.tsx
|
||||||
|
│ ├── pages/ # 页面组件
|
||||||
|
│ │ ├── HomePage.tsx # 首页
|
||||||
|
│ │ └── AgentPage.tsx # 智能体页面
|
||||||
|
│ ├── types/ # TypeScript类型定义
|
||||||
|
│ ├── utils/ # 工具函数
|
||||||
|
│ ├── App.tsx # 根组件
|
||||||
|
│ ├── main.tsx # 入口文件
|
||||||
|
│ └── index.css # 全局样式
|
||||||
|
├── index.html
|
||||||
|
├── package.json
|
||||||
|
├── tsconfig.json
|
||||||
|
├── vite.config.ts
|
||||||
|
└── tailwind.config.js
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🎨 功能特性
|
||||||
|
|
||||||
|
### Day 6 已完成
|
||||||
|
- ✅ 项目初始化(Vite + React + TypeScript)
|
||||||
|
- ✅ Ant Design UI组件库集成
|
||||||
|
- ✅ Tailwind CSS配置
|
||||||
|
- ✅ React Router路由配置
|
||||||
|
- ✅ 主布局组件(侧边栏 + 头部 + 内容区)
|
||||||
|
- ✅ 首页展示(12个智能体卡片)
|
||||||
|
- ✅ 智能体页面(占位符)
|
||||||
|
- ✅ Axios请求配置
|
||||||
|
- ✅ TypeScript类型定义
|
||||||
|
|
||||||
|
### Day 7-8 待开发
|
||||||
|
- ⏳ 项目管理功能
|
||||||
|
- ⏳ 知识库管理功能
|
||||||
|
- ⏳ 个人中心页面
|
||||||
|
- ⏳ 设置页面
|
||||||
|
|
||||||
|
### Day 9-10 待开发
|
||||||
|
- ⏳ 智能对话组件
|
||||||
|
- ⏳ 与后端API集成
|
||||||
|
- ⏳ 模型切换功能
|
||||||
|
- ⏳ 知识库@引用功能
|
||||||
|
|
||||||
|
## 🔧 开发规范
|
||||||
|
|
||||||
|
### 代码规范
|
||||||
|
- 使用TypeScript类型检查
|
||||||
|
- 使用ESLint代码检查
|
||||||
|
- 组件使用函数式组件 + Hooks
|
||||||
|
- 样式优先使用Tailwind CSS
|
||||||
|
- 复杂样式使用Ant Design内联style
|
||||||
|
|
||||||
|
### 命名规范
|
||||||
|
- 组件文件:PascalCase(如:HomePage.tsx)
|
||||||
|
- 普通文件:camelCase(如:request.ts)
|
||||||
|
- 常量:UPPER_SNAKE_CASE(如:API_BASE_URL)
|
||||||
|
|
||||||
|
### Git提交规范
|
||||||
|
- feat: 新功能
|
||||||
|
- fix: 修复bug
|
||||||
|
- docs: 文档更新
|
||||||
|
- style: 代码格式调整
|
||||||
|
- refactor: 代码重构
|
||||||
|
|
||||||
|
## 📡 API配置
|
||||||
|
|
||||||
|
后端API地址:http://localhost:3001/api/v1
|
||||||
|
|
||||||
|
代理配置已在`vite.config.ts`中设置:
|
||||||
|
```typescript
|
||||||
|
proxy: {
|
||||||
|
'/api': {
|
||||||
|
target: 'http://localhost:3001',
|
||||||
|
changeOrigin: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🎯 智能体列表
|
||||||
|
|
||||||
|
1. 📊 选题评价
|
||||||
|
2. 🔍 PICOS构建
|
||||||
|
3. 📚 文献检索
|
||||||
|
4. 🎯 文献筛选
|
||||||
|
5. 📋 数据提取
|
||||||
|
6. ⚖️ 偏倚评价
|
||||||
|
7. 📈 Meta分析
|
||||||
|
8. 🌲 森林图绘制
|
||||||
|
9. 💡 结果解读
|
||||||
|
10. 📝 方案撰写
|
||||||
|
11. ✍️ 文章撰写
|
||||||
|
12. 📬 投稿辅助
|
||||||
|
|
||||||
|
## 📞 技术支持
|
||||||
|
|
||||||
|
详见项目根目录的完整文档:
|
||||||
|
- `docs/01-设计文档/API设计规范.md`
|
||||||
|
- `docs/02-开发规范/代码规范.md`
|
||||||
|
- `docs/04-开发计划/开发里程碑.md`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Day 6 完成!前端基础架构搭建完毕!** 🎉
|
||||||
|
|
||||||
14
frontend/index.html
Normal file
14
frontend/index.html
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>AI临床研究平台</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
|
<script type="module" src="/src/main.tsx"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
5828
frontend/package-lock.json
generated
Normal file
5828
frontend/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
36
frontend/package.json
Normal file
36
frontend/package.json
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
{
|
||||||
|
"name": "ai-clinical-frontend",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"type": "module",
|
||||||
|
"description": "AI Clinical Research Platform - Frontend",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "tsc && vite build",
|
||||||
|
"preview": "vite preview",
|
||||||
|
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"react": "^18.3.1",
|
||||||
|
"react-dom": "^18.3.1",
|
||||||
|
"react-router-dom": "^6.28.0",
|
||||||
|
"antd": "^5.22.5",
|
||||||
|
"axios": "^1.7.9",
|
||||||
|
"@ant-design/icons": "^5.5.2"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/react": "^18.3.18",
|
||||||
|
"@types/react-dom": "^18.3.5",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^8.20.0",
|
||||||
|
"@typescript-eslint/parser": "^8.20.0",
|
||||||
|
"@vitejs/plugin-react": "^4.3.4",
|
||||||
|
"autoprefixer": "^10.4.20",
|
||||||
|
"eslint": "^9.18.0",
|
||||||
|
"eslint-plugin-react-hooks": "^5.1.0",
|
||||||
|
"eslint-plugin-react-refresh": "^0.4.16",
|
||||||
|
"postcss": "^8.4.49",
|
||||||
|
"tailwindcss": "^3.4.17",
|
||||||
|
"typescript": "^5.7.3",
|
||||||
|
"vite": "^6.0.7"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
7
frontend/postcss.config.js
Normal file
7
frontend/postcss.config.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
export default {
|
||||||
|
plugins: {
|
||||||
|
tailwindcss: {},
|
||||||
|
autoprefixer: {},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
19
frontend/src/App.tsx
Normal file
19
frontend/src/App.tsx
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import { Routes, Route, Navigate } from 'react-router-dom'
|
||||||
|
import MainLayout from './layouts/MainLayout'
|
||||||
|
import HomePage from './pages/HomePage'
|
||||||
|
import AgentPage from './pages/AgentPage'
|
||||||
|
|
||||||
|
function App() {
|
||||||
|
return (
|
||||||
|
<Routes>
|
||||||
|
<Route path="/" element={<MainLayout />}>
|
||||||
|
<Route index element={<HomePage />} />
|
||||||
|
<Route path="agent/:agentId" element={<AgentPage />} />
|
||||||
|
<Route path="*" element={<Navigate to="/" replace />} />
|
||||||
|
</Route>
|
||||||
|
</Routes>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default App
|
||||||
|
|
||||||
23
frontend/src/api/index.ts
Normal file
23
frontend/src/api/index.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import request from './request'
|
||||||
|
|
||||||
|
// 健康检查
|
||||||
|
export const healthCheck = () => {
|
||||||
|
return request.get('/health')
|
||||||
|
}
|
||||||
|
|
||||||
|
// API信息
|
||||||
|
export const getApiInfo = () => {
|
||||||
|
return request.get('/')
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Day 9+ 添加更多API
|
||||||
|
// - 项目管理API
|
||||||
|
// - 对话API
|
||||||
|
// - 知识库API
|
||||||
|
// - 用户API
|
||||||
|
|
||||||
|
export default {
|
||||||
|
healthCheck,
|
||||||
|
getApiInfo,
|
||||||
|
}
|
||||||
|
|
||||||
59
frontend/src/api/request.ts
Normal file
59
frontend/src/api/request.ts
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import axios, { AxiosInstance, AxiosResponse } from 'axios'
|
||||||
|
|
||||||
|
// 创建axios实例
|
||||||
|
const request: AxiosInstance = axios.create({
|
||||||
|
baseURL: '/api/v1',
|
||||||
|
timeout: 30000,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
// 请求拦截器
|
||||||
|
request.interceptors.request.use(
|
||||||
|
(config) => {
|
||||||
|
// 添加token
|
||||||
|
const token = localStorage.getItem('token')
|
||||||
|
if (token && config.headers) {
|
||||||
|
config.headers.Authorization = `Bearer ${token}`
|
||||||
|
}
|
||||||
|
return config
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
return Promise.reject(error)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// 响应拦截器
|
||||||
|
request.interceptors.response.use(
|
||||||
|
(response: AxiosResponse) => {
|
||||||
|
return response.data
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
if (error.response) {
|
||||||
|
const { status } = error.response
|
||||||
|
switch (status) {
|
||||||
|
case 401:
|
||||||
|
// 未授权,清除token并跳转登录
|
||||||
|
localStorage.removeItem('token')
|
||||||
|
window.location.href = '/login'
|
||||||
|
break
|
||||||
|
case 403:
|
||||||
|
console.error('没有权限访问该资源')
|
||||||
|
break
|
||||||
|
case 404:
|
||||||
|
console.error('请求的资源不存在')
|
||||||
|
break
|
||||||
|
case 500:
|
||||||
|
console.error('服务器错误')
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
console.error(`请求错误: ${status}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Promise.reject(error)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
export default request
|
||||||
|
|
||||||
23
frontend/src/index.css
Normal file
23
frontend/src/index.css
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
|
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Hiragino Sans GB',
|
||||||
|
'Microsoft YaHei', 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
}
|
||||||
|
|
||||||
|
#root {
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
168
frontend/src/layouts/MainLayout.tsx
Normal file
168
frontend/src/layouts/MainLayout.tsx
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
import { useState } from 'react'
|
||||||
|
import { Outlet, useNavigate, useLocation } from 'react-router-dom'
|
||||||
|
import { Layout, Menu, Avatar, Dropdown, Button } from 'antd'
|
||||||
|
import {
|
||||||
|
MenuFoldOutlined,
|
||||||
|
MenuUnfoldOutlined,
|
||||||
|
HomeOutlined,
|
||||||
|
ExperimentOutlined,
|
||||||
|
UserOutlined,
|
||||||
|
SettingOutlined,
|
||||||
|
LogoutOutlined,
|
||||||
|
} from '@ant-design/icons'
|
||||||
|
import type { MenuProps } from 'antd'
|
||||||
|
|
||||||
|
const { Header, Sider, Content } = Layout
|
||||||
|
|
||||||
|
// 12个智能体配置
|
||||||
|
const AGENTS = [
|
||||||
|
{ id: 'topic-evaluation', name: '选题评价', icon: '📊' },
|
||||||
|
{ id: 'picos-construction', name: 'PICOS构建', icon: '🔍' },
|
||||||
|
{ id: 'literature-search', name: '文献检索', icon: '📚' },
|
||||||
|
{ id: 'literature-screening', name: '文献筛选', icon: '🎯' },
|
||||||
|
{ id: 'data-extraction', name: '数据提取', icon: '📋' },
|
||||||
|
{ id: 'bias-assessment', name: '偏倚评价', icon: '⚖️' },
|
||||||
|
{ id: 'meta-analysis', name: 'Meta分析', icon: '📈' },
|
||||||
|
{ id: 'forest-plot', name: '森林图绘制', icon: '🌲' },
|
||||||
|
{ id: 'results-interpretation', name: '结果解读', icon: '💡' },
|
||||||
|
{ id: 'protocol-writing', name: '方案撰写', icon: '📝' },
|
||||||
|
{ id: 'article-writing', name: '文章撰写', icon: '✍️' },
|
||||||
|
{ id: 'submission-assistance', name: '投稿辅助', icon: '📬' },
|
||||||
|
]
|
||||||
|
|
||||||
|
const MainLayout = () => {
|
||||||
|
const [collapsed, setCollapsed] = useState(false)
|
||||||
|
const navigate = useNavigate()
|
||||||
|
const location = useLocation()
|
||||||
|
|
||||||
|
// 菜单项配置
|
||||||
|
const menuItems: MenuProps['items'] = [
|
||||||
|
{
|
||||||
|
key: '/',
|
||||||
|
icon: <HomeOutlined />,
|
||||||
|
label: '首页',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'agents',
|
||||||
|
icon: <ExperimentOutlined />,
|
||||||
|
label: '智能体',
|
||||||
|
children: AGENTS.map((agent) => ({
|
||||||
|
key: `/agent/${agent.id}`,
|
||||||
|
label: `${agent.icon} ${agent.name}`,
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
// 用户下拉菜单
|
||||||
|
const userMenuItems: MenuProps['items'] = [
|
||||||
|
{
|
||||||
|
key: 'profile',
|
||||||
|
icon: <UserOutlined />,
|
||||||
|
label: '个人中心',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'settings',
|
||||||
|
icon: <SettingOutlined />,
|
||||||
|
label: '设置',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'divider',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'logout',
|
||||||
|
icon: <LogoutOutlined />,
|
||||||
|
label: '退出登录',
|
||||||
|
danger: true,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const handleMenuClick = ({ key }: { key: string }) => {
|
||||||
|
navigate(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleUserMenuClick = ({ key }: { key: string }) => {
|
||||||
|
if (key === 'logout') {
|
||||||
|
console.log('退出登录')
|
||||||
|
// TODO: 实现登出逻辑
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Layout style={{ height: '100vh' }}>
|
||||||
|
{/* 侧边栏 */}
|
||||||
|
<Sider trigger={null} collapsible collapsed={collapsed} theme="light">
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
height: 64,
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
fontSize: collapsed ? 18 : 20,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
color: '#1890ff',
|
||||||
|
borderBottom: '1px solid #f0f0f0',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{collapsed ? '🏥' : '🏥 AI临床研究'}
|
||||||
|
</div>
|
||||||
|
<Menu
|
||||||
|
mode="inline"
|
||||||
|
selectedKeys={[location.pathname]}
|
||||||
|
defaultOpenKeys={['agents']}
|
||||||
|
items={menuItems}
|
||||||
|
onClick={handleMenuClick}
|
||||||
|
style={{ borderRight: 0 }}
|
||||||
|
/>
|
||||||
|
</Sider>
|
||||||
|
|
||||||
|
{/* 主内容区 */}
|
||||||
|
<Layout>
|
||||||
|
{/* 头部 */}
|
||||||
|
<Header
|
||||||
|
style={{
|
||||||
|
padding: '0 24px',
|
||||||
|
background: '#fff',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
borderBottom: '1px solid #f0f0f0',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
type="text"
|
||||||
|
icon={collapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
|
||||||
|
onClick={() => setCollapsed(!collapsed)}
|
||||||
|
style={{
|
||||||
|
fontSize: '16px',
|
||||||
|
width: 64,
|
||||||
|
height: 64,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Dropdown menu={{ items: userMenuItems, onClick: handleUserMenuClick }} placement="bottomRight">
|
||||||
|
<div style={{ cursor: 'pointer', display: 'flex', alignItems: 'center', gap: 8 }}>
|
||||||
|
<Avatar icon={<UserOutlined />} />
|
||||||
|
<span>研究员</span>
|
||||||
|
</div>
|
||||||
|
</Dropdown>
|
||||||
|
</Header>
|
||||||
|
|
||||||
|
{/* 内容区 */}
|
||||||
|
<Content
|
||||||
|
style={{
|
||||||
|
margin: '24px',
|
||||||
|
padding: 24,
|
||||||
|
background: '#fff',
|
||||||
|
borderRadius: 8,
|
||||||
|
overflow: 'auto',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Outlet />
|
||||||
|
</Content>
|
||||||
|
</Layout>
|
||||||
|
</Layout>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MainLayout
|
||||||
|
|
||||||
18
frontend/src/main.tsx
Normal file
18
frontend/src/main.tsx
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import ReactDOM from 'react-dom/client'
|
||||||
|
import { BrowserRouter } from 'react-router-dom'
|
||||||
|
import { ConfigProvider } from 'antd'
|
||||||
|
import zhCN from 'antd/locale/zh_CN'
|
||||||
|
import App from './App'
|
||||||
|
import './index.css'
|
||||||
|
|
||||||
|
ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||||
|
<React.StrictMode>
|
||||||
|
<ConfigProvider locale={zhCN}>
|
||||||
|
<BrowserRouter>
|
||||||
|
<App />
|
||||||
|
</BrowserRouter>
|
||||||
|
</ConfigProvider>
|
||||||
|
</React.StrictMode>,
|
||||||
|
)
|
||||||
|
|
||||||
92
frontend/src/pages/AgentPage.tsx
Normal file
92
frontend/src/pages/AgentPage.tsx
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
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: '评估研究选题的价值和可行性' },
|
||||||
|
'picos-construction': { name: 'PICOS构建', icon: '🔍', desc: '构建研究问题的PICOS框架' },
|
||||||
|
'literature-search': { name: '文献检索', icon: '📚', desc: '系统化检索相关医学文献' },
|
||||||
|
'literature-screening': { name: '文献筛选', icon: '🎯', desc: '根据纳入排除标准筛选文献' },
|
||||||
|
'data-extraction': { name: '数据提取', icon: '📋', desc: '从文献中提取关键数据' },
|
||||||
|
'bias-assessment': { name: '偏倚评价', icon: '⚖️', desc: '评估研究偏倚风险' },
|
||||||
|
'meta-analysis': { name: 'Meta分析', icon: '📈', desc: '进行统计学meta分析' },
|
||||||
|
'forest-plot': { name: '森林图绘制', icon: '🌲', desc: '绘制meta分析森林图' },
|
||||||
|
'results-interpretation': { name: '结果解读', icon: '💡', desc: '解读分析结果的临床意义' },
|
||||||
|
'protocol-writing': { name: '方案撰写', icon: '📝', desc: '撰写研究方案' },
|
||||||
|
'article-writing': { name: '文章撰写', icon: '✍️', desc: '撰写学术论文' },
|
||||||
|
'submission-assistance': { 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
|
||||||
|
|
||||||
72
frontend/src/pages/HomePage.tsx
Normal file
72
frontend/src/pages/HomePage.tsx
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
import { Card, Row, Col, Typography, Button } from 'antd'
|
||||||
|
import { useNavigate } from 'react-router-dom'
|
||||||
|
import { ExperimentOutlined, RocketOutlined } from '@ant-design/icons'
|
||||||
|
|
||||||
|
const { Title, Paragraph } = Typography
|
||||||
|
|
||||||
|
const AGENTS = [
|
||||||
|
{ id: 'topic-evaluation', name: '选题评价', icon: '📊', desc: '评估研究选题的价值和可行性' },
|
||||||
|
{ id: 'picos-construction', name: 'PICOS构建', icon: '🔍', desc: '构建研究问题的PICOS框架' },
|
||||||
|
{ id: 'literature-search', name: '文献检索', icon: '📚', desc: '系统化检索相关医学文献' },
|
||||||
|
{ id: 'literature-screening', name: '文献筛选', icon: '🎯', desc: '根据纳入排除标准筛选文献' },
|
||||||
|
{ id: 'data-extraction', name: '数据提取', icon: '📋', desc: '从文献中提取关键数据' },
|
||||||
|
{ id: 'bias-assessment', name: '偏倚评价', icon: '⚖️', desc: '评估研究偏倚风险' },
|
||||||
|
{ id: 'meta-analysis', name: 'Meta分析', icon: '📈', desc: '进行统计学meta分析' },
|
||||||
|
{ id: 'forest-plot', name: '森林图绘制', icon: '🌲', desc: '绘制meta分析森林图' },
|
||||||
|
{ id: 'results-interpretation', name: '结果解读', icon: '💡', desc: '解读分析结果的临床意义' },
|
||||||
|
{ id: 'protocol-writing', name: '方案撰写', icon: '📝', desc: '撰写研究方案' },
|
||||||
|
{ id: 'article-writing', name: '文章撰写', icon: '✍️', desc: '撰写学术论文' },
|
||||||
|
{ id: 'submission-assistance', name: '投稿辅助', icon: '📬', desc: '辅助期刊投稿' },
|
||||||
|
]
|
||||||
|
|
||||||
|
const HomePage = () => {
|
||||||
|
const navigate = useNavigate()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{/* 欢迎区域 */}
|
||||||
|
<Card style={{ marginBottom: 24, background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)' }}>
|
||||||
|
<div style={{ color: 'white', padding: '20px 0' }}>
|
||||||
|
<Title level={2} style={{ color: 'white', marginBottom: 16 }}>
|
||||||
|
<RocketOutlined /> 欢迎使用 AI临床研究平台
|
||||||
|
</Title>
|
||||||
|
<Paragraph style={{ color: 'white', fontSize: 16, marginBottom: 0 }}>
|
||||||
|
基于大语言模型的智能临床研究助手,覆盖研究全流程的12个智能体
|
||||||
|
</Paragraph>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
{/* 智能体卡片 */}
|
||||||
|
<Title level={3} style={{ marginBottom: 16 }}>
|
||||||
|
<ExperimentOutlined /> 智能体工具箱
|
||||||
|
</Title>
|
||||||
|
<Row gutter={[16, 16]}>
|
||||||
|
{AGENTS.map((agent) => (
|
||||||
|
<Col xs={24} sm={12} md={8} lg={6} key={agent.id}>
|
||||||
|
<Card
|
||||||
|
hoverable
|
||||||
|
style={{ height: '100%' }}
|
||||||
|
onClick={() => navigate(`/agent/${agent.id}`)}
|
||||||
|
>
|
||||||
|
<div style={{ textAlign: 'center' }}>
|
||||||
|
<div style={{ fontSize: 48, marginBottom: 12 }}>{agent.icon}</div>
|
||||||
|
<Title level={4} style={{ marginBottom: 8 }}>
|
||||||
|
{agent.name}
|
||||||
|
</Title>
|
||||||
|
<Paragraph type="secondary" style={{ marginBottom: 16, minHeight: 44 }}>
|
||||||
|
{agent.desc}
|
||||||
|
</Paragraph>
|
||||||
|
<Button type="primary" block>
|
||||||
|
开始使用
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
</Col>
|
||||||
|
))}
|
||||||
|
</Row>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default HomePage
|
||||||
|
|
||||||
89
frontend/src/types/index.ts
Normal file
89
frontend/src/types/index.ts
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
// 用户类型
|
||||||
|
export interface User {
|
||||||
|
id: string
|
||||||
|
email: string
|
||||||
|
name?: string
|
||||||
|
avatarUrl?: string
|
||||||
|
role: 'user' | 'admin'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 项目类型
|
||||||
|
export interface Project {
|
||||||
|
id: string
|
||||||
|
userId: string
|
||||||
|
name: string
|
||||||
|
description: string
|
||||||
|
conversationCount: number
|
||||||
|
createdAt: string
|
||||||
|
updatedAt: string
|
||||||
|
}
|
||||||
|
|
||||||
|
// 会话类型
|
||||||
|
export interface Conversation {
|
||||||
|
id: string
|
||||||
|
userId: string
|
||||||
|
projectId?: string
|
||||||
|
agentId: string
|
||||||
|
title: string
|
||||||
|
modelName: string
|
||||||
|
messageCount: number
|
||||||
|
totalTokens: number
|
||||||
|
createdAt: string
|
||||||
|
updatedAt: string
|
||||||
|
}
|
||||||
|
|
||||||
|
// 消息类型
|
||||||
|
export interface Message {
|
||||||
|
id: string
|
||||||
|
conversationId: string
|
||||||
|
role: 'user' | 'assistant'
|
||||||
|
content: string
|
||||||
|
metadata?: {
|
||||||
|
kbReferences?: string[]
|
||||||
|
citations?: any[]
|
||||||
|
modelParams?: any
|
||||||
|
}
|
||||||
|
tokens?: number
|
||||||
|
isPinned: boolean
|
||||||
|
createdAt: string
|
||||||
|
}
|
||||||
|
|
||||||
|
// 知识库类型
|
||||||
|
export interface KnowledgeBase {
|
||||||
|
id: string
|
||||||
|
userId: string
|
||||||
|
name: string
|
||||||
|
description?: string
|
||||||
|
difyDatasetId: string
|
||||||
|
fileCount: number
|
||||||
|
totalSizeBytes: number
|
||||||
|
createdAt: string
|
||||||
|
updatedAt: string
|
||||||
|
}
|
||||||
|
|
||||||
|
// 文档类型
|
||||||
|
export interface Document {
|
||||||
|
id: string
|
||||||
|
kbId: string
|
||||||
|
userId: string
|
||||||
|
filename: string
|
||||||
|
fileType: string
|
||||||
|
fileSizeBytes: number
|
||||||
|
fileUrl: string
|
||||||
|
difyDocumentId: string
|
||||||
|
status: 'uploading' | 'processing' | 'completed' | 'failed'
|
||||||
|
progress: number
|
||||||
|
errorMessage?: string
|
||||||
|
segmentsCount?: number
|
||||||
|
tokensCount?: number
|
||||||
|
uploadedAt: string
|
||||||
|
processedAt?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
// API响应类型
|
||||||
|
export interface ApiResponse<T = any> {
|
||||||
|
success: boolean
|
||||||
|
data: T
|
||||||
|
message?: string
|
||||||
|
}
|
||||||
|
|
||||||
15
frontend/tailwind.config.js
Normal file
15
frontend/tailwind.config.js
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
/** @type {import('tailwindcss').Config} */
|
||||||
|
export default {
|
||||||
|
content: [
|
||||||
|
"./index.html",
|
||||||
|
"./src/**/*.{js,ts,jsx,tsx}",
|
||||||
|
],
|
||||||
|
theme: {
|
||||||
|
extend: {},
|
||||||
|
},
|
||||||
|
plugins: [],
|
||||||
|
corePlugins: {
|
||||||
|
preflight: false, // 禁用 Tailwind 的基础样式重置,避免与 Ant Design 冲突
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
32
frontend/tsconfig.json
Normal file
32
frontend/tsconfig.json
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2020",
|
||||||
|
"useDefineForClassFields": true,
|
||||||
|
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||||
|
"module": "ESNext",
|
||||||
|
"skipLibCheck": true,
|
||||||
|
|
||||||
|
/* Bundler mode */
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
|
||||||
|
/* Linting */
|
||||||
|
"strict": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
|
||||||
|
/* Path alias */
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["src/*"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": ["src"],
|
||||||
|
"references": [{ "path": "./tsconfig.node.json" }]
|
||||||
|
}
|
||||||
|
|
||||||
12
frontend/tsconfig.node.json
Normal file
12
frontend/tsconfig.node.json
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"composite": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"strict": true
|
||||||
|
},
|
||||||
|
"include": ["vite.config.ts"]
|
||||||
|
}
|
||||||
|
|
||||||
23
frontend/vite.config.ts
Normal file
23
frontend/vite.config.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { defineConfig } from 'vite'
|
||||||
|
import react from '@vitejs/plugin-react'
|
||||||
|
import path from 'path'
|
||||||
|
|
||||||
|
// https://vitejs.dev/config/
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [react()],
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
'@': path.resolve(__dirname, './src'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
server: {
|
||||||
|
port: 3000,
|
||||||
|
proxy: {
|
||||||
|
'/api': {
|
||||||
|
target: 'http://localhost:3001',
|
||||||
|
changeOrigin: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
13
frontend/启动前端.bat
Normal file
13
frontend/启动前端.bat
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
@echo off
|
||||||
|
chcp 65001 >nul
|
||||||
|
echo ====================================
|
||||||
|
echo AI临床研究平台 - 前端开发服务器
|
||||||
|
echo ====================================
|
||||||
|
echo.
|
||||||
|
cd /d %~dp0
|
||||||
|
echo 正在启动前端开发服务器...
|
||||||
|
echo 访问地址: http://localhost:3000
|
||||||
|
echo.
|
||||||
|
call npm run dev
|
||||||
|
pause
|
||||||
|
|
||||||
Reference in New Issue
Block a user