Files
AIclinicalresearch/frontend-v2/src/modules/asl/components/ASLLayout.tsx
HaHafeng 2e8699c217 feat(asl): Week 2 Day 2 - Excel import with template download and intelligent dedup
Features:
- feat: Excel template generation and download (with examples)
- feat: Excel file parsing in memory (cloud-native, no disk write)
- feat: Field validation (title + abstract required)
- feat: Smart deduplication (DOI priority + Title fallback)
- feat: Literature preview table with statistics
- feat: Complete submission flow (create project + import literatures)

Components:
- feat: Create excelUtils.ts with full Excel processing toolkit
- feat: Enhance TitleScreeningSettings page with upload/preview/submit
- feat: Update API interface signatures and export unified aslApi object

Dependencies:
- chore: Add xlsx library for Excel file processing

Ref: Week 2 Frontend Development - Day 2
Scope: ASL Module MVP - Title Abstract Screening
Cloud-Native: Memory parsing, no file persistence
2025-11-19 10:24:47 +08:00

154 lines
3.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* ASL模块布局组件
*
* 左侧导航栏 + 右侧内容区
* 参考原型AI智能文献-标题摘要初筛原型.html
*/
import { Layout, Menu } from 'antd';
import { Outlet, useNavigate, useLocation } from 'react-router-dom';
import {
FileTextOutlined,
SearchOutlined,
FolderOpenOutlined,
FilterOutlined,
FileSearchOutlined,
DatabaseOutlined,
BarChartOutlined,
SettingOutlined,
CheckSquareOutlined,
UnorderedListOutlined,
} from '@ant-design/icons';
import type { MenuProps } from 'antd';
const { Sider, Content } = Layout;
type MenuItem = Required<MenuProps>['items'][number];
const ASLLayout = () => {
const navigate = useNavigate();
const location = useLocation();
// 菜单项配置
const menuItems: MenuItem[] = [
{
key: 'research-plan',
icon: <FileTextOutlined />,
label: '1. 研究方案生成',
disabled: true,
title: '敬请期待'
},
{
key: 'literature-search',
icon: <SearchOutlined />,
label: '2. 智能文献检索',
disabled: true,
title: '敬请期待'
},
{
key: 'literature-management',
icon: <FolderOpenOutlined />,
label: '3. 文献管理',
disabled: true,
title: '敬请期待'
},
{
key: 'title-screening',
icon: <FilterOutlined />,
label: '4. 标题摘要初筛',
children: [
{
key: '/literature/screening/title/settings',
icon: <SettingOutlined />,
label: '设置与启动',
},
{
key: '/literature/screening/title/workbench',
icon: <CheckSquareOutlined />,
label: '审核工作台',
},
{
key: '/literature/screening/title/results',
icon: <UnorderedListOutlined />,
label: '初筛结果',
},
],
},
{
key: 'fulltext-screening',
icon: <FileSearchOutlined />,
label: '5. 全文复筛',
disabled: true,
title: '敬请期待'
},
{
key: 'data-extraction',
icon: <DatabaseOutlined />,
label: '6. 全文解析与数据提取',
disabled: true,
title: '敬请期待'
},
{
key: 'data-analysis',
icon: <BarChartOutlined />,
label: '7. 数据综合分析与报告',
disabled: true,
title: '敬请期待'
},
];
// 处理菜单点击
const handleMenuClick: MenuProps['onClick'] = ({ key }) => {
if (key.startsWith('/')) {
navigate(key);
}
};
// 获取当前选中的菜单项和展开的子菜单
const currentPath = location.pathname;
const selectedKeys = [currentPath];
const openKeys = currentPath.includes('screening/title') ? ['title-screening'] : [];
return (
<Layout className="h-screen">
{/* 左侧导航栏 */}
<Sider
width={250}
className="bg-gray-50 border-r border-gray-200"
theme="light"
>
<div className="p-4 border-b border-gray-200">
<h2 className="text-lg font-bold text-gray-800">
<FilterOutlined className="mr-2" />
AI智能文献
</h2>
<p className="text-xs text-gray-500 mt-1">
MVP
</p>
</div>
<Menu
mode="inline"
selectedKeys={selectedKeys}
defaultOpenKeys={openKeys}
items={menuItems}
onClick={handleMenuClick}
className="border-none"
style={{ height: 'calc(100vh - 80px)', overflowY: 'auto' }}
/>
</Sider>
{/* 右侧内容区 */}
<Layout>
<Content className="bg-white overflow-auto">
<Outlet />
</Content>
</Layout>
</Layout>
);
};
export default ASLLayout;