feat: 1. 添加axios全局路由拦截进行自动添加请求jwt。 2.重新整理路由表。 3. 文档列表新增版本差异对比。 4.菜单路由可访问列表通过对接接口返回,添加全局路由检测。

5. 修改统一认证登录和管理员登录是通过接口形式进行,存储返回的accessToken。    6. 修改交叉评查的部分样式
This commit is contained in:
2025-11-18 11:06:24 +08:00
parent 8a50671c39
commit bfe39e45a9
53 changed files with 9503 additions and 2796 deletions
+41 -6
View File
@@ -36,6 +36,8 @@ interface Match {
export function Layout({ children, userRole = 'developer' as UserRole, frontendJWT = '' }: LayoutProps) {
const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
const [selectedApp, setSelectedApp] = useState<AppModule>('');
const [effectiveUserRole, setEffectiveUserRole] = useState<UserRole>(userRole);
const [effectiveFrontendJWT, setEffectiveFrontendJWT] = useState<string>(frontendJWT);
const matches = useMatches() as Match[];
const location = useLocation();
@@ -48,6 +50,39 @@ export function Layout({ children, userRole = 'developer' as UserRole, frontendJ
match.handle && match.handle.hideBreadcrumb === true
);
// 从 localStorage 读取用户信息和 JWT 作为备用方案
useEffect(() => {
if (typeof window === 'undefined') return;
try {
// 如果服务端没有传递 userRole,从 localStorage 读取
if (!userRole || userRole === '') {
const storedUserInfoStr = localStorage.getItem('user_info');
if (storedUserInfoStr) {
const storedUserInfo = JSON.parse(storedUserInfoStr);
const storedUserRole = storedUserInfo.user_role || 'common';
console.log('📖 [Layout] 从 localStorage 读取用户角色:', storedUserRole);
setEffectiveUserRole(storedUserRole as UserRole);
}
} else {
setEffectiveUserRole(userRole);
}
// 如果服务端没有传递 frontendJWT,从 localStorage 读取
if (!frontendJWT || frontendJWT === '') {
const storedToken = localStorage.getItem('access_token');
if (storedToken) {
console.log('📖 [Layout] 从 localStorage 读取 JWT token');
setEffectiveFrontendJWT(storedToken);
}
} else {
setEffectiveFrontendJWT(frontendJWT);
}
} catch (error) {
console.error('❌ [Layout] 读取 localStorage 失败:', error);
}
}, [userRole, frontendJWT]);
// 从sessionStorage中获取侧边栏状态和reviewType
useEffect(() => {
// 检查是否为移动端
@@ -62,7 +97,7 @@ export function Layout({ children, userRole = 'developer' as UserRole, frontendJ
} else if (savedState) {
setSidebarCollapsed(savedState === 'true');
}
// 从sessionStorage获取reviewType并设置对应的应用模块
if (typeof window !== 'undefined') {
try {
@@ -111,12 +146,12 @@ export function Layout({ children, userRole = 'developer' as UserRole, frontendJ
return (
<div className="layout-container">
{/* 侧边栏始终保留,不再使用条件渲染 */}
<Sidebar
collapsed={sidebarCollapsed}
onToggle={toggleSidebar}
userRole={userRole}
<Sidebar
collapsed={sidebarCollapsed}
onToggle={toggleSidebar}
userRole={effectiveUserRole}
selectedApp={selectedApp}
frontendJWT={frontendJWT}
frontendJWT={effectiveFrontendJWT}
/>
<div className={`main-content ${sidebarCollapsed ? 'sidebar-collapsed' : ''}`}>
+90 -35
View File
@@ -11,11 +11,28 @@ interface SidebarProps {
selectedApp?: string; // 添加所选应用模块参数
}
// 定义不同应用模块下显示的菜单项ID
// 定义不同应用模块下显示的菜单路径(使用路由路径进行匹配)
const APP_MENU_MAP = {
'contract': ['home', 'contract-template', 'file-management', 'rule-management', 'cross-checking', 'system-settings'],
'record': ['home', 'file-management', 'rule-management', 'cross-checking', 'system-settings'],
'model': ['chat-with-llm']
'contract': [
'/home', // 系统概览
'/documents', // 文档管理
'/contract-template', // 合同模板
'/rules', // 评查规则库
'/cross-checking', // 交叉评查
// '/chat-with-llm', // AI法务助手
'/settings' // 系统设置
],
'record': [
'/home', // 系统概览
'/documents', // 文档管理
'/rules', // 评查规则库
'/cross-checking', // 交叉评查
// '/chat-with-llm', // AI法务助手
'/settings' // 系统设置
],
'model': [
'/chat-with-llm' // AI法务助手
]
};
// 应用模块名称映射
@@ -62,28 +79,43 @@ export function Sidebar({ onToggle, collapsed, userRole, frontendJWT = '', selec
const fetchUserRoutes = async () => {
setIsLoadingRoutes(true);
try {
console.log('userRole', userRole);
// 优先使用传入的 frontendJWT,否则从 localStorage 读取
let jwt = frontendJWT;
if (!jwt && typeof window !== 'undefined') {
jwt = localStorage.getItem('access_token') || '';
console.log('📖 [Sidebar] 从 localStorage 读取 JWT');
}
if (!jwt) {
console.error('❌ [Sidebar] JWT token 未找到');
setMenuItems([]);
setIsLoadingRoutes(false);
return;
}
console.log('🔍 [Sidebar] 当前用户角色:', userRole);
const roleKey = mapUserRoleToRoleKey(userRole);
const result = await getUserRoutesByRole(roleKey, frontendJWT);
const result = await getUserRoutesByRole(roleKey, jwt);
if (result.success && result.data) {
setMenuItems(result.data);
console.log('用户路由权限加载成功:', result.data);
console.log('✅ [Sidebar] 用户路由权限加载成功:', result.data);
} else {
console.error('获取用户路由权限失败:', result.error);
console.error('❌ [Sidebar] 获取用户路由权限失败:', result.error);
// 如果需要重定向到首页
if (result.shouldRedirectToHome) {
console.log('重定向到首页');
console.log('🔄 [Sidebar] 重定向到首页');
navigate('/');
return;
}
// 其他错误情况,使用空数组
setMenuItems([]);
}
} catch (error) {
console.error('获取用户路由权限时发生错误:', error);
console.error('❌ [Sidebar] 获取用户路由权限时发生错误:', error);
// 发生异常时也重定向到首页
navigate('/');
return;
@@ -93,7 +125,7 @@ export function Sidebar({ onToggle, collapsed, userRole, frontendJWT = '', selec
};
fetchUserRoutes();
}, [userRole, navigate]);
}, [userRole, frontendJWT, navigate]);
// 组件挂载后从 sessionStorage 读取初始 reviewType
useEffect(() => {
@@ -225,37 +257,60 @@ export function Sidebar({ onToggle, collapsed, userRole, frontendJWT = '', selec
// console.log('子菜单点击:', child.title, '路径:', child.path);
};
// 获取当前应用模式下应显示的菜单ID列表
const visibleMenuIds = APP_MENU_MAP[currentApp as keyof typeof APP_MENU_MAP] || APP_MENU_MAP['contract'];
// const visibleMenuIds = APP_MENU_MAP[currentApp as keyof typeof APP_MENU_MAP]
// console.log('当前应用模式:', currentApp, '可见菜单ID:', visibleMenuIds);
// 获取当前应用模式下应显示的菜单路径列表
const visibleMenuPaths = APP_MENU_MAP[currentApp as keyof typeof APP_MENU_MAP] || APP_MENU_MAP['contract'];
// console.log('当前应用模式:', currentApp, '可见菜单路径:', visibleMenuPaths);
// 检查是否通过51707端口访问(省局)
// const isPort51708 = typeof window !== 'undefined' && window.location.port === '51708';
const isPort51707 = typeof window !== 'undefined' && window.location.port === '51707';
// 根据当前应用模式过滤菜单项
const filteredMenuItems = menuItems.filter(item => {
// 如果是51707端口,只显示交叉评查相关菜单
if (isPort51707) {
// 如果当前应用是智慧法务大模型,只显示AI对话菜单
if (currentApp === 'model') {
return item.id === 'chat-with-llm' ||
(item.path && item.path.startsWith('/chat-with-llm'));
}else{
return item.id === 'cross-checking' ||
(item.path && item.path.startsWith('/cross-checking'))
const filteredMenuItems = menuItems
.filter(item => {
// 如果是51707端口,只显示交叉评查相关菜单
if (isPort51707) {
// 如果当前应用是智慧法务大模型,只显示AI对话菜单
if (currentApp === 'model') {
return item.path && item.path.startsWith('/chat-with-llm');
} else {
return item.path && item.path.startsWith('/cross-checking');
}
}
}
// 检查当前菜单是否在所选应用模式中显示(使用路径匹配)
if (!visibleMenuPaths.includes(item.path)) {
return false;
}
// 检查当前菜单是否在所选应用模式中显示
if (!visibleMenuIds.includes(item.id)) {
return false;
}
return true;
})
.map(item => {
// 处理子菜单:过滤隐藏的子菜单
if (item.children && item.children.length > 0) {
// 过滤掉 hideBreadcrumb=true 的子菜单(这些通常是隐藏菜单)
const visibleChildren = item.children.filter(child => !child.hideBreadcrumb);
return true;
});
// 如果过滤后没有可见的子菜单,返回不带子菜单的父级(变成可点击的单级菜单)
if (visibleChildren.length === 0) {
const { children, ...itemWithoutChildren } = item;
return itemWithoutChildren;
}
// 如果还有可见的子菜单,返回带过滤后子菜单的项
return { ...item, children: visibleChildren };
}
// 处理空 children 数组或 undefined 的情况
if (item.children !== undefined) {
// 如果 children 存在但为空数组,移除它(让父级变成可点击的单级菜单)
const { children, ...itemWithoutChildren } = item;
return itemWithoutChildren;
}
// 没有子菜单的项直接返回
return item;
});
// filteredMenuItems = filteredMenuItems.map(item => {
// if(item.children && item.children.length > 0){