Skip to content

Commit

Permalink
feat: supports server storage
Browse files Browse the repository at this point in the history
  • Loading branch information
SmallStoneSK committed Nov 30, 2020
1 parent df65185 commit b30d5a7
Show file tree
Hide file tree
Showing 9 changed files with 246 additions and 23 deletions.
3 changes: 3 additions & 0 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,14 @@
"@antv/x6": "^0.13.0",
"ace-builds": "^1.4.12",
"antd": "^4.5.3",
"lodash.merge": "^4.6.2",

This comment has been minimized.

Copy link
@i5ting

i5ting Nov 30, 2020

Owner

example为啥放到dependency里

"query-string": "^6.13.7",
"react-ace": "^9.1.3",
"react-color": "^2.18.1",
"react-json-view": "^1.19.1"
},
"devDependencies": {
"@types/lodash.merge": "^4.6.6",
"@types/react": "^16.9.34",
"@types/react-color": "^3.0.4",
"@types/react-dom": "^16.9.7",
Expand Down
38 changes: 37 additions & 1 deletion packages/core/src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,18 @@ export interface ILocalConfig {
port: string;
}

export type ActionType = 'create' | 'update' | 'remove';

This comment has been minimized.

Copy link
@i5ting

i5ting Nov 30, 2020

Owner

不规范


export interface IModifyGraphAction {
type: string;
actionType: ActionType;
data: any;
}

export const localConfig: ILocalConfig = {
ip: '127.0.0.1',
port: '3500'
};
}

export const updateLocalConfig = (config: ILocalConfig) => {
localConfig.ip = config.ip || localConfig.ip;
Expand All @@ -27,3 +35,31 @@ export const localSave = (data: any) => {
body: JSON.stringify(data)
});
}

export const queryGraph = (projectId: string) => {

This comment has been minimized.

Copy link
@i5ting

i5ting Nov 30, 2020

Owner

重复代码

return fetch('/api/queryGraph', {
method: 'POST',
headers: {'Content-Type': 'application/json;charset=utf-8'},
body: JSON.stringify({
projectId
})
}).then(res => res.json()).then(res => {
const {success} = res;
if(success) {
return res;
} else {
return Promise.reject(res);
}
})
}

export const modifyGraph = (projectId: string, actions: IModifyGraphAction[]) => {
return fetch('/api/modifyGraph', {
method: 'POST',
headers: {'Content-Type': 'application/json;charset=utf-8'},
body: JSON.stringify({
projectId,
actions
})
});
}
21 changes: 2 additions & 19 deletions packages/core/src/mods/flowChart/createFlowChart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {Cell, Edge, Graph, Node} from '@antv/x6';
import {MIN_ZOOM, MAX_ZOOM} from '../../common/const';
import baseCellSchemaMap from '../../common/baseCell';
import previewCellSchemaMap from '../../common/previewCell';
import {registerServerStorage} from './registerServerStorage';
import MiniMapSimpleNode from '../../components/miniMapSimpleNode';

// X6 register base/preview cell shape
Expand Down Expand Up @@ -32,25 +33,6 @@ const registerEvents = (flowChart: Graph): void => {
}
}
});
flowChart.on('edge:mouseenter', (args) => {
const cell = args.cell as Cell;
cell.addTools([
{
name: 'target-arrowhead',
args: {
attrs: {
d: 'M -10.5 -6 1 0 -10.5 6 Z',
'stroke-width': 0,
fill: '#333'
}
}
}
]);
});
flowChart.on('edge:mouseleave', (args) => {
const cell = args.cell as Cell;
cell.removeTools(['target-arrowhead']);
});
};

const registerShortcuts = (flowChart: Graph): void => {
Expand Down Expand Up @@ -156,6 +138,7 @@ const createFlowChart = (container: HTMLDivElement, miniMapContainer: HTMLDivEle
});
registerEvents(flowChart);
registerShortcuts(flowChart);
registerServerStorage(flowChart);
return flowChart;
};

Expand Down
23 changes: 20 additions & 3 deletions packages/core/src/mods/flowChart/index.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,41 @@
import React, {useRef, useEffect} from 'react';
import React, { useRef, useEffect } from 'react';

import styles from './index.module.less';

import {Graph} from '@antv/x6';
import { message } from 'antd';
import { Graph } from '@antv/x6';
import { queryGraph } from '../../api';
import { parseQuery } from '../../utils';
import createFlowChart from './createFlowChart';

interface IProps {
onReady: (graph: Graph) => void;
}

