268 lines
10 KiB
TypeScript
268 lines
10 KiB
TypeScript
import { ReactNode, useState } from 'react'
|
||
import { Link, useLocation, useNavigate } from 'react-router-dom'
|
||
import { useAuth } from '../contexts/AuthContext'
|
||
import './DashboardLayout.css'
|
||
|
||
interface DashboardLayoutProps {
|
||
children: ReactNode
|
||
}
|
||
|
||
function DashboardLayout({ children }: DashboardLayoutProps) {
|
||
const { user, logout } = useAuth()
|
||
const location = useLocation()
|
||
const navigate = useNavigate()
|
||
const [sidebarOpen, setSidebarOpen] = useState(true)
|
||
const [rmoMenuOpen, setRmoMenuOpen] = useState(false)
|
||
const [responsibilityMenuOpen, setResponsibilityMenuOpen] = useState(false)
|
||
const [systemMenuOpen, setSystemMenuOpen] = useState(false)
|
||
|
||
const isActive = (path: string) => location.pathname === path
|
||
const isActiveParent = (path: string) => location.pathname.startsWith(path)
|
||
const isActiveParentMulti = (paths: string[]) => paths.some(path => location.pathname.startsWith(path))
|
||
|
||
// 根据角色判断权限
|
||
const isPolicyHolder = user?.role === '投保人'
|
||
const isInsurer = user?.role === '保险人'
|
||
|
||
// 投保人可见:项目列表、理赔进度、智能工具
|
||
// 保险人可见:询价列表、理赔进度
|
||
|
||
const handleLogout = () => {
|
||
logout()
|
||
navigate('/login')
|
||
}
|
||
|
||
return (
|
||
<div className="dashboard-layout">
|
||
{/* 侧边栏 */}
|
||
<aside className={`dashboard-sidebar ${sidebarOpen ? 'open' : 'closed'}`}>
|
||
<div className="sidebar-header">
|
||
<Link to="/dashboard" className="sidebar-logo">
|
||
<h2>RMO</h2>
|
||
<span>工作台</span>
|
||
</Link>
|
||
<button
|
||
className="sidebar-toggle"
|
||
onClick={() => setSidebarOpen(!sidebarOpen)}
|
||
aria-label="切换侧边栏"
|
||
>
|
||
{sidebarOpen ? '◀' : '▶'}
|
||
</button>
|
||
</div>
|
||
|
||
<nav className="sidebar-nav">
|
||
<Link
|
||
to="/dashboard"
|
||
className={`nav-item ${isActive('/dashboard') && !isActiveParentMulti(['/dashboard/projects', '/dashboard/project-quotes', '/dashboard/inquiries', '/dashboard/claims', '/dashboard/tools']) ? 'active' : ''}`}
|
||
>
|
||
<span className="nav-icon">📊</span>
|
||
{sidebarOpen && <span className="nav-text">工作台</span>}
|
||
</Link>
|
||
|
||
{/* 投保人可见:项目报价、项目列表 */}
|
||
{isPolicyHolder && (
|
||
<>
|
||
<Link
|
||
to="/dashboard/project-quotes"
|
||
className={`nav-item ${isActiveParent('/dashboard/project-quotes') ? 'active' : ''}`}
|
||
>
|
||
<span className="nav-icon">💰</span>
|
||
{sidebarOpen && <span className="nav-text">项目报价</span>}
|
||
</Link>
|
||
<Link
|
||
to="/dashboard/projects"
|
||
className={`nav-item ${isActiveParent('/dashboard/projects') ? 'active' : ''}`}
|
||
>
|
||
<span className="nav-icon">📋</span>
|
||
{sidebarOpen && <span className="nav-text">项目列表</span>}
|
||
</Link>
|
||
</>
|
||
)}
|
||
|
||
{/* 保险人可见:询价列表 */}
|
||
{isInsurer && (
|
||
<Link
|
||
to="/dashboard/inquiries"
|
||
className={`nav-item ${isActiveParent('/dashboard/inquiries') ? 'active' : ''}`}
|
||
>
|
||
<span className="nav-icon">💼</span>
|
||
{sidebarOpen && <span className="nav-text">询价列表</span>}
|
||
</Link>
|
||
)}
|
||
|
||
{/* 投保人、保险人可见:理赔进度 */}
|
||
{(isPolicyHolder || isInsurer) && (
|
||
<Link
|
||
to="/dashboard/claims"
|
||
className={`nav-item ${isActiveParent('/dashboard/claims') ? 'active' : ''}`}
|
||
>
|
||
<span className="nav-icon">📝</span>
|
||
{sidebarOpen && <span className="nav-text">理赔进度</span>}
|
||
</Link>
|
||
)}
|
||
|
||
{/* 智能工具:所有登录用户可见 */}
|
||
<div className="nav-group">
|
||
<div className={`nav-item ${isActiveParentMulti(['/dashboard/tools']) ? 'active' : ''}`}>
|
||
<span className="nav-icon">🛠️</span>
|
||
{sidebarOpen && <span className="nav-text">智能工具</span>}
|
||
</div>
|
||
{isActiveParentMulti(['/dashboard/tools']) && (
|
||
<div className="nav-submenu">
|
||
{/* 保费测算工具:所有登录用户可见 */}
|
||
<Link
|
||
to="/dashboard/tools/premium-calculator"
|
||
className={`nav-subitem ${isActive('/dashboard/tools/premium-calculator') ? 'active' : ''}`}
|
||
>
|
||
保费测算工具
|
||
</Link>
|
||
{/* ICF智能修改:仅投保人可见 */}
|
||
{isPolicyHolder && (
|
||
<Link
|
||
to="/dashboard/tools/icf-editor"
|
||
className={`nav-subitem ${isActive('/dashboard/tools/icf-editor') ? 'active' : ''}`}
|
||
>
|
||
ICF智能修改
|
||
</Link>
|
||
)}
|
||
{/* 方案风险评分:仅投保人可见 */}
|
||
{isPolicyHolder && (
|
||
<Link
|
||
to="/dashboard/tools/risk-scoring"
|
||
className={`nav-subitem ${isActive('/dashboard/tools/risk-scoring') ? 'active' : ''}`}
|
||
>
|
||
方案风险评分
|
||
</Link>
|
||
)}
|
||
{/* 方案风险评估:所有登录用户可见 */}
|
||
<Link
|
||
to="/dashboard/tools/protocol-risk"
|
||
className={`nav-subitem ${isActive('/dashboard/tools/protocol-risk') ? 'active' : ''}`}
|
||
>
|
||
方案风险评估
|
||
</Link>
|
||
{/* 药安查:所有登录用户可见 */}
|
||
<Link
|
||
to="/dashboard/tools/drug-safety"
|
||
className={`nav-subitem ${isActive('/dashboard/tools/drug-safety') ? 'active' : ''}`}
|
||
>
|
||
药安查
|
||
</Link>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</nav>
|
||
</aside>
|
||
|
||
{/* 主内容区 */}
|
||
<div className="dashboard-main">
|
||
{/* 顶部栏 */}
|
||
<header className="dashboard-header">
|
||
<div className="header-left">
|
||
<Link to="/" className="header-logo">
|
||
<h2>RMO</h2>
|
||
<span>生命科学风险管理</span>
|
||
</Link>
|
||
<nav className="header-nav">
|
||
<Link to="/" className={isActive('/') ? 'active' : ''}>
|
||
首页
|
||
</Link>
|
||
|
||
{/* 风险职责 */}
|
||
<div
|
||
className="nav-dropdown"
|
||
onMouseEnter={() => setResponsibilityMenuOpen(true)}
|
||
onMouseLeave={() => setResponsibilityMenuOpen(false)}
|
||
>
|
||
<Link
|
||
to="/RMO-About-Responsibility"
|
||
className={isActiveParentMulti(['/RMO-About-Responsibility', '/sponsor', '/Holder', '/institution', '/service-provider', '/participant']) ? 'active' : ''}
|
||
>
|
||
风险职责
|
||
</Link>
|
||
{responsibilityMenuOpen && (
|
||
<div className="dropdown-menu">
|
||
<Link to="/sponsor">申办者职责</Link>
|
||
<Link to="/Holder">持有人职责</Link>
|
||
<Link to="/participant">受试者专区</Link>
|
||
<Link to="/institution">研究中心</Link>
|
||
<Link to="/service-provider">CXO职责</Link>
|
||
</div>
|
||
)}
|
||
</div>
|
||
|
||
{/* 临床试验 */}
|
||
<div
|
||
className="nav-dropdown"
|
||
onMouseEnter={() => setRmoMenuOpen(true)}
|
||
onMouseLeave={() => setRmoMenuOpen(false)}
|
||
>
|
||
<Link
|
||
to="/rmo-mode"
|
||
className={isActiveParentMulti(['/rmo-mode', '/insurance', '/guarantee', '/insurance-guarantee']) ? 'active' : ''}
|
||
>
|
||
</Link>
|
||
{rmoMenuOpen && (
|
||
<div className="dropdown-menu">
|
||
<Link to="/rmo-mode/insurance">保险方案</Link>
|
||
<Link to="/rmo-mode/guarantee">保证方案</Link>
|
||
<Link to="/rmo-mode/insurance-guarantee">保险保证</Link>
|
||
</div>
|
||
)}
|
||
</div>
|
||
|
||
{/* 上市应用 */}
|
||
<Link to="/post-market" className={isActive('/post-market') ? 'active' : ''}>
|
||
上市应用
|
||
</Link>
|
||
|
||
{/* 海外风险 */}
|
||
<Link to="/overseas" className={isActive('/overseas') ? 'active' : ''}>
|
||
海外风险
|
||
</Link>
|
||
|
||
{/* 资源中心 */}
|
||
<div
|
||
className="nav-dropdown"
|
||
onMouseEnter={() => setSystemMenuOpen(true)}
|
||
onMouseLeave={() => setSystemMenuOpen(false)}
|
||
>
|
||
<Link
|
||
to="/system-management"
|
||
className={isActiveParentMulti(['/system-management', '/faq']) ? 'active' : ''}
|
||
>
|
||
资源中心
|
||
</Link>
|
||
{systemMenuOpen && (
|
||
<div className="dropdown-menu">
|
||
<Link to="/system-management/laws">法律法规</Link>
|
||
<Link to="/system-management/practice-guide">实践指南</Link>
|
||
<Link to="/system-management/training">培训材料</Link>
|
||
<Link to="/system-management/faq">常见问题</Link>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</nav>
|
||
</div>
|
||
<div className="header-right">
|
||
<div className="user-info">
|
||
<span className="user-name">{user?.name}</span>
|
||
<span className="user-role">{user?.role}</span>
|
||
</div>
|
||
<button onClick={handleLogout} className="logout-btn">
|
||
退出登录
|
||
</button>
|
||
</div>
|
||
</header>
|
||
|
||
{/* 页面内容 */}
|
||
<main className="dashboard-content">
|
||
{children}
|
||
</main>
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
export default DashboardLayout
|