import { useState, useRef, useEffect } from 'react' import { Cell, Graph, Node } from '@antv/x6' import { Dnd } from '@antv/x6-plugin-dnd' import { Transform } from '@antv/x6-plugin-transform' import { Snapline } from '@antv/x6-plugin-snapline' import { Clipboard } from '@antv/x6-plugin-clipboard' import { Selection } from '@repo/x6-plugin-selection/src/index' import { History } from '@antv/x6-plugin-history' import { Keyboard } from '@antv/x6-plugin-keyboard' import { Export } from '@antv/x6-plugin-export' import { useModel } from 'umi' import '@/components/PageContainer' import { handleGraphEvent } from '@/events/flowEvent' import { pageMenu, nodeMenu} from '@/utils/contentMenu'; import { bindKeys } from '@/utils/fastKey' export default function GraphModel() { const [graph, setGraph] = useState(); const [dnd, setDnd] = useState(); const pageNodeRef = useRef(); const dndRef = useRef(dnd); // 画布 const graphRef = useRef(); const { pageState } = useModel('appModel'); // 当前选中的节点 const [selectedCell, setSelectedCell] = useState([]); const [canRedo, setCanRedo] = useState(false); const [canUndo, setCanUndo] = useState(false); /**初始化页面节点 */ useEffect(() => { if(pageState.width && graphRef.current && !pageNodeRef.current) { const graph = graphRef.current; pageNodeRef.current = graph?.addNode({ shape: 'page-container-node', width: pageState.width, height: pageState.height, zIndex: -1, data: { isPage: true, ignoreDrag: true, ...pageState }, tools: [ { name: 'contextmenu', args: { menu: pageMenu, }, }, ] }); }; }, [pageState.width, graphRef.current]) useEffect(() => { pageNodeRef.current?.setData({ background: pageState.backgroundColor, ...pageState }); pageNodeRef.current?.setSize({ width: pageState.width, height: pageState.height }) }, [pageState]); const enabledTransform = (node: Node) => { const data = node.getData<{ isPage: boolean, lock: boolean }>(); return !data?.isPage && !data?.lock; } /**初始化画布 */ const initGraph = (instance: Graph) => { // 添加插件 instance .use( new Transform({ resizing: { enabled: enabledTransform, minWidth: 20, minHeight: 20 }, rotating: { enabled: enabledTransform, grid: 1 }, }), ) .use( new Selection({ enabled: true, multiple: true, rubberband: true, movable: true, showNodeSelectionBox: true, // showEdgeSelectionBox: true, pointerEvents: 'none', strict: true, filter: (cell: Cell) => { const data = cell.getData<{ isPage: boolean, lock: boolean }>(); return !data?.isPage && !data?.lock; }, }), ) .use(new Snapline({ sharp: true, resizing: true })) .use(new Keyboard({ enabled: true, global: true, })) .use(new Clipboard()) .use(new History({ enabled: true, beforeAddCommand: (event, args) => { // @ts-ignore 排除不用创建的节点 if(args?.cell?.data?.noCreate) return false; // @ts-ignore 排除页面节点 return !(event === 'cell:added' && args?.cell?.getData()?.isPage); }, })) .use(new Export()); setGraph(instance); graphRef.current = instance; // 选中的节点/边发生改变(增删)时触发 instance.on('selection:changed', ({selected}: {added: Cell[]; removed: Cell[]; selected: Cell[];}) => { setSelectedCell(selected); }) instance.on('history:change', () => { setCanRedo(instance.canRedo()); setCanUndo(instance.canUndo()); }) // 通用事件处理 handleGraphEvent(instance); // 绑定快捷键 bindKeys(instance); } /**初始化拖拽 */ const initDnd = (instance: Dnd) => { setDnd(instance); dndRef.current = instance; } /**组件库拖拽生成 */ const startDrag = (e: React.MouseEvent, node: Node.Metadata) => { if(!dndRef.current || !graphRef.current) return; // 往画布添加节点 const n = graphRef.current.createNode(node); // 右键菜单 n.addTools({ name: 'contextmenu', args: { menu: nodeMenu, }, }); dndRef.current.start(n, e.nativeEvent as any) }; // 撤销 const onUndo = () => { graphRef.current?.undo(); } // 重做 const onRedo = () => { graphRef.current?.redo(); } return { graph, dnd, initGraph, initDnd, startDrag, selectedCell, canRedo, canUndo, onUndo, onRedo }; };