const FlowChart: React.FC<IProps> = props => {

const {onReady} = props;
const graphRef = useRef<HTMLDivElement>(null);
const miniMapRef = useRef<HTMLDivElement>(null);

useEffect(() => {
if(graphRef.current && miniMapRef.current) {
if (graphRef.current && miniMapRef.current) {
const graph = createFlowChart(graphRef.current, miniMapRef.current);
onReady(graph);
fetchData(graph);
}
}, []);

const fetchData = (graph: Graph) => {
const { projectId } = parseQuery();
queryGraph(projectId as string).then(res => {
const { data: dsl } = res;
graph.fromJSON(dsl);
}).catch(err => {
message.error(err.msg);
});
};

return (
<div className={styles.container}>
<div className={styles.flowChart} ref={graphRef}/>
Expand Down
96 changes: 96 additions & 0 deletions packages/core/src/mods/flowChart/registerServerStorage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import { Graph } from '@antv/x6';
import merge from 'lodash.merge';
import { parseQuery } from '../../utils';
import { modifyGraph, ActionType, IModifyGraphAction } from '../../api';

const { projectId } = parseQuery();
const memQueue: IModifyGraphAction[] = [];

const validate = (type: string, data: any) => {
if (type === 'node') {
return true;
} else if (type === 'edge') {
const { source, target } = data;
return source.cell && target.cell;
} else {
return false;
}
}

const enqueue = (type: string, actionType: ActionType, data: any) => {

if (!validate(type, data)) {
return;
}

const foundIndex = memQueue.findIndex(item => item.type === type && item.actionType === actionType);
if (foundIndex > -1) {
const deleted = memQueue.splice(foundIndex, 1)[0];
data = merge(deleted, data);
}
memQueue.push({ type, actionType, data });
};

let modifyActionTimer = -1;
const save = (flowChart: Graph, type: string, actionType: ActionType, data: any) => {
enqueue(type, actionType, data);
clearTimeout(modifyActionTimer);
modifyActionTimer = setTimeout(() => {
const pushedActions = memQueue.slice(0);
if(pushedActions.length > 0) {
flowChart.trigger('graph:change:modify');
modifyGraph(projectId, memQueue).then(res => {
memQueue.splice(0, pushedActions.length);
flowChart.trigger('graph:modified', {success: true});
}).catch(err => {
flowChart.trigger('graph:modified', {success: true, error: err});
});
}
}, 100);
};

type ActionEventMap = {[key: string]: string[]};
const NODE_ACTION_EVENT_MAP: ActionEventMap = {
create: ['node:added'],
remove: ['node:removed'],
update: [
'node:moved',
'node:resized',
'node:rotated',
'node:change:ports',
'node:change:attrs',
'node:change:data',
'node:change:zIndex'
]
};

const EDGE_ACTION_EVENT_MAP: ActionEventMap = {
create: ['edge:connected'],
remove: ['edge:removed'],
update: [
'edge:moved',
]
};

export const registerServerStorage = (flowChart: Graph) => {

Object.keys(NODE_ACTION_EVENT_MAP).forEach((actionType) => {
const events = NODE_ACTION_EVENT_MAP[actionType];
events.forEach(event => {
flowChart.on(event, (args: any) => {
console.log('node event:', event, 'args:', args);
save(flowChart, 'node', actionType as ActionType, args.node.toJSON());
});
});
});

Object.keys(EDGE_ACTION_EVENT_MAP).forEach((actionType) => {
const events = EDGE_ACTION_EVENT_MAP[actionType];
events.forEach(event => {
flowChart.on(event, (args: any) => {
console.log('edge event:', event, 'args:', args);
save(flowChart, 'edge', actionType as ActionType, args.edge.toJSON());
});
});
});
};
2 changes: 2 additions & 0 deletions packages/core/src/mods/toolBar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import styles from './index.module.less';

import {Graph} from '@antv/x6';
import widgets from './widgets';
import ModifyStatus from './widgets/modifyStatus';

interface IProps {
flowChart: Graph;
Expand All @@ -31,6 +32,7 @@ const ToolBar: React.FC<IProps> = props => {
})}
</div>
))}
<ModifyStatus flowChart={flowChart}/>
</div>
);
};
Expand Down
4 changes: 4 additions & 0 deletions packages/core/src/mods/toolBar/widgets/index.module.less
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@
}
}

.modifyStatusContainer {
margin-left: 8px;
}

.bgColorContainer, .textColorContainer, .borderColorContainer {

position: relative;
Expand Down
69 changes: 69 additions & 0 deletions packages/core/src/mods/toolBar/widgets/modifyStatus.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import React, { useEffect, useState } from 'react';

import styles from './index.module.less';

import { Graph } from '@antv/x6';

enum Status {
pending = 0,
syncing = 1,
successful = 2,
failed = 3
}

interface IProps {
flowChart: Graph
}

const STATUS_TEXT_MAP = {
[Status.pending]: '',
[Status.syncing]: {
color: '#999',
text: '正在保存...'
},
[Status.successful]: {
color: '#999',
text: '所有更改已保存'
},
[Status.failed]: {
color: '#EC5B56',
text: '同步失败,进入离线模式'
}
};

const ModifyStatus: React.FC<IProps> = (props) => {

const {flowChart} = props;
const [status, setStatus] = useState<Status>(Status.pending);

useEffect(() => {
flowChart.on('graph:change:modify', () => {
setStatus(Status.syncing);
});
flowChart.on('graph:modified', (res: any) => {
const {success} = res;
if(success) {
setStatus(Status.successful);
} else {
setStatus(Status.failed);
}
});
return () => {
flowChart.off('graph:change:modify');
flowChart.off('graph:modified');
};
}, []);

if(status === Status.pending) {

This comment has been minimized.

Copy link
@i5ting

i5ting Nov 30, 2020

Owner

看起来逻辑可以更简单。另外常量。。。。。。

return null;
} else {
const {color, text} = STATUS_TEXT_MAP[status];
return (
<div className={styles.modifyStatusContainer}>
<span style={{color}}>{text}</span>
</div>
);
}
};

export default ModifyStatus;
13 changes: 13 additions & 0 deletions packages/core/src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { parse } from 'query-string';

export const safeParse = (json: string): Object => {
try {
return JSON.parse(json);
Expand All @@ -24,3 +26,14 @@ export const safeGet = (obj: any, keyChain: string, defaultVal?: any): any => {

return retVal;

This comment has been minimized.

Copy link
@i5ting

i5ting Nov 30, 2020

Owner

这命名。。。

};

const PARSE_CONFIG = {

This comment has been minimized.

Copy link
@i5ting

i5ting Nov 30, 2020

Owner

这种都是啥。。。

skipNull: true,
skipEmptyString: true,
parseNumbers: false,
parseBooleans: false,
};

export const parseQuery = (): {[key: string]: any} => {
return parse(location.search, PARSE_CONFIG);
};

0 comments on commit b30d5a7

Please sign in to comment.