support oneof for array type

This commit is contained in:
codetypes committed 2025-04-13 20:19:47 +08:00
1 parent 826b69a587
commit de9c9a48c9
6 files changed
+175 -117

No files matched your search

+1 -1
View File
@@ -121,7 +121,7 @@
"node.name": "Node name", "node.name": "Node name",
"node.noNodeSelected": "No node selected", "node.noNodeSelected": "No node selected",
"node.notFound": "Node '{{name}}' not found", "node.notFound": "Node '{{name}}' not found",
"node.oneof.error": "Only one is allowed between input '{{input}}' and argument '{{arg}}({{desc}})'", "node.oneof.error": "Only one is allowed between input '{{input}}' and argument '{{desc}}({{arg}})'",
"node.oneof.inputNotfound": "Input '{{input}}' not found", "node.oneof.inputNotfound": "Input '{{input}}' not found",
"node.outputVariable": "Output variables", "node.outputVariable": "Output variables",
"node.pasteDataError": "Paste data error", "node.pasteDataError": "Paste data error",
+1 -1
View File
@@ -121,7 +121,7 @@
"node.name": "节点名称", "node.name": "节点名称",
"node.noNodeSelected": "未选择任务节点", "node.noNodeSelected": "未选择任务节点",
"node.notFound": "节点 “{{name}}” 不存在", "node.notFound": "节点 “{{name}}” 不存在",
"node.oneof.error": "输入项 “{{input}}” 和参数 “{{arg}}({{desc}})” 之中,只能选择设置其中之一", "node.oneof.error": "输入项 “{{input}}” 和参数 “{{desc}}({{arg}})” 之中,只能选择设置其中之一",
"node.oneof.inputNotfound": "输入项“{{input}}”不存在", "node.oneof.inputNotfound": "输入项“{{input}}”不存在",
"node.outputVariable": "输出变量", "node.outputVariable": "输出变量",
"node.pasteDataError": "粘贴数据出错", "node.pasteDataError": "粘贴数据出错",
+14
View File
@@ -416,5 +416,19 @@
} }
], ],
"desc": "等待" "desc": "等待"
},
{
"name": "TestOneof",
"desc": "测试oneof",
"type": "Action",
"input": ["选项?"],
"args": [
{
"name": "option",
"desc": "选项",
"type": "int[]?",
"oneof": "选项"
}
]
} }
] ]
+9
View File
@@ -104,6 +104,15 @@
"args": { "args": {
"builtin": "afterRunFailure" "builtin": "afterRunFailure"
} }
},
{
"id": "13",
"name": "TestOneof",
"input": [
"context"
],
"output": [],
"args": {}
} }
] ]
} }
+143 -113
View File
@@ -760,7 +760,7 @@ const NodeInspector: FC = () => {
} }
}); });
} }
data.args[arg.name] = arr; data.args[arg.name] = !isNodeArgOptional(arg) ? arr : arr.length ? arr : undefined;
} else if (isJsonType(type)) { } else if (isJsonType(type)) {
try { try {
data.args[arg.name] = value === "null" ? null : JSON.parse(value); data.args[arg.name] = value === "null" ? null : JSON.parse(value);
@@ -1070,7 +1070,7 @@ const NodeInspector: FC = () => {
setFieldValue(`input.${i}`, value); setFieldValue(`input.${i}`, value);
validateFields([argName]); validateFields([argName]);
} }
if (!checkOneof(getFieldValue(argName) ?? "", value)) { if (!checkOneof(arg, getFieldValue(argName), value)) {
return Promise.reject( return Promise.reject(
new Error( new Error(
t("node.oneof.error", { t("node.oneof.error", {
@@ -1116,118 +1116,148 @@ const NodeInspector: FC = () => {
const type = getNodeArgRawType(arg); const type = getNodeArgRawType(arg);
if (isNodeArgArray(arg)) { if (isNodeArgArray(arg)) {
return ( return (
<Form.Item key={`args.${arg.name}`}> <Form.List
<Form.List name={`args.${arg.name}`}> key={`args.${arg.name}`}
{(items, { add, remove }, { errors }) => ( name={`args.${arg.name}`}
<div style={{ display: "flex", rowGap: 0, flexDirection: "column" }}> rules={[
{items.map((item, idx) => ( {
<Flex key={item.key} gap={4}> validator(_, value: unknown[]) {
<Form.Item if (!arg.oneof) {
name={item.name} return Promise.resolve();
label={idx === 0 ? `${arg.desc}[${idx}]` : `[${idx}]`} }
validateTrigger={["onChange", "onBlur"]} const idx = def.input?.findIndex(
style={{ width: "100%", marginBottom: 5 }} (input) => input.replace("?", "") === arg.oneof
initialValue={ );
isBoolType(type) ? arg.default ?? false : arg.default if (idx === undefined || idx < 0) {
} return Promise.reject(
valuePropName={isBoolType(type) ? "checked" : undefined} new Error(t("node.oneof.inputNotfound", { input: arg.oneof }))
rules={[ );
{ }
required, const inputName = `input.${idx}`;
message: t("fieldRequired", { field: arg.desc }), if (!form.isFieldValidating(inputName)) {
form.setFieldValue(`args.${arg.name}`, value);
form.validateFields([inputName]);
}
if (!checkOneof(arg, value, form.getFieldValue(inputName))) {
return Promise.reject(
new Error(
t("node.oneof.error", {
input: def.input![idx],
arg: arg.name,
desc: arg.desc ?? "",
})
)
);
} else {
return Promise.resolve();
}
},
},
]}
>
{(items, { add, remove }, { errors }) => (
<div style={{ display: "flex", rowGap: 0, flexDirection: "column" }}>
{items.map((item, idx) => (
<Flex key={item.key} gap={4}>
<Form.Item
name={item.name}
label={idx === 0 ? `${arg.desc}[${idx}]` : `[${idx}]`}
validateTrigger={["onChange", "onBlur"]}
style={{ width: "100%", marginBottom: 5 }}
initialValue={isBoolType(type) ? arg.default ?? false : arg.default}
valuePropName={isBoolType(type) ? "checked" : undefined}
rules={[
{
required,
message: t("fieldRequired", { field: arg.desc }),
},
() => ({
validator(_, value) {
return validateArg(
editingNode.data,
arg,
value,
workspace.usingVars
);
}, },
() => ({ }),
validator(_, value) { ]}
return validateArg(
editingNode.data,
arg,
value,
workspace.usingVars
);
},
}),
]}
>
{!hasArgOptions(arg) && isStringType(type) && (
<TextArea autoSize disabled={disabled} onBlur={submit} />
)}
{!hasArgOptions(arg) && isJsonType(type) && (
<TextArea autoSize disabled={disabled} onBlur={submit} />
)}
{!hasArgOptions(arg) && isIntType(type) && (
<InputNumber
disabled={disabled}
onBlur={submit}
precision={0}
/>
)}
{!hasArgOptions(arg) && isFloatType(type) && (
<InputNumber disabled={disabled} onBlur={submit} />
)}
{!hasArgOptions(arg) && isBoolType(type) && (
<Switch disabled={disabled} onChange={submit} />
)}
{!hasArgOptions(arg) && isExprType(type) && (
<Input disabled={disabled} onBlur={submit} />
)}
{hasArgOptions(arg) && (
<Select
showSearch
disabled={disabled}
onBlur={submit}
onChange={submit}
options={(arg.options ?? []).map((option) => {
return {
value: option.value,
label: `${option.name}(${option.value})`,
};
})}
filterOption={(value, option) => {
value = value.toUpperCase();
return !!option?.label
.toLocaleUpperCase()
.includes(value.toUpperCase());
}}
/>
)}
</Form.Item>
<MinusCircleOutlined
style={{ marginBottom: "6px" }}
onClick={() => {
remove(item.name);
submit();
}}
/>
</Flex>
))}
<Form.Item
label={items.length === 0 ? arg.desc : undefined}
style={{
marginLeft: items.length === 0 ? undefined : "100px",
marginRight: items.length === 0 ? undefined : "18px",
alignItems: "end",
}}
>
<Button
type="dashed"
onClick={() => {
add(arg.default ?? isBoolType(type) ? false : "");
if (isBoolType(type)) {
submit();
}
}}
style={{ width: "100%" }}
icon={<PlusOutlined />}
danger={items.length === 0 && !isNodeArgOptional(arg)}
> >
{t("add")} {!hasArgOptions(arg) && isStringType(type) && (
</Button> <TextArea autoSize disabled={disabled} onBlur={submit} />
<Form.ErrorList errors={errors} /> )}
</Form.Item> {!hasArgOptions(arg) && isJsonType(type) && (
</div> <TextArea autoSize disabled={disabled} onBlur={submit} />
)} )}
</Form.List> {!hasArgOptions(arg) && isIntType(type) && (
</Form.Item> <InputNumber disabled={disabled} onBlur={submit} precision={0} />
)}
{!hasArgOptions(arg) && isFloatType(type) && (
<InputNumber disabled={disabled} onBlur={submit} />
)}
{!hasArgOptions(arg) && isBoolType(type) && (
<Switch disabled={disabled} onChange={submit} />
)}
{!hasArgOptions(arg) && isExprType(type) && (
<Input disabled={disabled} onBlur={submit} />
)}
{hasArgOptions(arg) && (
<Select
showSearch
disabled={disabled}
onBlur={submit}
onChange={submit}
options={(arg.options ?? []).map((option) => {
return {
value: option.value,
label: `${option.name}(${option.value})`,
};
})}
filterOption={(value, option) => {
value = value.toUpperCase();
return !!option?.label
.toLocaleUpperCase()
.includes(value.toUpperCase());
}}
/>
)}
</Form.Item>
<MinusCircleOutlined
style={{ marginBottom: "6px" }}
onClick={() => {
remove(item.name);
submit();
}}
/>
</Flex>
))}
<Form.Item
label={items.length === 0 ? arg.desc : undefined}
style={{
marginLeft: items.length === 0 ? undefined : "100px",
marginRight: items.length === 0 ? undefined : "18px",
alignItems: "end",
}}
>
<Button
type="dashed"
onClick={() => {
add(arg.default ?? isBoolType(type) ? false : "");
if (isBoolType(type)) {
submit();
}
}}
style={{ width: "100%" }}
icon={<PlusOutlined />}
danger={items.length === 0 && !isNodeArgOptional(arg)}
>
{t("add")}
</Button>
<Form.ErrorList errors={errors} />
</Form.Item>
</div>
)}
</Form.List>
); );
} else { } else {
return ( return (
@@ -1264,7 +1294,7 @@ const NodeInspector: FC = () => {
setFieldValue(`args.${arg.name}`, value); setFieldValue(`args.${arg.name}`, value);
validateFields([inputName]); validateFields([inputName]);
} }
if (!checkOneof(getFieldValue(inputName) ?? "", value)) { if (!checkOneof(arg, value, form.getFieldValue(inputName))) {
return Promise.reject( return Promise.reject(
new Error( new Error(
t("node.oneof.error", { t("node.oneof.error", {
+7 -2
View File
@@ -307,7 +307,7 @@ export const checkNodeArg = (data: NodeData, conf: NodeDef, i: number, verbose?:
} }
if (arg.oneof !== undefined) { if (arg.oneof !== undefined) {
const idx = conf.input?.findIndex((v) => v.startsWith(arg.oneof!)) ?? -1; const idx = conf.input?.findIndex((v) => v.startsWith(arg.oneof!)) ?? -1;
if (!checkOneof(data.args?.[arg.name], data.input?.[idx])) { if (!checkOneof(arg, data.args?.[arg.name], data.input?.[idx])) {
if (verbose) { if (verbose) {
error( error(
data, data,
@@ -321,7 +321,12 @@ export const checkNodeArg = (data: NodeData, conf: NodeDef, i: number, verbose?:
return !hasError; return !hasError;
}; };
export const checkOneof = (argValue: unknown, inputValue: unknown) => { export const checkOneof = (arg: NodeArg, argValue: unknown, inputValue: unknown) => {
if (isNodeArgArray(arg)) {
if (argValue instanceof Array && argValue.length === 0) {
argValue = undefined;
}
}
argValue = argValue === undefined ? "" : argValue; argValue = argValue === undefined ? "" : argValue;
inputValue = inputValue ?? ""; inputValue = inputValue ?? "";
return (argValue !== "" && inputValue === "") || (argValue === "" && inputValue !== ""); return (argValue !== "" && inputValue === "") || (argValue === "" && inputValue !== "");