smallstonesk
4 years ago
9 changed files with 246 additions and 23 deletions
@ -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()); |
||||
|
}); |
||||
|
}); |
||||
|
}); |
||||
|
}; |
@ -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) { |
||||
|
return null; |
||||
|
} else { |
||||
|
const {color, text} = STATUS_TEXT_MAP[status]; |
||||
|
return ( |
||||
|
<div className={styles.modifyStatusContainer}> |
||||
|
<span style={{color}}>{text}</span> |
||||
|
</div> |
||||
|
); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
export default ModifyStatus; |
Loading…
Reference in new issue