support object type and oneof check
This commit is contained in:
1 parent
e417f3beba
commit
472bbbfbb8
5 files changed
+183
-48
No files matched your search
@@ -82,24 +82,27 @@
|
||||
"regnode.subtree": "Subtree: ",
|
||||
|
||||
"node.args": "Const arguments",
|
||||
"node.children": "Subnode limit",
|
||||
"node.children.unlimited": "Unlimited",
|
||||
"node.children": "Subnode limit",
|
||||
"node.debug": "Debug",
|
||||
"node.disabled": "Disabled",
|
||||
"node.deleteRootNodeDenied": "Unable to delete the root node",
|
||||
"node.editSubtreeDenied": "Unable to edit the subtree node",
|
||||
"node.desc": "Node desc",
|
||||
"node.fileRequired": "field '{{field}}' is required",
|
||||
"node.disabled": "Disabled",
|
||||
"node.editSubtreeDenied": "Unable to edit the subtree node",
|
||||
"node.fileRequired": "Field '{{field}}' is required",
|
||||
"node.id": "Node id",
|
||||
"node.inputVariable": "Input variables",
|
||||
"node.invalidValue": "Invalid value",
|
||||
"node.name": "Node name",
|
||||
"node.type": "Node type",
|
||||
"node.noNodeSelected": "No node selected",
|
||||
"node.oneof.error": "Only one is allowed between input '{{input}}' and argument '{{arg}}({{desc}})'",
|
||||
"node.oneof.inputNotfound": "Input '{{input}}' not found",
|
||||
"node.outputVariable": "Output variables",
|
||||
"node.pasteDataError": "Paste data error",
|
||||
"node.rootNodeAsSubtree": "Root node cann't be save as subtree",
|
||||
"node.subtree": "Subtree",
|
||||
"node.subtreePathError": "You should save the subtree inside the current workspace",
|
||||
"node.type": "Node type",
|
||||
|
||||
"node.unknown.desc": "New node"
|
||||
}
|
||||
@@ -82,24 +82,27 @@
|
||||
"regnode.subtree": "子树:",
|
||||
|
||||
"node.args": "常量参数",
|
||||
"node.children": "子节点数量",
|
||||
"node.children.unlimited": "无限制",
|
||||
"node.children": "子节点数量",
|
||||
"node.debug": "调试",
|
||||
"node.disabled": "禁用",
|
||||
"node.deleteRootNodeDenied": "不能删除根节点",
|
||||
"node.editSubtreeDenied": "不能编辑子树节点",
|
||||
"node.desc": "节点说明",
|
||||
"node.disabled": "禁用",
|
||||
"node.editSubtreeDenied": "不能编辑子树节点",
|
||||
"node.fileRequired": "字段“{{field}}”必填",
|
||||
"node.id": "节点标识",
|
||||
"node.inputVariable": "输入变量",
|
||||
"node.invalidValue": "无效的值",
|
||||
"node.name": "节点名称",
|
||||
"node.type": "节点类型",
|
||||
"node.noNodeSelected": "未选择任务节点",
|
||||
"node.oneof.error": "输入项 “{{input}}” 和参数 “{{arg}}({{desc}})” 之中,只能选择设置其中之一",
|
||||
"node.oneof.inputNotfound": "输入项“{{input}}”不存在",
|
||||
"node.outputVariable": "输出变量",
|
||||
"node.pasteDataError": "粘贴数据出错",
|
||||
"node.rootNodeAsSubtree": "根节点不能保存为子树",
|
||||
"node.subtree": "子树",
|
||||
"node.subtreePathError": "子树必须保存在当前工作区的目录内",
|
||||
"node.type": "节点类型",
|
||||
|
||||
"node.unknown.desc": "新建节点"
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { EditNode, EditTree, useWorkspace } from "@/contexts/workspace-context";
|
||||
import { NodeArgType, NodeModel, TreeGraphData } from "@/misc/b3type";
|
||||
import { NodeArg, NodeArgType, NodeModel, TreeGraphData } from "@/misc/b3type";
|
||||
import { Hotkey, isMacos } from "@/misc/keys";
|
||||
import { EditOutlined } from "@ant-design/icons";
|
||||
import {
|
||||
@@ -107,7 +107,14 @@ const NodeInspector: FC = () => {
|
||||
form.setFieldValue("children", def.children);
|
||||
}
|
||||
def.args?.forEach((v) => {
|
||||
form.setFieldValue(`args.${v.name}`, data.args?.[v.name] ?? v.default);
|
||||
if (v.type === "object" || v.type === "object?") {
|
||||
form.setFieldValue(
|
||||
`args.${v.name}`,
|
||||
JSON.stringify(data.args?.[v.name] ?? v.default, null, 2)
|
||||
);
|
||||
} else {
|
||||
form.setFieldValue(`args.${v.name}`, data.args?.[v.name] ?? v.default);
|
||||
}
|
||||
});
|
||||
def.input?.forEach((_, i) => {
|
||||
form.setFieldValue(`input.${i}`, data.input?.[i]);
|
||||
@@ -115,6 +122,7 @@ const NodeInspector: FC = () => {
|
||||
def.output?.forEach((_, i) => {
|
||||
form.setFieldValue(`output.${i}`, data.output?.[i]);
|
||||
});
|
||||
form.validateFields();
|
||||
}, [workspace.editingNode]);
|
||||
|
||||
// auto complete for node
|
||||
@@ -183,7 +191,11 @@ const NodeInspector: FC = () => {
|
||||
const v = values[`args.${arg.name}`];
|
||||
if (v !== null && v !== undefined && v !== "") {
|
||||
data.args ||= {};
|
||||
data.args[arg.name] = v;
|
||||
if (arg.type === "object" || arg.type === "object?") {
|
||||
data.args[arg.name] = JSON.parse(v);
|
||||
} else {
|
||||
data.args[arg.name] = v;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -305,7 +317,44 @@ const NodeInspector: FC = () => {
|
||||
label={desc}
|
||||
name={`input.${i}`}
|
||||
key={`input.${i}`}
|
||||
rules={[{ required, message: t("node.fileRequired", { field: desc }) }]}
|
||||
rules={[
|
||||
{ required, message: t("node.fileRequired", { field: desc }) },
|
||||
({ getFieldValue, setFieldValue, isFieldValidating, validateFields }) => ({
|
||||
validator(_, value) {
|
||||
const arg = def.args?.find((a) => a.oneof && v.startsWith(a.oneof));
|
||||
if (arg) {
|
||||
const argName = `args.${arg.name}`;
|
||||
const argValue = getFieldValue(argName) ?? "";
|
||||
value = value ?? "";
|
||||
if (
|
||||
(value === "" && argValue === "") ||
|
||||
(value !== "" && argValue !== "")
|
||||
) {
|
||||
if (!isFieldValidating(argName)) {
|
||||
setFieldValue(`input.${i}`, value);
|
||||
validateFields([argName]);
|
||||
}
|
||||
return Promise.reject(
|
||||
new Error(
|
||||
t("node.oneof.error", {
|
||||
input: v,
|
||||
arg: arg.name,
|
||||
desc: arg.desc ?? "",
|
||||
})
|
||||
)
|
||||
);
|
||||
} else {
|
||||
if (!isFieldValidating(argName)) {
|
||||
validateFields([argName]);
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
},
|
||||
}),
|
||||
]}
|
||||
>
|
||||
<AutoComplete
|
||||
disabled={disabled}
|
||||
@@ -337,11 +386,62 @@ const NodeInspector: FC = () => {
|
||||
key={`args.${v.name}`}
|
||||
initialValue={type === "boolean" ? v.default ?? false : v.default}
|
||||
valuePropName={type === "boolean" ? "checked" : undefined}
|
||||
rules={[{ required, message: t("node.fileRequired", { field: v.desc }) }]}
|
||||
rules={[
|
||||
{ required, message: t("node.fileRequired", { field: v.desc }) },
|
||||
({ getFieldValue, setFieldValue, isFieldValidating, validateFields }) => ({
|
||||
validator(_, value) {
|
||||
if (value && (v.type == "object" || v.type == "object?")) {
|
||||
try {
|
||||
JSON.parse(value);
|
||||
} catch (e) {
|
||||
return Promise.reject(new Error(t("node.invalidValue")));
|
||||
}
|
||||
}
|
||||
if (v.oneof === undefined) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
const idx = def.input?.findIndex((input) => input.startsWith(v.oneof!));
|
||||
if (idx === undefined || idx < 0) {
|
||||
return Promise.reject(
|
||||
new Error(t("node.oneof.inputNotfound", { input: v.oneof }))
|
||||
);
|
||||
}
|
||||
const inputName = `input.${idx}`;
|
||||
const inputValue = getFieldValue(inputName) ?? "";
|
||||
value = value ?? "";
|
||||
if (
|
||||
(value === "" && inputValue === "") ||
|
||||
(value !== "" && inputValue !== "")
|
||||
) {
|
||||
if (!isFieldValidating(inputName)) {
|
||||
setFieldValue(`args.${v.name}`, value);
|
||||
validateFields([inputName]);
|
||||
}
|
||||
return Promise.reject(
|
||||
new Error(
|
||||
t("node.oneof.error", {
|
||||
input: def.input![idx],
|
||||
arg: v.name,
|
||||
desc: v.desc ?? "",
|
||||
})
|
||||
)
|
||||
);
|
||||
} else {
|
||||
if (!isFieldValidating(inputName)) {
|
||||
validateFields([inputName]);
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
},
|
||||
}),
|
||||
]}
|
||||
>
|
||||
{type === "string" && (
|
||||
<TextArea autoSize disabled={disabled} onBlur={form.submit} />
|
||||
)}
|
||||
{type === "object" && (
|
||||
<TextArea autoSize disabled={disabled} onBlur={form.submit} />
|
||||
)}
|
||||
{type === "int" && (
|
||||
<InputNumber disabled={disabled} onBlur={form.submit} precision={0} />
|
||||
)}
|
||||
|
||||
+3
-2
@@ -1,6 +1,6 @@
|
||||
import { TreeGraphData as G6TreeGraphData } from "@antv/g6";
|
||||
|
||||
export type NodeArgType = "string" | "int" | "float" | "boolean" | "enum" | "code";
|
||||
export type NodeArgType = "boolean" | "string" | "int" | "float" | "object" | "enum" | "code";
|
||||
export type NodeType = "Action" | "Composite" | "Decorator" | "Condition" | "Other" | "Error";
|
||||
|
||||
export interface NodeArgOption {
|
||||
@@ -10,8 +10,9 @@ export interface NodeArgOption {
|
||||
|
||||
export interface NodeArg {
|
||||
name: string;
|
||||
type: string;
|
||||
type: NodeArgType | `${NodeArgType}?`;
|
||||
desc: string;
|
||||
oneof?: string;
|
||||
default?: unknown;
|
||||
options?: NodeArgOption[];
|
||||
}
|
||||
|
||||
+60
-32
@@ -1,5 +1,5 @@
|
||||
import { useWorkspace } from "@/contexts/workspace-context";
|
||||
import { NodeArgType, NodeModel, TreeGraphData, TreeModel } from "@/misc/b3type";
|
||||
import { NodeArg, NodeArgType, NodeModel, TreeGraphData, TreeModel } from "@/misc/b3type";
|
||||
import * as fs from "fs";
|
||||
import { message } from "./hooks";
|
||||
import Path from "./path";
|
||||
@@ -115,36 +115,57 @@ export const checkNodeData = (data: NodeModel | null | undefined) => {
|
||||
if (conf.args) {
|
||||
for (let i = 0; i < conf.args.length; i++) {
|
||||
const arg = conf.args[i];
|
||||
if (arg.type.indexOf("?") === -1) {
|
||||
const type = arg.type as NodeArgType;
|
||||
const value = data.args?.[arg.name];
|
||||
if (type === "float") {
|
||||
if (typeof value !== "number") {
|
||||
console.error(`check ${data.id}|${data.name}: '${arg.name}' must be a number`);
|
||||
hasError = true;
|
||||
}
|
||||
} else if (type === "int") {
|
||||
if (typeof value !== "number" || value !== Math.floor(value)) {
|
||||
console.error(`check ${data.id}|${data.name}: '${arg.name}' must be a int`);
|
||||
hasError = true;
|
||||
}
|
||||
} else if (type === "string") {
|
||||
if (!value || typeof value !== "string") {
|
||||
console.error(`check ${data.id}|${data.name}: '${arg.name}' must be a string`);
|
||||
hasError = true;
|
||||
}
|
||||
} else if (type === "enum") {
|
||||
if (!arg.options?.find((option) => option.value === value)) {
|
||||
console.error(
|
||||
`check ${data.id}|${data.name}: '${arg.name}' must be one of option value`
|
||||
);
|
||||
hasError = true;
|
||||
}
|
||||
} else if (type == "code") {
|
||||
if (!value || typeof value !== "string") {
|
||||
console.error(`check ${data.id}|${data.name}: '${arg.name}' must be a string`);
|
||||
hasError = true;
|
||||
}
|
||||
const type = arg.type.replace("?", "") as NodeArgType;
|
||||
const value = data.args?.[arg.name];
|
||||
if (type === "float") {
|
||||
if (!(typeof value === "number" || (value === undefined && arg.type === "float?"))) {
|
||||
console.error(`check ${data.id}|${data.name}: '${arg.name}=${value}' is not a number`);
|
||||
hasError = true;
|
||||
}
|
||||
} else if (type === "int") {
|
||||
if (
|
||||
!(
|
||||
(typeof value === "number" && value === Math.floor(value)) ||
|
||||
(value === undefined && arg.type === "int?")
|
||||
)
|
||||
) {
|
||||
console.error(`check ${data.id}|${data.name}: '${arg.name}=${value}' is not a int`);
|
||||
hasError = true;
|
||||
}
|
||||
} else if (type === "string") {
|
||||
if (
|
||||
!(
|
||||
(typeof value === "string" && value) ||
|
||||
((value === undefined || value === "") && arg.type === "string?")
|
||||
)
|
||||
) {
|
||||
console.error(`check ${data.id}|${data.name}: '${arg.name}=${value}' is not a string`);
|
||||
hasError = true;
|
||||
}
|
||||
} else if (type === "enum") {
|
||||
const found = !!arg.options?.find((option) => option.value === value);
|
||||
if (!(found || (value === undefined && arg.type === "enum?"))) {
|
||||
console.error(
|
||||
`check ${data.id}|${data.name}: '${arg.name}=${value}' is not a one of the option values`
|
||||
);
|
||||
hasError = true;
|
||||
}
|
||||
} else if (type == "code") {
|
||||
if (
|
||||
!(
|
||||
(typeof value === "string" && value) ||
|
||||
((value === undefined || value === "") && arg.type === "code?")
|
||||
)
|
||||
) {
|
||||
console.error(`check ${data.id}|${data.name}: '${arg.name}=${value}' is not a string`);
|
||||
hasError = true;
|
||||
}
|
||||
} else if (type == "object") {
|
||||
if (!((value !== undefined && value !== null && value !== "") || arg.type === "object?")) {
|
||||
console.error(
|
||||
`check ${data.id}|${data.name}: '${arg.name}=${value}' is not an invalid object`
|
||||
);
|
||||
hasError = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -368,7 +389,7 @@ export const checkTreeData = (data: TreeGraphData) => {
|
||||
}
|
||||
if (conf.args) {
|
||||
for (let i = 0; i < conf.args.length; i++) {
|
||||
const arg = conf.args[i];
|
||||
const arg = conf.args[i] as NodeArg;
|
||||
if (arg.type.indexOf("?") === -1) {
|
||||
const type = arg.type as NodeArgType;
|
||||
const value = data.args?.[arg.name];
|
||||
@@ -393,6 +414,13 @@ export const checkTreeData = (data: TreeGraphData) => {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else if (arg.oneof !== undefined) {
|
||||
const idx = conf.input?.findIndex((v) => v.startsWith(arg.oneof!)) ?? -1;
|
||||
const input = data.input?.[idx] ?? "";
|
||||
const value = data.args?.[arg.name] ?? "";
|
||||
if ((input !== "" && value !== "") || (input === "" && value === "")) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user