|
@@ -1,19 +1,19 @@
|
|
|
-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'
|
|
|
-import { useFindReplace } from '@/hooks/useFindReplace'
|
|
|
+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";
|
|
|
+import { useLocalStorageState } from "ahooks";
|
|
|
|
|
|
export default function GraphModel() {
|
|
|
const [graph, setGraph] = useState<Graph>();
|
|
@@ -23,159 +23,205 @@ export default function GraphModel() {
|
|
|
const dndRef = useRef(dnd);
|
|
|
// 画布
|
|
|
const graphRef = useRef<Graph>();
|
|
|
- const { pageState } = useModel('appModel');
|
|
|
+ const { pageState } = useModel("appModel");
|
|
|
// 当前选中的节点
|
|
|
const [selectedCell, setSelectedCell] = useState<Cell[]>([]);
|
|
|
const [canRedo, setCanRedo] = useState(false);
|
|
|
const [canUndo, setCanUndo] = useState(false);
|
|
|
|
|
|
+ const [projectInfo, setProjectInfo] = useLocalStorageState<{
|
|
|
+ name: string;
|
|
|
+ desc: string;
|
|
|
+ version: string;
|
|
|
+ author: string;
|
|
|
+ cells: Cell.Properties[];
|
|
|
+ }>("flow-project", {
|
|
|
+ defaultValue: {
|
|
|
+ name: "新建流程图",
|
|
|
+ desc: "",
|
|
|
+ version: "",
|
|
|
+ author: "",
|
|
|
+ cells: [],
|
|
|
+ },
|
|
|
+ });
|
|
|
+
|
|
|
+ const handleChangeAll = () => {
|
|
|
+ const cells = graph?.toJSON().cells || [];
|
|
|
+
|
|
|
+ projectInfo &&
|
|
|
+ setProjectInfo({
|
|
|
+ ...projectInfo,
|
|
|
+ cells: cells,
|
|
|
+ });
|
|
|
+ };
|
|
|
+
|
|
|
+ useEffect(() => {
|
|
|
+ console.log("画布加载完毕");
|
|
|
+ if (graph) {
|
|
|
+ graph.fromJSON(projectInfo?.cells || []);
|
|
|
+ graph.on("cell:change:*", handleChangeAll);
|
|
|
+ }
|
|
|
+ }, [graph]);
|
|
|
+
|
|
|
/**初始化页面节点 */
|
|
|
useEffect(() => {
|
|
|
- if(pageState.width && graphRef.current && !pageNodeRef.current) {
|
|
|
+ if (pageState.width && graphRef.current && !pageNodeRef.current) {
|
|
|
const graph = graphRef.current;
|
|
|
pageNodeRef.current = graph?.addNode({
|
|
|
- shape: 'page-container-node',
|
|
|
+ shape: "page-container-node",
|
|
|
width: pageState.width,
|
|
|
height: pageState.height,
|
|
|
zIndex: -1,
|
|
|
data: {
|
|
|
isPage: true,
|
|
|
ignoreDrag: true,
|
|
|
- ...pageState
|
|
|
+ ...pageState,
|
|
|
},
|
|
|
tools: [
|
|
|
{
|
|
|
- name: 'contextmenu',
|
|
|
+ name: "contextmenu",
|
|
|
args: {
|
|
|
menu: pageMenu,
|
|
|
},
|
|
|
},
|
|
|
- ]
|
|
|
+ ],
|
|
|
});
|
|
|
- };
|
|
|
- }, [pageState.width, graphRef.current])
|
|
|
+ }
|
|
|
+ }, [pageState.width, graphRef.current]);
|
|
|
|
|
|
useEffect(() => {
|
|
|
pageNodeRef.current?.setData({
|
|
|
background: pageState.backgroundColor,
|
|
|
- ...pageState
|
|
|
+ ...pageState,
|
|
|
});
|
|
|
pageNodeRef.current?.setSize({
|
|
|
width: pageState.width,
|
|
|
- height: pageState.height
|
|
|
- })
|
|
|
+ height: pageState.height,
|
|
|
+ });
|
|
|
}, [pageState]);
|
|
|
|
|
|
const enabledTransform = (node: Node) => {
|
|
|
- const data = node.getData<{ isPage: boolean, lock: boolean }>();
|
|
|
+ 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());
|
|
|
+ .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', () => {
|
|
|
+ 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<HTMLDivElement, MouseEvent>, node: Node.Metadata) => {
|
|
|
- if(!dndRef.current || !graphRef.current) return;
|
|
|
+ const startDrag = (
|
|
|
+ e: React.MouseEvent<HTMLDivElement, MouseEvent>,
|
|
|
+ node: Node.Metadata
|
|
|
+ ) => {
|
|
|
+ if (!dndRef.current || !graphRef.current) return;
|
|
|
|
|
|
// 往画布添加节点
|
|
|
const n = graphRef.current.createNode(node);
|
|
|
// 右键菜单
|
|
|
n.addTools({
|
|
|
- name: 'contextmenu',
|
|
|
+ name: "contextmenu",
|
|
|
args: {
|
|
|
menu: nodeMenu,
|
|
|
},
|
|
|
});
|
|
|
- dndRef.current.start(n, e.nativeEvent as any)
|
|
|
+ dndRef.current.start(n, e.nativeEvent as any);
|
|
|
};
|
|
|
|
|
|
// 撤销
|
|
|
const onUndo = () => {
|
|
|
graphRef.current?.undo();
|
|
|
- }
|
|
|
+ };
|
|
|
|
|
|
// 重做
|
|
|
const onRedo = () => {
|
|
|
graphRef.current?.redo();
|
|
|
- }
|
|
|
+ };
|
|
|
|
|
|
- return {
|
|
|
+ return {
|
|
|
graph,
|
|
|
dnd,
|
|
|
initGraph,
|
|
@@ -185,6 +231,6 @@ export default function GraphModel() {
|
|
|
canRedo,
|
|
|
canUndo,
|
|
|
onUndo,
|
|
|
- onRedo
|
|
|
- };
|
|
|
-};
|
|
|
+ onRedo,
|
|
|
+ };
|
|
|
+}
|