diff --git a/app/api/axios-client.ts b/app/api/axios-client.ts index 062ad49..c28c391 100644 --- a/app/api/axios-client.ts +++ b/app/api/axios-client.ts @@ -90,12 +90,12 @@ axiosInstance.interceptors.request.use( const token = localStorage.getItem('access_token'); if (token) { config.headers.Authorization = `Bearer ${token}`; - console.log('🔑 [Request Interceptor] 添加Authorization头:', { - url: config.url, - method: config.method, - hasToken: !!token, - tokenPreview: token.substring(0, 20) + '...' - }); + // console.log('🔑 [Request Interceptor] 添加Authorization头:', { + // url: config.url, + // method: config.method, + // hasToken: !!token, + // tokenPreview: token.substring(0, 20) + '...' + // }); } else { console.warn('⚠️ [Request Interceptor] 没有找到access_token:', { url: config.url, @@ -127,11 +127,11 @@ export class AuthenticationError extends Error { */ axiosInstance.interceptors.response.use( (response) => { - console.log('✅ [Response Interceptor] 请求成功:', { - url: response.config.url, - status: response.status, - statusText: response.statusText - }); + // console.log('✅ [Response Interceptor] 请求成功:', { + // url: response.config.url, + // status: response.status, + // statusText: response.statusText + // }); return response; }, (error) => { diff --git a/app/components/reviews/FilePreview.tsx b/app/components/reviews/FilePreview.tsx index 6f7de00..36d7bd2 100644 --- a/app/components/reviews/FilePreview.tsx +++ b/app/components/reviews/FilePreview.tsx @@ -3,19 +3,14 @@ * 显示文档内容和评查点高亮 */ import { useState, useEffect, useRef, ChangeEvent } from 'react'; -import { pdfjs } from 'react-pdf'; -import { DOCUMENT_URL } from '~/api/axios-client'; import { CollaboraViewer, type CollaboraViewerHandle } from '~/components/collabora/CollaboraViewer'; import { requestPageInfo, customGotoPage } from '~/components/collabora/lib'; import { PdfPreview } from './previewComponents/PdfPreview'; - -// 设置worker路径为public目录下的worker文件 -pdfjs.GlobalWorkerOptions.workerSrc = '/pdf.worker.js'; - -// 导入统一的ReviewPoint类型 -import { type ReviewPoint } from './'; import { toastService } from '../ui/Toast'; +// 直接从ReviewPointsList导入类型,避免循环依赖 +import { type ReviewPoint } from './ReviewPointsList'; + // 定义文档内容类型 interface FileContent { title: string; @@ -68,34 +63,24 @@ export function FilePreview({ fileContent, activeReviewPointResultId, targetPage const isDocx = fileExtension === 'docx'; const isPdf = fileExtension === 'pdf'; - // 如果是PDF文件,直接使用PdfPreview组件 - if (isPdf && real_path) { - // console.log('fileContent', fileContent) - // console.log('activeReviewPointResultId', activeReviewPointResultId) - const pageOffset = fileContent.ocrResult?.__meta?.page_offset || 0; - return ( - - ); - } - - // DOCX 和其他文件类型继续使用原有逻辑 + // ✅ 将所有hooks移到条件return之前,确保遵守React Hooks规则 + // Refs const contentRef = useRef(null); const collaboraViewerRef = useRef(null); + const prevTargetPageRef = useRef(undefined); + const lastMousePosRef = useRef({ x: 0, y: 0 }); + + // States const [numPages, setNumPages] = useState(null); const [pageInputValue, setPageInputValue] = useState(''); + const [dragMode, setDragMode] = useState(false); + const [isDragging, setIsDragging] = useState(false); + const [dragCursor, setDragCursor] = useState('default'); + // ✅ 将所有useEffect移到条件return之前 // DOCX 页数获取: 使用 requestPageInfo 方法 useEffect(() => { - if (!isDocx) return; - - // console.log('[FilePreview] DOCX 文档加载,尝试获取页数'); + if (!isDocx || isPdf) return; // PDF文件不需要执行 let intervalCleared = false; @@ -108,7 +93,6 @@ export function FilePreview({ fileContent, activeReviewPointResultId, targetPage return; } - // console.log('[FilePreview] Collabora 已就绪,尝试获取页数'); clearInterval(checkInterval); intervalCleared = true; @@ -132,13 +116,97 @@ export function FilePreview({ fileContent, activeReviewPointResultId, targetPage return () => { clearInterval(checkInterval); }; - }, [isDocx]); + }, [isDocx, isPdf]); - // 拖拽状态管理(仅用于 DOCX) - const [dragMode, setDragMode] = useState(false); - const [isDragging, setIsDragging] = useState(false); - const [dragCursor, setDragCursor] = useState('default'); - const lastMousePosRef = useRef({ x: 0, y: 0 }); + // 监听鼠标离开窗口事件 + useEffect(() => { + if (isPdf) return; // PDF不需要拖拽功能 + + const handleMouseLeave = () => { + if (dragMode && isDragging) { + setIsDragging(false); + setDragCursor('grab'); + } + }; + + const handleMouseUp = () => { + if (!dragMode) return; + setIsDragging(false); + setDragCursor('grab'); + }; + + document.addEventListener('mouseleave', handleMouseLeave); + document.addEventListener('mouseup', handleMouseUp as EventListener); + + return () => { + document.removeEventListener('mouseleave', handleMouseLeave); + document.removeEventListener('mouseup', handleMouseUp as EventListener); + }; + }, [isDragging, dragMode, isPdf]); + + // 处理页面跳转 + useEffect(() => { + if (isPdf) return; // PDF由PdfPreview处理 + + // 如果有目标页码,并且与上次相同,提示用户 + if(targetPage && numPages && targetPage <= numPages && targetPage === prevTargetPageRef.current){ + // toastService.success(`已跳转至目标页码`); + } + // 如果有目标页码,并且与上次不同或activeReviewPointId变化了,则执行跳转 + if (targetPage && numPages && targetPage <= numPages) { + prevTargetPageRef.current = targetPage; + let newTargetPage = targetPage; + + // 页码偏移量 + try { + // 安全地访问ocrResult + if (fileContent.ocrResult && fileContent.ocrResult.__meta && fileContent.ocrResult.__meta.page_offset) { + // 可以根据需要使用page_offset调整目标页面 + newTargetPage = targetPage + fileContent.ocrResult.__meta.page_offset; + } + } catch (error) { + console.error("访问ocrResult时出错:", error); + toastService.error("访问ocrResult时出错:" + (error instanceof Error ? error.message : '未知错误')); + } + + const pageElementId = `page-${newTargetPage}${isStructuredView ? '-structured' : ''}`; + + const pageElement = document.getElementById(pageElementId); + if (pageElement) { + pageElement.scrollIntoView({ behavior: 'smooth', block: 'start' }); + } else { + console.warn(`未找到页面元素: ${pageElementId}`); + } + } + }, [targetPage, numPages, fileContent, activeReviewPointResultId, isStructuredView, isPdf]); + + // 调试日志 + console.log('[FilePreview] 组件渲染', { + real_path, + fileExtension, + isDocx, + isPdf, + hasPath: !!fileContent.path, + hasTemplatePath: !!fileContent.template_contract_path + }); + + // 如果是PDF文件,直接使用PdfPreview组件 + if (isPdf && real_path) { + console.log('[FilePreview] 渲染PDF预览', { real_path, targetPage, charPositions }); + const pageOffset = fileContent.ocrResult?.__meta?.page_offset || 0; + return ( + + ); + } + + // DOCX 和其他文件类型继续使用原有逻辑 // 放大文档(仅用于 DOCX) const handleZoomIn = () => { @@ -209,66 +277,7 @@ export function FilePreview({ fileContent, activeReviewPointResultId, targetPage setIsDragging(false); setDragCursor('grab'); }; - - // 监听鼠标离开窗口事件 - useEffect(() => { - const handleMouseLeave = () => { - if (dragMode && isDragging) { - setIsDragging(false); - setDragCursor('grab'); - } - }; - - document.addEventListener('mouseleave', handleMouseLeave); - document.addEventListener('mouseup', handleMouseUp as EventListener); - - return () => { - document.removeEventListener('mouseleave', handleMouseLeave); - document.removeEventListener('mouseup', handleMouseUp as EventListener); - }; - }, [isDragging, dragMode]); - - // 处理页面跳转 - const prevTargetPageRef = useRef(undefined); - useEffect(() => { - // 调试信息:记录组件状态 - // console.log(`FilePreview更新 - isStructuredView:${isStructuredView}, targetPage:${targetPage}, activeReviewPointResultId:${activeReviewPointResultId}, numPages:${numPages}`); - - // 如果有目标页码,并且与上次相同,提示用户 - if(targetPage && numPages && targetPage <= numPages && targetPage === prevTargetPageRef.current){ - // toastService.success(`已跳转至目标页码`); - } - // 如果有目标页码,并且与上次不同或activeReviewPointId变化了,则执行跳转 - if (targetPage && numPages && targetPage <= numPages) { - // if (targetPage && numPages && targetPage <= numPages && (targetPage !== prevTargetPageRef.current || activeReviewPointResultId)) { - prevTargetPageRef.current = targetPage; - let newTargetPage = targetPage; - - // 页码偏移量 - try { - // 安全地访问ocrResult - if (fileContent.ocrResult && fileContent.ocrResult.__meta && fileContent.ocrResult.__meta.page_offset) { - // 可以根据需要使用page_offset调整目标页面 - newTargetPage = targetPage + fileContent.ocrResult.__meta.page_offset; - } - } catch (error) { - console.error("访问ocrResult时出错:", error); - toastService.error("访问ocrResult时出错:" + (error instanceof Error ? error.message : '未知错误')); - } - - const pageElementId = `page-${newTargetPage}${isStructuredView ? '-structured' : ''}`; - // console.log(`尝试跳转到元素ID: ${pageElementId}`); - - const pageElement = document.getElementById(pageElementId); - if (pageElement) { - // console.log(`跳转到第${newTargetPage}页,对应评查点结果ID: ${activeReviewPointResultId}`); - pageElement.scrollIntoView({ behavior: 'smooth', block: 'start' }); - } else { - console.warn(`未找到页面元素: ${pageElementId}`); - } - } - }, [targetPage, numPages, fileContent, activeReviewPointResultId, isStructuredView]); - + // 获取评查点对应的样式类 // const getHighlightClass = (status: string) => { // switch (status) { diff --git a/app/routes/reviews.tsx b/app/routes/reviews.tsx index e511678..eda0085 100644 --- a/app/routes/reviews.tsx +++ b/app/routes/reviews.tsx @@ -178,8 +178,10 @@ export async function loader({ request }: LoaderFunctionArgs) { const url = new URL(request.url); const id = url.searchParams.get('id') || undefined; const previousRoute = url.searchParams.get('previousRoute') || ''; - // console.log("id-------",id); + // console.log("[Reviews Loader] 开始加载,id:", id, "previousRoute:", previousRoute); + if (!id) { + console.error("[Reviews Loader] 文件ID不能为空"); return Response.json({ result: false, message: '文件ID不能为空' }); } @@ -190,16 +192,13 @@ export async function loader({ request }: LoaderFunctionArgs) { // 获取评查点数据,传递request对象 const reviewData = await getReviewPoints(id, request); - // console.log("documentData-------",JSON.stringify(documentData.data,null,2)); - // console.log("reviewData-------",JSON.stringify('data' in reviewData ? reviewData.data : '',null,2)); if ('error' in reviewData && reviewData.error) { - console.error("获取评查点数据错误:", reviewData.error); + console.error("[Reviews Loader] 获取评查点数据错误:", reviewData.error); return Response.json({ result: false, message: reviewData.error }); } // 确保reviewData有效且具有预期的属性 if ('document' in reviewData && 'data' in reviewData && 'reviewInfo' in reviewData && 'stats' in reviewData) { - // console.log("reviewData-------",JSON.stringify(reviewData.data)); return Response.json({ previousRoute: previousRoute, document: reviewData.document, @@ -211,12 +210,13 @@ export async function loader({ request }: LoaderFunctionArgs) { frontendJWT }); } else { - console.error("返回的评查数据格式不正确",JSON.stringify(reviewData,null,2)); + console.error("[Reviews Loader] 返回的评查数据格式不正确,完整数据:", JSON.stringify(reviewData, null, 2)); return Response.json({ result: false, message: '返回的评查数据格式不正确' }); } } catch (error) { - console.error('获取评查数据失败:', error); - return Response.json({ result: false, message: '获取评查数据失败' }); + console.error('[Reviews Loader] 获取评查数据失败:', error); + console.error('[Reviews Loader] 错误堆栈:', error instanceof Error ? error.stack : '无堆栈信息'); + return Response.json({ result: false, message: `获取评查数据失败: ${error instanceof Error ? error.message : '未知错误'}` }); } } @@ -226,7 +226,7 @@ export async function action({ request }: ActionFunctionArgs) { const formData = await request.formData(); const intent = formData.get("intent") as string; - console.log('Action接收到请求, intent:', intent); + // console.log('Action接收到请求, intent:', intent); if (intent === "updateReviewResult") { const reviewPointResultId = formData.get("reviewPointResultId") as string; @@ -292,8 +292,18 @@ export async function action({ request }: ActionFunctionArgs) { export default function ReviewDetails() { const navigate = useNavigate(); const loaderData = useLoaderData(); + + // 调试:查看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('preview'); // 'preview', 'analysis', 'fileinfo' const [reviewData, setReviewData] = useState(null); @@ -306,10 +316,15 @@ export default function ReviewDetails() { newStatus: string; message: string; } | null>(null); - + // loader 数据加载出错 useEffect(()=>{ loadingBarService.hide(); + console.log('[Reviews Component] useEffect检查loaderData:', { + hasResultKey: Object.keys(loaderData).find(key => key === 'result'), + resultValue: loaderData.result, + willNavigateBack: Object.keys(loaderData).find(key => key === 'result') && !loaderData.result + }); if(Object.keys(loaderData).find(key => key === 'result') && !loaderData.result){ messageService.show({ title: '错误', @@ -717,15 +732,26 @@ export default function ReviewDetails() {
{/* 左侧:文件预览 */}
- + {(() => { + console.log('[Reviews] 准备渲染FilePreview', { + hasDocument: !!document, + documentPath: document?.path, + targetPage, + hasCharPositions: !!charPositions, + charPositionsLength: charPositions?.length + }); + return ( + + ); + })()}
- + {/* 右侧:评查结果 */}