键盘输入组件修复: 接入 words 字段与三种输入方式

This commit is contained in:
yu 2026-11-03 16:38:03 +08:00
parent 6b05a65664
commit 46348413c1
2 changed files with 62 additions and 2 deletions

View File

@ -99,10 +99,22 @@ export function psString(value: unknown): string {
}
const keyboardSend: CapabilityHandler = async (_ctx, node, args) => {
const keys = text(args, ["keys", "key", "text", "value"]);
// 字段: words=输入内容(键盘输入), keys/key=按键组合(按键系列), interval=输入间隔(V1毫秒/基础版秒), fuctype=输入方式(0后台接口/1人工模拟/2剪贴板粘贴)
const keys = text(args, ["words", "keys", "key", "text", "value"]);
if (!keys) throw new RuntimeError("validation_error", "键盘输入为空", { nodeId: node.id, componentId: node.componentId });
await manualDelay();
await powershell(`Add-Type -AssemblyName System.Windows.Forms; [System.Windows.Forms.SendKeys]::SendWait(${psString(keys)})`);
const rawInterval = number(args, ["interval"], 0);
const intervalMs = node.componentId === "Keyboard_Send" ? Math.max(0, Math.round(rawInterval * 1000)) : Math.max(0, Math.round(rawInterval));
const mode = text(args, ["fuctype"], "0");
const isTextSend = node.componentId === "Keyboard_Send" || node.componentId === "Keyboard_Send_V1";
// 文本输入需转义 SendKeys 特殊字符(+^%~(){}[])按字面输入;按键组合组件(Keyboard_Press)与粘贴模式保留原文
const escape = isTextSend && mode !== "2" ? `$t = $t -replace '[+^%~(){}[\\]]','{$0}'; ` : "";
const send = mode === "2"
? `Set-Clipboard -Value $t; Start-Sleep -Milliseconds 120; [System.Windows.Forms.SendKeys]::SendWait('^v')`
: mode === "1"
? `foreach ($ch in $t.ToCharArray()) { $c = [string]$ch; if ('+^%~(){}[]'.Contains($c)) { $c = '{' + $c + '}' }; [System.Windows.Forms.SendKeys]::SendWait($c); Start-Sleep -Milliseconds ${intervalMs} }`
: `[System.Windows.Forms.SendKeys]::SendWait($t)`;
await powershell(`Add-Type -AssemblyName System.Windows.Forms; $t = ${psString(keys)}; ${escape}${send}`);
await manualDelay();
return keys;
};

View File

@ -0,0 +1,48 @@
import { describe, expect, it } from "vitest";
import type { CatalogComponent, FlowNode } from "../../shared/types";
import { getHandler } from "../engine/registry";
import type { RunContext } from "../engine/types";
import "./desktop";
const component = {} as CatalogComponent;
const node = (id: string): FlowNode => ({ id: "smoke-kb", componentId: id, position: { x: 0, y: 0 }, values: {}, options: {} });
const ctx = {
runId: "kb-smoke", flowId: "kb-smoke-flow", flow: {} as RunContext["flow"],
vars: {}, secrets: {}, signal: new AbortController().signal, evidenceDir: "D:/office-test2",
emit: () => {}, log: () => {}, componentOf: () => undefined
} as unknown as RunContext;
async function invoke(componentId: string, args: Record<string, unknown>): Promise<unknown> {
const handler = getHandler(componentId);
expect(handler).toBeTypeOf("function");
return handler!(ctx, node(componentId), args, component);
}
describe("键盘输入组件真实冒烟", () => {
it("words 字段取值(此前读不到报「键盘输入为空」), 后台接口模式整段发送特殊字符不抛错", async () => {
const out = await invoke("Keyboard_Send_V1", { words: "哈哈哈(测试)100%^2", interval: "10", fuctype: "0" });
expect(out).toBe("哈哈哈(测试)100%^2");
console.log("[键盘输入] 后台接口模式整段发送 ✓:", out);
}, 60000);
it("人工模拟模式逐字符+间隔毫秒真实发送", async () => {
const out = await invoke("Keyboard_Send_V1", { words: "逐字输入ABC", interval: "5", fuctype: "1" });
expect(out).toBe("逐字输入ABC");
console.log("[键盘输入] 人工模拟逐字符 ✓:", out);
}, 60000);
it("剪切板粘贴模式: 原文进入剪贴板并发送 Ctrl+V", async () => {
const content = `粘贴${Date.now()}`;
const out = await invoke("Keyboard_Send_V1", { words: content, fuctype: "2" });
expect(out).toBe(content);
const clip = await invoke("Clipboard_GetText", {}) as string;
expect(clip).toBe(content);
console.log("[键盘输入] 剪贴板粘贴模式 ✓ 剪贴板内容一致:", clip);
}, 60000);
it("空输入仍报「键盘输入为空」", async () => {
await expect(invoke("Keyboard_Send_V1", { words: "", interval: "10" })).rejects.toThrow("键盘输入为空");
console.log("[键盘输入] 空输入校验 ✓");
}, 60000);
});