feat(frontend): add frontend-v2 modular architecture (Task 17)
- React 19 + TypeScript + Vite - Module registration mechanism with dynamic loading - Permission management system (basic/advanced/premium) - Route guards for access control - Error boundaries for module isolation - 6 business module placeholders (AIA/ASL/PKB/DC/SSA/ST) - Top navigation layout - Tailwind CSS 3 + Ant Design 5
This commit is contained in:
140
frontend-v2/src/framework/layout/TopNavigation.tsx
Normal file
140
frontend-v2/src/framework/layout/TopNavigation.tsx
Normal file
@@ -0,0 +1,140 @@
|
||||
import { useNavigate, useLocation } from 'react-router-dom'
|
||||
import { Dropdown, Avatar, Tooltip } from 'antd'
|
||||
import { UserOutlined, LogoutOutlined, SettingOutlined, LockOutlined } from '@ant-design/icons'
|
||||
import type { MenuProps } from 'antd'
|
||||
import { getAvailableModules } from '../modules/moduleRegistry'
|
||||
import { usePermission } from '../permission'
|
||||
|
||||
/**
|
||||
* 顶部导航栏组件
|
||||
*
|
||||
* @description
|
||||
* - 显示Logo和平台名称
|
||||
* - 显示模块导航(根据用户权限过滤)⭐ Week 2 Day 7 新增
|
||||
* - 显示用户菜单
|
||||
*
|
||||
* @version Week 2 Day 7 - 任务17:集成权限系统
|
||||
*/
|
||||
const TopNavigation = () => {
|
||||
const navigate = useNavigate()
|
||||
const location = useLocation()
|
||||
const { user, checkModulePermission, logout } = usePermission()
|
||||
|
||||
// 获取用户有权访问的模块列表(权限过滤)⭐ 新增
|
||||
const availableModules = getAvailableModules(user?.version || 'basic')
|
||||
|
||||
// 获取当前激活的模块
|
||||
const activeModule = availableModules.find(module =>
|
||||
location.pathname.startsWith(module.path)
|
||||
)
|
||||
|
||||
// 用户菜单
|
||||
const userMenuItems: MenuProps['items'] = [
|
||||
{
|
||||
key: 'profile',
|
||||
icon: <UserOutlined />,
|
||||
label: '个人中心',
|
||||
},
|
||||
{
|
||||
key: 'settings',
|
||||
icon: <SettingOutlined />,
|
||||
label: '设置',
|
||||
},
|
||||
{
|
||||
type: 'divider',
|
||||
},
|
||||
{
|
||||
key: 'logout',
|
||||
icon: <LogoutOutlined />,
|
||||
label: '退出登录',
|
||||
danger: true,
|
||||
},
|
||||
]
|
||||
|
||||
// 处理用户菜单点击
|
||||
const handleUserMenuClick = ({ key }: { key: string }) => {
|
||||
if (key === 'logout') {
|
||||
logout()
|
||||
navigate('/')
|
||||
} else {
|
||||
navigate(`/user/${key}`)
|
||||
}
|
||||
}
|
||||
|
||||
// 处理模块点击(检查权限)
|
||||
const handleModuleClick = (modulePath: string, requiredVersion?: string) => {
|
||||
if (!checkModulePermission(requiredVersion as any)) {
|
||||
// 理论上不会到这里,因为已经过滤了
|
||||
console.warn('权限不足,无法访问该模块')
|
||||
return
|
||||
}
|
||||
navigate(modulePath)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="h-16 bg-white border-b border-gray-200 px-6 flex items-center justify-between">
|
||||
{/* Logo */}
|
||||
<div
|
||||
className="flex items-center gap-3 cursor-pointer"
|
||||
onClick={() => navigate('/')}
|
||||
>
|
||||
<div className="text-2xl">🏥</div>
|
||||
<span className="text-xl font-bold text-blue-600">AI临床研究平台</span>
|
||||
</div>
|
||||
|
||||
{/* 导航菜单 - 根据用户权限动态显示 ⭐ Week 2 Day 7 更新 */}
|
||||
<div className="flex items-center gap-2">
|
||||
{availableModules.map(module => {
|
||||
const hasPermission = checkModulePermission(module.requiredVersion as any)
|
||||
const isActive = activeModule?.id === module.id
|
||||
|
||||
return (
|
||||
<Tooltip
|
||||
key={module.id}
|
||||
title={!hasPermission ? `需要${module.requiredVersion}版本` : ''}
|
||||
>
|
||||
<div
|
||||
onClick={() => hasPermission && handleModuleClick(module.path, module.requiredVersion)}
|
||||
className={`
|
||||
px-4 py-2 rounded-md transition-all
|
||||
${!hasPermission
|
||||
? 'text-gray-400 cursor-not-allowed opacity-50'
|
||||
: isActive
|
||||
? 'bg-blue-50 text-blue-600 font-semibold cursor-pointer'
|
||||
: 'text-gray-600 hover:bg-gray-50 hover:text-blue-600 cursor-pointer'
|
||||
}
|
||||
`}
|
||||
>
|
||||
<span className="flex items-center gap-2">
|
||||
{!hasPermission && <LockOutlined className="text-xs" />}
|
||||
{module.name}
|
||||
</span>
|
||||
</div>
|
||||
</Tooltip>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
|
||||
{/* 用户菜单 - 显示真实用户信息 ⭐ Week 2 Day 7 更新 */}
|
||||
<Dropdown
|
||||
menu={{ items: userMenuItems, onClick: handleUserMenuClick }}
|
||||
placement="bottomRight"
|
||||
>
|
||||
<div className="flex items-center gap-2 cursor-pointer px-3 py-2 rounded-md hover:bg-gray-50">
|
||||
<Avatar
|
||||
src={user?.avatar}
|
||||
icon={<UserOutlined />}
|
||||
size="small"
|
||||
/>
|
||||
<div className="flex flex-col">
|
||||
<span className="text-gray-700 text-sm">{user?.name || '访客'}</span>
|
||||
<span className="text-xs text-gray-400">{user?.version || 'basic'}</span>
|
||||
</div>
|
||||
</div>
|
||||
</Dropdown>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default TopNavigation
|
||||
|
||||
Reference in New Issue
Block a user