@coding-flow/flow-design 0.2.3 → 0.2.4
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/dist/api/workflow.d.ts +6 -1
- package/dist/api/workflow.js +5 -2
- package/dist/components/design-import/design_import.module.js +5 -0
- package/dist/components/design-import/design_import_module.css +4 -0
- package/dist/components/design-import/import-file.d.ts +10 -0
- package/dist/components/design-import/import-file.js +33 -0
- package/dist/components/design-import/index.d.ts +4 -0
- package/dist/components/design-import/index.js +124 -23
- package/dist/components/design-import/upload.js +4 -1
- package/dist/components/design-panel/layout/header.js +56 -29
- package/package.json +7 -7
package/dist/api/workflow.d.ts
CHANGED
|
@@ -13,4 +13,9 @@ export declare const createCustomAction: () => Promise<import("@coding-flow/flow
|
|
|
13
13
|
export declare const save: (body: any) => Promise<import("@coding-flow/flow-core").Response>;
|
|
14
14
|
export declare const load: (id: string) => Promise<import("@coding-flow/flow-core").Response>;
|
|
15
15
|
export declare const exportWorkflow: (id: string) => Promise<void>;
|
|
16
|
-
export
|
|
16
|
+
export interface WorkflowImportRequest {
|
|
17
|
+
file: string;
|
|
18
|
+
mode: 'REPLACE' | 'INCREMENTAL';
|
|
19
|
+
}
|
|
20
|
+
export declare const importWorkflow: (request: WorkflowImportRequest) => Promise<import("@coding-flow/flow-core").Response>;
|
|
21
|
+
export declare const workflowCodeExists: (code: string) => Promise<import("@coding-flow/flow-core").Response>;
|
package/dist/api/workflow.js
CHANGED
|
@@ -33,5 +33,8 @@ const exportWorkflow = (id)=>{
|
|
|
33
33
|
const filename = `workflow_${id}.json`;
|
|
34
34
|
return httpClient.download('/api/cmd/workflow/export?id=' + id, filename);
|
|
35
35
|
};
|
|
36
|
-
const importWorkflow = (
|
|
37
|
-
|
|
36
|
+
const importWorkflow = (request)=>httpClient.post('/api/cmd/workflow/import', request);
|
|
37
|
+
const workflowCodeExists = (code)=>httpClient.get('/api/cmd/workflow/exists', {
|
|
38
|
+
code
|
|
39
|
+
});
|
|
40
|
+
export { changeState, changeVersion, create, createCustomAction, createNode, deleteVersion, exportWorkflow, importWorkflow, list, load, meta, options, remove, save, updateVersionName, versions, workflowCodeExists };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface WorkflowImportFileInfo {
|
|
2
|
+
code: string;
|
|
3
|
+
title: string;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* 从V1迁移包或历史单流程文件中读取流程编码。
|
|
7
|
+
*/
|
|
8
|
+
export declare const readImportWorkflowInfo: (file: string) => WorkflowImportFileInfo;
|
|
9
|
+
export declare const readImportWorkflowCode: (file: string) => string;
|
|
10
|
+
export declare const validateResetImportCode: (file: string, currentWorkflowCode: string) => string;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
const isJsonObject = (value)=>'object' == typeof value && null !== value && !Array.isArray(value);
|
|
2
|
+
const decodeBase64File = (file)=>{
|
|
3
|
+
const separatorIndex = file.indexOf(',');
|
|
4
|
+
const encoded = separatorIndex >= 0 ? file.substring(separatorIndex + 1) : file;
|
|
5
|
+
const binary = globalThis.atob(encoded);
|
|
6
|
+
const bytes = Uint8Array.from(binary, (character)=>character.charCodeAt(0));
|
|
7
|
+
return new TextDecoder().decode(bytes);
|
|
8
|
+
};
|
|
9
|
+
const readImportWorkflowInfo = (file)=>{
|
|
10
|
+
let parsed;
|
|
11
|
+
try {
|
|
12
|
+
parsed = JSON.parse(decodeBase64File(file));
|
|
13
|
+
} catch {
|
|
14
|
+
throw new Error('无法解析导入文件,请确认文件为有效的流程JSON文件');
|
|
15
|
+
}
|
|
16
|
+
if (!isJsonObject(parsed)) throw new Error('导入文件内容必须为JSON对象');
|
|
17
|
+
const workflow = parsed.workflow;
|
|
18
|
+
const workflowData = isJsonObject(workflow) ? workflow : parsed;
|
|
19
|
+
const code = workflowData.code;
|
|
20
|
+
if ('string' != typeof code || 0 === code.trim().length) throw new Error('导入文件中缺少流程编码');
|
|
21
|
+
const title = workflowData.title;
|
|
22
|
+
return {
|
|
23
|
+
code,
|
|
24
|
+
title: 'string' == typeof title && title.trim().length > 0 ? title : '未命名流程'
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
const readImportWorkflowCode = (file)=>readImportWorkflowInfo(file).code;
|
|
28
|
+
const validateResetImportCode = (file, currentWorkflowCode)=>{
|
|
29
|
+
const importedCode = readImportWorkflowCode(file);
|
|
30
|
+
if (importedCode !== currentWorkflowCode) throw new Error(`导入流程编码“${importedCode}”与当前流程编码“${currentWorkflowCode}”不一致,无法重置导入`);
|
|
31
|
+
return importedCode;
|
|
32
|
+
};
|
|
33
|
+
export { readImportWorkflowCode, readImportWorkflowInfo, validateResetImportCode };
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
+
export type WorkflowImportScene = 'MANAGEMENT' | 'RESET';
|
|
2
3
|
interface DesignImportProps {
|
|
3
4
|
open: boolean;
|
|
4
5
|
onClose: () => void;
|
|
6
|
+
scene?: WorkflowImportScene;
|
|
7
|
+
currentWorkflowCode?: string;
|
|
8
|
+
onImported?: (workId: string) => void;
|
|
5
9
|
}
|
|
6
10
|
export declare const DesignImport: React.FC<DesignImportProps>;
|
|
7
11
|
export {};
|
|
@@ -1,37 +1,138 @@
|
|
|
1
|
-
import { jsx } from "react/jsx-runtime";
|
|
2
|
-
import { Form, Modal, message } from "antd";
|
|
3
|
-
import "react";
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Alert, Descriptions, Form, Modal, message } from "antd";
|
|
3
|
+
import react from "react";
|
|
4
4
|
import { Upload } from "./upload.js";
|
|
5
|
-
import { importWorkflow } from "../../api/workflow.js";
|
|
5
|
+
import { importWorkflow, workflowCodeExists } from "../../api/workflow.js";
|
|
6
6
|
import { FlowMessageKey, FlowMessageRegistry } from "@coding-flow/flow-core";
|
|
7
|
+
import { readImportWorkflowInfo, validateResetImportCode } from "./import-file.js";
|
|
8
|
+
import design_import_module from "./design_import.module.js";
|
|
7
9
|
const DesignImport = (props)=>{
|
|
8
10
|
const [form] = Form.useForm();
|
|
9
|
-
|
|
10
|
-
|
|
11
|
+
const [submitting, setSubmitting] = react.useState(false);
|
|
12
|
+
const [fileInfo, setFileInfo] = react.useState();
|
|
13
|
+
const resetImport = 'RESET' === props.scene;
|
|
14
|
+
react.useEffect(()=>{
|
|
15
|
+
if (!props.open) {
|
|
16
|
+
form.resetFields();
|
|
17
|
+
setFileInfo(void 0);
|
|
18
|
+
}
|
|
19
|
+
}, [
|
|
20
|
+
form,
|
|
21
|
+
props.open
|
|
22
|
+
]);
|
|
23
|
+
const executeImport = async (values)=>{
|
|
24
|
+
setSubmitting(true);
|
|
25
|
+
try {
|
|
26
|
+
const result = await importWorkflow({
|
|
27
|
+
file: values.file,
|
|
28
|
+
mode: resetImport ? 'REPLACE' : 'INCREMENTAL'
|
|
29
|
+
});
|
|
30
|
+
if (result.success) {
|
|
31
|
+
message.success(FlowMessageRegistry.getInstance().get(FlowMessageKey.DESIGN_IMPORT_SUCCESS));
|
|
32
|
+
props.onImported?.(result.data);
|
|
33
|
+
props.onClose();
|
|
34
|
+
}
|
|
35
|
+
} finally{
|
|
36
|
+
setSubmitting(false);
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
const confirmImport = (values, info)=>{
|
|
40
|
+
Modal.confirm({
|
|
41
|
+
title: resetImport ? '确认重置导入' : '确认导入流程',
|
|
42
|
+
content: resetImport ? `重置导入将使用“${info.title}(${info.code})”完全覆盖当前流程的全部版本和配置。该操作不可撤销,确认继续吗?` : `流程编码“${info.code}”已存在,系统将自动生成新编码并创建一条独立流程。确认继续吗?`,
|
|
43
|
+
okText: resetImport ? '确认重置' : '确认导入',
|
|
44
|
+
cancelText: '取消',
|
|
45
|
+
okButtonProps: resetImport ? {
|
|
46
|
+
danger: true
|
|
47
|
+
} : void 0,
|
|
48
|
+
onOk: ()=>executeImport(values)
|
|
49
|
+
});
|
|
50
|
+
};
|
|
51
|
+
const submitImport = async (values)=>{
|
|
52
|
+
let info;
|
|
53
|
+
try {
|
|
54
|
+
info = readImportWorkflowInfo(values.file);
|
|
55
|
+
if (resetImport) validateResetImportCode(values.file, props.currentWorkflowCode || '');
|
|
56
|
+
} catch (error) {
|
|
57
|
+
message.error(error instanceof Error ? error.message : '无法解析导入流程信息');
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
if (resetImport) return void confirmImport(values, info);
|
|
61
|
+
setSubmitting(true);
|
|
62
|
+
try {
|
|
63
|
+
const existsResult = await workflowCodeExists(info.code);
|
|
64
|
+
if (true === existsResult.data) return void confirmImport(values, info);
|
|
65
|
+
} finally{
|
|
66
|
+
setSubmitting(false);
|
|
67
|
+
}
|
|
68
|
+
await executeImport(values);
|
|
69
|
+
};
|
|
70
|
+
const handleFileChange = (changedValues)=>{
|
|
71
|
+
if (void 0 === changedValues.file) return;
|
|
72
|
+
if (!changedValues.file) return void setFileInfo(void 0);
|
|
73
|
+
try {
|
|
74
|
+
setFileInfo(readImportWorkflowInfo(changedValues.file));
|
|
75
|
+
} catch (error) {
|
|
76
|
+
setFileInfo(void 0);
|
|
77
|
+
message.error(error instanceof Error ? error.message : '无法解析导入流程信息');
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
return /*#__PURE__*/ jsxs(Modal, {
|
|
81
|
+
title: resetImport ? '重置导入' : '流程导入',
|
|
11
82
|
open: props.open,
|
|
12
83
|
onCancel: props.onClose,
|
|
13
84
|
destroyOnHidden: true,
|
|
85
|
+
okText: resetImport ? '重置导入' : '导入',
|
|
86
|
+
confirmLoading: submitting,
|
|
14
87
|
onOk: ()=>{
|
|
15
88
|
form.submit();
|
|
16
89
|
},
|
|
17
|
-
children:
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
90
|
+
children: [
|
|
91
|
+
/*#__PURE__*/ jsx(Alert, {
|
|
92
|
+
className: design_import_module.notice,
|
|
93
|
+
type: resetImport ? 'warning' : 'info',
|
|
94
|
+
showIcon: true,
|
|
95
|
+
message: resetImport ? '重置导入说明' : '流程导入说明',
|
|
96
|
+
description: resetImport ? '仅允许导入与当前流程编码一致的文件。确认后,当前流程的全部版本和配置将被导入数据覆盖。' : '系统会检测导入流程的编码。编码未占用时保留原编码;编码已存在时自动生成新编码,并创建一条独立流程。'
|
|
97
|
+
}),
|
|
98
|
+
/*#__PURE__*/ jsxs(Form, {
|
|
99
|
+
form: form,
|
|
100
|
+
layout: "vertical",
|
|
101
|
+
onFinish: submitImport,
|
|
102
|
+
onValuesChange: handleFileChange,
|
|
103
|
+
children: [
|
|
104
|
+
/*#__PURE__*/ jsx(Form.Item, {
|
|
105
|
+
name: "file",
|
|
106
|
+
label: "流程设计文件",
|
|
107
|
+
help: "请选择导出的设计文件,文件格式为json格式",
|
|
108
|
+
rules: [
|
|
109
|
+
{
|
|
110
|
+
required: true,
|
|
111
|
+
message: '请选择流程设计文件'
|
|
112
|
+
}
|
|
113
|
+
],
|
|
114
|
+
children: /*#__PURE__*/ jsx(Upload, {})
|
|
115
|
+
}),
|
|
116
|
+
fileInfo && /*#__PURE__*/ jsx(Descriptions, {
|
|
117
|
+
size: "small",
|
|
118
|
+
bordered: true,
|
|
119
|
+
column: 1,
|
|
120
|
+
items: [
|
|
121
|
+
{
|
|
122
|
+
key: 'title',
|
|
123
|
+
label: '流程名称',
|
|
124
|
+
children: fileInfo.title
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
key: 'code',
|
|
128
|
+
label: '流程编码',
|
|
129
|
+
children: fileInfo.code
|
|
130
|
+
}
|
|
131
|
+
]
|
|
132
|
+
})
|
|
133
|
+
]
|
|
33
134
|
})
|
|
34
|
-
|
|
135
|
+
]
|
|
35
136
|
});
|
|
36
137
|
};
|
|
37
138
|
export { DesignImport };
|
|
@@ -10,11 +10,14 @@ const fileToBase64 = (file)=>new Promise((resolve, reject)=>{
|
|
|
10
10
|
const upload_Upload = (props)=>/*#__PURE__*/ jsx(Upload, {
|
|
11
11
|
maxCount: 1,
|
|
12
12
|
accept: ".json,application/json",
|
|
13
|
+
onRemove: ()=>{
|
|
14
|
+
props.onChange?.('');
|
|
15
|
+
return true;
|
|
16
|
+
},
|
|
13
17
|
customRequest: async ({ file, onSuccess })=>{
|
|
14
18
|
const currentFile = file;
|
|
15
19
|
const base64 = await fileToBase64(currentFile);
|
|
16
20
|
props.onChange?.(base64);
|
|
17
|
-
currentFile.name;
|
|
18
21
|
const url = URL.createObjectURL(currentFile);
|
|
19
22
|
onSuccess({
|
|
20
23
|
url: url,
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
import react from "react";
|
|
3
|
-
import { Button, Form, Input, Popover, Space, Tabs, message } from "antd";
|
|
3
|
+
import { Button, Divider, Form, Input, Popover, Space, Tabs, message } from "antd";
|
|
4
4
|
import { LayoutHeaderHeight } from "../types.js";
|
|
5
5
|
import { useDesignContext } from "../hooks/use-design-context.js";
|
|
6
|
-
import { CloseOutlined,
|
|
6
|
+
import { CloseOutlined, ExportOutlined, ImportOutlined, SaveOutlined } from "@ant-design/icons";
|
|
7
7
|
import { EventBus, FlowMessageKey, FlowMessageRegistry } from "@coding-flow/flow-core";
|
|
8
8
|
import { exportWorkflow } from "../../../api/workflow.js";
|
|
9
|
+
import { DesignImport } from "../../design-import/index.js";
|
|
9
10
|
const Left = ()=>/*#__PURE__*/ jsx("div", {
|
|
10
11
|
style: {
|
|
11
12
|
width: 150
|
|
@@ -86,36 +87,62 @@ const SaveAsButton = ()=>{
|
|
|
86
87
|
};
|
|
87
88
|
const Right = ()=>{
|
|
88
89
|
const { state, context } = useDesignContext();
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
marginRight: 20
|
|
92
|
-
},
|
|
90
|
+
const [resetImportVisible, setResetImportVisible] = react.useState(false);
|
|
91
|
+
return /*#__PURE__*/ jsxs(Fragment, {
|
|
93
92
|
children: [
|
|
94
|
-
/*#__PURE__*/
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
loading: state.view.loading,
|
|
98
|
-
onClick: ()=>{
|
|
99
|
-
context.save().then(()=>{
|
|
100
|
-
message.success(FlowMessageRegistry.getInstance().get(FlowMessageKey.DESIGN_SAVE));
|
|
101
|
-
});
|
|
102
|
-
},
|
|
103
|
-
children: "保存"
|
|
104
|
-
}),
|
|
105
|
-
/*#__PURE__*/ jsx(SaveAsButton, {}),
|
|
106
|
-
/*#__PURE__*/ jsx(Button, {
|
|
107
|
-
icon: /*#__PURE__*/ jsx(DownloadOutlined, {}),
|
|
108
|
-
onClick: ()=>{
|
|
109
|
-
exportWorkflow(state.workflow.id);
|
|
93
|
+
/*#__PURE__*/ jsxs(Space, {
|
|
94
|
+
style: {
|
|
95
|
+
marginRight: 20
|
|
110
96
|
},
|
|
111
|
-
children:
|
|
97
|
+
children: [
|
|
98
|
+
/*#__PURE__*/ jsx(Button, {
|
|
99
|
+
icon: /*#__PURE__*/ jsx(ExportOutlined, {}),
|
|
100
|
+
onClick: ()=>{
|
|
101
|
+
exportWorkflow(state.workflow.id);
|
|
102
|
+
},
|
|
103
|
+
children: "导出流程"
|
|
104
|
+
}),
|
|
105
|
+
/*#__PURE__*/ jsx(Button, {
|
|
106
|
+
icon: /*#__PURE__*/ jsx(ImportOutlined, {}),
|
|
107
|
+
onClick: ()=>{
|
|
108
|
+
setResetImportVisible(true);
|
|
109
|
+
},
|
|
110
|
+
children: "重置流程"
|
|
111
|
+
}),
|
|
112
|
+
/*#__PURE__*/ jsx(Divider, {
|
|
113
|
+
type: "vertical"
|
|
114
|
+
}),
|
|
115
|
+
/*#__PURE__*/ jsx(Button, {
|
|
116
|
+
icon: /*#__PURE__*/ jsx(SaveOutlined, {}),
|
|
117
|
+
type: "primary",
|
|
118
|
+
loading: state.view.loading,
|
|
119
|
+
onClick: ()=>{
|
|
120
|
+
context.save().then(()=>{
|
|
121
|
+
message.success(FlowMessageRegistry.getInstance().get(FlowMessageKey.DESIGN_SAVE));
|
|
122
|
+
});
|
|
123
|
+
},
|
|
124
|
+
children: "保存"
|
|
125
|
+
}),
|
|
126
|
+
/*#__PURE__*/ jsx(SaveAsButton, {}),
|
|
127
|
+
/*#__PURE__*/ jsx(Button, {
|
|
128
|
+
icon: /*#__PURE__*/ jsx(CloseOutlined, {}),
|
|
129
|
+
onClick: ()=>{
|
|
130
|
+
context.close();
|
|
131
|
+
},
|
|
132
|
+
children: "关闭"
|
|
133
|
+
})
|
|
134
|
+
]
|
|
112
135
|
}),
|
|
113
|
-
/*#__PURE__*/ jsx(
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
136
|
+
/*#__PURE__*/ jsx(DesignImport, {
|
|
137
|
+
open: resetImportVisible,
|
|
138
|
+
scene: "RESET",
|
|
139
|
+
currentWorkflowCode: state.workflow.code,
|
|
140
|
+
onImported: (workId)=>{
|
|
141
|
+
context.getPresenter().loadDesign(workId);
|
|
117
142
|
},
|
|
118
|
-
|
|
143
|
+
onClose: ()=>{
|
|
144
|
+
setResetImportVisible(false);
|
|
145
|
+
}
|
|
119
146
|
})
|
|
120
147
|
]
|
|
121
148
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@coding-flow/flow-design",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.4",
|
|
4
4
|
"description": "flow-engine design components ",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": [
|
|
@@ -54,16 +54,16 @@
|
|
|
54
54
|
"nanoid": "^5.1.6",
|
|
55
55
|
"react-redux": "^9.2.0",
|
|
56
56
|
"styled-components": "^5.3.11",
|
|
57
|
-
"@coding-flow/flow-core": "0.2.
|
|
58
|
-
"@coding-flow/flow-
|
|
59
|
-
"@coding-flow/flow-
|
|
60
|
-
"@coding-flow/flow-
|
|
57
|
+
"@coding-flow/flow-core": "0.2.4",
|
|
58
|
+
"@coding-flow/flow-pc-ui": "0.2.4",
|
|
59
|
+
"@coding-flow/flow-types": "0.2.4",
|
|
60
|
+
"@coding-flow/flow-icons": "0.2.4"
|
|
61
61
|
},
|
|
62
62
|
"devDependencies": {
|
|
63
63
|
"@types/lodash-es": "^4.17.12",
|
|
64
64
|
"@types/styled-components": "^5.1.36",
|
|
65
|
-
"@coding-flow/flow-core": "0.2.
|
|
66
|
-
"@coding-flow/flow-types": "0.2.
|
|
65
|
+
"@coding-flow/flow-core": "0.2.4",
|
|
66
|
+
"@coding-flow/flow-types": "0.2.4"
|
|
67
67
|
},
|
|
68
68
|
"scripts": {
|
|
69
69
|
"build": "rslib build",
|