/** * DOCX 预览组件(设计稿 7c-简化C-极简 · 中栏实现) * * 使用 CollaboraViewer 渲染 DOCX 文件。 * 工具栏与 PdfPreviewTest 一致,额外增加清除高亮、返回顶部按钮。 */ import { useState, useEffect, useMemo, useRef } from 'react'; import { CollaboraViewer } from '~/components/collabora/CollaboraViewer'; import type { CollaboraViewerHandle } from '~/components/collabora/types'; import { customGotoPage } from '~/components/collabora/lib'; import { toastService } from '~/components/ui/Toast'; import type { ReviewPoint } from '../ReviewPointsList'; interface DocxPreviewTestProps { filePath: string; targetPage?: number; charPositions?: Array<{ box: number[][]; char: string; score: number }>; activeReviewPointResultId?: string | null; reviewPoints?: ReviewPoint[]; highlightValue?: string; aiSuggestionReplace?: { searchText: string; replaceText: string; pageNumber: number; silentReplace?: boolean; }; userInfo?: { sub: string; nick_name: string }; } export function DocxPreviewTest({ filePath, targetPage, activeReviewPointResultId, reviewPoints, highlightValue, aiSuggestionReplace, userInfo, }: DocxPreviewTestProps) { const collaboraRef = useRef(null); const [pageInputValue, setPageInputValue] = useState(''); const [isClearingHighlights, setIsClearingHighlights] = useState(false); const [isScrollingToTop, setIsScrollingToTop] = useState(false); // 当前激活的评查点 const activePoint = useMemo( () => reviewPoints?.find(p => p.id === activeReviewPointResultId), [reviewPoints, activeReviewPointResultId], ); // 当前高亮标签 const highlightLabel = useMemo(() => { if (!activePoint) return null; const code = activePoint.pointCode || activePoint.id; const name = activePoint.pointName || activePoint.title || ''; return `${code}${name ? ' · ' + name : ''}`; }, [activePoint]); // ── 页码跳转 ── const handlePageInputChange = (e: React.ChangeEvent) => { setPageInputValue(e.target.value.replace(/\D/g, '')); }; const handlePageJump = async () => { if (!pageInputValue) return; const targetPageNum = parseInt(pageInputValue, 10); const iframeWindow = collaboraRef.current?.getIframeWindow?.(); if (!iframeWindow) { toastService.warning('文档尚未加载完成,请稍候...'); return; } if (targetPageNum > 0) { try { await customGotoPage(iframeWindow, targetPageNum); setPageInputValue(''); } catch (error) { toastService.error(`跳转失败: ${error instanceof Error ? error.message : '未知错误'}`); } } }; // ── 清除高亮 ── const handleClearAllHighlights = async () => { if (!collaboraRef.current?.isReady) { toastService.warning('文档尚未加载完成,请稍候...'); return; } setIsClearingHighlights(true); try { await collaboraRef.current.clearAllHighlights(); toastService.success('已清除所有高亮'); } catch (error) { console.error('[DocxPreviewTest] 清除高亮失败:', error); toastService.error('清除高亮失败'); } finally { setTimeout(() => setIsClearingHighlights(false), 500); } }; // ── 返回顶部 ── const handleScrollToTop = async () => { setIsScrollingToTop(true); try { await collaboraRef.current?.unoCommands.scrollToTop(); } catch (error) { console.error('[DocxPreviewTest] 返回顶部失败:', error); toastService.error('返回顶部失败'); } finally { setTimeout(() => setIsScrollingToTop(false), 500); } }; // ── 跳转到高亮页 ── const jumpToHighlight = () => { if (!activePoint) return; // 触发 targetPage 重新跳转(由父组件控制) toastService.info('已定位到当前评查点所在页'); }; return (
{/* ═══ 顶部工具栏 ═══ */}
{/* 页码跳转 */} e.currentTarget.select()} onBlur={handlePageJump} onKeyDown={e => { if (e.key === 'Enter') handlePageJump(); }} className="w-8 h-6 text-center bg-slate-50 border border-slate-200 rounded text-[12px] font-mono outline-none focus:border-[#00684a]" placeholder="-" /> | {/* 返回顶部 */} {/* 清除高亮 */}
{/* 右侧:当前高亮标签 */}
{highlightLabel && ( <> 当前高亮: {highlightLabel} )}
{/* ═══ Collabora 文档区域 ═══ */}
); }