feat: sync rule management and review ui fixes

This commit is contained in:
wren
2026-05-07 17:27:42 +08:00
parent 87e82d1caa
commit c00e5feff0
13 changed files with 565 additions and 161 deletions
+4 -1
View File
@@ -1,4 +1,5 @@
import React, { useState, useEffect } from 'react';
import type { MenuItem } from '~/api/auth/user-routes';
import { Sidebar } from './Sidebar';
// import { Header } from './Header';
import { Breadcrumb } from './Breadcrumb';
@@ -10,6 +11,7 @@ interface LayoutProps {
userRole?: UserRole;
frontendJWT?: string;
isMobile?: boolean; // 是否为移动端设备(服务端通过 User-Agent 检测)
menuItems?: MenuItem[];
}
// 添加一个接口表示路由handle可能包含的属性
@@ -37,7 +39,7 @@ type RulesTestDetailData = {
};
};
export function Layout({ children, userRole = 'developer' as UserRole, frontendJWT = '', isMobile = false }: LayoutProps) {
export function Layout({ children, userRole = 'developer' as UserRole, frontendJWT = '', isMobile = false, menuItems = [] }: LayoutProps) {
const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
const [effectiveUserRole, setEffectiveUserRole] = useState<UserRole>(userRole);
const [effectiveFrontendJWT, setEffectiveFrontendJWT] = useState<string>(frontendJWT);
@@ -153,6 +155,7 @@ export function Layout({ children, userRole = 'developer' as UserRole, frontendJ
onToggle={toggleSidebar}
userRole={effectiveUserRole}
frontendJWT={effectiveFrontendJWT}
menuItems={menuItems}
/>
{/* 规则详情页顶部栏 */}
+10 -15
View File
@@ -10,13 +10,14 @@ interface SidebarProps {
collapsed: boolean;
userRole: UserRole;
frontendJWT?: string;
menuItems?: MenuItem[];
}
export function Sidebar({ onToggle, collapsed, userRole, frontendJWT = '' }: SidebarProps) {
export function Sidebar({ onToggle, collapsed, userRole, frontendJWT = '', menuItems: initialMenuItems = [] }: SidebarProps) {
const location = useLocation();
const [expandedMenus, setExpandedMenus] = useState<Record<string, boolean>>({});
const [menuItems, setMenuItems] = useState<MenuItem[]>([]); // 动态菜单项
const [isLoadingRoutes, setIsLoadingRoutes] = useState<boolean>(true); // 路由加载状态
const [menuItems, setMenuItems] = useState<MenuItem[]>(initialMenuItems); // 动态菜单项
const [isLoadingRoutes, setIsLoadingRoutes] = useState<boolean>(initialMenuItems.length === 0); // 路由加载状态
const [isMobile, setIsMobile] = useState<boolean>(false); // 移动端检测
const [selectedModuleName, setSelectedModuleName] = useState<string>(''); // 当前选中的模块名称
const [selectedModulePicPath, setSelectedModulePicPath] = useState<string>(''); // 当前选中的模块图片路径
@@ -39,12 +40,15 @@ export function Sidebar({ onToggle, collapsed, userRole, frontendJWT = '' }: Sid
// 获取用户路由权限
useEffect(() => {
// console.log('🔍 [Sidebar] useEffect 触发,开始获取路由权限');
if (initialMenuItems.length > 0) {
setMenuItems(initialMenuItems);
setIsLoadingRoutes(false);
return;
}
const fetchUserRoutes = async () => {
setIsLoadingRoutes(true);
try {
// 优先使用传入的 frontendJWT,否则从 localStorage 读取
let jwt = frontendJWT;
if (!jwt && typeof window !== 'undefined') {
@@ -59,29 +63,20 @@ export function Sidebar({ onToggle, collapsed, userRole, frontendJWT = '' }: Sid
return;
}
// console.log('🔍 [Sidebar] 当前用户角色:', userRole, 'JWT前20字符:', jwt.substring(0, 20));
// console.log('🔍 [Sidebar] 映射后的角色key:', roleKey);
const result = await getUserRoutesByRole(userRole, jwt);
if (result.success && result.data) {
setMenuItems(result.data);
// console.log('✅ [Sidebar] 用户路由权限加载成功:', result.data);
} else {
console.error('❌ [Sidebar] 获取用户路由权限失败:', result.error);
// 如果需要重定向到首页
if (result.shouldRedirectToHome) {
// console.log('🔄 [Sidebar] 重定向到首页');
navigate('/');
return;
}
// 其他错误情况,使用空数组
setMenuItems([]);
}
} catch (error) {
console.error('❌ [Sidebar] 获取用户路由权限时发生错误:', error);
// 发生异常时也重定向到首页
navigate('/');
return;
} finally {
@@ -90,7 +85,7 @@ export function Sidebar({ onToggle, collapsed, userRole, frontendJWT = '' }: Sid
};
fetchUserRoutes();
}, [userRole, frontendJWT, navigate]);
}, [userRole, frontendJWT, navigate, initialMenuItems]);
// 🔑 检查是否处于系统设置模式或交叉评查模式
const [isSettingsMode, setIsSettingsMode] = useState<boolean>(false);
+22 -11
View File
@@ -27,12 +27,13 @@ interface ReviewTabsProps {
comparisonId?: number;
};
onConfirmResults: () => void;
onExportReport?: () => void;
jwtToken?: string | null;
/** 下载前保存文档的回调,返回 true 表示保存成功可以继续下载 */
onSaveBeforeDownload?: () => Promise<boolean>;
}
export function ReviewTabs({ activeTab, onTabChange, children, fileInfo, onConfirmResults, jwtToken, onSaveBeforeDownload }: ReviewTabsProps) {
export function ReviewTabs({ activeTab, onTabChange, children, fileInfo, onConfirmResults, onExportReport, jwtToken, onSaveBeforeDownload }: ReviewTabsProps) {
const [isNavigating, setIsNavigating] = useState(false);
const [isReuploadModalOpen, setIsReuploadModalOpen] = useState(false);
const [selectedTemplateFiles, setSelectedTemplateFiles] = useState<File[]>([]);
@@ -58,14 +59,21 @@ export function ReviewTabs({ activeTab, onTabChange, children, fileInfo, onConfi
: previousRoute === 'filesUpload'
? "/files/upload"
: "/rules-files";
// 立即导航返回
navigate(returnTo);
// 触发上级页面数据重新加载
navigate(returnTo);
setTimeout(() => {
revalidator.revalidate();
setIsNavigating(false);
loadingBarService.hide();
}, 0);
};
// 下载原文件
const handleDownloadFile = async () => {
if (!fileInfo.path) {
toastService.warning('当前文档暂无可下载原文件');
return;
}
try {
// 如果有保存回调,先执行保存(仅对 DOCX 文件有效)
if (onSaveBeforeDownload) {
@@ -311,12 +319,15 @@ export function ReviewTabs({ activeTab, onTabChange, children, fileInfo, onConfi
</>
)}
</button>
{/* <button
className="ant-btn ant-btn-default flex items-center"
onClick={handleExportReport}
>
<i className="ri-file-copy-line mr-1"></i> 导出评查报告
</button> */}
{onExportReport && (
<button
className="ant-btn ant-btn-default inline-flex items-center my-2"
onClick={onExportReport}
disabled={isNavigating}
>
<i className="ri-file-copy-line mr-1"></i>
</button>
)}
<button
className={`ant-btn ant-btn-primary my-2 flex items-center ${fileInfo.auditStatus === 1 ? 'hidden' : ''}`}
onClick={onConfirmResults}
@@ -437,4 +448,4 @@ export function ReviewTabs({ activeTab, onTabChange, children, fileInfo, onConfi
</Modal>
</div>
);
}
}