diff --git a/resources/capability-duplicates.json b/resources/capability-duplicates.json index 5159071..8f70505 100644 --- a/resources/capability-duplicates.json +++ b/resources/capability-duplicates.json @@ -5610,6 +5610,13 @@ "name": "插入分页符", "status": "keep", "reason": "唯一能力" + }, + { + "id": "Image_Base64Decode", + "name": "图片base64编码转二进制", + "canonical": "Image_Base64Encode", + "status": "hide", + "reason": "与图片Base64编解码(Image_Base64Encode)互逆重复, 已统一为转换方向切换, 保留执行兼容" } ] } \ No newline at end of file diff --git a/resources/catalog.json b/resources/catalog.json index 2e5b859..f61041f 100644 --- a/resources/catalog.json +++ b/resources/catalog.json @@ -67139,7 +67139,7 @@ "required": true, "defaultValue": "", "placeholder": "如 D:\\files\\demo.png", - "description": "截图保存的完整文件路径(含文件名),点右侧按钮可直接选保存位置;留空时保存到「截图输出目录+截图文件名称」,再留空则保存到流程资源目录", + "description": "截图保存的完整文件路径(含文件名),点右侧按钮可直接选保存位置;留空时保存到流程资源目录并自动生成文件名", "advanced": false, "valueTypes": [ "rpa_str_file", @@ -67159,22 +67159,6 @@ "advanced": false, "valueTypes": [] }, - { - "key": "outImagePath", - "label": "截图输出目录", - "widget": "folder", - "type": "object", - "displayType": "selectFile", - "required": false, - "defaultValue": "", - "placeholder": "如 D:\\output", - "description": "备用输出目录;「截图保存路径」为空时,截图保存到该目录下的「截图文件名称」", - "advanced": true, - "valueTypes": [ - "rpa_str_folder", - "rpa_str" - ] - }, { "key": "outImageName", "label": "截图文件名称", @@ -67183,9 +67167,9 @@ "displayType": "text-expression", "required": false, "defaultValue": "", - "placeholder": "输入内容或 ${变量}", - "description": "备用文件名;仅当「截图保存路径」为空时使用,留空则自动生成文件名", - "advanced": true, + "placeholder": "留空则自动生成,如 screenshot_1730000000.png", + "description": "「截图保存路径」只填了目录或留空时使用的文件名;已含文件名时无需填写", + "advanced": false, "valueTypes": [ "rpa_str" ] @@ -67379,22 +67363,25 @@ }, { "id": "Image_Base64Encode", - "name": "图片转base64编码", + "name": "图片Base64编解码", "kind": "vendor", "group": "vision", "categories": [ "image" ], "categorySignature": "image", - "description": "将图片转为base64编码。", - "messageTemplate": "将图片 $1 转base64编码, 然后把结果保存到 $0", + "description": "将图片文件转为 base64 编码,或将 base64 编码还原保存为图片文件。", + "messageTemplate": "对图片执行 base64 $1 , 然后把结果保存到 $0", "baseType": "Image_Base64Encode", "functionName": "Image.Base64Encode", "functionCalls": [ { "function": "Image.Base64Encode", "args": [ - "path" + "mode", + "path", + "val", + "output" ], "assignTo": "var_name" } @@ -67411,6 +67398,29 @@ "icon": "vision", "risk": "low", "fields": [ + { + "key": "mode", + "label": "转换方向", + "widget": "radio", + "type": "string", + "displayType": "buttonGroup", + "required": false, + "defaultValue": "encode", + "placeholder": "", + "description": "编码:图片文件转为 base64 字符串;解码:base64 字符串还原保存为图片文件", + "advanced": false, + "valueTypes": [], + "options": [ + { + "label": "图片 → Base64", + "value": "encode" + }, + { + "label": "Base64 → 图片", + "value": "decode" + } + ] + }, { "key": "path", "label": "图片对象", @@ -67420,17 +67430,48 @@ "required": true, "defaultValue": "", "placeholder": "截取或选择图像", - "description": "填写参数值,支持 ${变量} 引用上游输出", + "description": "编码方向的源图片路径,支持 ${变量} 引用上游输出(如上游截图的保存路径)", "advanced": false, "valueTypes": [ "rpa_str" ] + }, + { + "key": "val", + "label": "Base64编码", + "widget": "text", + "type": "string", + "displayType": "text-expression", + "required": true, + "defaultValue": "", + "placeholder": "如 ${图片编码值}", + "description": "解码方向的 base64 字符串,支持 ${变量} 引用上游输出;自动忽略 data:image/...;base64, 前缀", + "advanced": false, + "valueTypes": [ + "rpa_str" + ] + }, + { + "key": "output", + "label": "图片保存路径", + "widget": "file", + "type": "object", + "displayType": "selectFile", + "required": false, + "defaultValue": "", + "placeholder": "如 D:\\files\\restored.png", + "description": "解码方向的保存位置(含文件名);留空自动生成临时图片文件", + "advanced": false, + "valueTypes": [ + "rpa_str_file", + "rpa_str" + ] } ], "output": { "label": "输出到变量", "key": "var_name", - "suggestedName": "图片编码值", + "suggestedName": "结果值", "valueTypes": [ "rpa_str" ], @@ -67438,7 +67479,7 @@ { "key": "var_name", "label": "输出到变量", - "defaultValue": "图片编码值", + "defaultValue": "结果值", "valueTypes": [ "rpa_str" ] diff --git a/src/main/capabilities/remaining-core.ts b/src/main/capabilities/remaining-core.ts index 1411461..f78f178 100644 --- a/src/main/capabilities/remaining-core.ts +++ b/src/main/capabilities/remaining-core.ts @@ -165,7 +165,25 @@ async function captureScreen(file: string) { fs.mkdirSync(path.dirname(file), { recursive: true }); await powershell(`Add-Type -AssemblyName System.Drawing;Add-Type -AssemblyName System.Windows.Forms;$bounds=[System.Windows.Forms.Screen]::PrimaryScreen.Bounds;$bitmap=New-Object System.Drawing.Bitmap $bounds.Width,$bounds.Height;$graphics=[System.Drawing.Graphics]::FromImage($bitmap);$graphics.CopyFromScreen($bounds.Location,[System.Drawing.Point]::Empty,$bounds.Size);$bitmap.Save(${psString(file)});$graphics.Dispose();$bitmap.Dispose()`); } -register("Image_Base64Encode", async (_ctx, _node, args) => fs.readFileSync(path.resolve(text(args, ["path", "image"]))).toString("base64")); +// 图片Base64编解码: 统一组件按 mode 分流(encode=图片→Base64, decode=Base64→图片)。 +// Image_Base64Decode 已隐藏进该组件,但保留注册以防旧流程直接引用旧 id。 +register("Image_Base64Encode", async (_ctx, node, args) => { + const mode = text(args, ["mode"], "encode"); + if (mode === "decode") { + const value = text(args, ["val", "value"]).replace(/^data:[^;]+;base64,/, ""); + if (!value.trim()) throw new RuntimeError("validation_error", "Base64编码不能为空", { nodeId: node.id, componentId: node.componentId, recommendation: "填写 base64 字符串,或用 ${变量} 引用上游编码输出" }); + const custom = text(args, ["output"]).trim(); + const file = custom ? path.resolve(custom) : path.resolve(process.cwd(), `image-${Date.now()}.png`); + fs.mkdirSync(path.dirname(file), { recursive: true }); + fs.writeFileSync(file, Buffer.from(value, "base64")); + return file; + } + const imageArg = text(args, ["path", "image"]); + if (!imageArg.trim()) throw new RuntimeError("validation_error", "图片对象不能为空", { nodeId: node.id, componentId: node.componentId, recommendation: "填写源图片路径,或用 ${变量} 引用上游截图输出" }); + const source = path.resolve(imageArg); + if (!fs.existsSync(source)) throw new RuntimeError("validation_error", `找不到图片文件:${source}`, { nodeId: node.id, componentId: node.componentId, recommendation: "确认路径正确,或先用「屏幕截图」组件生成图片" }); + return fs.readFileSync(source).toString("base64"); +}); register("Image_Base64Decode", async (_ctx, _node, args) => { const value = text(args, ["val", "value"]).replace(/^data:[^;]+;base64,/, ""); const file = path.resolve(process.cwd(), `image-${Date.now()}.png`); fs.mkdirSync(path.dirname(file), { recursive: true }); fs.writeFileSync(file, Buffer.from(value, "base64")); return file; diff --git a/src/renderer/src/validation/flowValidation.ts b/src/renderer/src/validation/flowValidation.ts index 26b9195..0e2f6e5 100644 --- a/src/renderer/src/validation/flowValidation.ts +++ b/src/renderer/src/validation/flowValidation.ts @@ -29,6 +29,12 @@ export function fieldIsActive(componentId: string, key: string, values: Record