feat: 1. 将交叉评查转移在入口页。

2. 交叉评查渲染的pdf预览组件复用评查点详情的,同时在评查结果中的数据也添加坐标信息。
This commit is contained in:
2025-11-26 10:49:15 +08:00
parent d1f764028c
commit fe75b4fabd
8 changed files with 181 additions and 93 deletions
+55 -4
View File
@@ -51,6 +51,8 @@ export async function loader({ request }: LoaderFunctionArgs) {
// 🔑 检查用户是否有系统设置权限
let hasSettingsAccess = false;
let hasCrossCheckingAccess = false;
if (userRole && frontendJWT) {
const { getUserRoutesByRole } = await import('~/api/auth/user-routes');
const routesResult = await getUserRoutesByRole(userRole, frontendJWT, true); // includeHidden=true
@@ -58,12 +60,15 @@ export async function loader({ request }: LoaderFunctionArgs) {
if (routesResult.success && routesResult.data) {
// 检查是否存在顶级路由 '/settings'
hasSettingsAccess = routesResult.data.some(route => route.path === '/settings');
// 检查是否存在顶级路由 '/cross-checking'
hasCrossCheckingAccess = routesResult.data.some(route => route.path === '/cross-checking');
// console.log(`🔑 [Index Loader] 用户${hasSettingsAccess ? '有' : '没有'}系统设置权限`);
// console.log(`🔑 [Index Loader] 用户${hasCrossCheckingAccess ? '有' : '没有'}交叉评查权限`);
}
}
// 返回用户信息、入口模块和系统设置权限给客户端
return Response.json({ userRole, userInfo, entryModules, hasSettingsAccess });
// 返回用户信息、入口模块和权限给客户端
return Response.json({ userRole, userInfo, entryModules, hasSettingsAccess, hasCrossCheckingAccess });
}
export default function Index() {
@@ -108,13 +113,20 @@ export default function Index() {
// console.log('👤 [Index] 当前用户信息:', userInfo);
}, [userRole, userInfo]);
// 🔑 清除系统设置模式标志(当用户返回首页时)
// 🔑 清除系统设置模式和交叉评查模式标志(当用户返回首页时)
useEffect(() => {
if (typeof window !== 'undefined') {
const settingsMode = sessionStorage.getItem('settingsMode');
const crossCheckingMode = sessionStorage.getItem('crossCheckingMode');
if (settingsMode === 'true') {
sessionStorage.removeItem('settingsMode');
console.log('🔄 [Index] 清除系统设置模式标志');
// console.log('🔄 [Index] 清除系统设置模式标志');
}
if (crossCheckingMode === 'true') {
sessionStorage.removeItem('crossCheckingMode');
// console.log('🔄 [Index] 清除交叉评查模式标志');
}
}
}, []); // 只在组件挂载时执行一次
@@ -233,12 +245,31 @@ export default function Index() {
sessionStorage.removeItem('selectedModuleId');
sessionStorage.removeItem('selectedModuleName');
sessionStorage.removeItem('selectedModulePicPath');
// 清除交叉评查模式标志
sessionStorage.removeItem('crossCheckingMode');
}
// 跳转到系统设置的默认页面
navigate('/rule-groups');
};
// 处理进入交叉评查
const handleEnterCrossChecking = () => {
if (typeof window !== 'undefined') {
// 🔑 设置标志:表示用户通过交叉评查入口进入
sessionStorage.setItem('crossCheckingMode', 'true');
// 清除模块相关的标志(因为不是从入口模块进入)
sessionStorage.removeItem('selectedModuleId');
sessionStorage.removeItem('selectedModuleName');
sessionStorage.removeItem('selectedModulePicPath');
// 清除系统设置模式标志
sessionStorage.removeItem('settingsMode');
}
// 跳转到交叉评查的默认页面
navigate('/cross-checking');
};
return (
<div className="home-page">
{/* 登出表单 - 隐藏 */}
@@ -309,6 +340,25 @@ export default function Index() {
</div>
))}
{/* 🔑 交叉评查入口 - 只有有权限的用户才能看到 */}
{loaderData.hasCrossCheckingAccess && (
<div
className="module-card"
onClick={handleEnterCrossChecking}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
handleEnterCrossChecking();
}
}}
role="button"
tabIndex={0}
aria-label="交叉评查"
>
<i className="ri-shuffle-line text-5xl text-primary"></i>
<span className="module-name"></span>
</div>
)}
{/* 🔑 系统设置入口 - 只有有权限的用户才能看到 */}
{loaderData.hasSettingsAccess && (
<div
@@ -327,6 +377,7 @@ export default function Index() {
<span className="module-name"></span>
</div>
)}
</>
) : (
<div className="text-center text-gray-500 py-8">
+18 -13
View File
@@ -37,8 +37,8 @@ import {
ReviewPointsList
} from "~/components/cross-checking";
// 从ReviewPointsList组件中导入ReviewPoint类型
import { type ReviewPoint } from '~/components/cross-checking';
// 从ReviewPointsList组件中导入ReviewPoint类型和CharPosition类型
import { type ReviewPoint, type CharPosition } from '~/components/cross-checking';
import { messageService } from "~/components/ui/MessageModal";
import { loadingBarService } from "~/components/ui/LoadingBar";
import { Breadcrumb } from "~/components/layout/Breadcrumb";
@@ -297,7 +297,7 @@ export async function action({ request }: ActionFunctionArgs) {
}
export default function CrossCheckingResult() {
console.log('[组件] CrossCheckingResult 渲染');
// console.log('[组件] CrossCheckingResult 渲染');
const navigate = useNavigate();
const loaderData = useLoaderData<typeof loader>();
@@ -306,18 +306,19 @@ export default function CrossCheckingResult() {
const [reviewData, setReviewData] = useState<ReviewData | null>(null);
const [activeReviewPointResultId, setActiveReviewPointResultId] = useState<string | null>(null);
const [targetPage, setTargetPage] = useState<number | undefined>(undefined);
const [charPositions, setCharPositions] = useState<CharPosition[] | undefined>(undefined);
const [localScoringProposals, setLocalScoringProposals] = useState<ScoringProposal[]>(scoring_proposals || []); // 本地状态管理scoringProposals
// 使用ref来跟踪loading状态,避免不必要的重新渲染
const isProcessingRef = useRef(false);
// 添加组件挂载/卸载日志
useEffect(() => {
console.log('[组件] CrossCheckingResult 挂载');
return () => {
console.log('[组件] CrossCheckingResult 卸载');
};
}, []);
// useEffect(() => {
// console.log('[组件] CrossCheckingResult 挂载');
// return () => {
// console.log('[组件] CrossCheckingResult 卸载');
// };
// }, []);
// 同步外部scoring_proposals到本地状态
useEffect(() => {
@@ -387,19 +388,22 @@ export default function CrossCheckingResult() {
}, [document, reviewPoints, statistics, reviewInfo]);
const handleReviewPointSelect = useCallback((reviewPointId: string, page?: number) => {
const handleReviewPointSelect = useCallback((reviewPointId: string, page?: number, charPositions?: CharPosition[]) => {
// 如果点击的是相同的评查点,但有page参数,先重置targetPage以确保useEffect能够触发
if (reviewPointId === activeReviewPointResultId && page) {
setTargetPage(undefined);
// 使用setTimeout确保状态更新后再设置新的targetPage
setCharPositions(undefined);
// 使用setTimeout确保状态更新后再设置新的targetPage和charPositions
setTimeout(() => {
setActiveReviewPointResultId(reviewPointId);
setTargetPage(page);
setCharPositions(charPositions);
}, 0);
} else {
// 正常设置activeReviewPointIdtargetPage
// 正常设置activeReviewPointIdtargetPage和charPositions
setActiveReviewPointResultId(reviewPointId);
setTargetPage(page);
setCharPositions(charPositions);
}
}, [activeReviewPointResultId]);
@@ -724,11 +728,12 @@ export default function CrossCheckingResult() {
<div className="flex flex-col lg:flex-row space-y-4 lg:space-y-0 lg:space-x-4 lg:justify-between">
{/* 左侧:文件预览 */}
<div className="w-full lg:w-[62%]">
<FilePreview
<FilePreview
fileContent={document}
reviewPoints={reviewData.reviewPoints}
activeReviewPointResultId={activeReviewPointResultId}
targetPage={targetPage}
charPositions={charPositions}
/>
</div>
+2 -10
View File
@@ -234,7 +234,7 @@ export async function action({ request }: ActionFunctionArgs) {
const result = formData.get("result") as string;
const message = formData.get("message") as string;
console.log('更新评查结果参数:', { reviewPointResultId, editAuditStatusId, result, message });
// console.log('更新评查结果参数:', { reviewPointResultId, editAuditStatusId, result, message });
try {
const response = await updateReviewResult(reviewPointResultId, editAuditStatusId, result, message, request);
@@ -257,7 +257,7 @@ export async function action({ request }: ActionFunctionArgs) {
if (intent === "confirmReviewResults") {
const documentId = formData.get("documentId") as string;
console.log('确认评查结果参数:', { documentId });
// console.log('确认评查结果参数:', { documentId });
try {
const response = await confirmReviewResults(documentId, request);
@@ -293,17 +293,9 @@ export default function ReviewDetails() {
const navigate = useNavigate();
const loaderData = useLoaderData<typeof loader>();
// 调试:查看loaderData内容 - 强制刷新
console.log('[Reviews Component] loaderData keys:', Object.keys(loaderData));
console.log('[Reviews Component] loaderData:', loaderData);
const fetcher = useFetcher();
const { document, reviewPoints, statistics, reviewInfo, comparison_document, frontendJWT } = loaderData;
// 调试:查看解构后的数据
console.log('[Reviews Component] 解构后的document:', document);
console.log('[Reviews Component] 解构后的reviewPoints length:', reviewPoints?.length);
const [isLoading, setIsLoading] = useState(false); // 已经通过loader加载了数据,不需要再显示加载状态
const [activeTab, setActiveTab] = useState<string>('preview'); // 'preview', 'analysis', 'fileinfo'
const [reviewData, setReviewData] = useState<ReviewData | null>(null);