- Add ProfilePage with avatar upload, password change, and module status display - Update logo and favicon for login page and browser tab - Redirect Data Cleaning module default route to Tool C - Hide Settings button from top navigation for MVP - Add avatar display in top navigation bar with refresh sync - Add Prompt knowledge base integration development plan docs
157 lines
4.7 KiB
TypeScript
157 lines
4.7 KiB
TypeScript
import { useNavigate, useLocation } from 'react-router-dom'
|
||
import { Dropdown, Avatar } from 'antd'
|
||
import {
|
||
UserOutlined,
|
||
LogoutOutlined,
|
||
// SettingOutlined, // MVP阶段暂时隐藏设置按钮
|
||
ControlOutlined,
|
||
BankOutlined,
|
||
} from '@ant-design/icons'
|
||
import type { MenuProps } from 'antd'
|
||
import { MODULES } from '../modules/moduleRegistry'
|
||
import { useAuth } from '../auth'
|
||
|
||
/**
|
||
* 顶部导航栏组件
|
||
*
|
||
* @description
|
||
* - 显示Logo和平台名称
|
||
* - 显示模块导航(根据用户权限过滤)⭐ Week 2 Day 7 新增
|
||
* - 显示用户菜单
|
||
*
|
||
* @version Week 2 Day 7 - 任务17:集成权限系统
|
||
*/
|
||
const TopNavigation = () => {
|
||
const navigate = useNavigate()
|
||
const location = useLocation()
|
||
const { user, logout: authLogout, hasModule } = useAuth()
|
||
|
||
// 根据用户模块权限过滤可显示的模块
|
||
const availableModules = MODULES.filter(module => {
|
||
// 没有 moduleCode 的模块跳过(占位模块)
|
||
if (!module.moduleCode) return false;
|
||
// 检查用户是否有权限访问
|
||
return hasModule(module.moduleCode);
|
||
});
|
||
|
||
// 获取当前激活的模块
|
||
const activeModule = availableModules.find(module =>
|
||
location.pathname.startsWith(module.path)
|
||
)
|
||
|
||
// 检查用户权限,决定显示哪些切换入口
|
||
const userRole = user?.role || ''
|
||
const canAccessAdmin = ['SUPER_ADMIN', 'PROMPT_ENGINEER'].includes(userRole)
|
||
const canAccessOrg = ['HOSPITAL_ADMIN', 'PHARMA_ADMIN', 'DEPARTMENT_ADMIN', 'SUPER_ADMIN'].includes(userRole)
|
||
|
||
// 用户菜单 - 动态构建
|
||
const userMenuItems: MenuProps['items'] = [
|
||
{
|
||
key: 'profile',
|
||
icon: <UserOutlined />,
|
||
label: '个人中心',
|
||
},
|
||
// MVP阶段暂时隐藏设置按钮
|
||
// {
|
||
// key: 'settings',
|
||
// icon: <SettingOutlined />,
|
||
// label: '设置',
|
||
// },
|
||
// 切换入口 - 根据权限显示
|
||
...(canAccessOrg || canAccessAdmin ? [{ type: 'divider' as const }] : []),
|
||
...(canAccessOrg ? [{
|
||
key: 'switch-org',
|
||
icon: <BankOutlined />,
|
||
label: '切换到机构管理',
|
||
}] : []),
|
||
...(canAccessAdmin ? [{
|
||
key: 'switch-admin',
|
||
icon: <ControlOutlined />,
|
||
label: '切换到运营管理',
|
||
}] : []),
|
||
{
|
||
type: 'divider',
|
||
},
|
||
{
|
||
key: 'logout',
|
||
icon: <LogoutOutlined />,
|
||
label: '退出登录',
|
||
danger: true,
|
||
},
|
||
]
|
||
|
||
// 处理用户菜单点击
|
||
const handleUserMenuClick = ({ key }: { key: string }) => {
|
||
if (key === 'logout') {
|
||
authLogout()
|
||
navigate('/login')
|
||
} else if (key === 'switch-admin') {
|
||
navigate('/admin/dashboard')
|
||
} else if (key === 'switch-org') {
|
||
navigate('/org/dashboard')
|
||
} else {
|
||
navigate(`/user/${key}`)
|
||
}
|
||
}
|
||
|
||
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('/')}
|
||
>
|
||
<img
|
||
src="/logo-new.png"
|
||
alt="AI临床研究平台"
|
||
className="h-[48px] w-auto"
|
||
/>
|
||
<span className="text-xl font-bold text-blue-600">AI临床研究平台</span>
|
||
</div>
|
||
|
||
{/* 导航菜单 - 只显示有权限的模块 ⭐ 2026-01-16 更新为模块权限系统 */}
|
||
<div className="flex items-center gap-2">
|
||
{availableModules.map(module => {
|
||
const isActive = activeModule?.id === module.id
|
||
|
||
return (
|
||
<div
|
||
key={module.id}
|
||
onClick={() => navigate(module.path)}
|
||
className={`
|
||
px-4 py-2 rounded-md transition-all cursor-pointer
|
||
${isActive
|
||
? 'bg-blue-50 text-blue-600 font-semibold'
|
||
: 'text-gray-600 hover:bg-gray-50 hover:text-blue-600'
|
||
}
|
||
`}
|
||
>
|
||
{module.name}
|
||
</div>
|
||
)
|
||
})}
|
||
</div>
|
||
|
||
{/* 用户菜单 - 显示真实用户信息和头像 */}
|
||
<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?.avatarUrl}
|
||
icon={!user?.avatarUrl && <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?.modules?.length || 0} 个模块</span>
|
||
</div>
|
||
</div>
|
||
</Dropdown>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
export default TopNavigation
|