openxiangda 1.0.26 → 1.0.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -0
- package/lib/cli.js +324 -17
- package/openxiangda-skills/SKILL.md +1 -1
- package/openxiangda-skills/references/automation-v3.md +56 -0
- package/openxiangda-skills/references/component-guide.md +9 -9
- package/openxiangda-skills/references/openxiangda-api.md +4 -0
- package/openxiangda-skills/references/style-system.md +121 -114
- package/openxiangda-skills/references/workflow-v3.md +44 -0
- package/openxiangda-skills/skills/openxiangda-page/SKILL.md +2 -2
- package/openxiangda-skills/skills/openxiangda-workflow-automation/SKILL.md +4 -0
- package/package.json +6 -1
- package/packages/sdk/dist/workflow/index.cjs +230 -0
- package/packages/sdk/dist/workflow/index.cjs.map +1 -0
- package/packages/sdk/dist/workflow/index.d.mts +61 -0
- package/packages/sdk/dist/workflow/index.d.ts +61 -0
- package/packages/sdk/dist/workflow/index.mjs +209 -0
- package/packages/sdk/dist/workflow/index.mjs.map +1 -0
- package/templates/sy-lowcode-app-workspace/examples/best-practices/design-style.md +4 -0
- package/templates/sy-lowcode-app-workspace/scripts/build-js-code.mjs +56 -16
- package/templates/sy-lowcode-app-workspace/tsconfig.js-code-nodes.json +1 -1
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
type WorkflowNodeRef = string;
|
|
2
|
+
interface WorkflowCompileResult {
|
|
3
|
+
definitionJson: {
|
|
4
|
+
version: "v3";
|
|
5
|
+
nodes: any[];
|
|
6
|
+
edges: any[];
|
|
7
|
+
flowConfig: Record<string, any[]>;
|
|
8
|
+
globalSettings: Record<string, any>;
|
|
9
|
+
};
|
|
10
|
+
previewJson: {
|
|
11
|
+
kind: "workflow_code_preview";
|
|
12
|
+
version: "preview_v1";
|
|
13
|
+
steps: any[];
|
|
14
|
+
edges: any[];
|
|
15
|
+
sourceMode: "workflow_code_ts";
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
interface WorkflowDefinitionInput {
|
|
19
|
+
name?: string;
|
|
20
|
+
formCode?: string;
|
|
21
|
+
formUuid?: string;
|
|
22
|
+
globalSettings?: Record<string, any>;
|
|
23
|
+
build: (flow: WorkflowBuilder) => void;
|
|
24
|
+
}
|
|
25
|
+
type NodeOptions = Record<string, any>;
|
|
26
|
+
declare class WorkflowBuilder {
|
|
27
|
+
private readonly meta;
|
|
28
|
+
private nodes;
|
|
29
|
+
private edges;
|
|
30
|
+
private flowConfig;
|
|
31
|
+
private globalSettings;
|
|
32
|
+
constructor(meta?: Omit<WorkflowDefinitionInput, "build">);
|
|
33
|
+
start(id?: string, data?: NodeOptions): WorkflowNodeRef;
|
|
34
|
+
end(id?: string, data?: NodeOptions): WorkflowNodeRef;
|
|
35
|
+
approval(id: string, data: NodeOptions): WorkflowNodeRef;
|
|
36
|
+
copy(id: string, data: NodeOptions): WorkflowNodeRef;
|
|
37
|
+
jsCode(id: string, data: NodeOptions): WorkflowNodeRef;
|
|
38
|
+
callbackWait(id: string, data: NodeOptions): WorkflowNodeRef;
|
|
39
|
+
connectorCall(id: string, data: NodeOptions): WorkflowNodeRef;
|
|
40
|
+
workNotification(id: string, data: NodeOptions): WorkflowNodeRef;
|
|
41
|
+
dingtalkCard(id: string, data: NodeOptions): WorkflowNodeRef;
|
|
42
|
+
condition(id: string, data: NodeOptions): WorkflowNodeRef;
|
|
43
|
+
data: {
|
|
44
|
+
retrieveSingle: (id: string, data: NodeOptions) => string;
|
|
45
|
+
retrieveBatch: (id: string, data: NodeOptions) => string;
|
|
46
|
+
create: (id: string, data: NodeOptions) => string;
|
|
47
|
+
update: (id: string, data: NodeOptions) => string;
|
|
48
|
+
};
|
|
49
|
+
node(type: string, id: string, data: NodeOptions): WorkflowNodeRef;
|
|
50
|
+
edge(source: WorkflowNodeRef, target: WorkflowNodeRef, data?: NodeOptions): void;
|
|
51
|
+
sequence(...refs: WorkflowNodeRef[]): void;
|
|
52
|
+
setFieldPermissions(nodeId: WorkflowNodeRef, permissions: any[]): void;
|
|
53
|
+
setGlobalSettings(settings: Record<string, any>): void;
|
|
54
|
+
compile(): WorkflowCompileResult;
|
|
55
|
+
}
|
|
56
|
+
declare function defineWorkflow(input: WorkflowDefinitionInput): {
|
|
57
|
+
__openxiangdaWorkflow: boolean;
|
|
58
|
+
compile(): WorkflowCompileResult;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export { WorkflowBuilder, type WorkflowCompileResult, type WorkflowDefinitionInput, type WorkflowNodeRef, defineWorkflow };
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
type WorkflowNodeRef = string;
|
|
2
|
+
interface WorkflowCompileResult {
|
|
3
|
+
definitionJson: {
|
|
4
|
+
version: "v3";
|
|
5
|
+
nodes: any[];
|
|
6
|
+
edges: any[];
|
|
7
|
+
flowConfig: Record<string, any[]>;
|
|
8
|
+
globalSettings: Record<string, any>;
|
|
9
|
+
};
|
|
10
|
+
previewJson: {
|
|
11
|
+
kind: "workflow_code_preview";
|
|
12
|
+
version: "preview_v1";
|
|
13
|
+
steps: any[];
|
|
14
|
+
edges: any[];
|
|
15
|
+
sourceMode: "workflow_code_ts";
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
interface WorkflowDefinitionInput {
|
|
19
|
+
name?: string;
|
|
20
|
+
formCode?: string;
|
|
21
|
+
formUuid?: string;
|
|
22
|
+
globalSettings?: Record<string, any>;
|
|
23
|
+
build: (flow: WorkflowBuilder) => void;
|
|
24
|
+
}
|
|
25
|
+
type NodeOptions = Record<string, any>;
|
|
26
|
+
declare class WorkflowBuilder {
|
|
27
|
+
private readonly meta;
|
|
28
|
+
private nodes;
|
|
29
|
+
private edges;
|
|
30
|
+
private flowConfig;
|
|
31
|
+
private globalSettings;
|
|
32
|
+
constructor(meta?: Omit<WorkflowDefinitionInput, "build">);
|
|
33
|
+
start(id?: string, data?: NodeOptions): WorkflowNodeRef;
|
|
34
|
+
end(id?: string, data?: NodeOptions): WorkflowNodeRef;
|
|
35
|
+
approval(id: string, data: NodeOptions): WorkflowNodeRef;
|
|
36
|
+
copy(id: string, data: NodeOptions): WorkflowNodeRef;
|
|
37
|
+
jsCode(id: string, data: NodeOptions): WorkflowNodeRef;
|
|
38
|
+
callbackWait(id: string, data: NodeOptions): WorkflowNodeRef;
|
|
39
|
+
connectorCall(id: string, data: NodeOptions): WorkflowNodeRef;
|
|
40
|
+
workNotification(id: string, data: NodeOptions): WorkflowNodeRef;
|
|
41
|
+
dingtalkCard(id: string, data: NodeOptions): WorkflowNodeRef;
|
|
42
|
+
condition(id: string, data: NodeOptions): WorkflowNodeRef;
|
|
43
|
+
data: {
|
|
44
|
+
retrieveSingle: (id: string, data: NodeOptions) => string;
|
|
45
|
+
retrieveBatch: (id: string, data: NodeOptions) => string;
|
|
46
|
+
create: (id: string, data: NodeOptions) => string;
|
|
47
|
+
update: (id: string, data: NodeOptions) => string;
|
|
48
|
+
};
|
|
49
|
+
node(type: string, id: string, data: NodeOptions): WorkflowNodeRef;
|
|
50
|
+
edge(source: WorkflowNodeRef, target: WorkflowNodeRef, data?: NodeOptions): void;
|
|
51
|
+
sequence(...refs: WorkflowNodeRef[]): void;
|
|
52
|
+
setFieldPermissions(nodeId: WorkflowNodeRef, permissions: any[]): void;
|
|
53
|
+
setGlobalSettings(settings: Record<string, any>): void;
|
|
54
|
+
compile(): WorkflowCompileResult;
|
|
55
|
+
}
|
|
56
|
+
declare function defineWorkflow(input: WorkflowDefinitionInput): {
|
|
57
|
+
__openxiangdaWorkflow: boolean;
|
|
58
|
+
compile(): WorkflowCompileResult;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export { WorkflowBuilder, type WorkflowCompileResult, type WorkflowDefinitionInput, type WorkflowNodeRef, defineWorkflow };
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
// packages/sdk/src/workflow/index.ts
|
|
2
|
+
function sanitizeId(value) {
|
|
3
|
+
return String(value || "").trim().replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
4
|
+
}
|
|
5
|
+
function createPosition(index) {
|
|
6
|
+
return { x: 400, y: 100 + index * 150 };
|
|
7
|
+
}
|
|
8
|
+
var WorkflowBuilder = class {
|
|
9
|
+
constructor(meta = {}) {
|
|
10
|
+
this.meta = meta;
|
|
11
|
+
this.nodes = [];
|
|
12
|
+
this.edges = [];
|
|
13
|
+
this.flowConfig = {};
|
|
14
|
+
this.globalSettings = {};
|
|
15
|
+
this.data = {
|
|
16
|
+
retrieveSingle: (id, data) => this.node("data_retrieve_single", id, {
|
|
17
|
+
type: "data_retrieve_single",
|
|
18
|
+
label: data.label || "\u83B7\u53D6\u5355\u6761\u6570\u636E",
|
|
19
|
+
filterType: data.filterType || "condition",
|
|
20
|
+
...data
|
|
21
|
+
}),
|
|
22
|
+
retrieveBatch: (id, data) => this.node("data_retrieve_batch", id, {
|
|
23
|
+
type: "data_retrieve_batch",
|
|
24
|
+
label: data.label || "\u83B7\u53D6\u591A\u6761\u6570\u636E",
|
|
25
|
+
filterType: data.filterType || "condition",
|
|
26
|
+
...data
|
|
27
|
+
}),
|
|
28
|
+
create: (id, data) => this.node("data_create", id, {
|
|
29
|
+
type: "data_create",
|
|
30
|
+
label: data.label || "\u65B0\u589E\u6570\u636E",
|
|
31
|
+
insertType: data.insertType || "form",
|
|
32
|
+
assignments: data.assignments || [],
|
|
33
|
+
...data
|
|
34
|
+
}),
|
|
35
|
+
update: (id, data) => this.node("data_update", id, {
|
|
36
|
+
type: "data_update",
|
|
37
|
+
label: data.label || "\u66F4\u65B0\u6570\u636E",
|
|
38
|
+
updateType: data.updateType || "direct_form",
|
|
39
|
+
assignments: data.assignments || [],
|
|
40
|
+
noneOperation: data.noneOperation || "ignored",
|
|
41
|
+
...data
|
|
42
|
+
})
|
|
43
|
+
};
|
|
44
|
+
this.globalSettings = { ...meta.globalSettings || {} };
|
|
45
|
+
}
|
|
46
|
+
start(id = "start", data = {}) {
|
|
47
|
+
return this.node("start", id, { label: "\u5F00\u59CB\u8282\u70B9", ...data });
|
|
48
|
+
}
|
|
49
|
+
end(id = "end", data = {}) {
|
|
50
|
+
return this.node("end", id, { label: "\u7ED3\u675F\u8282\u70B9", ...data });
|
|
51
|
+
}
|
|
52
|
+
approval(id, data) {
|
|
53
|
+
return this.node("approval", id, {
|
|
54
|
+
label: data.label || "\u5BA1\u6279",
|
|
55
|
+
value: data.value || "",
|
|
56
|
+
approverType: data.approverType || "ext_target_approval",
|
|
57
|
+
approvals: data.approvals || [],
|
|
58
|
+
approvalNames: data.approvalNames || [],
|
|
59
|
+
multiApprove: data.multiApprove || "or",
|
|
60
|
+
...data
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
copy(id, data) {
|
|
64
|
+
return this.node("copy", id, {
|
|
65
|
+
label: data.label || "\u6284\u9001",
|
|
66
|
+
value: data.value || "",
|
|
67
|
+
approverType: data.approverType || "ext_target_approval",
|
|
68
|
+
approvals: data.approvals || [],
|
|
69
|
+
approvalNames: data.approvalNames || [],
|
|
70
|
+
...data
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
jsCode(id, data) {
|
|
74
|
+
return this.node("js_code", id, {
|
|
75
|
+
label: data.label || "JS\u4EE3\u7801\u8282\u70B9",
|
|
76
|
+
runtimeMode: "trusted_node",
|
|
77
|
+
sourceType: data.sourceFile ? "file_snapshot" : data.sourceType || "inline",
|
|
78
|
+
timeout: data.timeout || data.timeoutMs || 3e4,
|
|
79
|
+
...data
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
callbackWait(id, data) {
|
|
83
|
+
return this.node("callback_wait", id, {
|
|
84
|
+
type: "callback_wait",
|
|
85
|
+
label: data.label || "\u56DE\u8C03\u7B49\u5F85",
|
|
86
|
+
timeoutSeconds: data.timeoutSeconds || 86400,
|
|
87
|
+
timeoutStrategy: data.timeoutStrategy || "FAIL",
|
|
88
|
+
...data
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
connectorCall(id, data) {
|
|
92
|
+
return this.node("connector_call", id, {
|
|
93
|
+
type: "connector_call",
|
|
94
|
+
label: data.label || "\u8FDE\u63A5\u5668",
|
|
95
|
+
timeout: data.timeout || 3e4,
|
|
96
|
+
...data
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
workNotification(id, data) {
|
|
100
|
+
return this.node("work_notification", id, {
|
|
101
|
+
label: data.label || "\u5DE5\u4F5C\u901A\u77E5",
|
|
102
|
+
buttonText: data.buttonText || "\u67E5\u770B\u8BE6\u60C5",
|
|
103
|
+
linkType: data.linkType || "current_form",
|
|
104
|
+
...data
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
dingtalkCard(id, data) {
|
|
108
|
+
return this.node("dingtalk_card", id, {
|
|
109
|
+
config: {
|
|
110
|
+
type: "dingtalk_card",
|
|
111
|
+
label: data.label || "\u9489\u9489\u6D88\u606F\u5361\u7247",
|
|
112
|
+
sameFieldStrategy: "create",
|
|
113
|
+
...data.config || data
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
condition(id, data) {
|
|
118
|
+
return this.node("condition_branch", id, {
|
|
119
|
+
type: "condition_branch",
|
|
120
|
+
label: data.label || "\u6761\u4EF6\u5206\u652F",
|
|
121
|
+
condition: data.condition || { ruleType: "group", condition: "AND", rules: [] },
|
|
122
|
+
isElse: data.isElse === true,
|
|
123
|
+
priority: data.priority || "1",
|
|
124
|
+
trueNodeId: data.trueNodeId,
|
|
125
|
+
falseNodeId: data.falseNodeId,
|
|
126
|
+
...data
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
node(type, id, data) {
|
|
130
|
+
const nodeId = sanitizeId(id);
|
|
131
|
+
if (!nodeId) throw new Error("workflow node id is required");
|
|
132
|
+
if (this.nodes.some((node) => node.id === nodeId)) {
|
|
133
|
+
throw new Error(`duplicate workflow node id: ${nodeId}`);
|
|
134
|
+
}
|
|
135
|
+
this.nodes.push({
|
|
136
|
+
id: nodeId,
|
|
137
|
+
type,
|
|
138
|
+
data,
|
|
139
|
+
position: createPosition(this.nodes.length)
|
|
140
|
+
});
|
|
141
|
+
if (Array.isArray(data.fieldPermissions)) {
|
|
142
|
+
this.flowConfig[nodeId] = data.fieldPermissions;
|
|
143
|
+
}
|
|
144
|
+
return nodeId;
|
|
145
|
+
}
|
|
146
|
+
edge(source, target, data = {}) {
|
|
147
|
+
const id = data.id || `edge_${source}_${target}`;
|
|
148
|
+
this.edges.push({
|
|
149
|
+
id,
|
|
150
|
+
source,
|
|
151
|
+
target,
|
|
152
|
+
type: data.type || "custom",
|
|
153
|
+
...data.label ? { label: data.label } : {}
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
sequence(...refs) {
|
|
157
|
+
for (let index = 0; index < refs.length - 1; index += 1) {
|
|
158
|
+
this.edge(refs[index], refs[index + 1]);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
setFieldPermissions(nodeId, permissions) {
|
|
162
|
+
this.flowConfig[nodeId] = permissions;
|
|
163
|
+
}
|
|
164
|
+
setGlobalSettings(settings) {
|
|
165
|
+
this.globalSettings = { ...this.globalSettings, ...settings };
|
|
166
|
+
}
|
|
167
|
+
compile() {
|
|
168
|
+
return {
|
|
169
|
+
definitionJson: {
|
|
170
|
+
version: "v3",
|
|
171
|
+
nodes: this.nodes,
|
|
172
|
+
edges: this.edges,
|
|
173
|
+
flowConfig: this.flowConfig,
|
|
174
|
+
globalSettings: this.globalSettings
|
|
175
|
+
},
|
|
176
|
+
previewJson: {
|
|
177
|
+
kind: "workflow_code_preview",
|
|
178
|
+
version: "preview_v1",
|
|
179
|
+
sourceMode: "workflow_code_ts",
|
|
180
|
+
steps: this.nodes.map((node) => ({
|
|
181
|
+
id: node.id,
|
|
182
|
+
type: node.type,
|
|
183
|
+
label: node.data?.label || node.id,
|
|
184
|
+
config: node.data
|
|
185
|
+
})),
|
|
186
|
+
edges: this.edges.map((edge) => ({
|
|
187
|
+
source: edge.source,
|
|
188
|
+
target: edge.target,
|
|
189
|
+
label: edge.label
|
|
190
|
+
}))
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
function defineWorkflow(input) {
|
|
196
|
+
return {
|
|
197
|
+
__openxiangdaWorkflow: true,
|
|
198
|
+
compile() {
|
|
199
|
+
const builder = new WorkflowBuilder(input);
|
|
200
|
+
input.build(builder);
|
|
201
|
+
return builder.compile();
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
export {
|
|
206
|
+
WorkflowBuilder,
|
|
207
|
+
defineWorkflow
|
|
208
|
+
};
|
|
209
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/workflow/index.ts"],"sourcesContent":["export type WorkflowNodeRef = string;\n\nexport interface WorkflowCompileResult {\n definitionJson: {\n version: \"v3\";\n nodes: any[];\n edges: any[];\n flowConfig: Record<string, any[]>;\n globalSettings: Record<string, any>;\n };\n previewJson: {\n kind: \"workflow_code_preview\";\n version: \"preview_v1\";\n steps: any[];\n edges: any[];\n sourceMode: \"workflow_code_ts\";\n };\n}\n\nexport interface WorkflowDefinitionInput {\n name?: string;\n formCode?: string;\n formUuid?: string;\n globalSettings?: Record<string, any>;\n build: (flow: WorkflowBuilder) => void;\n}\n\ntype NodeOptions = Record<string, any>;\n\nfunction sanitizeId(value: string) {\n return String(value || \"\")\n .trim()\n .replace(/[^a-zA-Z0-9_-]/g, \"_\");\n}\n\nfunction createPosition(index: number) {\n return { x: 400, y: 100 + index * 150 };\n}\n\nexport class WorkflowBuilder {\n private nodes: any[] = [];\n private edges: any[] = [];\n private flowConfig: Record<string, any[]> = {};\n private globalSettings: Record<string, any> = {};\n\n constructor(private readonly meta: Omit<WorkflowDefinitionInput, \"build\"> = {}) {\n this.globalSettings = { ...(meta.globalSettings || {}) };\n }\n\n start(id = \"start\", data: NodeOptions = {}): WorkflowNodeRef {\n return this.node(\"start\", id, { label: \"开始节点\", ...data });\n }\n\n end(id = \"end\", data: NodeOptions = {}): WorkflowNodeRef {\n return this.node(\"end\", id, { label: \"结束节点\", ...data });\n }\n\n approval(id: string, data: NodeOptions): WorkflowNodeRef {\n return this.node(\"approval\", id, {\n label: data.label || \"审批\",\n value: data.value || \"\",\n approverType: data.approverType || \"ext_target_approval\",\n approvals: data.approvals || [],\n approvalNames: data.approvalNames || [],\n multiApprove: data.multiApprove || \"or\",\n ...data,\n });\n }\n\n copy(id: string, data: NodeOptions): WorkflowNodeRef {\n return this.node(\"copy\", id, {\n label: data.label || \"抄送\",\n value: data.value || \"\",\n approverType: data.approverType || \"ext_target_approval\",\n approvals: data.approvals || [],\n approvalNames: data.approvalNames || [],\n ...data,\n });\n }\n\n jsCode(id: string, data: NodeOptions): WorkflowNodeRef {\n return this.node(\"js_code\", id, {\n label: data.label || \"JS代码节点\",\n runtimeMode: \"trusted_node\",\n sourceType: data.sourceFile ? \"file_snapshot\" : data.sourceType || \"inline\",\n timeout: data.timeout || data.timeoutMs || 30000,\n ...data,\n });\n }\n\n callbackWait(id: string, data: NodeOptions): WorkflowNodeRef {\n return this.node(\"callback_wait\", id, {\n type: \"callback_wait\",\n label: data.label || \"回调等待\",\n timeoutSeconds: data.timeoutSeconds || 86400,\n timeoutStrategy: data.timeoutStrategy || \"FAIL\",\n ...data,\n });\n }\n\n connectorCall(id: string, data: NodeOptions): WorkflowNodeRef {\n return this.node(\"connector_call\", id, {\n type: \"connector_call\",\n label: data.label || \"连接器\",\n timeout: data.timeout || 30000,\n ...data,\n });\n }\n\n workNotification(id: string, data: NodeOptions): WorkflowNodeRef {\n return this.node(\"work_notification\", id, {\n label: data.label || \"工作通知\",\n buttonText: data.buttonText || \"查看详情\",\n linkType: data.linkType || \"current_form\",\n ...data,\n });\n }\n\n dingtalkCard(id: string, data: NodeOptions): WorkflowNodeRef {\n return this.node(\"dingtalk_card\", id, {\n config: {\n type: \"dingtalk_card\",\n label: data.label || \"钉钉消息卡片\",\n sameFieldStrategy: \"create\",\n ...(data.config || data),\n },\n });\n }\n\n condition(id: string, data: NodeOptions): WorkflowNodeRef {\n return this.node(\"condition_branch\", id, {\n type: \"condition_branch\",\n label: data.label || \"条件分支\",\n condition: data.condition || { ruleType: \"group\", condition: \"AND\", rules: [] },\n isElse: data.isElse === true,\n priority: data.priority || \"1\",\n trueNodeId: data.trueNodeId,\n falseNodeId: data.falseNodeId,\n ...data,\n });\n }\n\n data = {\n retrieveSingle: (id: string, data: NodeOptions) =>\n this.node(\"data_retrieve_single\", id, {\n type: \"data_retrieve_single\",\n label: data.label || \"获取单条数据\",\n filterType: data.filterType || \"condition\",\n ...data,\n }),\n retrieveBatch: (id: string, data: NodeOptions) =>\n this.node(\"data_retrieve_batch\", id, {\n type: \"data_retrieve_batch\",\n label: data.label || \"获取多条数据\",\n filterType: data.filterType || \"condition\",\n ...data,\n }),\n create: (id: string, data: NodeOptions) =>\n this.node(\"data_create\", id, {\n type: \"data_create\",\n label: data.label || \"新增数据\",\n insertType: data.insertType || \"form\",\n assignments: data.assignments || [],\n ...data,\n }),\n update: (id: string, data: NodeOptions) =>\n this.node(\"data_update\", id, {\n type: \"data_update\",\n label: data.label || \"更新数据\",\n updateType: data.updateType || \"direct_form\",\n assignments: data.assignments || [],\n noneOperation: data.noneOperation || \"ignored\",\n ...data,\n }),\n };\n\n node(type: string, id: string, data: NodeOptions): WorkflowNodeRef {\n const nodeId = sanitizeId(id);\n if (!nodeId) throw new Error(\"workflow node id is required\");\n if (this.nodes.some((node) => node.id === nodeId)) {\n throw new Error(`duplicate workflow node id: ${nodeId}`);\n }\n this.nodes.push({\n id: nodeId,\n type,\n data,\n position: createPosition(this.nodes.length),\n });\n if (Array.isArray(data.fieldPermissions)) {\n this.flowConfig[nodeId] = data.fieldPermissions;\n }\n return nodeId;\n }\n\n edge(source: WorkflowNodeRef, target: WorkflowNodeRef, data: NodeOptions = {}) {\n const id = data.id || `edge_${source}_${target}`;\n this.edges.push({\n id,\n source,\n target,\n type: data.type || \"custom\",\n ...(data.label ? { label: data.label } : {}),\n });\n }\n\n sequence(...refs: WorkflowNodeRef[]) {\n for (let index = 0; index < refs.length - 1; index += 1) {\n this.edge(refs[index], refs[index + 1]);\n }\n }\n\n setFieldPermissions(nodeId: WorkflowNodeRef, permissions: any[]) {\n this.flowConfig[nodeId] = permissions;\n }\n\n setGlobalSettings(settings: Record<string, any>) {\n this.globalSettings = { ...this.globalSettings, ...settings };\n }\n\n compile(): WorkflowCompileResult {\n return {\n definitionJson: {\n version: \"v3\",\n nodes: this.nodes,\n edges: this.edges,\n flowConfig: this.flowConfig,\n globalSettings: this.globalSettings,\n },\n previewJson: {\n kind: \"workflow_code_preview\",\n version: \"preview_v1\",\n sourceMode: \"workflow_code_ts\",\n steps: this.nodes.map((node) => ({\n id: node.id,\n type: node.type,\n label: node.data?.label || node.id,\n config: node.data,\n })),\n edges: this.edges.map((edge) => ({\n source: edge.source,\n target: edge.target,\n label: edge.label,\n })),\n },\n };\n }\n}\n\nexport function defineWorkflow(input: WorkflowDefinitionInput) {\n return {\n __openxiangdaWorkflow: true,\n compile() {\n const builder = new WorkflowBuilder(input);\n input.build(builder);\n return builder.compile();\n },\n };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -24,6 +24,10 @@ OpenXiangda business apps should feel like focused operational tools.
|
|
|
24
24
|
|
|
25
25
|
- Use platform CSS variables, Tailwind semantic classes, Ant Design, and
|
|
26
26
|
antd-mobile.
|
|
27
|
+
- Treat platform tokens as the default baseline, not a hard limit. Tailwind
|
|
28
|
+
ordinary utilities, arbitrary values, page CSS variables, scoped CSS, and
|
|
29
|
+
small dynamic inline styles are acceptable when they better match the product
|
|
30
|
+
design.
|
|
27
31
|
- Use mature packages for mature interactions: antd controls instead of native
|
|
28
32
|
inputs, ECharts for charts, GSAP for complex animation timelines, and
|
|
29
33
|
maintained drag/drop or virtual-list libraries when those behaviors are
|
|
@@ -8,10 +8,23 @@ import { fileURLToPath } from "node:url";
|
|
|
8
8
|
import { build } from "vite";
|
|
9
9
|
|
|
10
10
|
const rootDir = fileURLToPath(new URL("..", import.meta.url));
|
|
11
|
-
const sourceRoot = path.join(rootDir, "src", "js-code-nodes");
|
|
12
|
-
const outputRoot = path.join(rootDir, "dist", "js-code-nodes");
|
|
13
11
|
const args = process.argv.slice(2);
|
|
14
12
|
|
|
13
|
+
const sourceKinds = {
|
|
14
|
+
"js-code-nodes": {
|
|
15
|
+
name: "js-code-nodes",
|
|
16
|
+
sourceRoot: path.join(rootDir, "src", "js-code-nodes"),
|
|
17
|
+
outputRoot: path.join(rootDir, "dist", "js-code-nodes"),
|
|
18
|
+
label: "JS_CODE",
|
|
19
|
+
},
|
|
20
|
+
automations: {
|
|
21
|
+
name: "automations",
|
|
22
|
+
sourceRoot: path.join(rootDir, "src", "automations"),
|
|
23
|
+
outputRoot: path.join(rootDir, "dist", "automations"),
|
|
24
|
+
label: "automation code",
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
|
|
15
28
|
function readArg(name) {
|
|
16
29
|
const prefix = `--${name}=`;
|
|
17
30
|
const inline = args.find((arg) => arg.startsWith(prefix));
|
|
@@ -20,12 +33,12 @@ function readArg(name) {
|
|
|
20
33
|
return index >= 0 ? args[index + 1] : undefined;
|
|
21
34
|
}
|
|
22
35
|
|
|
23
|
-
async function listScriptCodes() {
|
|
24
|
-
if (!existsSync(sourceRoot)) return [];
|
|
25
|
-
const entries = await readdir(sourceRoot);
|
|
36
|
+
async function listScriptCodes(kind) {
|
|
37
|
+
if (!existsSync(kind.sourceRoot)) return [];
|
|
38
|
+
const entries = await readdir(kind.sourceRoot);
|
|
26
39
|
const result = [];
|
|
27
40
|
for (const entry of entries) {
|
|
28
|
-
const entryDir = path.join(sourceRoot, entry);
|
|
41
|
+
const entryDir = path.join(kind.sourceRoot, entry);
|
|
29
42
|
const entryStat = await stat(entryDir);
|
|
30
43
|
if (!entryStat.isDirectory()) continue;
|
|
31
44
|
if (existsSync(path.join(entryDir, "index.ts"))) result.push(entry);
|
|
@@ -45,11 +58,11 @@ function typecheckScripts() {
|
|
|
45
58
|
if (result.status !== 0) throw new Error("JS_CODE TypeScript validation failed");
|
|
46
59
|
}
|
|
47
60
|
|
|
48
|
-
async function buildScript(scriptCode) {
|
|
49
|
-
const entry = path.join(sourceRoot, scriptCode, "index.ts");
|
|
50
|
-
if (!existsSync(entry)) throw new Error(
|
|
61
|
+
async function buildScript(kind, scriptCode) {
|
|
62
|
+
const entry = path.join(kind.sourceRoot, scriptCode, "index.ts");
|
|
63
|
+
if (!existsSync(entry)) throw new Error(`${kind.label} script not found: ${entry}`);
|
|
51
64
|
|
|
52
|
-
const outDir = path.join(outputRoot, scriptCode);
|
|
65
|
+
const outDir = path.join(kind.outputRoot, scriptCode);
|
|
53
66
|
const external = Array.from(
|
|
54
67
|
new Set([...builtinModules, ...builtinModules.map((name) => `node:${name}`)]),
|
|
55
68
|
);
|
|
@@ -82,19 +95,46 @@ async function buildScript(scriptCode) {
|
|
|
82
95
|
},
|
|
83
96
|
});
|
|
84
97
|
|
|
85
|
-
console.log(`built ${scriptCode} -> ${path.relative(rootDir, path.join(outDir, "index.cjs"))}`);
|
|
98
|
+
console.log(`built ${kind.name}/${scriptCode} -> ${path.relative(rootDir, path.join(outDir, "index.cjs"))}`);
|
|
86
99
|
}
|
|
87
100
|
|
|
88
101
|
const onlyScript = readArg("script");
|
|
89
|
-
const
|
|
102
|
+
const sourceArg = readArg("source");
|
|
103
|
+
const selectedKind = sourceArg ? sourceKinds[sourceArg] : undefined;
|
|
104
|
+
if (sourceArg && !selectedKind) {
|
|
105
|
+
throw new Error(`unsupported source: ${sourceArg}. Expected js-code-nodes or automations`);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async function resolveBuildTargets() {
|
|
109
|
+
if (onlyScript) {
|
|
110
|
+
if (selectedKind) return [{ kind: selectedKind, scriptCode: onlyScript }];
|
|
111
|
+
for (const kind of Object.values(sourceKinds)) {
|
|
112
|
+
if (existsSync(path.join(kind.sourceRoot, onlyScript, "index.ts"))) {
|
|
113
|
+
return [{ kind, scriptCode: onlyScript }];
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return [{ kind: sourceKinds["js-code-nodes"], scriptCode: onlyScript }];
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const kinds = selectedKind ? [selectedKind] : Object.values(sourceKinds);
|
|
120
|
+
const targets = [];
|
|
121
|
+
for (const kind of kinds) {
|
|
122
|
+
for (const scriptCode of await listScriptCodes(kind)) {
|
|
123
|
+
targets.push({ kind, scriptCode });
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return targets;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const targets = await resolveBuildTargets();
|
|
90
130
|
|
|
91
|
-
if (
|
|
92
|
-
console.log("no JS_CODE scripts found under src/js-code-nodes/<scriptCode>/index.ts");
|
|
131
|
+
if (targets.length === 0) {
|
|
132
|
+
console.log("no JS_CODE scripts found under src/js-code-nodes/<scriptCode>/index.ts or src/automations/<scriptCode>/index.ts");
|
|
93
133
|
process.exit(0);
|
|
94
134
|
}
|
|
95
135
|
|
|
96
136
|
typecheckScripts();
|
|
97
137
|
|
|
98
|
-
for (const
|
|
99
|
-
await buildScript(scriptCode);
|
|
138
|
+
for (const target of targets) {
|
|
139
|
+
await buildScript(target.kind, target.scriptCode);
|
|
100
140
|
}
|