Skip to content

Commit f3c2617

Browse files
committed
add Flow Circulate Node
1 parent 10b24d7 commit f3c2617

File tree

37 files changed

+526
-64
lines changed

37 files changed

+526
-64
lines changed

admin-ui/src/components/Flow/index.tsx

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {DndPanel, Menu, MiniMap, Snapshot} from "@logicflow/extension";
88
import Start from "@/components/Flow/nodes/Start";
99
import Node from "@/components/Flow/nodes/Node";
1010
import Over from "@/components/Flow/nodes/Over";
11+
import Circulate from "@/components/Flow/nodes/Circulate";
1112
import ControlPanel from "@/components/Flow/layout/ControlPanel";
1213
import NodePanel from "@/components/Flow/layout/NodePanel";
1314
import {message} from "antd";
@@ -22,6 +23,15 @@ interface FlowProps {
2223
actionRef?: React.Ref<any>
2324
}
2425

26+
27+
const generateUUID = ()=> {
28+
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
29+
const r = (Math.random() * 16) | 0;
30+
const v = c === 'x' ? r : (r & 0x3) | 0x8;
31+
return v.toString(16);
32+
});
33+
}
34+
2535
const Flow: React.FC<FlowProps> = (props) => {
2636
const container = useRef<HTMLDivElement>(null);
2737
const lfRef = useRef<LogicFlow>(null);
@@ -79,6 +89,7 @@ const Flow: React.FC<FlowProps> = (props) => {
7989
lfRef.current.register(Start);
8090
lfRef.current.register(Node);
8191
lfRef.current.register(Over);
92+
lfRef.current.register(Circulate);
8293

8394
lfRef.current.render(data);
8495

@@ -121,7 +132,7 @@ const Flow: React.FC<FlowProps> = (props) => {
121132
className={"flow-panel"}
122133
onDrag={async (type, properties) => {
123134
if (await nodeVerify(type)) {
124-
const UUID = crypto.randomUUID();
135+
const UUID = generateUUID();
125136
lfRef.current?.dnd.startDrag({
126137
id: UUID,
127138
type: type,

admin-ui/src/components/Flow/layout/NodePanel.tsx

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {StartView} from "@/components/Flow/nodes/Start";
44
import {NodeView} from "@/components/Flow/nodes/Node";
55
import {OverView} from "@/components/Flow/nodes/Over";
66
import GroovyScript from "@/components/Flow/utils/script";
7+
import {CirculateView} from "@/components/Flow/nodes/Circulate";
78

89
interface NodePanelProps {
910
className: string,
@@ -81,6 +82,28 @@ const NodePanel: React.FC<NodePanelProps> = (props) => {
8182
>
8283
<OverView name={"结束节点"}/>
8384
</div>
85+
86+
<div
87+
className={"node-item"}
88+
onMouseDown={() => {
89+
props.onDrag('circulate-node',
90+
{
91+
name: '抄送节点',
92+
code: 'circulate',
93+
type: 'CIRCULATE',
94+
view: 'default',
95+
operatorMatcher: GroovyScript.creatorOperatorMatcher,
96+
editable: true,
97+
titleGenerator: GroovyScript.defaultTitleGenerator,
98+
errTrigger: '',
99+
approvalType: 'CIRCULATE',
100+
timeout: 0
101+
}
102+
);
103+
}}
104+
>
105+
<CirculateView name={"抄送节点"}/>
106+
</div>
84107
</div>
85108
</div>
86109
)
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
2+
$color: #61aa1f;
3+
4+
.circulate-node {
5+
display: flex;
6+
align-items: center;
7+
max-width: 200px;
8+
max-height: 40px;
9+
border: 1px solid $color;
10+
border-radius: 12px;
11+
padding: 10px;
12+
position: relative;
13+
14+
.icon {
15+
margin-left: 10px;
16+
position: absolute;
17+
color: $color;
18+
font-size: 16px;
19+
left: 0;
20+
}
21+
22+
.code {
23+
margin-left: 20px;
24+
font-size: 14px;
25+
color: #4a5e63;
26+
}
27+
28+
.title {
29+
margin-left: 5px;
30+
font-size: 14px;
31+
color: black;
32+
}
33+
34+
35+
.setting {
36+
margin-right: 10px;
37+
font-size: 14px;
38+
position: absolute;
39+
color: $color;
40+
right: 0;
41+
}
42+
43+
}
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
import {HtmlNode, HtmlNodeModel} from '@logicflow/core';
2+
import React from "react";
3+
import ReactDOM from "react-dom/client";
4+
import "./index.scss";
5+
import {InboxOutlined, SettingFilled} from "@ant-design/icons";
6+
import CirculateSettingPanel from "@/components/Flow/panel/circulate";
7+
8+
type CirculateProperties = {
9+
id: string;
10+
name: string;
11+
code: string;
12+
type: string;
13+
view: string;
14+
operatorMatcher: string;
15+
editable: boolean;
16+
titleGenerator: string;
17+
errTrigger: string;
18+
approvalType: string;
19+
timeout: number;
20+
}
21+
22+
interface CirculateProps {
23+
name: string;
24+
code?: string;
25+
update?: (values: any) => void;
26+
settingVisible?: boolean;
27+
properties?: CirculateProperties;
28+
}
29+
30+
export const CirculateView: React.FC<CirculateProps> = (props) => {
31+
const [visible, setVisible] = React.useState(false);
32+
33+
return (
34+
<div className="circulate-node">
35+
<InboxOutlined
36+
className={"icon"}
37+
/>
38+
<div>
39+
<span className={"code"}>
40+
{props.code && (
41+
<> ({props.code})</>
42+
)}
43+
</span>
44+
<span className={"title"}>{props.name}</span>
45+
</div>
46+
47+
{props.settingVisible && (
48+
<SettingFilled
49+
className={"setting"}
50+
onClick={() => {
51+
setVisible(true);
52+
}}
53+
/>
54+
)}
55+
56+
<CirculateSettingPanel
57+
visible={visible}
58+
setVisible={setVisible}
59+
properties={props.properties}
60+
onSettingChange={(values) => {
61+
props.update && props.update(values);
62+
}}
63+
/>
64+
</div>
65+
);
66+
}
67+
68+
class CirculateModel extends HtmlNodeModel {
69+
setAttributes() {
70+
this.width = 200;
71+
this.height = 45;
72+
this.text.editable = false;
73+
this.menu = [];
74+
75+
this.sourceRules = [
76+
{
77+
message: `不允许输出`,
78+
validate: (sourceNode: any, targetNode: any, sourceAnchor, targetAnchor) => {
79+
const edges = this.graphModel.getNodeOutgoingEdge(sourceNode.id);
80+
if (edges.length >= 1) {
81+
return false;
82+
} else {
83+
return true;
84+
}
85+
},
86+
},
87+
];
88+
89+
this.anchorsOffset = [
90+
[this.width / 2, 0],
91+
[0, this.height / 2],
92+
[-this.width / 2, 0],
93+
[0, -this.height / 2],
94+
];
95+
}
96+
97+
98+
}
99+
100+
class CirculateNode extends HtmlNode {
101+
setHtml(rootEl: SVGForeignObjectElement) {
102+
const {properties} = this.props.model as HtmlNodeModel<CirculateProperties>;
103+
const div = document.createElement('div');
104+
ReactDOM.createRoot(div).render(
105+
<CirculateView
106+
name={properties.name}
107+
code={properties.code}
108+
properties={properties}
109+
settingVisible={true}
110+
update={async (values) => {
111+
this.props.model.setProperties(values);
112+
}}/>,
113+
);
114+
//需要清空
115+
rootEl.innerHTML = '';
116+
rootEl.appendChild(div);
117+
super.setHtml(rootEl);
118+
}
119+
}
120+
121+
export default {
122+
type: 'circulate-node',
123+
view: CirculateNode,
124+
model: CirculateModel,
125+
};
126+

admin-ui/src/components/Flow/panel/NodePanel.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ const NodePanel: React.FC<NodePanelProps> = (props) => {
4848
/>
4949
<ProFormText
5050
name={"code"}
51-
disabled={props.type !== 'node'}
51+
disabled={props.type === 'start' || props.type === 'over'}
5252
label={"节点编码"}
5353
rules={[
5454
{
@@ -144,6 +144,7 @@ const NodePanel: React.FC<NodePanelProps> = (props) => {
144144
step: 1
145145
}}
146146
name={"timeout"}
147+
hidden={props.type === 'circulate'}
147148
label={"超时时间"}
148149
/>
149150

@@ -238,7 +239,7 @@ const NodePanel: React.FC<NodePanelProps> = (props) => {
238239
</ProForm>
239240

240241
<ScriptModal
241-
onFinish={(values)=>{
242+
onFinish={(values) => {
242243
const type = values.type;
243244
props.form.setFieldsValue({
244245
[type]: values.script
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import React from "react";
2+
import {ProForm} from "@ant-design/pro-components";
3+
import {Button, Drawer, Space, Tabs} from "antd";
4+
import NodePanel from "@/components/Flow/panel/NodePanel";
5+
import EdgePanel from "@/components/Flow/panel/EdgePanel";
6+
import {SettingPanelProps} from "@/components/Flow/panel/panel.types";
7+
8+
const CirculateSettingPanel: React.FC<SettingPanelProps> = (props) => {
9+
10+
const [form] = ProForm.useForm();
11+
12+
return (
13+
<Drawer
14+
title={"节点设置"}
15+
width={"40%"}
16+
destroyOnClose={true}
17+
onClose={() => {
18+
props.setVisible(false);
19+
}}
20+
open={props.visible}
21+
extra={(
22+
<Space>
23+
<Button
24+
type={"primary"}
25+
onClick={() => {
26+
form.submit();
27+
props.setVisible(false);
28+
}}
29+
>确认</Button>
30+
31+
<Button
32+
onClick={() => {
33+
props.setVisible(false);
34+
}}
35+
>关闭</Button>
36+
</Space>
37+
)}
38+
>
39+
<Tabs
40+
items={[
41+
{
42+
label: "节点设置",
43+
key: "nodes",
44+
children: (
45+
<NodePanel
46+
type={"circulate"}
47+
form={form}
48+
id={props.properties?.id}
49+
data={props.properties}
50+
onFinish={props.onSettingChange}
51+
/>
52+
)
53+
},
54+
{
55+
label: "关系设置",
56+
key: "edges",
57+
children: (
58+
<EdgePanel
59+
type={"node"}
60+
id={props.properties?.id}/>
61+
)
62+
}
63+
]}
64+
/>
65+
66+
</Drawer>
67+
)
68+
69+
}
70+
71+
export default CirculateSettingPanel;

admin-ui/src/components/Flow/panel/over.tsx

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,7 @@ import React from "react";
22
import {ProForm} from "@ant-design/pro-components";
33
import {Button, Drawer, Space} from "antd";
44
import NodePanel from "@/components/Flow/panel/NodePanel";
5-
6-
interface SettingPanelProps {
7-
visible: boolean;
8-
setVisible: (visible: boolean) => void;
9-
properties: any;
10-
onSettingChange: (values: any) => void;
11-
}
5+
import {SettingPanelProps} from "@/components/Flow/panel/panel.types";
126

137
const OverSettingPanel: React.FC<SettingPanelProps> = (props) => {
148

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
2+
export interface SettingPanelProps {
3+
visible: boolean;
4+
setVisible: (visible: boolean) => void;
5+
properties: any;
6+
onSettingChange: (values: any) => void;
7+
}

admin-ui/src/components/Flow/panel/start.tsx

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,7 @@ import {Button, Drawer, Space, Tabs} from "antd";
33
import EdgePanel from "@/components/Flow/panel/EdgePanel";
44
import NodePanel from "@/components/Flow/panel/NodePanel";
55
import {ProForm} from "@ant-design/pro-components";
6-
7-
interface SettingPanelProps {
8-
visible: boolean;
9-
setVisible: (visible: boolean) => void;
10-
properties: any;
11-
onSettingChange: (values: any) => void;
12-
}
6+
import {SettingPanelProps} from "@/components/Flow/panel/panel.types";
137

148
const StartSettingPanel: React.FC<SettingPanelProps> = (props) => {
159

0 commit comments

Comments
 (0)