Files
LiangShiyong 4fcc92a381 feat: 1. 接入CollaboraViewer选中的高亮效果,清除高亮功能,页面销毁自动清除高亮。
2. 合同模板对比接入monaco editor的效果。
3. 添加交叉评查的案卷类型的数据查询。

fix: 1. 修复文档列表的打开模态框蒙板层显示效果。
2025-11-30 19:33:05 +08:00

214 lines
6.1 KiB
TypeScript

/**
* Collabora Online Python 脚本调用工具
*
* 职责: 统一封装所有 Python 脚本的调用逻辑和响应处理
* @encoding UTF-8
*/
export interface ScriptResult {
success: boolean;
message: string;
data?: {
count?: number;
action?: string;
unit?: string;
[key: string]: unknown;
};
timestamp: number;
}
export interface CallScriptOptions {
timeout?: number;
verbose?: boolean;
}
interface PostMessageResponse {
MessageId?: string;
Values?: {
commandName?: string;
success?: boolean;
result?: string | { type?: string; value?: string };
};
}
export async function callPythonScript(
iframeWindow: Window,
scriptFile: string,
functionName: string,
args?: Record<string, unknown>,
options?: CallScriptOptions
): Promise<ScriptResult> {
const timeout = options?.timeout || 10000;
const verbose = options?.verbose ?? true;
if (verbose) {
// console.log('[CallCustomScript] 调用 Python 脚本:', { scriptFile, functionName, args });
}
return new Promise((resolve, reject) => {
const cleanup = () => {
clearTimeout(timeoutId);
window.removeEventListener('message', handleMessage);
};
const timeoutId: NodeJS.Timeout = setTimeout(() => {
cleanup();
reject(new Error(`Python 脚本调用超时 (${timeout}ms): ${scriptFile}.${functionName}`));
}, timeout);
const handleMessage = (event: MessageEvent) => {
try {
if (event.source !== iframeWindow) return;
const data: PostMessageResponse = typeof event.data === 'string'
? JSON.parse(event.data) : event.data;
// 兼容两种 MessageId 格式:
// - 'CallPythonScript_Resp' (预期格式)
// - 'CallPythonScript-Result' (bundle.js 实际发送的格式)
if (data.MessageId !== 'CallPythonScript-Result') return;
cleanup();
if (verbose) {
// console.log('[CallCustomScript] 收到 Python 脚本响应:', data);
}
const result = parseScriptResponse(data, verbose);
resolve(result);
} catch (error) {
cleanup();
reject(error);
}
};
window.addEventListener('message', handleMessage);
// 关键修复: 将所有参数包装为 PropertyValue 格式
// Collabora 源码中的 JsonUtil::makePropertyValue() 要求:
// { "propertyName": { "type": "string", "value": "actual_value" } }
const wrappedArgs: Record<string, { type: string; value: unknown }> = {};
if (args) {
for (const [key, value] of Object.entries(args)) {
wrappedArgs[key] = {
type: typeof value === 'number' ? 'long' : 'string',
value: value
};
}
}
const message = {
MessageId: 'CallPythonScript',
ScriptFile: scriptFile,
Function: functionName,
Values: wrappedArgs,
};
if (verbose) {
// console.log('[CallCustomScript] 发送 PostMessage:', message);
}
iframeWindow.postMessage(JSON.stringify(message), '*');
});
}
function parseScriptResponse(data: PostMessageResponse, verbose: boolean): ScriptResult {
const values = data.Values;
if (!values || typeof values !== 'object') {
throw new Error('响应格式错误: Values 字段缺失或格式不正确');
}
let resultValue: string | undefined;
if (typeof values.result === 'string') {
resultValue = values.result;
} else if (values.result && typeof values.result === 'object') {
resultValue = (values.result as { value?: string }).value;
}
if (verbose) {
// console.log('[CallCustomScript] 解析结果:', {
// commandName: values.commandName,
// unoSuccess: values.success,
// resultRaw: values.result,
// resultExtracted: resultValue,
// });
}
if (values.success === false) {
return {
success: false,
message: resultValue || 'UNO 命令执行失败',
timestamp: Date.now(),
};
}
if (typeof resultValue !== 'string') {
throw new Error('Python 脚本返回值格式错误: result 不是字符串');
}
return parseStandardResponse(resultValue);
}
function parseStandardResponse(message: string): ScriptResult {
const timestamp = Date.now();
if (message.includes('Error:') || message.toLowerCase().includes('error')) {
return { success: false, message, timestamp };
}
const countMatch = message.match(/(\w+)\s+(\d+)\s+(regions?|instances?|items?)/i);
if (countMatch) {
const count = parseInt(countMatch[2], 10);
return {
success: true,
message,
data: {
count,
action: countMatch[1].toLowerCase(),
unit: countMatch[3].toLowerCase(),
},
timestamp,
};
}
return { success: true, message, timestamp };
}
export async function callPythonScriptBatch(
iframeWindow: Window,
calls: Array<{
scriptFile: string;
functionName: string;
args?: Record<string, unknown>;
}>,
options?: CallScriptOptions
): Promise<ScriptResult[]> {
const results: ScriptResult[] = [];
for (const call of calls) {
try {
const result = await callPythonScript(
iframeWindow,
call.scriptFile,
call.functionName,
call.args,
options
);
results.push(result);
} catch (error) {
results.push({
success: false,
message: error instanceof Error ? error.message : String(error),
timestamp: Date.now(),
});
}
}
return results;
}