import { BrowserWindow } from "electron"; import { register } from "../engine/registry"; import { RuntimeError } from "../engine/types"; interface FormFieldSpec { key?: string; name?: string; label?: string; type?: string; value?: string; options?: unknown[] } function parseFields(raw: unknown, nodeId: string): FormFieldSpec[] { if (raw === undefined || raw === null || raw === "") { throw new RuntimeError("validation_error", "表单配置 jsonlist 为空", { nodeId, componentId: nodeId, recommendation: "填写 JSON 数组,例如 [{\"key\":\"name\",\"label\":\"名称\",\"type\":\"text\"}]" }); } if (Array.isArray(raw)) return raw as FormFieldSpec[]; const text = String(raw).trim(); if (text.startsWith("[")) { try { const parsed = JSON.parse(text) as unknown; if (Array.isArray(parsed)) return parsed as FormFieldSpec[]; } catch { /* fall through */ } } throw new RuntimeError("validation_error", "表单配置 jsonlist 无效", { nodeId, componentId: nodeId, recommendation: "填写 JSON 数组,例如 [{\"key\":\"name\",\"label\":\"名称\",\"type\":\"text\"}]" }); } function fieldKey(f: FormFieldSpec, index: number): string { return String(f.key ?? f.name ?? f.label ?? "field" + (index + 1)); } function escapeHtml(s: string): string { return s.replace(/&/g, "&").replace(//g, ">").replace(/"/g, """); } async function showForm(options: { title: string; fields: FormFieldSpec[]; timeoutSec: number }): Promise> { return new Promise((resolve) => { const rows = options.fields.map((f, i) => { const key = escapeHtml(fieldKey(f, i)); const label = escapeHtml(f.label ?? fieldKey(f, i)); const value = escapeHtml(String(f.value ?? "")); const type = String(f.type ?? "text").toLowerCase(); if (type === "select" || (Array.isArray(f.options) && f.options.length)) { const opts = (f.options ?? []).map(o => { const ov = typeof o === "string" ? o : String((o as { value?: string }).value ?? (o as { label?: string }).label ?? ""); const ol = typeof o === "string" ? o : String((o as { label?: string }).label ?? ov); return '"; }).join(""); return "
"; } if (type === "textarea") { return "
"; } if (type === "checkbox") { const checked = String(f.value ?? "") === "true" || String(f.value ?? "") === "1" ? " checked" : ""; return '
"; } const inputType = type === "password" ? "password" : "text"; return "
'; }).join("\n"); const html = '

' + escapeHtml(options.title) + "

" + rows + ''; const win = new BrowserWindow({ width: 480, height: 0, useContentSize: true, autoHideMenuBar: true, title: options.title, alwaysOnTop: true, webPreferences: { contextIsolation: true, nodeIntegration: false, sandbox: true } }); let settled = false; const timer = setInterval(async () => { try { const values = await win.webContents.executeJavaScript("window.__formValues || null", true); if (values) { clearInterval(timer); win.destroy(); resolve(values as Record); } } catch { clearInterval(timer); resolve({}); } }, 250); const timeout = setTimeout(() => { try { void win.webContents.executeJavaScript("document.getElementById(\"ok\").click()"); } catch {} }, Math.max(5, options.timeoutSec) * 1000); win.on("closed", () => { clearInterval(timer); clearTimeout(timeout); resolve({}); }); void win.loadURL("data:text/html;charset=utf-8," + encodeURIComponent(html)); }); } const DIALOG_IDS = [ "Dialog_GetConfig", "Dialog_GetConfigV1", "Dialog_GetConfigV2", "Dialog_GetConfigV3", "Dialog_GetConfigV4", "Dialog_GetConfigV5", "Dialog_GetConfigV6", "Dialog_GetConfigV7", "Dialog_GetConfigV8" ]; register(DIALOG_IDS, async (ctx, node, args) => { const fields = parseFields(args.jsonlist, node.id); const title = String(args.title ?? args.configName ?? "请输入"); const timeoutSec = Number(args.wait_time ?? 30) || 30; const values = await showForm({ title, fields, timeoutSec }); ctx.log("已获取人机交互表单输入:" + JSON.stringify(values), "info", node.id); return values; });