feat:组装UNO命令实现特定页面搜索替换功能
This commit is contained in:
@@ -0,0 +1,306 @@
|
||||
/**
|
||||
* Collabora Online 搜索和替换功能
|
||||
*
|
||||
* 职责: 封装 UNO 搜索替换命令
|
||||
*
|
||||
* @encoding UTF-8
|
||||
*/
|
||||
|
||||
import { sendUnoCommand } from '../Uno';
|
||||
|
||||
/**
|
||||
* 搜索命令类型
|
||||
*/
|
||||
export enum SearchCommand {
|
||||
/** 查找下一个 */
|
||||
FindNext = 0,
|
||||
/** 查找上一个 */
|
||||
FindPrevious = 1,
|
||||
/** 替换当前选中 */
|
||||
Replace = 2,
|
||||
/** 替换全部 */
|
||||
ReplaceAll = 3,
|
||||
}
|
||||
|
||||
/**
|
||||
* 搜索选项
|
||||
*/
|
||||
export interface SearchOptions {
|
||||
/** 区分大小写 */
|
||||
caseSensitive?: boolean;
|
||||
/** 全字匹配 */
|
||||
wholeWord?: boolean;
|
||||
/** 使用正则表达式 */
|
||||
regexp?: boolean;
|
||||
/** 向后搜索 */
|
||||
backward?: boolean;
|
||||
/** 静默模式(不显示搜索结果提示) */
|
||||
quiet?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 替换选项
|
||||
*/
|
||||
export interface ReplaceOptions extends SearchOptions {
|
||||
/** 替换全部 */
|
||||
replaceAll?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 搜索文本 - 查找下一个匹配项
|
||||
* @param iframeWindow - iframe 的 contentWindow
|
||||
* @param text - 要搜索的文本
|
||||
* @param options - 搜索选项
|
||||
*/
|
||||
export function unoSearchNext(
|
||||
iframeWindow: Window,
|
||||
text: string,
|
||||
options: SearchOptions = {}
|
||||
): void {
|
||||
const {
|
||||
caseSensitive = false,
|
||||
wholeWord = false,
|
||||
regexp = false,
|
||||
backward = false,
|
||||
quiet = true,
|
||||
} = options;
|
||||
|
||||
// 计算 SearchFlags
|
||||
let searchFlags = 0;
|
||||
if (wholeWord) searchFlags |= 0x00010000; // SEARCH_WORD
|
||||
|
||||
sendUnoCommand(iframeWindow, '.uno:ExecuteSearch', {
|
||||
'SearchItem.SearchString': { type: 'string', value: text },
|
||||
'SearchItem.Command': { type: 'long', value: backward ? SearchCommand.FindPrevious : SearchCommand.FindNext },
|
||||
'SearchItem.Backward': { type: 'boolean', value: backward },
|
||||
'SearchItem.SearchCaseSensitive': { type: 'boolean', value: caseSensitive },
|
||||
'SearchItem.SearchRegularExpression': { type: 'boolean', value: regexp },
|
||||
'SearchItem.SearchFlags': { type: 'long', value: searchFlags },
|
||||
'SearchItem.AlgorithmType': { type: 'short', value: regexp ? 1 : 0 },
|
||||
'Quiet': { type: 'boolean', value: quiet },
|
||||
});
|
||||
|
||||
console.log('[SearchReplace] 搜索下一个:', text, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* 搜索文本 - 查找上一个匹配项
|
||||
* @param iframeWindow - iframe 的 contentWindow
|
||||
* @param text - 要搜索的文本
|
||||
* @param options - 搜索选项
|
||||
*/
|
||||
export function unoSearchPrevious(
|
||||
iframeWindow: Window,
|
||||
text: string,
|
||||
options: SearchOptions = {}
|
||||
): void {
|
||||
unoSearchNext(iframeWindow, text, { ...options, backward: true });
|
||||
console.log('[SearchReplace] 搜索上一个:', text);
|
||||
}
|
||||
|
||||
/**
|
||||
* 替换当前选中的匹配项
|
||||
* @param iframeWindow - iframe 的 contentWindow
|
||||
* @param searchText - 要搜索的文本
|
||||
* @param replaceText - 替换后的文本
|
||||
* @param options - 替换选项
|
||||
*/
|
||||
export function unoReplaceCurrent(
|
||||
iframeWindow: Window,
|
||||
searchText: string,
|
||||
replaceText: string,
|
||||
options: SearchOptions = {}
|
||||
): void {
|
||||
const {
|
||||
caseSensitive = false,
|
||||
wholeWord = false,
|
||||
regexp = false,
|
||||
quiet = true,
|
||||
} = options;
|
||||
|
||||
let searchFlags = 0;
|
||||
if (wholeWord) searchFlags |= 0x00010000;
|
||||
|
||||
sendUnoCommand(iframeWindow, '.uno:ExecuteSearch', {
|
||||
'SearchItem.SearchString': { type: 'string', value: searchText },
|
||||
'SearchItem.ReplaceString': { type: 'string', value: replaceText },
|
||||
'SearchItem.Command': { type: 'long', value: SearchCommand.Replace },
|
||||
'SearchItem.Backward': { type: 'boolean', value: false },
|
||||
'SearchItem.SearchCaseSensitive': { type: 'boolean', value: caseSensitive },
|
||||
'SearchItem.SearchRegularExpression': { type: 'boolean', value: regexp },
|
||||
'SearchItem.SearchFlags': { type: 'long', value: searchFlags },
|
||||
'SearchItem.AlgorithmType': { type: 'short', value: regexp ? 1 : 0 },
|
||||
'Quiet': { type: 'boolean', value: quiet },
|
||||
});
|
||||
|
||||
console.log('[SearchReplace] 替换当前:', searchText, '->', replaceText);
|
||||
}
|
||||
|
||||
/**
|
||||
* 替换所有匹配项
|
||||
* @param iframeWindow - iframe 的 contentWindow
|
||||
* @param searchText - 要搜索的文本
|
||||
* @param replaceText - 替换后的文本
|
||||
* @param options - 替换选项
|
||||
*/
|
||||
export function unoReplaceAll(
|
||||
iframeWindow: Window,
|
||||
searchText: string,
|
||||
replaceText: string,
|
||||
options: SearchOptions = {}
|
||||
): void {
|
||||
const {
|
||||
caseSensitive = false,
|
||||
wholeWord = false,
|
||||
regexp = false,
|
||||
quiet = true,
|
||||
} = options;
|
||||
|
||||
let searchFlags = 0;
|
||||
if (wholeWord) searchFlags |= 0x00010000;
|
||||
|
||||
sendUnoCommand(iframeWindow, '.uno:ExecuteSearch', {
|
||||
'SearchItem.SearchString': { type: 'string', value: searchText },
|
||||
'SearchItem.ReplaceString': { type: 'string', value: replaceText },
|
||||
'SearchItem.Command': { type: 'long', value: SearchCommand.ReplaceAll },
|
||||
'SearchItem.SearchAll': { type: 'boolean', value: true },
|
||||
'SearchItem.Backward': { type: 'boolean', value: false },
|
||||
'SearchItem.SearchCaseSensitive': { type: 'boolean', value: caseSensitive },
|
||||
'SearchItem.SearchRegularExpression': { type: 'boolean', value: regexp },
|
||||
'SearchItem.SearchFlags': { type: 'long', value: searchFlags },
|
||||
'SearchItem.AlgorithmType': { type: 'short', value: regexp ? 1 : 0 },
|
||||
'Quiet': { type: 'boolean', value: quiet },
|
||||
});
|
||||
|
||||
console.log('[SearchReplace] 替换全部:', searchText, '->', replaceText);
|
||||
}
|
||||
|
||||
/**
|
||||
* 统一的搜索替换接口
|
||||
* @param iframeWindow - iframe 的 contentWindow
|
||||
* @param searchText - 要搜索的文本
|
||||
* @param replaceText - 替换后的文本(可选,不提供则只搜索)
|
||||
* @param options - 选项
|
||||
*/
|
||||
export function unoSearchAndReplace(
|
||||
iframeWindow: Window,
|
||||
searchText: string,
|
||||
replaceText?: string,
|
||||
options: ReplaceOptions = {}
|
||||
): void {
|
||||
if (replaceText === undefined || replaceText === null) {
|
||||
// 只搜索
|
||||
unoSearchNext(iframeWindow, searchText, options);
|
||||
} else if (options.replaceAll) {
|
||||
// 替换全部
|
||||
unoReplaceAll(iframeWindow, searchText, replaceText, options);
|
||||
} else {
|
||||
// 替换当前
|
||||
unoReplaceCurrent(iframeWindow, searchText, replaceText, options);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消搜索选中状态
|
||||
* @param iframeWindow - iframe 的 contentWindow
|
||||
*/
|
||||
export function unoCancelSearch(iframeWindow: Window): void {
|
||||
sendUnoCommand(iframeWindow, '.uno:Escape', {});
|
||||
console.log('[SearchReplace] 取消搜索');
|
||||
}
|
||||
|
||||
/**
|
||||
* 在指定页面替换文本(组合命令)
|
||||
* 流程:跳转页面 -> 等待 -> 搜索选中 -> 等待 -> 替换
|
||||
*
|
||||
* @param iframeWindow - iframe 的 contentWindow
|
||||
* @param pageNumber - 目标页码(从1开始)
|
||||
* @param searchText - 要搜索的文本
|
||||
* @param replaceText - 替换后的文本
|
||||
* @param options - 搜索选项
|
||||
* @returns Promise<boolean> - 是否成功
|
||||
*/
|
||||
export async function replaceTextInPage(
|
||||
iframeWindow: Window,
|
||||
pageNumber: number,
|
||||
searchText: string,
|
||||
replaceText: string,
|
||||
options: SearchOptions = {}
|
||||
): Promise<{ success: boolean; message: string }> {
|
||||
const {
|
||||
caseSensitive = false,
|
||||
wholeWord = false,
|
||||
regexp = false,
|
||||
quiet = true,
|
||||
} = options;
|
||||
|
||||
console.log('[SearchReplace] 开始在第', pageNumber, '页替换:', searchText, '->', replaceText);
|
||||
|
||||
try {
|
||||
// 1. 发送自定义消息跳转到指定页面
|
||||
const gotoMessage = {
|
||||
MessageId: 'custompostMessage',
|
||||
SendTime: Date.now(),
|
||||
Values: {
|
||||
Command: 'GOTO_PAGE',
|
||||
Args: { pageNumber },
|
||||
},
|
||||
};
|
||||
iframeWindow.postMessage(JSON.stringify(gotoMessage), '*');
|
||||
console.log('[SearchReplace] 步骤1: 跳转到第', pageNumber, '页');
|
||||
|
||||
// 等待页面跳转完成
|
||||
await delay(500);
|
||||
|
||||
// 2. 搜索文本(查找下一个)
|
||||
let searchFlags = 0;
|
||||
if (wholeWord) searchFlags |= 0x00010000;
|
||||
|
||||
sendUnoCommand(iframeWindow, '.uno:ExecuteSearch', {
|
||||
'SearchItem.SearchString': { type: 'string', value: searchText },
|
||||
'SearchItem.Command': { type: 'long', value: SearchCommand.FindNext },
|
||||
'SearchItem.Backward': { type: 'boolean', value: false },
|
||||
'SearchItem.SearchCaseSensitive': { type: 'boolean', value: caseSensitive },
|
||||
'SearchItem.SearchRegularExpression': { type: 'boolean', value: regexp },
|
||||
'SearchItem.SearchFlags': { type: 'long', value: searchFlags },
|
||||
'SearchItem.AlgorithmType': { type: 'short', value: regexp ? 1 : 0 },
|
||||
'Quiet': { type: 'boolean', value: quiet },
|
||||
});
|
||||
console.log('[SearchReplace] 步骤2: 搜索文本');
|
||||
|
||||
// 等待搜索完成
|
||||
await delay(300);
|
||||
|
||||
// 3. 替换选中的文本
|
||||
sendUnoCommand(iframeWindow, '.uno:ExecuteSearch', {
|
||||
'SearchItem.SearchString': { type: 'string', value: searchText },
|
||||
'SearchItem.ReplaceString': { type: 'string', value: replaceText },
|
||||
'SearchItem.Command': { type: 'long', value: SearchCommand.Replace },
|
||||
'SearchItem.Backward': { type: 'boolean', value: false },
|
||||
'SearchItem.SearchCaseSensitive': { type: 'boolean', value: caseSensitive },
|
||||
'SearchItem.SearchRegularExpression': { type: 'boolean', value: regexp },
|
||||
'SearchItem.SearchFlags': { type: 'long', value: searchFlags },
|
||||
'SearchItem.AlgorithmType': { type: 'short', value: regexp ? 1 : 0 },
|
||||
'Quiet': { type: 'boolean', value: quiet },
|
||||
});
|
||||
console.log('[SearchReplace] 步骤3: 替换文本');
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: `已在第${pageNumber}页替换: "${searchText}" -> "${replaceText}"`,
|
||||
};
|
||||
} catch (e) {
|
||||
console.error('[SearchReplace] 替换失败:', e);
|
||||
return {
|
||||
success: false,
|
||||
message: e instanceof Error ? e.message : '未知错误',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 延迟函数
|
||||
*/
|
||||
function delay(ms: number): Promise<void> {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
Reference in New Issue
Block a user