Compare commits

...

1 Commits
main ... v1.2.2

Author SHA1 Message Date
github-actions[bot] 51eb6063a4
Feat: refactor the editing mode of the workflow (#342)
Signed-off-by: barnettZQG <barnett.zqg@gmail.com>
(cherry picked from commit 3563dbe98d)

Co-authored-by: barnettZQG <barnett.zqg@gmail.com>
2022-01-25 18:41:39 +08:00
15 changed files with 487 additions and 371 deletions

View File

@ -42,10 +42,6 @@
"@alifd/meet-react": "^2.0.0",
"@antv/g6": "4.3.11",
"@b-design/ui": "^1.0.63",
"@types/js-yaml": "^4.0.1",
"@types/lodash": "^4.14.176",
"@types/react": "^16.3.0",
"@types/react-cookies": "^0.1.0",
"ansi-to-react": "^6.1.6",
"axios": "0.24.0",
"diagram-maker": "^1.3.0",
@ -72,7 +68,8 @@
"redux": "4.1.2",
"remark-gfm": "3.0.1",
"tiny-pubsub": "^1.1.0",
"tsx-control-statements": "4.1.1"
"tsx-control-statements": "4.1.1",
"uuid": "^8.3.2"
},
"devDependencies": {
"@babel/core": "^7.12.10",
@ -87,6 +84,11 @@
"@types/jest": "^26.0.24",
"@types/react-copy-to-clipboard": "^5.0.2",
"@types/react-dom": "^17.0.9",
"@types/js-yaml": "^4.0.1",
"@types/lodash": "^4.14.176",
"@types/react": "^16.3.0",
"@types/react-cookies": "^0.1.0",
"@types/uuid": "^8.3.4",
"@typescript-eslint/eslint-plugin": "^4.0.0",
"@typescript-eslint/parser": "^4.0.0",
"@umijs/fabric": "2.8.1",

View File

@ -196,6 +196,7 @@ class UISchema extends Component<Props, State> {
<Switch
id={switchResult.id}
onChange={switchResult.onChange}
size="medium"
checked={switchResult.value ? true : false}
/>
</Form.Item>

View File

@ -260,3 +260,10 @@ export interface Trigger {
createTime?: string;
updateTime?: string;
}
export interface WorkflowStep {
name: string;
alias: string;
description?: string;
type: string;
}

View File

@ -48,7 +48,7 @@ class TopBar extends Component<Props, State> {
}
componentDidMount() {
this.loadSystemInfo();
//this.loadSystemInfo();
}
loadSystemInfo = () => {
@ -183,13 +183,13 @@ class TopBar extends Component<Props, State> {
<Icon size={14} type="help1" />
</a>
</div>
<div className="vela-item">
{/* <div className="vela-item">
<Icon
onClick={this.showUserExperienceImprovementPlan}
size={14}
type="exclamation-circle"
/>
</div>
</div> */}
</div>
</Row>
<Dialog

View File

@ -105,29 +105,34 @@ export default {
workflow.default = option.default;
const { nodes, edges } = data;
const nodeIndex = {};
let index = 0;
const steps = Object.keys(nodes).map((key) => {
Object.keys(nodes).map((key) => {
if (nodes[key]) {
nodeIndex[nodes[key].id] = index;
index++;
const nodeConfig = nodes[key].consumerData;
let nodeConfig = Object.assign({}, nodes[key].consumerData);
if (nodeConfig && nodeConfig.properties && typeof nodeConfig.properties != 'string') {
return Object.assign(nodeConfig, { properties: JSON.stringify(nodeConfig.properties) });
nodeConfig = Object.assign(nodeConfig, {
properties: JSON.stringify(nodeConfig.properties),
});
}
return nodeConfig;
nodeIndex[nodes[key].id] = nodeConfig;
}
});
let next = edges.prev;
let steps = [];
let srcMap = {};
Object.keys(edges).map((key) => {
const edge = edges[key];
if (nodeIndex[edge.src] > nodeIndex[edge.dest]) {
const i = steps[nodeIndex[edge.src]];
const oldIndex = nodeIndex[edge.src];
steps[nodeIndex[edge.src]] = steps[nodeIndex[edge.dest]];
steps[nodeIndex[edge.dest]] = i;
nodeIndex[edge.src] = nodeIndex[edge.dest];
nodeIndex[edge.dest] = oldIndex;
if (edges[key].src == 'prev') {
next = edges[key];
}
srcMap[edges[key].src] = edges[key];
});
while (next != undefined) {
if (next.dest && next.dest != 'next') {
steps.push(nodeIndex[next.dest]);
next = srcMap[next.dest];
} else {
next = undefined;
}
}
workflow.steps = steps;
yield call(createWorkFlow, workflow);
if (action.callback) {
@ -141,7 +146,7 @@ function transData(workflowList = [], appName) {
const newWorkflows = _.cloneDeep(workflowList);
if (newWorkflows && newWorkflows.length != 0) {
newWorkflows.forEach((workflow) => {
convertWorkflowStep(workflow, appName, 32);
convertWorkflowStep(workflow, appName, 0);
});
return newWorkflows;
} else {
@ -149,18 +154,39 @@ function transData(workflowList = [], appName) {
}
}
export function convertWorkflowStep(workflow, appName, initPosition = 32) {
const nodeWidth = 140;
const nodeHeight = 40;
const nodeInterval = 80;
export function convertWorkflowStep(workflow, appName, initPosition = 32, edit = false) {
const nodeWidth = 200;
const nodeHeight = 80;
const nodeInterval = 120;
const nodes = {};
const edges = {};
let position = initPosition;
if (edit) {
nodes.prev = {
id: 'prev',
typeId: 'prev',
diagramMakerData: {
position: {
x: position,
y: 130,
},
size: {
width: nodeWidth,
height: nodeHeight,
},
selected: false,
},
};
position += nodeWidth + nodeInterval;
}
if (workflow.steps) {
workflow.steps.forEach((item, index, array) => {
edges[item.name] = {};
edges[item.name].dest =
workflow.steps && workflow.steps[index + 1] && workflow.steps[index + 1].name;
if (workflow.steps && workflow.steps[index + 1]) {
edges[item.name].dest = workflow.steps[index + 1].name;
} else if (edit) {
edges[item.name].dest = 'next';
}
edges[item.name].diagramMakerData = {
selected: false,
};
@ -169,7 +195,7 @@ export function convertWorkflowStep(workflow, appName, initPosition = 32) {
nodes[item.name] = {};
nodes[item.name].id = item.name;
nodes[item.name].typeId = item.type;
nodes[item.name].typeId = 'common';
nodes[item.name].consumerData = {
alias: item.alias || '',
dependsOn: null,
@ -191,8 +217,32 @@ export function convertWorkflowStep(workflow, appName, initPosition = 32) {
};
position += nodeWidth + nodeInterval;
});
edges.prev = {
dest: workflow.steps[0].name,
src: 'prev',
id: 'prev',
diagramMakerData: {
selected: false,
},
};
}
if (edit) {
nodes.next = {
id: 'next',
typeId: 'next',
diagramMakerData: {
position: {
x: position,
y: 130,
},
size: {
width: nodeWidth,
height: nodeHeight,
},
selected: false,
},
};
}
workflow.appName = appName;
workflow.option = {
edit: false,

View File

@ -2,31 +2,6 @@ import type { DiagramMakerNodes, DiagramMakerEdges } from 'diagram-maker';
export const WORFLOW_NODE_WIDTH = 120;
export const WORKFLOW_COMMON_PANNEL = {
p1: {
id: 'p1',
position: {
x: 10,
y: 10,
},
size: {
width: 230,
height: 280,
},
},
p2: {
id: 'p2',
position: {
x: 10,
y: 10,
},
size: {
width: 420,
height: 40,
},
},
};
export type WorkFlowOption = {
default: boolean;
edit: boolean;

View File

@ -1,13 +1,11 @@
import React, { Component } from 'react';
import { If } from 'tsx-control-statements/components';
import { Button } from '@b-design/ui';
import { connect } from 'dva';
import WrokflowComponent from './workflow-component';
import WorkflowComponent from './workflow-component';
import type { WorkFlowData } from './entity';
import { getWorkFlowDefinitions } from '../../api/workflows';
import './index.less';
import Translation from '../../components/Translation';
type Props = {
workflowList: WorkFlowData[];
@ -72,19 +70,19 @@ class Workflow extends Component<Props, State> {
return (
<div style={{ height: '100%' }} className="workflow-wraper">
<If condition={workflowList.length === 0}>
<div className="empty-container">
{/* <div className="empty-container">
<div className="empty-word">
<Translation>Please create workflow</Translation>
</div>
<Button type="primary" onClick={this.addWrokflow}>
<Translation>New Workflow</Translation>
</Button>
</div>
</div> */}
</If>
<If condition={workflowList.length > 0}>
<React.Fragment>
{workflowList.map((workflow: WorkFlowData) => (
<WrokflowComponent
<WorkflowComponent
key={workflow.name + params.appName + workflow.steps}
appName={params.appName}
data={workflow}

View File

@ -62,12 +62,11 @@ export type NodeItem = {
typeId: string;
};
class WorkFlowComponent extends Component<Props, State> {
class WorkflowComponent extends Component<Props, State> {
field;
workflowItem: any;
constructor(props: any) {
super(props);
this.state = {
errorFocus: false,
loading: false,
@ -115,33 +114,52 @@ class WorkFlowComponent extends Component<Props, State> {
}
const { data } = this.props;
const workflowData = this.workflowItem.getValues();
const { nodes } = workflowData;
if (nodes && Object.keys(nodes).length === 0) {
Message.error('plase add workflow item');
const { nodes, edges } = workflowData;
if (edges && !edges.prev) {
let next = undefined;
Object.keys(edges).map((key) => {
if (edges[key].src == 'prev') {
next = edges[key];
}
});
if (!next) {
Message.warning('Please specify the first step');
return;
}
}
const reallyNodes: any = {};
Object.keys(nodes).map((nodeKey) => {
if (nodes[nodeKey].typeId !== 'next' && nodes[nodeKey].typeId !== 'prev') {
reallyNodes[nodeKey] = nodes[nodeKey];
}
});
if (reallyNodes && Object.keys(reallyNodes).length === 0) {
Message.warning('Please add at least one workflow step');
this.setState({
errorFocus: true,
});
return;
}
const nodeArr: NodeItem[] = Object.values(nodes);
const find = nodeArr.find((item) => !item.consumerData);
const nodeArr: NodeItem[] = Object.values(reallyNodes);
const find = nodeArr.find((item) => !item.consumerData || item.consumerData.name == '');
if (find) {
return Message.error('please enter workflow step name and type');
return Message.warning('Please set the workflow step name and type');
}
this.setState({ loading: true });
const { name, alias, description } = values;
data.appName = data.appName || this.props.appName;
data.name = name;
data.alias = alias;
data.description = description;
data.data = workflowData;
data.data = Object.assign(workflowData, { nodes: reallyNodes });
this.props.dispatch({
type: 'workflow/saveWorkflow',
payload: data,
callback: () => {
Message.success('save workflow success');
Message.success('Save the workflow success');
this.props.getWorkflow();
this.setState({ loading: false });
},
@ -170,14 +188,14 @@ class WorkFlowComponent extends Component<Props, State> {
<Translation>{option.default ? 'Cancel Default' : 'Set As Default'}</Translation>
</Menu.Item>
</If>
<Menu.Item onClick={() => this.deleteWorkflow(data.name)}>
{/* <Menu.Item onClick={() => this.deleteWorkflow(data.name)}>
<Translation>Remove</Translation>
</Menu.Item>
</Menu.Item> */}
</Menu>
);
const { init } = this.field;
const newData = _.cloneDeep(data);
convertWorkflowStep(newData, data.appName, option.edit ? 250 : 32);
convertWorkflowStep(newData, data.appName, 32, option.edit);
const workflowData = newData.data || { nodes: {}, edges: {} };
return (
<Loading visible={loading} style={{ width: '100%' }}>
@ -266,13 +284,15 @@ class WorkFlowComponent extends Component<Props, State> {
</a>
</If>
<div className="option-item">
<Dropdown
trigger={<Icon type="ellipsis" className="options-icon" />}
triggerType={['click']}
className="workflow-more"
>
{menu}
</Dropdown>
<If condition={option.edit}>
<Dropdown
trigger={<Icon type="ellipsis" className="options-icon" />}
triggerType={['click']}
className="workflow-more"
>
{menu}
</Dropdown>
</If>
</div>
</div>
</div>
@ -292,4 +312,4 @@ class WorkFlowComponent extends Component<Props, State> {
}
}
export default WorkFlowComponent;
export default WorkflowComponent;

View File

@ -1,8 +1,8 @@
.workflow-edge-container {
// height: 10px;
// background-color: green;
// width: 10px;
// line-height: 10px;
color: white;
width: 14px;
height: 14px;
color: #fff;
line-height: 14px;
text-align: center;
background-color: #1b58f4;
}

View File

@ -1,10 +1,19 @@
import { Balloon } from '@b-design/ui';
import React, { Component } from 'react';
import Translation from '../../../components/Translation';
import './index.less';
export interface EdgeData {
dest: string;
id: string;
src: string;
}
type Props = {
id: string;
data?: any;
data: EdgeData;
editMode?: boolean;
addNode: () => void;
};
type State = {};
@ -12,14 +21,27 @@ type State = {};
class WorkFlowEdge extends Component<Props, State> {
constructor(props: any) {
super(props);
this.state = {};
}
componentDidMount() {}
render() {
return <div className="workflow-edge-container">{/* + */}</div>;
const { editMode } = this.props;
if (editMode) {
return (
<Balloon
trigger={
<div onClick={this.props.addNode} className="workflow-edge-container">
+
</div>
}
>
<Translation>Click to add Workflow Step</Translation>
</Balloon>
);
}
return <div />;
}
}

View File

@ -1,12 +1,8 @@
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { v4 as uuid } from 'uuid';
import { Message } from '@b-design/ui';
import type {
DiagramMakerData,
DiagramMakerNode,
DiagramMakerEdge,
DiagramMakerPanel,
} from 'diagram-maker';
import type { DiagramMakerData, DiagramMakerNode, DiagramMakerEdge } from 'diagram-maker';
import {
DiagramMaker,
ConnectorPlacement,
@ -17,17 +13,15 @@ import {
NodeActions,
} from 'diagram-maker';
import WorkFlowNode from '../workflow-node';
import type { EdgeData } from '../workflow-edge';
import WorkFlowEdge from '../workflow-edge';
import WorkFlowPannel from '../workflow-panel';
import WorkFlowToolTip from '../workflow-tooltip';
import type { EdgesAndNodes, WorkFlowNodeType, WorkFlowEdgeType } from '../entity';
import { WORKFLOW_COMMON_PANNEL } from '../entity';
import WorkflowForm from './workflow-form';
import 'diagram-maker/dist/diagramMaker.css';
import './index.less';
import { If } from 'tsx-control-statements/components';
import type { NodeItem } from '../workflow-component';
import type { Definition } from '../../../interface/addon';
import type { WorkflowStep } from '../../../interface/application';
type WorkFlowItemProps = {
workflowId: string;
@ -57,9 +51,9 @@ class WorkFlowItem extends Component<WorkFlowItemProps, State> {
}
const { data, edit } = this.props;
const containerWidth = this.container.clientWidth;
const nodeWidth = Object.keys(data.nodes).length * 200 + 300;
const basicePlatformConfig = {
panels: WORKFLOW_COMMON_PANNEL,
const nodeWidth = Object.keys(data.nodes).length * 360;
const basicPlatformConfig = {
panels: {},
workspace: {
position: {
x: 0,
@ -82,20 +76,31 @@ class WorkFlowItem extends Component<WorkFlowItemProps, State> {
const initialData: DiagramMakerData<WorkFlowNodeType, WorkFlowEdgeType> = Object.assign(
{},
data,
basicePlatformConfig,
basicPlatformConfig,
);
const { workFlowDefinitions } = this.props;
const nodeTypeConfigs: any = {};
workFlowDefinitions.map((definition: Definition) => {
nodeTypeConfigs[definition.name] = {
const nodeTypeConfigs: any = {
prev: {
size: {
width: 140,
height: 40,
width: 200,
height: 80,
},
visibleConnectorTypes: VisibleConnectorTypes.OUTPUT_ONLY,
},
next: {
size: {
width: 200,
height: 80,
},
visibleConnectorTypes: VisibleConnectorTypes.INPUT_ONLY,
},
common: {
size: {
width: 200,
height: 80,
},
definition: definition,
visibleConnectorTypes: VisibleConnectorTypes.BOTH,
};
});
},
};
this.diagramMaker = new DiagramMaker(
this.container,
{
@ -108,24 +113,34 @@ class WorkFlowItem extends Component<WorkFlowItemProps, State> {
ReactDOM.render(
<WorkFlowNode
id={node.id}
data={node.consumerData}
data={node.consumerData as WorkflowStep}
selected={node.diagramMakerData.selected}
workflowId={this.props.workflowId}
showDetail={this.showStepDetail}
editMode={edit}
deleteNode={this.deleteNode}
typeId={node.typeId}
/>,
container,
);
},
edge: (edge: DiagramMakerEdge<{}>, container: HTMLElement) => {
ReactDOM.render(<WorkFlowEdge id={edge.id} data={edge.consumerData} />, container);
ReactDOM.render(
<WorkFlowEdge
editMode={edit}
id={edge.id}
addNode={() => {
this.newNode(edge.src);
}}
data={edge as EdgeData}
/>,
container,
);
},
destroy: (container: HTMLElement) => {
ReactDOM.unmountComponentAtNode(container);
},
panels: {
p1: this.renderLeftPannel,
p2: this.renderTopPannel,
},
panels: {},
},
nodeTypeConfig: nodeTypeConfigs,
actionInterceptor: (action: any, dispatch, getState) => {
@ -144,27 +159,34 @@ class WorkFlowItem extends Component<WorkFlowItemProps, State> {
}
}
}
dispatch(action);
if (action.type == 'NODE_CREATE') {
this.setState({
currentSelectedNodeData: Object.assign(action.payload, {
consumerData: { type: action.payload.typeId },
}),
visible: true,
});
if (action.type == NodeActions.NODE_DRAG) {
if (action.payload.workspaceRectangle.size.height > 400) {
this.diagramMaker.api.resetZoom();
}
}
if (action.type == 'DELETE_ITEMS') {
if (Array.isArray(action.payload.edgeIds) && action.payload.edgeIds.length > 0) {
Message.warning(
'If remove the edge and save the workflow, the nodes that after this edge will be removed.',
);
}
}
dispatch(action);
},
},
{
initialData,
consumerRootReducer: (state: any, action: any) => {
switch (action.type) {
case 'UPDATENODE':
case 'UPDATE_NODE':
const newNode: any = {};
newNode[action.payload.id] = action.payload;
const newNodes = Object.assign({}, state.nodes, newNode);
const newState = Object.assign({}, state, { nodes: newNodes });
return newState;
return Object.assign({}, state, { nodes: newNodes });
case 'UPDATE_NODES':
return Object.assign({}, state, { nodes: action.payload });
case 'UPDATE_EDGES':
return Object.assign({}, state, { edges: action.payload });
default:
return state;
}
@ -172,24 +194,6 @@ class WorkFlowItem extends Component<WorkFlowItemProps, State> {
eventListener: () => {},
},
);
this.diagramMaker.store.subscribe(() => {
const { nodes } = this.diagramMaker.store.getState();
let currentNode: any = null;
Object.keys(nodes).forEach((key) => {
const obj = nodes[key];
if (obj && obj.diagramMakerData && obj.diagramMakerData.selected) {
currentNode = obj;
}
});
const { visible } = this.state;
if (currentNode && !visible) {
this.setState({
currentSelectedNodeData: currentNode,
visible: true,
});
}
});
}
fit = () => {
@ -204,66 +208,6 @@ class WorkFlowItem extends Component<WorkFlowItemProps, State> {
});
};
undo = () => {
this.diagramMaker.api.undo();
};
redo = () => {
this.diagramMaker.api.redo();
};
zoomIn = () => {
this.diagramMaker.api.zoomIn();
};
zoomOut = () => {
this.diagramMaker.api.zoomOut();
};
renderLeftPannel = (
panel: DiagramMakerPanel,
state: DiagramMakerData<WorkFlowNodeType, WorkFlowEdgeType>,
diagramMakerContainer: HTMLElement,
) => {
const { workFlowDefinitions, edit } = this.props;
const parentContainer = diagramMakerContainer.parentElement;
if (parentContainer) {
parentContainer.style.display = 'block';
}
if (!edit && parentContainer) {
parentContainer.style.display = 'none';
return;
}
ReactDOM.render(
<WorkFlowPannel
definitions={workFlowDefinitions}
id={panel.id}
workflowId={this.props.workflowId}
/>,
diagramMakerContainer,
);
};
renderTopPannel = (
panel: DiagramMakerPanel,
state: DiagramMakerData<WorkFlowNodeType, WorkFlowEdgeType>,
diagramMakerContainer: HTMLElement,
) => {
ReactDOM.render(
<WorkFlowToolTip
id={panel.id}
edit={this.props.edit}
undo={this.undo}
redo={this.redo}
autoLayout={this.autoLayout}
zoomIn={this.zoomIn}
zoomOut={this.zoomOut}
fit={this.fit}
/>,
diagramMakerContainer,
);
};
componentWillReceiveProps = (nextProps: WorkFlowItemProps) => {
if (nextProps.edit !== this.props.edit) {
if (nextProps.edit) {
@ -278,6 +222,142 @@ class WorkFlowItem extends Component<WorkFlowItemProps, State> {
return { edges, nodes };
};
deleteNode = (id: string) => {
const { edges, nodes } = this.diagramMaker.store.getState();
const newNodes: any = {};
const deleteNode = nodes[id];
if (!deleteNode) {
return;
}
Object.keys(nodes).map((key: string) => {
if (key == id) {
return;
}
const step: NodeItem = nodes[key];
let stepNew: NodeItem = Object.assign({}, step);
if (stepNew.diagramMakerData.position.x >= deleteNode.diagramMakerData.position.x) {
stepNew = Object.assign(stepNew, {
diagramMakerData: {
selected: stepNew.diagramMakerData.selected,
size: stepNew.diagramMakerData.size,
position: {
x: stepNew.diagramMakerData.position.x - 320,
y: stepNew.diagramMakerData.position.y,
},
},
});
}
newNodes[stepNew.id] = stepNew;
});
const newEdges: any = {};
let preEdge: EdgeData = { id: '', dest: '', src: '' };
let dest = '';
Object.keys(edges).map((key: string) => {
const edge: EdgeData = edges[key];
if (edge.src == id) {
dest = edge.dest;
return;
}
if (edge.dest == id) {
preEdge = Object.assign({}, edge);
return;
}
newEdges[key] = edge;
});
preEdge.dest = dest;
newEdges[preEdge.id] = preEdge;
this.diagramMaker.store.dispatch({
type: 'UPDATE_NODES',
payload: newNodes,
});
this.diagramMaker.store.dispatch({
type: 'UPDATE_EDGES',
payload: newEdges,
});
};
newNode = (source: string) => {
const { nodes, edges } = this.diagramMaker.store.getState();
const sourceNode = nodes[source];
let newNode: NodeItem = Object.assign({}, sourceNode);
newNode = Object.assign(newNode, {
consumerData: { name: '', properties: '', type: '' },
id: uuid(),
typeId: 'common',
diagramMakerData: {
selected: newNode.diagramMakerData.selected,
size: newNode.diagramMakerData.size,
position: {
x: newNode.diagramMakerData.position.x + 320,
y: newNode.diagramMakerData.position.y,
},
},
});
const newNodes: any = {};
Object.keys(nodes).map((key: string) => {
const step: NodeItem = nodes[key];
let stepNew: NodeItem = Object.assign({}, step);
if (stepNew.diagramMakerData.position.x >= newNode.diagramMakerData.position.x) {
stepNew = Object.assign(stepNew, {
diagramMakerData: {
selected: stepNew.diagramMakerData.selected,
size: stepNew.diagramMakerData.size,
position: {
x: stepNew.diagramMakerData.position.x + 320,
y: stepNew.diagramMakerData.position.y,
},
},
});
}
newNodes[stepNew.id] = stepNew;
});
newNodes[newNode.id] = newNode;
const newEdges: any = {};
Object.keys(edges).map((key: string) => {
const edge: EdgeData = edges[key];
if (edge.src == source) {
const pre = Object.assign({}, edge);
const next = Object.assign({}, edge);
pre.dest = newNode.id;
pre.id = pre.src;
next.src = newNode.id;
next.id = next.src;
newEdges[pre.id] = pre;
newEdges[next.id] = next;
return;
}
newEdges[key] = edge;
});
this.diagramMaker.store.dispatch({
type: 'UPDATE_NODES',
payload: newNodes,
});
this.diagramMaker.store.dispatch({
type: 'UPDATE_EDGES',
payload: newEdges,
});
this.setState({
currentSelectedNodeData: newNode,
visible: true,
});
};
showStepDetail = (id: string) => {
const { edit } = this.props;
if (!edit) {
return;
}
const { nodes } = this.diagramMaker.store.getState();
const consumerData: NodeItem = nodes[id];
this.setState({
currentSelectedNodeData: consumerData,
visible: true,
});
};
openDrawer = () => {
this.setState({
visible: true,
@ -296,20 +376,24 @@ class WorkFlowItem extends Component<WorkFlowItemProps, State> {
createOrUpdateNode = (values: any) => {
const { nodes } = this.diagramMaker.store.getState();
const { currentSelectedNodeData } = this.state;
let consumerData: NodeItem = nodes[currentSelectedNodeData.id];
consumerData = Object.assign({}, consumerData, {
consumerData: values,
diagramMakerData: {
selected: false,
size: consumerData.diagramMakerData.size,
position: consumerData.diagramMakerData.position,
},
});
this.diagramMaker.store.dispatch({
type: 'UPDATENODE',
payload: consumerData,
});
this.closeDrawer();
if (currentSelectedNodeData) {
let consumerData: NodeItem = nodes[currentSelectedNodeData.id];
if (consumerData) {
consumerData = Object.assign({}, consumerData, {
consumerData: values,
diagramMakerData: {
selected: false,
size: consumerData.diagramMakerData.size,
position: consumerData.diagramMakerData.position,
},
});
this.diagramMaker.store.dispatch({
type: 'UPDATE_NODE',
payload: consumerData,
});
this.closeDrawer();
}
}
};
onDelete = (values: NodeItem) => {
@ -335,7 +419,6 @@ class WorkFlowItem extends Component<WorkFlowItemProps, State> {
/>
<If condition={visible}>
<WorkflowForm
onDelete={() => this.onDelete(currentSelectedNodeData)}
createOrUpdateNode={this.createOrUpdateNode}
data={currentSelectedNodeData}
checkStepName={(name: string) => {

View File

@ -21,13 +21,12 @@ import locale from '../../../utils/locale';
type Props = {
createOrUpdateNode: (data: any) => void;
data: DiagramMakerNode<WorkFlowNodeType>;
data?: DiagramMakerNode<WorkFlowNodeType>;
workFlowDefinitions: [];
appName?: string;
componentName?: string;
closeDrawer: () => void;
checkStepName: (name: string) => boolean;
onDelete: () => void;
dispatch?: ({}) => {};
t: (key: string) => {};
};
@ -56,14 +55,16 @@ class WorkflowForm extends Component<Props, State> {
}
componentDidMount = () => {
const { consumerData } = this.props.data;
this.field.setValues(consumerData || '');
let properties = consumerData && consumerData.properties;
if (properties && typeof properties === 'string') {
properties = JSON.parse(properties);
if (this.props.data) {
const { consumerData } = this.props.data;
this.field.setValues(consumerData || '');
let properties = consumerData && consumerData.properties;
if (properties && typeof properties === 'string') {
properties = JSON.parse(properties);
}
this.field.setValues({ properties: properties });
this.onDetailsComponeDefinition((consumerData && consumerData.type) || '');
}
this.field.setValues({ properties: properties });
this.onDetailsComponeDefinition((consumerData && consumerData.type) || '');
};
setValues = (values: any | null) => {
@ -83,13 +84,6 @@ class WorkflowForm extends Component<Props, State> {
});
};
onDelete = () => {
const { onDelete } = this.props;
if (onDelete) {
onDelete();
}
};
transDefinitions() {
const { workFlowDefinitions } = this.props;
return (workFlowDefinitions || []).map((item: { name: string }) => ({
@ -129,20 +123,22 @@ class WorkflowForm extends Component<Props, State> {
};
const checkWorkflowStepName = (rule: Rule, value: any, callback: (error?: string) => void) => {
const { consumerData } = this.props.data;
if (checkStepName(value)) {
if (consumerData?.name && value == consumerData?.name) {
callback();
return;
if (data) {
const { consumerData } = data;
if (checkStepName(value)) {
if (consumerData?.name && value == consumerData?.name) {
callback();
return;
}
callback('name is exist');
}
callback('name is exist');
}
callback();
};
const edit = data && data.consumerData?.name != undefined && data.consumerData?.name != '';
return (
<DrawerWithFooter
title={<Translation>Edit Workflow Step</Translation>}
title={<Translation>{edit ? 'Edit Workflow Step' : 'Add Workflow Step'}</Translation>}
placement="right"
width={800}
onClose={closeDrawer}
@ -151,26 +147,12 @@ class WorkflowForm extends Component<Props, State> {
<Button key={'cancel'} style={{ marginRight: '16px' }} onClick={closeDrawer}>
Cancel
</Button>,
<Button
key={'delete'}
style={{ marginRight: '16px' }}
onClick={() => {
this.onDelete();
}}
title="Delete this step"
>
Delete
</Button>,
]}
>
<Form field={this.field}>
<Row>
<Col span={24} style={{ padding: '0 8px' }}>
<FormItem
label={<Translation>Workflow Type</Translation>}
required
disabled={this.field.getValue('type')}
>
<FormItem label={<Translation>Workflow Type</Translation>} required disabled={edit}>
<Select
locale={locale.Select}
className="select"
@ -202,7 +184,7 @@ class WorkflowForm extends Component<Props, State> {
maxLength={32}
placeholder={t('Please enter').toString()}
{...init('name', {
initValue: data.consumerData?.type,
initValue: data && data.consumerData?.type,
rules: [
{
required: true,

View File

@ -1,63 +1,27 @@
@primarycolor: #1b58f4;
.workflow-node-container {
width: auto;
height: 40px;
width: 200px;
height: 80px;
padding: 16px;
color: black;
line-height: 40px;
white-space: nowrap;
text-align: center;
text-overflow: ellipsis;
border: 1px solid #ccc;
border-radius: 12px;
cursor: pointer;
&.active {
border: 1px solid @primarycolor;
}
}
.workflow-switch-node-container {
position: relative;
width: 80px;
height: 80px;
border: none;
&.active {
border: none;
.rhombus-container {
border: 1px solid @primarycolor;
.workflow-step-type {
padding-top: 8px;
color: #a6a6a6;
}
.workflow-step-delete {
position: absolute;
top: 8px;
right: 8px;
:hover {
color: @primarycolor;
}
}
.rhombus-container {
position: absolute;
top: 11px;
left: 11px;
width: 57px;
height: 57px;
border: 1px solid #ccc;
transform: rotate(45deg);
}
.content {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
.start-connector {
position: absolute;
top: 31px;
left: -4px;
width: 14px;
height: 14px;
background-color: blue;
border-radius: 50%;
}
.end-connector {
position: absolute;
top: 31px;
right: -4px;
width: 14px;
height: 14px;
background-color: blue;
border-radius: 50%;
}
}

View File

@ -1,15 +1,20 @@
import React, { Component } from 'react';
import { DiagramMakerComponents } from 'diagram-maker';
import { Icon } from '@b-design/ui';
import './index.less';
import type { WorkflowStep } from '../../../interface/application';
import { Balloon, Icon } from '@b-design/ui';
import { If } from 'tsx-control-statements/components';
import Translation from '../../../components/Translation';
type Props = {
id: string;
typeId?: string;
workflowId: string;
selected?: boolean;
data?: any;
showDetail: (id: string) => void;
deleteNode: (id: string) => void;
data?: WorkflowStep;
editMode?: boolean;
};
type State = {};
@ -17,69 +22,66 @@ type State = {};
class WorkFlowNode extends Component<Props, State> {
constructor(props: any) {
super(props);
this.state = {};
}
componentDidMount() {}
render() {
const { data = {}, selected, id, workflowId, typeId } = this.props;
const { data, selected, id, workflowId, editMode, typeId } = this.props;
const showName =
data && data.name ? (
data.alias || data.name
) : (
<Translation>Click to config Workflow Step</Translation>
);
if (typeId == 'prev' || typeId == 'next') {
return <React.Fragment />;
}
return (
<React.Fragment>
<If condition={typeId !== 'switch'}>
<div
className={selected ? 'workflow-node-container active' : 'workflow-node-container'}
id={id}
workflow-id={workflowId}
>
{data.alias || data.name || <Translation>Click Edit</Translation>}
<div
data-event-target="true"
data-dropzone="true"
data-type={DiagramMakerComponents.NODE_CONNECTOR}
data-id={id}
/>
<div
data-event-target="true"
data-draggable="true"
data-type={DiagramMakerComponents.NODE_CONNECTOR}
data-id={id}
/>
</div>
</If>
<If condition={typeId === 'switch'}>
<div
className={
selected
? 'workflow-node-container workflow-switch-node-container active'
: 'workflow-node-container workflow-switch-node-container'
}
id={id}
workflow-id={workflowId}
title={data.text}
>
<div className="rhombus-container" />
<div className="content">
{data.text || <Translation>Click Edit</Translation>}
<Icon type="ashbin1" />
<div
className={selected ? 'workflow-node-container active' : 'workflow-node-container'}
id={id}
style={{
cursor: editMode ? 'pointer' : 'auto',
}}
onClick={() => this.props.showDetail(id)}
workflow-id={workflowId}
>
<If condition={editMode}>
<div className="workflow-step-delete">
<Balloon
trigger={
<Icon
size={14}
onClick={(event: any) => {
event.stopPropagation();
this.props.deleteNode(id);
}}
type="delete"
/>
}
>
<Translation>Click to remove Workflow Step</Translation>
</Balloon>
</div>
<div
className="start-connector"
data-event-target="true"
data-dropzone="true"
data-type={DiagramMakerComponents.NODE_CONNECTOR}
data-id={id}
/>
<div
className="end-connector"
data-event-target="true"
data-draggable="true"
data-type={DiagramMakerComponents.NODE_CONNECTOR}
data-id={id}
/>
</div>
</If>
</If>
{showName}
<div className="workflow-step-type">{data?.type}</div>
<div
data-event-target="true"
data-dropzone="false"
data-type={DiagramMakerComponents.NODE_CONNECTOR}
data-id={id}
/>
<div
data-event-target="true"
data-draggable="false"
data-type={DiagramMakerComponents.NODE_CONNECTOR}
data-id={id}
/>
</div>
</React.Fragment>
);
}

View File

@ -1993,6 +1993,11 @@
resolved "https://registry.nlark.com/@types/unist/download/@types/unist-2.0.6.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.nlark.com%2F%40types%2Funist%2Fdownload%2F%40types%2Funist-2.0.6.tgz#250a7b16c3b91f672a24552ec64678eeb1d3a08d"
integrity sha1-JQp7FsO5H2cqJFUuxkZ47rHToI0=
"@types/uuid@^8.3.4":
version "8.3.4"
resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.4.tgz#bd86a43617df0594787d38b735f55c805becf1bc"
integrity sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==
"@types/webpack-sources@*":
version "3.2.0"
resolved "https://registry.nlark.com/@types/webpack-sources/download/@types/webpack-sources-3.2.0.tgz?cache=0&sync_timestamp=1629709718286&other_urls=https%3A%2F%2Fregistry.nlark.com%2F%40types%2Fwebpack-sources%2Fdownload%2F%40types%2Fwebpack-sources-3.2.0.tgz#16d759ba096c289034b26553d2df1bf45248d38b"
@ -12210,6 +12215,11 @@ uuid@^3.3.2, uuid@^3.4.0:
resolved "https://registry.npmmirror.com/uuid/download/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
integrity sha1-sj5DWK+oogL+ehAK8fX4g/AgB+4=
uuid@^8.3.2:
version "8.3.2"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
uvu@^0.5.0:
version "0.5.2"
resolved "https://registry.npmmirror.com/uvu/download/uvu-0.5.2.tgz#c145e7f4b5becf80099cf22fd8a4a05f0112b2c0"