|
@@ -0,0 +1,458 @@
|
|
|
+import { ContextMenuTool, edgeMenu } from "./contentMenu";
|
|
|
+import Text from "@/components/basic/text";
|
|
|
+import baseNode from "@/components/Base";
|
|
|
+import { Cell, Edge, Graph, Node } from "@antv/x6";
|
|
|
+import { nodeMenu } from "./contentMenu";
|
|
|
+import { setCellZIndex } from "@/utils";
|
|
|
+import { exportImage } from "@/components/ExportImage";
|
|
|
+import { BaseEdge } from "@/components/Edge";
|
|
|
+
|
|
|
+/**
|
|
|
+ * 执行粘贴
|
|
|
+ * @param graph
|
|
|
+ * @param position
|
|
|
+ */
|
|
|
+export const handlePaste = (
|
|
|
+ graph: Graph,
|
|
|
+ position?: { x: number; y: number }
|
|
|
+) => {
|
|
|
+ // 读取剪切板数据
|
|
|
+ navigator.clipboard.read().then((items) => {
|
|
|
+ console.log("剪切板内容:", items);
|
|
|
+ const item = items?.[0];
|
|
|
+ if (item) {
|
|
|
+ /**读取图片数据 */
|
|
|
+ if (item.types[0] === "image/png") {
|
|
|
+ item.getType("image/png").then((blob) => {
|
|
|
+ const reader = new FileReader();
|
|
|
+ reader.readAsDataURL(blob);
|
|
|
+ reader.onload = function (event) {
|
|
|
+ const dataUrl = event.target?.result as string;
|
|
|
+ // 获取图片大小
|
|
|
+ const img = new Image();
|
|
|
+ img.src = dataUrl;
|
|
|
+ img.onload = function () {
|
|
|
+ const width = img.width;
|
|
|
+ const height = img.height;
|
|
|
+ // 插入图片
|
|
|
+ const node = {
|
|
|
+ ...baseNode,
|
|
|
+ data: {
|
|
|
+ ...baseNode.data,
|
|
|
+ fill: {
|
|
|
+ ...baseNode.data.fill,
|
|
|
+ fillType: "image",
|
|
|
+ imageUrl: dataUrl,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ size: {
|
|
|
+ width,
|
|
|
+ height,
|
|
|
+ },
|
|
|
+ position,
|
|
|
+ };
|
|
|
+ graph.addNode(node);
|
|
|
+ };
|
|
|
+ };
|
|
|
+ });
|
|
|
+ }
|
|
|
+ /**读取文本数据 */
|
|
|
+ if (item.types[0] === "text/plain") {
|
|
|
+ item.getType("text/plain").then((blob) => {
|
|
|
+ const reader = new FileReader();
|
|
|
+ reader.readAsText(blob);
|
|
|
+ reader.onload = function (event) {
|
|
|
+ const text = event.target?.result as string;
|
|
|
+ const exd = position ? { position } : {};
|
|
|
+ // 内部复制方法
|
|
|
+ if (text === " ") {
|
|
|
+ // 执行cell复制操作
|
|
|
+ // todo 多选设置位置
|
|
|
+ graph.paste({
|
|
|
+ offset: {
|
|
|
+ dx: 20,
|
|
|
+ dy: 20,
|
|
|
+ },
|
|
|
+ nodeProps: {
|
|
|
+ ...exd,
|
|
|
+ // @ts-ignore
|
|
|
+ tools: [
|
|
|
+ {
|
|
|
+ name: "contextmenu",
|
|
|
+ args: {
|
|
|
+ menu: nodeMenu,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ edgeProps: {
|
|
|
+ ...exd,
|
|
|
+ // @ts-ignore
|
|
|
+ tools: [
|
|
|
+ {
|
|
|
+ name: "contextmenu",
|
|
|
+ args: {
|
|
|
+ menu: edgeMenu,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ useLocalStorage: true,
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ // 宽度最低100 最高300 高度等于行高*行数
|
|
|
+ const width =
|
|
|
+ text.length * 15 >= 300
|
|
|
+ ? 300
|
|
|
+ : text.length * 15 < 100
|
|
|
+ ? 100
|
|
|
+ : text.length * 15;
|
|
|
+ const height = text.split("\n").length * 20;
|
|
|
+ const node = {
|
|
|
+ ...Text.node,
|
|
|
+ data: {
|
|
|
+ ...Text.node.data,
|
|
|
+ label: text,
|
|
|
+ },
|
|
|
+ position,
|
|
|
+ size: {
|
|
|
+ width,
|
|
|
+ height,
|
|
|
+ },
|
|
|
+ tools: [
|
|
|
+ {
|
|
|
+ name: "contextmenu",
|
|
|
+ args: {
|
|
|
+ menu: nodeMenu,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ };
|
|
|
+ // 插入文本
|
|
|
+ graph.addNode(node);
|
|
|
+ }
|
|
|
+ };
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * 执行拷贝
|
|
|
+ * @param graph
|
|
|
+ */
|
|
|
+const handleCopy = (graph: Graph) => {
|
|
|
+ graph.copy(graph.getSelectedCells(), { useLocalStorage: true, deep: false });
|
|
|
+ navigator.clipboard.writeText(" ");
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * 执行复用
|
|
|
+ * @param graph
|
|
|
+ */
|
|
|
+const handleDuplicate = (graph: Graph) => {
|
|
|
+ handleCopy(graph);
|
|
|
+ graph.paste({
|
|
|
+ offset: {
|
|
|
+ dx: 20,
|
|
|
+ dy: 20,
|
|
|
+ },
|
|
|
+ nodeProps: {
|
|
|
+ // @ts-ignore
|
|
|
+ tools: [
|
|
|
+ {
|
|
|
+ name: "contextmenu",
|
|
|
+ args: {
|
|
|
+ menu: nodeMenu,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ edgeProps: {
|
|
|
+ // @ts-ignore
|
|
|
+ tools: [
|
|
|
+ {
|
|
|
+ name: "contextmenu",
|
|
|
+ args: {
|
|
|
+ menu: edgeMenu,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ useLocalStorage: true,
|
|
|
+ });
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * 创建边线
|
|
|
+ * @param graph
|
|
|
+ */
|
|
|
+const handleCreateEdge = (graph: Graph) => {
|
|
|
+ let createEdgeMode = true;
|
|
|
+ let start: { x: number; y: number } | undefined;
|
|
|
+ let edge: Edge | undefined;
|
|
|
+
|
|
|
+ const graphRoot = document.querySelector(
|
|
|
+ "#graph-container"
|
|
|
+ ) as HTMLDivElement;
|
|
|
+ if (graphRoot) {
|
|
|
+ graphRoot.style.cursor = "crosshair";
|
|
|
+ }
|
|
|
+ const pageRoot = document.querySelector(
|
|
|
+ "#flow_canvas_container"
|
|
|
+ ) as HTMLDivElement;
|
|
|
+ if (pageRoot) {
|
|
|
+ pageRoot.style.cursor = "crosshair";
|
|
|
+ }
|
|
|
+ const handleCreate = ({ e, cell }: { e: MouseEvent; cell?: Cell }) => {
|
|
|
+ if (cell && !cell.getData()?.isPage) return;
|
|
|
+ start = { x: e.offsetX, y: e.offsetY };
|
|
|
+ graphRoot.style.cursor = "default";
|
|
|
+ pageRoot.style.cursor = "default";
|
|
|
+ };
|
|
|
+ const handleMove = ({
|
|
|
+ e,
|
|
|
+ x,
|
|
|
+ y,
|
|
|
+ node,
|
|
|
+ }: {
|
|
|
+ e: MouseEvent;
|
|
|
+ x: number;
|
|
|
+ y: number;
|
|
|
+ node?: Node;
|
|
|
+ }) => {
|
|
|
+ if (node && !node.getData()?.isPage) return;
|
|
|
+ if (!createEdgeMode || !start) return;
|
|
|
+
|
|
|
+ // 判断起始点是否移动了10px
|
|
|
+ if (Math.abs(x - start.x) > 10 || Math.abs(y - start.y) > 10) {
|
|
|
+ // 判断是否已创建边,创建了则修改目标位置
|
|
|
+ if (!edge) {
|
|
|
+ edge = graph.addEdge({
|
|
|
+ ...BaseEdge,
|
|
|
+ source: {
|
|
|
+ x: start.x,
|
|
|
+ y: start.y,
|
|
|
+ },
|
|
|
+ target: {
|
|
|
+ x: e.offsetX,
|
|
|
+ y: e.offsetY,
|
|
|
+ },
|
|
|
+ tools: [
|
|
|
+ {
|
|
|
+ name: "contextmenu",
|
|
|
+ args: {
|
|
|
+ menu: edgeMenu,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ edge.setTarget({
|
|
|
+ x: e.offsetX,
|
|
|
+ y: e.offsetY,
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ const handleCancel = () => {
|
|
|
+ createEdgeMode = false;
|
|
|
+ start = undefined;
|
|
|
+ graph.off("blank:mousedown", handleCreate);
|
|
|
+ graph.off("node:mousedown", handleCreate);
|
|
|
+ graph.off("blank:mousemove", handleMove);
|
|
|
+ graph.off("node:mousemove", handleMove);
|
|
|
+ };
|
|
|
+
|
|
|
+ graph.on("blank:mousedown", handleCreate);
|
|
|
+ graph.on("node:mousedown", handleCreate);
|
|
|
+ graph.on("blank:mousemove", handleMove);
|
|
|
+ graph.on("node:mousemove", handleMove);
|
|
|
+
|
|
|
+ graph.on("cell:click", ({ cell }) => {
|
|
|
+ if (cell && cell.getData()?.isPage) return;
|
|
|
+ handleCancel();
|
|
|
+ });
|
|
|
+ graph.on("cell:mouseup", handleCancel);
|
|
|
+ graph.on("blank:mouseup", handleCancel);
|
|
|
+ graph.on("cancel", handleCancel);
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * 插入文本节点
|
|
|
+ * @param graph
|
|
|
+ */
|
|
|
+export const handleInsertText = (graph: Graph) => {
|
|
|
+ let createEdgeMode = true;
|
|
|
+
|
|
|
+ const graphRoot = document.querySelector(
|
|
|
+ "#graph-container"
|
|
|
+ ) as HTMLDivElement;
|
|
|
+ if (graphRoot) {
|
|
|
+ graphRoot.style.cursor = "crosshair";
|
|
|
+ }
|
|
|
+ const pageRoot = document.querySelector(
|
|
|
+ "#flow_canvas_container"
|
|
|
+ ) as HTMLDivElement;
|
|
|
+ if (pageRoot) {
|
|
|
+ pageRoot.style.cursor = "crosshair";
|
|
|
+ }
|
|
|
+ const handleCreate = ({ e, cell }: { e: MouseEvent; cell?: Cell }) => {
|
|
|
+ if (cell && !cell.getData()?.isPage) {
|
|
|
+ handleCancel();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if(createEdgeMode) {
|
|
|
+ const node = {
|
|
|
+ ...Text.node,
|
|
|
+ position: {
|
|
|
+ x: e.offsetX,
|
|
|
+ y: e.offsetY
|
|
|
+ },
|
|
|
+ tools: [
|
|
|
+ {
|
|
|
+ name: "contextmenu",
|
|
|
+ args: {
|
|
|
+ menu: nodeMenu,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ };
|
|
|
+ graph.addNode(node);
|
|
|
+ handleCancel();
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ const handleCancel = () => {
|
|
|
+ createEdgeMode = false;
|
|
|
+ graph.off("blank:click", handleCreate);
|
|
|
+ graph.off("node:click", handleCreate);
|
|
|
+ graphRoot.style.cursor = "default";
|
|
|
+ pageRoot.style.cursor = "default";
|
|
|
+ };
|
|
|
+
|
|
|
+ graph.on("blank:click", handleCreate);
|
|
|
+ graph.on("node:click", handleCreate);
|
|
|
+
|
|
|
+ graph.on("cancel", handleCancel);
|
|
|
+};
|
|
|
+
|
|
|
+export const menuHander = {
|
|
|
+ /**复制 */
|
|
|
+ copy(tool: ContextMenuTool) {
|
|
|
+ tool.graph.select(tool.cell);
|
|
|
+ handleCopy(tool.graph);
|
|
|
+ },
|
|
|
+ /**剪切 */
|
|
|
+ cut(tool: ContextMenuTool) {
|
|
|
+ tool.graph.select(tool.cell);
|
|
|
+ tool.graph.cut(tool.graph.getSelectedCells(), {
|
|
|
+ useLocalStorage: true,
|
|
|
+ deep: false,
|
|
|
+ });
|
|
|
+ navigator.clipboard.writeText(" ");
|
|
|
+ },
|
|
|
+ /**复用 */
|
|
|
+ duplicate(tool: ContextMenuTool) {
|
|
|
+ tool.graph.select(tool.cell);
|
|
|
+ handleDuplicate(tool.graph);
|
|
|
+ },
|
|
|
+ /**删除 */
|
|
|
+ delete(tool: ContextMenuTool) {
|
|
|
+ tool.graph.select(tool.cell);
|
|
|
+ tool.graph.removeCells(tool.graph.getSelectedCells());
|
|
|
+ },
|
|
|
+ /**粘贴 */
|
|
|
+ paste(tool: ContextMenuTool, e: MouseEvent) {
|
|
|
+ handlePaste(tool.graph, { x: e.offsetX, y: e.offsetY });
|
|
|
+ },
|
|
|
+ /**放大 */
|
|
|
+ zoomIn(tool: ContextMenuTool) {
|
|
|
+ const { sx } = tool.graph.scale();
|
|
|
+ tool.graph.zoomTo(sx + 0.1);
|
|
|
+ },
|
|
|
+ /**缩小 */
|
|
|
+ zoomOut(tool: ContextMenuTool) {
|
|
|
+ const { sx } = tool.graph.scale();
|
|
|
+ tool.graph.zoomTo(sx - 0.1);
|
|
|
+ },
|
|
|
+ /**重置视图 */
|
|
|
+ resetView(tool: ContextMenuTool) {
|
|
|
+ tool.graph.zoomToFit({});
|
|
|
+ },
|
|
|
+ // 全选
|
|
|
+ selectAll(tool: ContextMenuTool) {
|
|
|
+ tool.graph.getCells().forEach((cell) => {
|
|
|
+ if (!cell.getData()?.isPage) {
|
|
|
+ tool.graph.select(cell);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ },
|
|
|
+ // 创建连线
|
|
|
+ createLine(tool: ContextMenuTool) {
|
|
|
+ handleCreateEdge(tool.graph);
|
|
|
+ },
|
|
|
+ // 插入图片
|
|
|
+ insertImage() {},
|
|
|
+ // 上移一层
|
|
|
+ up(tool: ContextMenuTool, e: MouseEvent) {
|
|
|
+ tool.graph.select(tool.cell);
|
|
|
+ setCellZIndex("up", tool.graph.getSelectedCells());
|
|
|
+ },
|
|
|
+ // 下移一层
|
|
|
+ down(tool: ContextMenuTool, e: MouseEvent) {
|
|
|
+ tool.graph.select(tool.cell);
|
|
|
+ setCellZIndex("down", tool.graph.getSelectedCells());
|
|
|
+ },
|
|
|
+ // 置顶
|
|
|
+ top(tool: ContextMenuTool, e: MouseEvent) {
|
|
|
+ tool.graph.select(tool.cell);
|
|
|
+ setCellZIndex("top", tool.graph.getSelectedCells());
|
|
|
+ },
|
|
|
+ // 置底
|
|
|
+ bottom(tool: ContextMenuTool, e: MouseEvent) {
|
|
|
+ tool.graph.select(tool.cell);
|
|
|
+ setCellZIndex("bottom", tool.graph.getSelectedCells());
|
|
|
+ },
|
|
|
+ // 设置默认样式
|
|
|
+ defaultStyle(tool: ContextMenuTool, e: MouseEvent) {},
|
|
|
+ // 恢复默认样式
|
|
|
+ resetStyle(tool: ContextMenuTool, e: MouseEvent) {},
|
|
|
+ // 锁定
|
|
|
+ lock(tool: ContextMenuTool, e: MouseEvent) {
|
|
|
+ tool.cell.setData({
|
|
|
+ lock: true,
|
|
|
+ });
|
|
|
+ },
|
|
|
+ // 解锁
|
|
|
+ unlock(tool: ContextMenuTool, e: MouseEvent) {
|
|
|
+ tool.cell.setData({
|
|
|
+ lock: false,
|
|
|
+ });
|
|
|
+ },
|
|
|
+ // 导出图形
|
|
|
+ exportImage(tool: ContextMenuTool, e: MouseEvent) {
|
|
|
+ exportImage(tool.graph);
|
|
|
+ },
|
|
|
+ // 复制所选图形为图片
|
|
|
+ copyAsImage(tool: ContextMenuTool, e: MouseEvent) {
|
|
|
+ tool.graph.toPNG((dataUrl) => {
|
|
|
+ // base64转bolb
|
|
|
+ let arr = dataUrl.split(",");
|
|
|
+ let mime = arr[0].match(/:(.*?);/)?.[1];
|
|
|
+ let bstr = atob(arr[1]);
|
|
|
+ let n = bstr.length;
|
|
|
+ let u8arr = new Uint8Array(n);
|
|
|
+ while (n--) {
|
|
|
+ u8arr[n] = bstr.charCodeAt(n);
|
|
|
+ }
|
|
|
+ const bolb = new Blob([u8arr], { type: mime });
|
|
|
+ const data = [new ClipboardItem({ [bolb.type]: bolb })];
|
|
|
+ navigator.clipboard.write(data);
|
|
|
+ });
|
|
|
+ },
|
|
|
+ // 替换图形
|
|
|
+ replace(tool: ContextMenuTool, e: MouseEvent) {},
|
|
|
+};
|