Skip to content

Commit a5c83b2

Browse files
authored
Merge pull request #53 from codingapi/dev
feat(flow-design): 优化流程导入交互并发布 0.2.4
2 parents 982bb45 + 096ed50 commit a5c83b2

29 files changed

Lines changed: 403 additions & 43 deletions

File tree

packages/flow-approval-presenter/CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# @coding-flow/flow-approval-presenter
22

3+
## 0.2.4
4+
5+
### Patch Changes
6+
7+
- @coding-flow/flow-core@0.2.4
8+
- @coding-flow/flow-types@0.2.4
9+
310
## 0.2.3
411

512
### Patch Changes

packages/flow-approval-presenter/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@coding-flow/flow-approval-presenter",
3-
"version": "0.2.3",
3+
"version": "0.2.4",
44
"description": "flow-engine approval presenter framework ",
55
"type": "module",
66
"sideEffects": [

packages/flow-core/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# @coding-flow/flow-core
22

3+
## 0.2.4
4+
35
## 0.2.3
46

57
### Patch Changes

packages/flow-core/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@coding-flow/flow-core",
3-
"version": "0.2.3",
3+
"version": "0.2.4",
44
"description": "flow-engine core lib framework ",
55
"type": "module",
66
"sideEffects": [

packages/flow-design/CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
11
# @coding-flow/flow-design
22

3+
## 0.2.4
4+
5+
### Patch Changes
6+
7+
- 优化流程导入与导出交互:导入文件后展示流程名称和编码,仅在编码冲突时确认创建新流程;设计器新增带编码校验和覆盖确认的重置流程入口,并调整导入导出按钮的名称、图标与布局。
8+
- @coding-flow/flow-core@0.2.4
9+
- @coding-flow/flow-icons@0.2.4
10+
- @coding-flow/flow-pc-ui@0.2.4
11+
- @coding-flow/flow-types@0.2.4
12+
313
## 0.2.3
414

515
### Patch Changes

packages/flow-design/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@coding-flow/flow-design",
3-
"version": "0.2.3",
3+
"version": "0.2.4",
44
"description": "flow-engine design components ",
55
"type": "module",
66
"sideEffects": [

packages/flow-design/src/api/workflow.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,15 @@ export const exportWorkflow = (id:string) => {
6161
return httpClient.download('/api/cmd/workflow/export?id='+id,filename);
6262
}
6363

64-
export const importWorkflow = (file:string) => {
65-
return httpClient.post('/api/cmd/workflow/import',file);
64+
export interface WorkflowImportRequest {
65+
file: string;
66+
mode: 'REPLACE' | 'INCREMENTAL';
6667
}
6768

69+
export const importWorkflow = (request: WorkflowImportRequest) => {
70+
return httpClient.post('/api/cmd/workflow/import',request);
71+
}
72+
73+
export const workflowCodeExists = (code: string) => {
74+
return httpClient.get('/api/cmd/workflow/exists', {code});
75+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.notice {
2+
margin-bottom: 16px;
3+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
type JsonObject = Record<string, unknown>;
2+
3+
export interface WorkflowImportFileInfo {
4+
code: string;
5+
title: string;
6+
}
7+
8+
const isJsonObject = (value: unknown): value is JsonObject => {
9+
return typeof value === 'object' && value !== null && !Array.isArray(value);
10+
};
11+
12+
const decodeBase64File = (file: string): string => {
13+
const separatorIndex = file.indexOf(',');
14+
const encoded = separatorIndex >= 0 ? file.substring(separatorIndex + 1) : file;
15+
const binary = globalThis.atob(encoded);
16+
const bytes = Uint8Array.from(binary, character => character.charCodeAt(0));
17+
return new TextDecoder().decode(bytes);
18+
};
19+
20+
/**
21+
* 从V1迁移包或历史单流程文件中读取流程编码。
22+
*/
23+
export const readImportWorkflowInfo = (file: string): WorkflowImportFileInfo => {
24+
let parsed: unknown;
25+
try {
26+
parsed = JSON.parse(decodeBase64File(file));
27+
} catch {
28+
throw new Error('无法解析导入文件,请确认文件为有效的流程JSON文件');
29+
}
30+
if (!isJsonObject(parsed)) {
31+
throw new Error('导入文件内容必须为JSON对象');
32+
}
33+
34+
const workflow = parsed.workflow;
35+
const workflowData = isJsonObject(workflow) ? workflow : parsed;
36+
const code = workflowData.code;
37+
if (typeof code !== 'string' || code.trim().length === 0) {
38+
throw new Error('导入文件中缺少流程编码');
39+
}
40+
const title = workflowData.title;
41+
return {
42+
code,
43+
title: typeof title === 'string' && title.trim().length > 0 ? title : '未命名流程'
44+
};
45+
};
46+
47+
export const readImportWorkflowCode = (file: string): string => {
48+
return readImportWorkflowInfo(file).code;
49+
};
50+
51+
export const validateResetImportCode = (file: string, currentWorkflowCode: string): string => {
52+
const importedCode = readImportWorkflowCode(file);
53+
if (importedCode !== currentWorkflowCode) {
54+
throw new Error(
55+
`导入流程编码“${importedCode}”与当前流程编码“${currentWorkflowCode}”不一致,无法重置导入`
56+
);
57+
}
58+
return importedCode;
59+
};
Lines changed: 128 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,166 @@
1-
import {Form, message, Modal} from "antd";
1+
import {Alert, Descriptions, Form, message, Modal} from "antd";
22
import React from "react";
33
import {Upload} from "./upload";
4-
import {importWorkflow} from "@/api/workflow";
4+
import {importWorkflow, type WorkflowImportRequest, workflowCodeExists} from "@/api/workflow";
55
import {FlowMessageKey, FlowMessageRegistry} from "@coding-flow/flow-core";
6+
import {
7+
readImportWorkflowInfo,
8+
validateResetImportCode,
9+
type WorkflowImportFileInfo
10+
} from "./import-file";
11+
import styles from "./design_import.module.scss";
612

13+
export type WorkflowImportScene = 'MANAGEMENT' | 'RESET';
714

815
interface DesignImportProps{
916
open: boolean;
1017
onClose: () => void;
18+
scene?: WorkflowImportScene;
19+
currentWorkflowCode?: string;
20+
onImported?: (workId: string) => void;
1121
}
1222

23+
type ImportFormValues = Pick<WorkflowImportRequest, 'file'>;
24+
1325
export const DesignImport:React.FC<DesignImportProps> = (props) => {
1426

1527
const [form] = Form.useForm();
28+
const [submitting, setSubmitting] = React.useState(false);
29+
const [fileInfo, setFileInfo] = React.useState<WorkflowImportFileInfo>();
30+
const resetImport = props.scene === 'RESET';
31+
32+
React.useEffect(() => {
33+
if (!props.open) {
34+
form.resetFields();
35+
setFileInfo(undefined);
36+
}
37+
}, [form, props.open]);
38+
39+
const executeImport = async (values: ImportFormValues) => {
40+
setSubmitting(true);
41+
try {
42+
const result = await importWorkflow({
43+
file: values.file,
44+
mode: resetImport ? 'REPLACE' : 'INCREMENTAL'
45+
});
46+
if (result.success) {
47+
message.success(
48+
FlowMessageRegistry.getInstance().get(FlowMessageKey.DESIGN_IMPORT_SUCCESS)
49+
);
50+
props.onImported?.(result.data as string);
51+
props.onClose();
52+
}
53+
} finally {
54+
setSubmitting(false);
55+
}
56+
};
57+
58+
const confirmImport = (values: ImportFormValues, info: WorkflowImportFileInfo) => {
59+
Modal.confirm({
60+
title: resetImport ? '确认重置导入' : '确认导入流程',
61+
content: resetImport
62+
? `重置导入将使用“${info.title}${info.code})”完全覆盖当前流程的全部版本和配置。该操作不可撤销,确认继续吗?`
63+
: `流程编码“${info.code}”已存在,系统将自动生成新编码并创建一条独立流程。确认继续吗?`,
64+
okText: resetImport ? '确认重置' : '确认导入',
65+
cancelText: '取消',
66+
okButtonProps: resetImport ? {danger: true} : undefined,
67+
onOk: () => executeImport(values)
68+
});
69+
};
70+
71+
const submitImport = async (values: ImportFormValues) => {
72+
let info: WorkflowImportFileInfo;
73+
try {
74+
info = readImportWorkflowInfo(values.file);
75+
if (resetImport) {
76+
validateResetImportCode(values.file, props.currentWorkflowCode || '');
77+
}
78+
} catch (error) {
79+
message.error(error instanceof Error ? error.message : '无法解析导入流程信息');
80+
return;
81+
}
1682

83+
if (resetImport) {
84+
confirmImport(values, info);
85+
return;
86+
}
87+
88+
setSubmitting(true);
89+
try {
90+
const existsResult = await workflowCodeExists(info.code);
91+
if (existsResult.data === true) {
92+
confirmImport(values, info);
93+
return;
94+
}
95+
} finally {
96+
setSubmitting(false);
97+
}
98+
await executeImport(values);
99+
};
100+
101+
const handleFileChange = (changedValues: Partial<ImportFormValues>) => {
102+
if (changedValues.file === undefined) {
103+
return;
104+
}
105+
if (!changedValues.file) {
106+
setFileInfo(undefined);
107+
return;
108+
}
109+
try {
110+
setFileInfo(readImportWorkflowInfo(changedValues.file));
111+
} catch (error) {
112+
setFileInfo(undefined);
113+
message.error(error instanceof Error ? error.message : '无法解析导入流程信息');
114+
}
115+
};
17116

18117
return (
19118
<Modal
20-
title={"流程导入"}
119+
title={resetImport ? '重置导入' : '流程导入'}
21120
open={props.open}
22121
onCancel={props.onClose}
23122
destroyOnHidden={true}
123+
okText={resetImport ? '重置导入' : '导入'}
124+
confirmLoading={submitting}
24125
onOk={()=>{
25126
form.submit();
26127
}}
27128
>
129+
<Alert
130+
className={styles.notice}
131+
type={resetImport ? 'warning' : 'info'}
132+
showIcon={true}
133+
message={resetImport ? '重置导入说明' : '流程导入说明'}
134+
description={resetImport
135+
? '仅允许导入与当前流程编码一致的文件。确认后,当前流程的全部版本和配置将被导入数据覆盖。'
136+
: '系统会检测导入流程的编码。编码未占用时保留原编码;编码已存在时自动生成新编码,并创建一条独立流程。'}
137+
/>
28138
<Form
29139
form={form}
30140
layout="vertical"
31-
onFinish={(values)=>{
32-
importWorkflow(values).then((res)=>{
33-
if(res.success){
34-
message.success(
35-
FlowMessageRegistry.getInstance().get(FlowMessageKey.DESIGN_IMPORT_SUCCESS)
36-
);
37-
props.onClose();
38-
}
39-
})
40-
}}
141+
onFinish={submitImport}
142+
onValuesChange={handleFileChange}
41143
>
42144
<Form.Item
43145
name="file"
44146
label={"流程设计文件"}
45147
help={"请选择导出的设计文件,文件格式为json格式"}
148+
rules={[{required: true, message: '请选择流程设计文件'}]}
46149
>
47150
<Upload/>
48151
</Form.Item>
49-
152+
{fileInfo && (
153+
<Descriptions
154+
size="small"
155+
bordered={true}
156+
column={1}
157+
items={[
158+
{key: 'title', label: '流程名称', children: fileInfo.title},
159+
{key: 'code', label: '流程编码', children: fileInfo.code}
160+
]}
161+
/>
162+
)}
50163
</Form>
51164
</Modal>
52165
)
53-
}
166+
}

0 commit comments

Comments
 (0)