From d60405ceda63667f6441f235fe6812431049d8f8 Mon Sep 17 00:00:00 2001 From: PingChuan <1259732256@qq.com> Date: Tue, 9 Dec 2025 17:56:45 +0800 Subject: [PATCH] =?UTF-8?q?fix=EF=BC=9A=E4=BF=AE=E5=A4=8DCollabora?= =?UTF-8?q?=E7=BB=84=E4=BB=B6=E9=AB=98=E4=BA=AE=E4=BC=A0=E5=85=A5=E9=A1=B5?= =?UTF-8?q?=E7=A0=81=E6=97=B6=E5=A4=B1=E6=95=88=E7=9A=84=E9=97=AE=E9=A2=98?= =?UTF-8?q?=EF=BC=8C=E5=A2=9E=E5=BC=BA=E4=B8=A4=E7=A7=8D=E9=AB=98=E4=BA=AE?= =?UTF-8?q?=E6=96=B9=E6=B3=95=E7=9A=84=E5=85=BC=E5=AE=B9=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/components/collabora/CollaboraViewer.tsx | 1196 +++++++++--------- 1 file changed, 620 insertions(+), 576 deletions(-) diff --git a/app/components/collabora/CollaboraViewer.tsx b/app/components/collabora/CollaboraViewer.tsx index 43b5663..2856881 100644 --- a/app/components/collabora/CollaboraViewer.tsx +++ b/app/components/collabora/CollaboraViewer.tsx @@ -10,25 +10,25 @@ * @encoding UTF-8 */ -import { useRef, forwardRef, useImperativeHandle, useState, useEffect } from 'react'; -import type { CollaboraViewerProps, CollaboraViewerHandle } from './types'; -import { useCollaboraConfig, useDocumentReady, useCollaboraUnoCommands } from './hooks'; -import { sendUnoCommand } from './Uno'; -import { clearHighlights } from './lib/ClearHighlight'; +import { forwardRef, useEffect, useImperativeHandle, useRef, useState } from 'react'; +import { useCollaboraConfig, useCollaboraUnoCommands, useDocumentReady } from './hooks'; import { - unoScrollToTop, - requestPageInfo, customGotoPage, - unoSearchNext, - unoReplaceCurrent, - unoReplaceAll, - unoCancelSearch, + highlightText as pythonHighlightText, replaceTextInPage, + requestPageInfo, + unoCancelSearch, unoHighlightText, - unoClearHighlight, - type PageInfo, - type GotoPageResponse + unoReplaceAll, + unoReplaceCurrent, + unoScrollToTop, + unoSearchNext, + type GotoPageResponse, + type PageInfo } from './lib'; +import { clearHighlights } from './lib/ClearHighlight'; +import type { CollaboraViewerHandle, CollaboraViewerProps } from './types'; +import { sendUnoCommand } from './Uno'; /** * Collabora 文档查看器组件 @@ -185,7 +185,9 @@ export const CollaboraViewer = forwardRef { // 如果文档未加载完成,不执行跳转和高亮 if (!isDocumentLoaded || !iframeRef.current?.contentWindow) { @@ -199,7 +201,7 @@ export const CollaboraViewer = forwardRef setTimeout(resolve, 100)); - // 步骤2:使用 UNO 命令高亮新文本(搜索 + 设置背景色) - console.log(`[CollaboraViewer] 步骤2:高亮文本 "${textToHighlight}"`); - unoHighlightText(iframeWindow, textToHighlight, 16776960); // 黄色 + // 步骤2:根据是否有 targetPage 选择高亮方式 + if (targetPage !== undefined && targetPage !== null) { + // 方案A:有 targetPage - 使用 Python 脚本(跨页搜索 + 高亮 + 跳转) + console.log(`[CollaboraViewer] 步骤2A:使用 Python 脚本跳转到第 ${targetPage} 页并高亮 "${textToHighlight}"`); - // 短暂延迟,确保高亮操作完成 - await new Promise(resolve => setTimeout(resolve, 100)); + const result = await pythonHighlightText(iframeWindow, textToHighlight, { + color: 16776960, // 黄色 + page: targetPage, + }); - // 步骤3:取消选中状态(避免高亮后文本仍被选中) - console.log('[CollaboraViewer] 步骤3:取消选中状态...'); - sendUnoCommand(iframeWindow, '.uno:Escape', {}); + if (result.success) { + console.log(`[CollaboraViewer] ✓ Python 高亮成功: "${textToHighlight}" (第${targetPage}页, 共${result.highlightedCount}处)`); + } else { + console.error('[CollaboraViewer] ✗ Python 高亮失败:', result.message); + } + } else { + // 方案B:无 targetPage - 使用 UNO 命令(当前页面高亮) + console.log(`[CollaboraViewer] 步骤2B:使用 UNO 命令在当前页高亮 "${textToHighlight}"`); - console.log(`[CollaboraViewer] ✓ 高亮完成: "${textToHighlight}"`); + unoHighlightText(iframeWindow, textToHighlight, 16776960); // 黄色 + + // 短暂延迟,确保高亮操作完成 + await new Promise(resolve => setTimeout(resolve, 100)); + + // 取消选中状态(避免高亮后文本仍被选中) + console.log('[CollaboraViewer] 步骤3:取消选中状态...'); + sendUnoCommand(iframeWindow, '.uno:Escape', {}); + + console.log(`[CollaboraViewer] ✓ UNO 高亮完成: "${textToHighlight}"`); + } } catch (error) { console.error('[CollaboraViewer] 高亮失败:', error); } @@ -397,442 +417,466 @@ export const CollaboraViewer = forwardRef -
-
-

加载文档配置中...

+ // 加载中状态 + if (loading) { + return ( +
+
+
+

加载文档配置中...

+
-
- ); - } + ); + } - // 错误状态 - if (error || !config) { - return ( -
-
- -

{error}

- {/*

请刷新页面重试或联系管理员

*/} + // 错误状态 + if (error || !config) { + return ( +
+
+ +

