diff --git a/packages/rpa-core/src/capabilities/data.ts b/packages/rpa-core/src/capabilities/data.ts index 50e042a..9d7c423 100644 --- a/packages/rpa-core/src/capabilities/data.ts +++ b/packages/rpa-core/src/capabilities/data.ts @@ -111,9 +111,29 @@ register("StrUtil_InStr", (_ctx, _node, args) => { }); register("StrUtil_Replace", (_ctx, _node, args) => { const text = str(first(args, ["string"])); - const search = str(first(args, ["search", "Config"])); + // catalog 契约:search 为下拉枚举("0"内容/"1"数字/"2"手机号/"3"邮箱/"4"身份证/"5"自定义正则),查找目标在 Config + // 旧实现直接把枚举值 "0" 当字面文本分割,导致按内容替换永远不生效(W26 系枚举映射 bug 同族) + const modeRaw = first(args, ["search"]); + const mode = str(modeRaw === undefined ? "0" : modeRaw); + const config = str(first(args, ["Config"])); const replacement = str(first(args, ["replace"])); - return text.split(search).join(replacement); + const presets: Record = { + "1": /\d+/g, + "2": /1[3-9]\d{9}/g, + "3": /[\w.+-]+@[\w-]+(?:\.[\w-]+)+/g, + "4": /\d{17}[\dXx]/g + }; + if (presets[mode]) return text.replace(presets[mode], replacement); + if (mode === "5") { + try { + return text.replace(new RegExp(config, "g"), replacement); + } catch { + return text; + } + } + // "0" = 指定内容;兼容旧契约:Config 为空时退回把 search 当字面文本 + const target = config || mode; + return text.split(target).join(replacement); }); register(["StrUtil_LowerCase"], (_ctx, _node, args) => str(first(args, ["string"])).toLowerCase()); register(["StrUtil_UpperCase"], (_ctx, _node, args) => str(first(args, ["string"])).toUpperCase()); diff --git a/packages/rpa-core/src/capabilities/desktop.ts b/packages/rpa-core/src/capabilities/desktop.ts index 6e7a828..c72af81 100644 --- a/packages/rpa-core/src/capabilities/desktop.ts +++ b/packages/rpa-core/src/capabilities/desktop.ts @@ -154,7 +154,10 @@ const notice: CapabilityHandler = async (_ctx, _node, args) => { // MessageBox 阻塞等用户点确定,同样不能吃 powershell() 默认 30s 超时;且 MB_TOPMOST 保证弹窗不被主窗口盖住 const waitSeconds = number(args, ["wait_time", "waitTime", "timeout"], 600); const timeoutMs = Math.max(10000, Math.round(waitSeconds * 1000)); - await powershell(`Add-Type -AssemblyName System.Windows.Forms; [System.Windows.Forms.MessageBox]::Show(${psString(content)},${psString(title)},'OK','Information','Button1','HelpButton',1) | Out-Null`, timeoutMs); + // 置顶方案: MB_TOPMOST/262144 是 Win32 老接口标志, .NET MessageBoxOptions 枚举里没有(只有 RightAlign/RtlReading/ServiceNotification 等), + // 传 262144 会被 PS 枚举转换拒绝。正确做法是传一个 TopMost 的隐形属主窗体; + // 属主窗体万一失败则 catch 降级为普通 Show, 保证弹窗一定出现不阻塞流程。 + await powershell(`Add-Type -AssemblyName System.Windows.Forms; $f = New-Object System.Windows.Forms.Form; $f.TopMost = $true; try { [System.Windows.Forms.MessageBox]::Show($f,${psString(content)},${psString(title)},'OK','Information') | Out-Null } catch { [System.Windows.Forms.MessageBox]::Show(${psString(content)},${psString(title)},'OK','Information') | Out-Null }; $f.Dispose()`, timeoutMs); return true; }; diff --git a/packages/rpa-core/src/capabilities/files.ts b/packages/rpa-core/src/capabilities/files.ts index aeb7f94..f594bd2 100644 --- a/packages/rpa-core/src/capabilities/files.ts +++ b/packages/rpa-core/src/capabilities/files.ts @@ -246,6 +246,10 @@ register("File_CompressFile", async (_ctx, node, args) => { register("File_DecompressFile", async (_ctx, node, args) => { const source = requirePath(pick(args, ["folder_path", "path", "zip_path"]), "压缩包路径", node.id, node.componentId); + // adm-zip 对不存在的文件抛 "ADM-ZIP: Invalid filename",极具误导性;先显式检查文件存在性 + await fs.access(source).catch(() => { + throw new RuntimeError("file_not_found", `压缩包不存在:${source}`, { nodeId: node.id, componentId: node.componentId, recommendation: "确认压缩包实际产物路径(压缩组件默认在原文件名后追加 .zip,如 a.txt 会压成 a.txt.zip),或用压缩节点的输出变量传入" }); + }); const saveToSourceDir = String(args.direct_type ?? "1") !== "0"; const target = saveToSourceDir ? path.dirname(source)