index.tsx 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. import {
  2. FileAddOutlined,
  3. LayoutOutlined,
  4. PlusCircleFilled,
  5. ZoomInOutlined,
  6. ZoomOutOutlined,
  7. } from "@ant-design/icons";
  8. import { Button, Divider, Dropdown, Tooltip, Popover } from "antd";
  9. import NodeMenu from "@/components/NodeMenu";
  10. import type { MenuProps } from "antd/lib";
  11. import { useRef, useEffect, useState } from "react";
  12. import { MiniMap } from "@antv/x6-plugin-minimap";
  13. import { useModel } from "umi";
  14. import { DagreLayout } from "@antv/layout";
  15. export default function index() {
  16. const minimapRef = useRef<HTMLDivElement>(null);
  17. const { graph } = useModel("flowModel");
  18. const [scale, setScale] = useState(100);
  19. const zoomItems: MenuProps["items"] = [
  20. { key: "1", label: "25%" },
  21. { key: "2", label: "75%" },
  22. { key: "3", label: "100%" },
  23. { key: "4", label: "200%" },
  24. { key: "5", type: "divider" },
  25. { key: "6", label: "自适应" },
  26. ];
  27. useEffect(() => {
  28. if (graph && minimapRef.current) {
  29. const minimap = new MiniMap({
  30. container: minimapRef.current,
  31. width: 144,
  32. height: 100,
  33. padding: 10,
  34. scalable: true,
  35. minScale: 0.2,
  36. maxScale: 2,
  37. graphOptions: {
  38. background: {
  39. color: "#e9ebf0",
  40. },
  41. },
  42. });
  43. graph.use(minimap);
  44. }
  45. }, [graph, minimapRef.current]);
  46. // 添加备注节点
  47. const handleAddNotice = () => {
  48. const { width = 100, height = 100 } = graph?.getGraphArea() || {};
  49. graph?.addNode({
  50. shape: "notice-node",
  51. zIndex: -1,
  52. position: {
  53. x: width / 2 - 150,
  54. y: height / 2 - 100,
  55. },
  56. data: {
  57. name: "",
  58. text: "",
  59. },
  60. });
  61. };
  62. useEffect(() => {
  63. graph?.on("scale", (scaleInfo) => {
  64. setScale(parseInt(scaleInfo.sx * 100 + ""));
  65. });
  66. }, [graph]);
  67. const handleZoom = (value: number) => {
  68. graph?.zoomTo(value / 100);
  69. };
  70. const handleZoomFit = () => {
  71. graph?.zoomToFit({});
  72. };
  73. // 设置缩放值
  74. const handleOnChange = (value: number) => {
  75. setScale(Math.round(value));
  76. handleZoom(value);
  77. };
  78. // 自动布局
  79. const handleAutoLayout = () => {
  80. // 获取全部元素
  81. const nodes = graph?.getNodes();
  82. const edges = graph?.getEdges();
  83. const dagreLayout = new DagreLayout({
  84. type: "dagre",
  85. rankdir: "LR",
  86. align: "UR",
  87. ranksep: 35,
  88. nodesep: 15,
  89. // begin: [0, 0]
  90. });
  91. const model = dagreLayout.layout({
  92. nodes,
  93. edges
  94. });
  95. // graph?.fromJSON(model);
  96. console.log( nodes, edges, model);
  97. };
  98. return (
  99. <div className="absolute left-32px bottom-32px z-2 flex gap-12px">
  100. <div className="w-120px h-40px rounded-12px bg-#fff box-shadow-sm flex items-center justify-between px-12px">
  101. <Button
  102. type="text"
  103. icon={<ZoomOutOutlined />}
  104. disabled={scale <= 50}
  105. onClick={() => handleOnChange(scale - 10)}
  106. />
  107. <Dropdown
  108. menu={{
  109. items: zoomItems,
  110. style: { width: "160px", transform: "translate(-65px, -10px)" },
  111. }}
  112. >
  113. <span>{scale}%</span>
  114. </Dropdown>
  115. <Button
  116. type="text"
  117. icon={<ZoomInOutlined />}
  118. disabled={scale >= 200}
  119. onClick={() => handleOnChange(scale + 10)}
  120. />
  121. </div>
  122. <div
  123. className="absolute w-144px h-100px bg-#e9ebf0 top--110px rounded-12px overflow-hidden"
  124. ref={minimapRef}
  125. ></div>
  126. <div className="w-60px h-40px rounded-12px bg-#fff box-shadow-sm flex items-center justify-between px-12px">
  127. <Button
  128. type="text"
  129. icon={<i className="iconfont icon-undo" />}
  130. disabled={true}
  131. />
  132. <Button type="text" icon={<i className="iconfont icon-redo" />} />
  133. </div>
  134. <div className="h-40px rounded-12px bg-#fff box-shadow-sm flex items-center justify-between px-12px">
  135. {/* <Popover
  136. content={<NodeMenu graph={graph} />}
  137. trigger="click"
  138. placement="top"
  139. arrow={false}
  140. >
  141. <Button type="text" icon={<PlusCircleFilled />}>
  142. 添加节点
  143. </Button>
  144. </Popover> */}
  145. <Tooltip title="添加文本">
  146. <Button
  147. type="text"
  148. icon={<FileAddOutlined />}
  149. onClick={handleAddNotice}
  150. />
  151. </Tooltip>
  152. <Divider type="vertical" />
  153. <Tooltip title="指针模式">
  154. <Button
  155. type="text"
  156. icon={<i className="iconfont icon-24gl-pointer" />}
  157. />
  158. </Tooltip>
  159. <Tooltip title="手模式">
  160. <Button type="text" icon={<i className="iconfont icon-shou1" />} />
  161. </Tooltip>
  162. <Divider type="vertical" />
  163. <Tooltip title="自动布局">
  164. <Button
  165. type="text"
  166. icon={<LayoutOutlined />}
  167. onClick={handleAutoLayout}
  168. />
  169. </Tooltip>
  170. </div>
  171. </div>
  172. );
  173. }