{error}

+ {/*

请刷新页面重试或联系管理员

*/} +
-
- ); - } - - // 发送 UNO 命令的处理函数(测试用) - const sendUno = () => { - if (!iframeRef.current?.contentWindow) { - setUnoResult('iframe 不可用'); - return; + ); } - let args: Record = {}; - const raw = (unoArgs || '').trim(); - if (raw !== '') { - try { - args = JSON.parse(raw) as Record; - } catch (err) { - try { - // fallback: replace single quotes with double quotes and parse - args = JSON.parse(raw.replace(/'(.*?)'/g, '"$1"')) as Record; - } catch (err2) { - console.error('解析 UNO Args 失败:', err2); - setUnoResult('Args 解析失败,请使用有效 JSON'); - return; - } - } - } - - try { - // 使用正确的 sendUnoCommand 方法 - sendUnoCommand(iframeRef.current.contentWindow, unoCmd, args); - setUnoResult(`已发送: ${unoCmd}`); - console.log('[UNO Debug] 发送命令:', unoCmd, args); - } catch (e) { - console.error('发送 UNO 失败:', e); - setUnoResult('发送失败,请查看控制台'); - } - }; - - // 高亮测试处理函数 - const handleSwitchHighlight = async () => { - if (!iframeRef.current?.contentWindow) { - // setHighlightResult('iframe 未就绪'); - return; - } - - if (!highlightTextInput || highlightTextInput.trim() === '') { - setHighlightResult('请输入要高亮的文本'); - return; - } - - try { - // 解析页码 (可选) - const page = highlightPage && highlightPage.trim() !== '' - ? parseInt(highlightPage.trim(), 10) - : undefined; - - // 验证页码 - if (page !== undefined && (isNaN(page) || page < 1)) { - setHighlightResult('页码必须是大于0的整数'); + // 发送 UNO 命令的处理函数(测试用) + const sendUno = () => { + if (!iframeRef.current?.contentWindow) { + setUnoResult('iframe 不可用'); return; } - await performTextHighlight( - iframeRef.current.contentWindow, - highlightTextInput.trim(), - { page } - ); - - // 更新上一次高亮的文本 - setPreviousHighlightText(highlightTextInput.trim()); - const pageInfo = page ? ` (第${page}页)` : ''; - setHighlightResult(`✓ 已切换高亮: ${highlightTextInput.trim()}${pageInfo}`); - } catch (e) { - console.error('切换高亮失败:', e); - setHighlightResult(`切换失败: ${e instanceof Error ? e.message : '未知错误'}`); - } - }; - - // 滚动到顶部处理函数 - const handleScrollToTop = async () => { - if (!iframeRef.current?.contentWindow) { - console.error('iframe 未就绪'); - return; - } - - try { - await unoScrollToTop(iframeRef.current.contentWindow); - // console.log('[CollaboraViewer] 已滚动到文档顶部'); - } catch (e) { - console.error('滚动到顶部失败:', e); - } - }; - - // 获取页数信息处理函数 - const handleGetPageInfo = async () => { - if (!iframeRef.current?.contentWindow) { - // setPageInfoResult('iframe 未就绪'); - return; - } - - setIsLoadingPageInfo(true); - setPageInfoResult('正在获取页数信息...'); - - try { - const info: PageInfo = await requestPageInfo(iframeRef.current.contentWindow); - setPageInfoResult( - `✓ 总页数: ${info.totalPages} | 当前页: ${info.currentPage + 1} (索引: ${info.currentPage})` - ); - console.log('[CollaboraViewer] 页数信息:', info); - - // 3秒后清除提示 - setTimeout(() => { - setPageInfoResult(null); - }, 3000); - } catch (e) { - console.error('获取页数信息失败:', e); - setPageInfoResult(`✗ 获取失败: ${e instanceof Error ? e.message : '未知错误'}`); - - // 5秒后清除错误提示 - setTimeout(() => { - setPageInfoResult(null); - }, 5000); - } finally { - setIsLoadingPageInfo(false); - } - }; - - // 页面跳转处理函数 - const handleGotoPage = async () => { - if (!iframeRef.current?.contentWindow) { - // setGotoPageResult('iframe 未就绪'); - return; - } - - const pageNumber = parseInt(gotoPageInput.trim(), 10); - if (isNaN(pageNumber) || pageNumber < 1) { - setGotoPageResult('请输入有效的页码 (大于0的整数)'); - return; - } - - setIsJumping(true); - // setGotoPageResult(`正在跳转到第 ${pageNumber} 页...`); - - try { - const response: GotoPageResponse = await customGotoPage( - iframeRef.current.contentWindow, - pageNumber - ); - setGotoPageResult( - `✓ 已跳转到第 ${response.pageNumber} 页 (总页数: ${response.totalPages})` - ); - // console.log('[CollaboraViewer] 页面跳转成功:', response); - - // 3秒后清除提示 - setTimeout(() => { - setGotoPageResult(null); - }, 3000); - } catch (e) { - console.error('页面跳转失败:', e); - setGotoPageResult(`✗ 跳转失败: ${e instanceof Error ? e.message : '未知错误'}`); - - // 5秒后清除错误提示 - setTimeout(() => { - setGotoPageResult(null); - }, 5000); - } finally { - setIsJumping(false); - } - }; - - // 清除高亮处理函数 - const handleClearHighlights = async () => { - if (!iframeRef.current?.contentWindow) { - // setClearHighlightResult('iframe 未就绪'); - return; - } - - setIsClearing(true); - setClearHighlightResult('正在清除高亮...'); - - try { - const result = await clearHighlights(iframeRef.current.contentWindow, { - color: 16776960, // 黄色 - timeout: 10000, - }); - - if (result.success) { - setClearHighlightResult(`✓ ${result.message}`); - // console.log('[CollaboraViewer] 清除高亮成功:', result); - - // 清空之前记录的高亮文本 - setPreviousHighlightText(null); - } else { - setClearHighlightResult(`✗ 清除失败: ${result.message}`); - console.error('[CollaboraViewer] 清除高亮失败:', result); - } - - // 3秒后清除提示 - setTimeout(() => { - setClearHighlightResult(null); - }, 3000); - } catch (e) { - console.error('清除高亮失败:', e); - setClearHighlightResult(`✗ 清除失败: ${e instanceof Error ? e.message : '未知错误'}`); - - // 5秒后清除错误提示 - setTimeout(() => { - setClearHighlightResult(null); - }, 5000); - } finally { - setIsClearing(false); - } - }; - - // 搜索下一个处理函数 - const handleSearchNext = async () => { - if (!iframeRef.current?.contentWindow) { - // setSearchReplaceResult('iframe 未就绪'); - return; - } - - if (!searchText.trim()) { - setSearchReplaceResult('请输入搜索文本'); - return; - } - - try { - const currentSearchText = searchText.trim(); - const currentPageNumber = searchReplacePageNumber.trim(); - - // 判断是否需要跳转页面(搜索文本或页码发生变化) - const needsPageJump = - lastSearchParams.text !== currentSearchText || - lastSearchParams.page !== currentPageNumber; - - // 如果指定了页码且需要跳转,先跳转到该页 - if (needsPageJump && currentPageNumber !== '') { - const pageNumber = parseInt(currentPageNumber, 10); - if (isNaN(pageNumber) || pageNumber < 1) { - setSearchReplaceResult('✗ 页码必须是大于0的整数'); - return; - } - - // setSearchReplaceResult(`正在跳转到第 ${pageNumber} 页...`); + let args: Record = {}; + const raw = (unoArgs || '').trim(); + if (raw !== '') { try { - await customGotoPage(iframeRef.current.contentWindow, pageNumber); - // 短暂延迟等待页面渲染 + args = JSON.parse(raw) as Record; + } catch (err) { + try { + // fallback: replace single quotes with double quotes and parse + args = JSON.parse(raw.replace(/'(.*?)'/g, '"$1"')) as Record; + } catch (err2) { + console.error('解析 UNO Args 失败:', err2); + setUnoResult('Args 解析失败,请使用有效 JSON'); + return; + } + } + } + + try { + // 使用正确的 sendUnoCommand 方法 + sendUnoCommand(iframeRef.current.contentWindow, unoCmd, args); + setUnoResult(`已发送: ${unoCmd}`); + console.log('[UNO Debug] 发送命令:', unoCmd, args); + } catch (e) { + console.error('发送 UNO 失败:', e); + setUnoResult('发送失败,请查看控制台'); + } + }; + + // 高亮测试处理函数 + const handleSwitchHighlight = async () => { + if (!iframeRef.current?.contentWindow) { + // setHighlightResult('iframe 未就绪'); + return; + } + + if (!highlightTextInput || highlightTextInput.trim() === '') { + setHighlightResult('请输入要高亮的文本'); + return; + } + + try { + // 解析页码 (可选) + const page = highlightPage && highlightPage.trim() !== '' + ? parseInt(highlightPage.trim(), 10) + : undefined; + + // 验证页码 + if (page !== undefined && (isNaN(page) || page < 1)) { + setHighlightResult('页码必须是大于0的整数'); + return; + } + + const iframeWindow = iframeRef.current.contentWindow; + const textToHighlight = highlightTextInput.trim(); + + // 先清除旧高亮 + await clearHighlights(iframeWindow, { + color: 16776960, + timeout: 5000, + }); + + await new Promise(resolve => setTimeout(resolve, 100)); + + // 根据是否有页码选择高亮方式 + if (page !== undefined) { + // 使用 Python 脚本(跨页搜索 + 高亮 + 跳转) + const result = await pythonHighlightText(iframeWindow, textToHighlight, { + color: 16776960, + page: page, + }); + + if (result.success) { + setPreviousHighlightText(textToHighlight); + setHighlightResult(`✓ 已切换高亮: ${textToHighlight} (第${page}页, 共${result.highlightedCount}处)`); + } else { + setHighlightResult(`✗ 高亮失败: ${result.message}`); + } + } else { + // 使用 UNO 命令(当前页面高亮) + unoHighlightText(iframeWindow, textToHighlight, 16776960); + await new Promise(resolve => setTimeout(resolve, 100)); + sendUnoCommand(iframeWindow, '.uno:Escape', {}); + + setPreviousHighlightText(textToHighlight); + setHighlightResult(`✓ 已切换高亮: ${textToHighlight} (当前页)`); + } + } catch (e) { + console.error('切换高亮失败:', e); + setHighlightResult(`切换失败: ${e instanceof Error ? e.message : '未知错误'}`); + } + }; + + // 滚动到顶部处理函数 + const handleScrollToTop = async () => { + if (!iframeRef.current?.contentWindow) { + console.error('iframe 未就绪'); + return; + } + + try { + await unoScrollToTop(iframeRef.current.contentWindow); + // console.log('[CollaboraViewer] 已滚动到文档顶部'); + } catch (e) { + console.error('滚动到顶部失败:', e); + } + }; + + // 获取页数信息处理函数 + const handleGetPageInfo = async () => { + if (!iframeRef.current?.contentWindow) { + // setPageInfoResult('iframe 未就绪'); + return; + } + + setIsLoadingPageInfo(true); + setPageInfoResult('正在获取页数信息...'); + + try { + const info: PageInfo = await requestPageInfo(iframeRef.current.contentWindow); + setPageInfoResult( + `✓ 总页数: ${info.totalPages} | 当前页: ${info.currentPage + 1} (索引: ${info.currentPage})` + ); + console.log('[CollaboraViewer] 页数信息:', info); + + // 3秒后清除提示 + setTimeout(() => { + setPageInfoResult(null); + }, 3000); + } catch (e) { + console.error('获取页数信息失败:', e); + setPageInfoResult(`✗ 获取失败: ${e instanceof Error ? e.message : '未知错误'}`); + + // 5秒后清除错误提示 + setTimeout(() => { + setPageInfoResult(null); + }, 5000); + } finally { + setIsLoadingPageInfo(false); + } + }; + + // 页面跳转处理函数 + const handleGotoPage = async () => { + if (!iframeRef.current?.contentWindow) { + // setGotoPageResult('iframe 未就绪'); + return; + } + + const pageNumber = parseInt(gotoPageInput.trim(), 10); + if (isNaN(pageNumber) || pageNumber < 1) { + setGotoPageResult('请输入有效的页码 (大于0的整数)'); + return; + } + + setIsJumping(true); + // setGotoPageResult(`正在跳转到第 ${pageNumber} 页...`); + + try { + const response: GotoPageResponse = await customGotoPage( + iframeRef.current.contentWindow, + pageNumber + ); + setGotoPageResult( + `✓ 已跳转到第 ${response.pageNumber} 页 (总页数: ${response.totalPages})` + ); + // console.log('[CollaboraViewer] 页面跳转成功:', response); + + // 3秒后清除提示 + setTimeout(() => { + setGotoPageResult(null); + }, 3000); + } catch (e) { + console.error('页面跳转失败:', e); + setGotoPageResult(`✗ 跳转失败: ${e instanceof Error ? e.message : '未知错误'}`); + + // 5秒后清除错误提示 + setTimeout(() => { + setGotoPageResult(null); + }, 5000); + } finally { + setIsJumping(false); + } + }; + + // 清除高亮处理函数 + const handleClearHighlights = async () => { + if (!iframeRef.current?.contentWindow) { + // setClearHighlightResult('iframe 未就绪'); + return; + } + + setIsClearing(true); + setClearHighlightResult('正在清除高亮...'); + + try { + const result = await clearHighlights(iframeRef.current.contentWindow, { + color: 16776960, // 黄色 + timeout: 10000, + }); + + if (result.success) { + setClearHighlightResult(`✓ ${result.message}`); + // console.log('[CollaboraViewer] 清除高亮成功:', result); + + // 清空之前记录的高亮文本 + setPreviousHighlightText(null); + } else { + setClearHighlightResult(`✗ 清除失败: ${result.message}`); + console.error('[CollaboraViewer] 清除高亮失败:', result); + } + + // 3秒后清除提示 + setTimeout(() => { + setClearHighlightResult(null); + }, 3000); + } catch (e) { + console.error('清除高亮失败:', e); + setClearHighlightResult(`✗ 清除失败: ${e instanceof Error ? e.message : '未知错误'}`); + + // 5秒后清除错误提示 + setTimeout(() => { + setClearHighlightResult(null); + }, 5000); + } finally { + setIsClearing(false); + } + }; + + // 搜索下一个处理函数 + const handleSearchNext = async () => { + if (!iframeRef.current?.contentWindow) { + // setSearchReplaceResult('iframe 未就绪'); + return; + } + + if (!searchText.trim()) { + setSearchReplaceResult('请输入搜索文本'); + return; + } + + try { + const currentSearchText = searchText.trim(); + const currentPageNumber = searchReplacePageNumber.trim(); + + // 判断是否需要跳转页面(搜索文本或页码发生变化) + const needsPageJump = + lastSearchParams.text !== currentSearchText || + lastSearchParams.page !== currentPageNumber; + + // 如果指定了页码且需要跳转,先跳转到该页 + if (needsPageJump && currentPageNumber !== '') { + const pageNumber = parseInt(currentPageNumber, 10); + if (isNaN(pageNumber) || pageNumber < 1) { + setSearchReplaceResult('✗ 页码必须是大于0的整数'); + return; + } + + // setSearchReplaceResult(`正在跳转到第 ${pageNumber} 页...`); + try { + await customGotoPage(iframeRef.current.contentWindow, pageNumber); + // 短暂延迟等待页面渲染 + await new Promise(resolve => setTimeout(resolve, 300)); + } catch (e) { + console.error('跳转页面失败:', e); + setSearchReplaceResult(`✗ 跳转失败: ${e instanceof Error ? e.message : '未知错误'}`); + return; + } + } + + // 执行搜索(如果是页面内循环查找,会自动找到下一个匹配项) + unoSearchNext(iframeRef.current.contentWindow, currentSearchText); + + // 更新搜索参数记录 + setLastSearchParams({ + text: currentSearchText, + page: currentPageNumber + }); + + // const pageInfo = currentPageNumber !== '' + // ? ` (第${currentPageNumber}页内循环查找)` + // : ''; + // const jumpInfo = needsPageJump && currentPageNumber !== '' ? ' [已跳转]' : ''; + // setSearchReplaceResult(`✓ 搜索: "${currentSearchText}"${pageInfo}${jumpInfo}`); + + // 3秒后清除提示 + setTimeout(() => setSearchReplaceResult(null), 3000); + } catch (e) { + console.error('搜索失败:', e); + setSearchReplaceResult(`✗ 搜索失败: ${e instanceof Error ? e.message : '未知错误'}`); + } + }; + + // 替换处理函数 + const handleReplace = async () => { + if (!iframeRef.current?.contentWindow) { + // setSearchReplaceResult('iframe 未就绪'); + return; + } + + if (!searchText.trim()) { + setSearchReplaceResult('请输入搜索文本'); + return; + } + + try { + // 如果指定了页码,先跳转到该页 + if (searchReplacePageNumber && searchReplacePageNumber.trim() !== '') { + const pageNumber = parseInt(searchReplacePageNumber.trim(), 10); + if (isNaN(pageNumber) || pageNumber < 1) { + setSearchReplaceResult('✗ 页码必须是大于0的整数'); + return; + } + + // setSearchReplaceResult(`正在跳转到第 ${pageNumber} 页...`); + try { + await customGotoPage(iframeRef.current.contentWindow, pageNumber); + // 短暂延迟等待页面渲染 + await new Promise(resolve => setTimeout(resolve, 300)); + } catch (e) { + console.error('跳转页面失败:', e); + setSearchReplaceResult(`✗ 跳转失败: ${e instanceof Error ? e.message : '未知错误'}`); + return; + } + } + + const pageInfo = searchReplacePageNumber && searchReplacePageNumber.trim() !== '' + ? ` (第${searchReplacePageNumber}页)` + : ''; + + if (replaceAllMode) { + // 替换全部 + unoReplaceAll(iframeRef.current.contentWindow, searchText.trim(), replaceText); + setSearchReplaceResult(`✓ 已替换全部: "${searchText.trim()}" → "${replaceText}"${pageInfo}`); + } else { + // 替换当前选中项:先搜索,再替换 + console.log('[handleReplace] 开始替换流程:先搜索,再替换'); + + // 步骤1:先执行搜索,确保文本被选中 + unoSearchNext(iframeRef.current.contentWindow, searchText.trim()); + + // 步骤2:等待搜索完成后执行替换 await new Promise(resolve => setTimeout(resolve, 300)); - } catch (e) { - console.error('跳转页面失败:', e); - setSearchReplaceResult(`✗ 跳转失败: ${e instanceof Error ? e.message : '未知错误'}`); - return; - } - } - // 执行搜索(如果是页面内循环查找,会自动找到下一个匹配项) - unoSearchNext(iframeRef.current.contentWindow, currentSearchText); + // 步骤3:执行替换 + unoReplaceCurrent(iframeRef.current.contentWindow, searchText.trim(), replaceText); - // 更新搜索参数记录 - setLastSearchParams({ - text: currentSearchText, - page: currentPageNumber - }); + setSearchReplaceResult(`✓ 已替换: "${searchText.trim()}" → "${replaceText}"${pageInfo}`); - // const pageInfo = currentPageNumber !== '' - // ? ` (第${currentPageNumber}页内循环查找)` - // : ''; - // const jumpInfo = needsPageJump && currentPageNumber !== '' ? ' [已跳转]' : ''; - // setSearchReplaceResult(`✓ 搜索: "${currentSearchText}"${pageInfo}${jumpInfo}`); - - // 3秒后清除提示 - setTimeout(() => setSearchReplaceResult(null), 3000); - } catch (e) { - console.error('搜索失败:', e); - setSearchReplaceResult(`✗ 搜索失败: ${e instanceof Error ? e.message : '未知错误'}`); - } - }; - - // 替换处理函数 - const handleReplace = async () => { - if (!iframeRef.current?.contentWindow) { - // setSearchReplaceResult('iframe 未就绪'); - return; - } - - if (!searchText.trim()) { - setSearchReplaceResult('请输入搜索文本'); - return; - } - - try { - // 如果指定了页码,先跳转到该页 - if (searchReplacePageNumber && searchReplacePageNumber.trim() !== '') { - const pageNumber = parseInt(searchReplacePageNumber.trim(), 10); - if (isNaN(pageNumber) || pageNumber < 1) { - setSearchReplaceResult('✗ 页码必须是大于0的整数'); - return; + console.log('[handleReplace] 替换完成'); } - // setSearchReplaceResult(`正在跳转到第 ${pageNumber} 页...`); - try { - await customGotoPage(iframeRef.current.contentWindow, pageNumber); - // 短暂延迟等待页面渲染 - await new Promise(resolve => setTimeout(resolve, 300)); - } catch (e) { - console.error('跳转页面失败:', e); - setSearchReplaceResult(`✗ 跳转失败: ${e instanceof Error ? e.message : '未知错误'}`); - return; + // 3秒后清除提示 + setTimeout(() => setSearchReplaceResult(null), 3000); + } catch (e) { + console.error('替换失败:', e); + setSearchReplaceResult(`✗ 替换失败: ${e instanceof Error ? e.message : '未知错误'}`); + } + }; + + // 取消搜索处理函数 + const handleCancelSearch = () => { + if (!iframeRef.current?.contentWindow) { + return; + } + + try { + unoCancelSearch(iframeRef.current.contentWindow); + // 重置搜索参数记录,下次搜索会重新定位 + setLastSearchParams({ text: '', page: '' }); + setSearchReplaceResult('✓ 已取消搜索'); + setTimeout(() => setSearchReplaceResult(null), 2000); + } catch (e) { + console.error('取消搜索失败:', e); + } + }; + + // 页面定点替换处理函数 + const handleReplaceInPage = async () => { + if (!iframeRef.current?.contentWindow) { + // setReplaceInPageResult('iframe 未就绪'); + return; + } + + const pageNum = parseInt(replaceInPageNumber.trim(), 10); + if (isNaN(pageNum) || pageNum < 1) { + setReplaceInPageResult('请输入有效的页码 (大于0的整数)'); + return; + } + + if (!replaceInPageSearch.trim()) { + setReplaceInPageResult('请输入原文'); + return; + } + + setIsReplacingInPage(true); + setReplaceInPageResult(`正在第 ${pageNum} 页执行替换...`); + + try { + const result = await replaceTextInPage( + iframeRef.current.contentWindow, + pageNum, + replaceInPageSearch.trim(), + replaceInPageReplace + ); + + if (result.success) { + setReplaceInPageResult(`✓ ${result.message}`); + } else { + setReplaceInPageResult(`✗ ${result.message}`); } + + // 5秒后清除提示 + setTimeout(() => setReplaceInPageResult(null), 5000); + } catch (e) { + console.error('页面定点替换失败:', e); + setReplaceInPageResult(`✗ 执行失败: ${e instanceof Error ? e.message : '未知错误'}`); + + // 5秒后清除错误提示 + setTimeout(() => setReplaceInPageResult(null), 5000); + } finally { + setIsReplacingInPage(false); } + }; - const pageInfo = searchReplacePageNumber && searchReplacePageNumber.trim() !== '' - ? ` (第${searchReplacePageNumber}页)` - : ''; - - if (replaceAllMode) { - // 替换全部 - unoReplaceAll(iframeRef.current.contentWindow, searchText.trim(), replaceText); - setSearchReplaceResult(`✓ 已替换全部: "${searchText.trim()}" → "${replaceText}"${pageInfo}`); - } else { - // 替换当前选中项:先搜索,再替换 - console.log('[handleReplace] 开始替换流程:先搜索,再替换'); - - // 步骤1:先执行搜索,确保文本被选中 - unoSearchNext(iframeRef.current.contentWindow, searchText.trim()); - - // 步骤2:等待搜索完成后执行替换 - await new Promise(resolve => setTimeout(resolve, 300)); - - // 步骤3:执行替换 - unoReplaceCurrent(iframeRef.current.contentWindow, searchText.trim(), replaceText); - - setSearchReplaceResult(`✓ 已替换: "${searchText.trim()}" → "${replaceText}"${pageInfo}`); - - console.log('[handleReplace] 替换完成'); - } - - // 3秒后清除提示 - setTimeout(() => setSearchReplaceResult(null), 3000); - } catch (e) { - console.error('替换失败:', e); - setSearchReplaceResult(`✗ 替换失败: ${e instanceof Error ? e.message : '未知错误'}`); - } - }; - - // 取消搜索处理函数 - const handleCancelSearch = () => { - if (!iframeRef.current?.contentWindow) { - return; - } - - try { - unoCancelSearch(iframeRef.current.contentWindow); - // 重置搜索参数记录,下次搜索会重新定位 - setLastSearchParams({ text: '', page: '' }); - setSearchReplaceResult('✓ 已取消搜索'); - setTimeout(() => setSearchReplaceResult(null), 2000); - } catch (e) { - console.error('取消搜索失败:', e); - } - }; - - // 页面定点替换处理函数 - const handleReplaceInPage = async () => { - if (!iframeRef.current?.contentWindow) { - // setReplaceInPageResult('iframe 未就绪'); - return; - } - - const pageNum = parseInt(replaceInPageNumber.trim(), 10); - if (isNaN(pageNum) || pageNum < 1) { - setReplaceInPageResult('请输入有效的页码 (大于0的整数)'); - return; - } - - if (!replaceInPageSearch.trim()) { - setReplaceInPageResult('请输入原文'); - return; - } - - setIsReplacingInPage(true); - setReplaceInPageResult(`正在第 ${pageNum} 页执行替换...`); - - try { - const result = await replaceTextInPage( - iframeRef.current.contentWindow, - pageNum, - replaceInPageSearch.trim(), - replaceInPageReplace - ); - - if (result.success) { - setReplaceInPageResult(`✓ ${result.message}`); - } else { - setReplaceInPageResult(`✗ ${result.message}`); - } - - // 5秒后清除提示 - setTimeout(() => setReplaceInPageResult(null), 5000); - } catch (e) { - console.error('页面定点替换失败:', e); - setReplaceInPageResult(`✗ 执行失败: ${e instanceof Error ? e.message : '未知错误'}`); - - // 5秒后清除错误提示 - setTimeout(() => setReplaceInPageResult(null), 5000); - } finally { - setIsReplacingInPage(false); - } - }; - - return ( -
- {/* UNO 命令测试面板 - 临时隐藏 */} - {/*
+ return ( +
+ {/* UNO 命令测试面板 - 临时隐藏 */} + {/*
UNO: */} - {/* 高亮测试面板 - 临时隐藏 */} - {/*
+ {/* 高亮测试面板 - 临时隐藏 */} + {/*
*/} - {/* 清除高亮测试面板 - 临时隐藏 */} - {/*
+ {/* 清除高亮测试面板 - 临时隐藏 */} + {/*
-
+ {/* 搜索替换测试面板 - 移动到左上角并添加关闭按钮 */} + {showSearchReplacePanel && ( +
+ {/* 标题栏和关闭按钮 */} +
+ 搜索替换 + +
-
- 搜索: - setSearchText(e.target.value)} - placeholder="输入要搜索的文本" - aria-label="搜索文本" - onKeyDown={(e) => { - if (e.key === 'Enter') { - handleSearchNext(); - } - }} - /> - 页码: - setSearchReplacePageNumber(e.target.value)} - placeholder="页码" - aria-label="页码" - type="number" - min="1" - title="可选:指定搜索/替换的页码" - /> - - -
-
-
- 替换: - setReplaceText(e.target.value)} - placeholder="输入替换后的文本" - aria-label="替换文本" - onKeyDown={(e) => { - if (e.key === 'Enter') { - handleReplace(); - } - }} - /> - - {/*
- {searchReplaceResult && ( -
- {searchReplaceResult} + +
+ {searchReplaceResult && ( +
+ {searchReplaceResult} +
+ )}
)} -
- )} - {/* 页面定点替换测试面板 - 临时隐藏 */} - {/*
+ {/* 页面定点替换测试面板 - 临时隐藏 */} + {/*
页面定点替换
页码: @@ -1112,37 +1155,38 @@ export const CollaboraViewer = forwardRef */} - {/* 文档加载提示 */} - {!isDocumentLoaded && ( -
-
-
-

正在加载文档...

-

{config.fileName}

+ {/* 文档加载提示 */} + {!isDocumentLoaded && ( +
+
+
+

正在加载文档...

+

{config.fileName}

+
-
- )} + )} - {/* Collabora iframe - tabIndex is needed for keyboard navigation */} - {/* eslint-disable jsx-a11y/no-noninteractive-tabindex */} -