index.tsx 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. import { CompressOutlined, ExpandOutlined, MinusOutlined, PlusOutlined, QuestionCircleFilled } from "@ant-design/icons";
  2. import { Button, ConfigProvider, Divider, Slider, Tooltip } from "antd";
  3. import React, { useEffect, useRef, useState } from "react";
  4. import { useFullscreen } from "ahooks";
  5. import { useModel } from "umi";
  6. import { MiniMap } from '@antv/x6-plugin-minimap';
  7. import insertCss from 'insert-css';
  8. insertCss(`
  9. .navigation-view {
  10. position: absolute;
  11. bottom: 32px;
  12. right: 32px;
  13. width: 330px;
  14. height: 210px;
  15. background-color: #fff;
  16. border: 1px solid #e9edf2;
  17. border-radius: 8px 8px 0 0;
  18. overflow: hidden;
  19. z-index: 1;
  20. box-shadow: 0 4px 10px 1px rgba(0, 0, 0, .1);
  21. }`);
  22. export default function Footer() {
  23. const [isFullscreen, { toggleFullscreen }] = useFullscreen(document.body);
  24. const navigationViewRef = useRef(null);
  25. const { graph, selectedCell } = useModel("graphModel");
  26. const [showNavigation, setShowNavigation] = useState(false);
  27. const [countCell, setCountCell] = useState(0);
  28. const [scale, setScale] = useState(100);
  29. useEffect(() => {
  30. if(!graph || !navigationViewRef.current) return;
  31. graph.use(
  32. new MiniMap({
  33. container: navigationViewRef.current,
  34. width: 330,
  35. height: 210,
  36. padding: 10,
  37. }),
  38. )
  39. }, [graph, navigationViewRef.current]);
  40. useEffect(() => {
  41. graph?.on('cell:change:*', () => {
  42. const count = graph?.getCells().length || 0;
  43. setCountCell(count > 0 ? count - 1 : 0)
  44. });
  45. graph?.on('scale', (scaleInfo) => {
  46. setScale(parseInt(scaleInfo.sx * 100 + ''));
  47. })
  48. }, [graph]);
  49. const handleZoom = (value: number) => {
  50. graph?.zoomTo(value / 100)
  51. }
  52. const handleZoomFit = () => {
  53. graph?.zoomToFit({ })
  54. }
  55. const handleOnChange = (value: number) => {
  56. setScale(value);
  57. handleZoom(value)
  58. }
  59. return (
  60. <ConfigProvider componentSize="small">
  61. <div className="h-24px bg-white flex justify-between items-center px-16px relative">
  62. <div className="footer-left"></div>
  63. <div className="footer-right flex items-center">
  64. <div>图形:{selectedCell?.length}/{countCell}</div>
  65. <Divider type="vertical" />
  66. <Tooltip title="模板">
  67. <Button type="text" icon={<i className="iconfont icon-buju" />} />
  68. </Tooltip>
  69. <Tooltip title={showNavigation ? "关闭视图导航" : "显示视图导航"}>
  70. <Button type="text" icon={<i className="iconfont icon-map" />} onClick={() => setShowNavigation(!showNavigation)}/>
  71. </Tooltip>
  72. <div className="navigation-view" style={{display: showNavigation ? 'block' : 'none'}} ref={navigationViewRef}></div>
  73. <Button type="text" icon={<MinusOutlined/>} onClick={() => handleZoom( scale - 2)}/>
  74. <Slider min={20} max={200} className="w-120px m-0 mx-8px" tooltip={{formatter: (val) => `${val}%`}} value={scale} onChange={handleOnChange}/>
  75. <Button type="text" icon={<PlusOutlined/>} onClick={() => handleZoom( scale + 2)}/>
  76. <Tooltip title="重置缩放">
  77. <div className="cursor-pointer mx-8px w-40px" onClick={handleZoomFit}>{scale}%</div>
  78. </Tooltip>
  79. {
  80. isFullscreen
  81. ? <Button type="text" icon={<CompressOutlined/>} onClick={toggleFullscreen}/>
  82. : <Button type="text" icon={<ExpandOutlined/>} onClick={toggleFullscreen}/>
  83. }
  84. <Divider type="vertical" />
  85. <Button type="text" icon={<QuestionCircleFilled/>} />
  86. </div>
  87. </div>
  88. </ConfigProvider>
  89. );
  90. }