Explorar el Código

feat: 初始化画布、添加节点

liaojiaxing hace 6 meses
padre
commit
ccbd52a28e

+ 2 - 2
apps/er-designer/.umirc.ts

@@ -8,7 +8,7 @@ export default defineConfig({
     '/favicon.ico'
   ],
   styles: [
-    '//at.alicdn.com/t/c/font_4676747_4jkbw9dya3f.css'
+    '//at.alicdn.com/t/c/font_4767192_1ktdaesgr6i.css'
   ],
   metas: [
     { name: 'viewport', content: 'width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no' }
@@ -16,7 +16,7 @@ export default defineConfig({
   scripts: [
     // 字体加载
     // '//ajax.googleapis.com/ajax/libs/webfont/1.6.26/webfont.js'
-    '//at.alicdn.com/t/c/font_4676747_4jkbw9dya3f.js'
+    '//at.alicdn.com/t/c/font_4767192_1ktdaesgr6i.js'
   ],
   plugins: [
     require.resolve('@umijs/plugins/dist/unocss'),

+ 23 - 0
apps/er-designer/src/components/TableNode.tsx

@@ -0,0 +1,23 @@
+import React from "react";
+import { register } from "@antv/x6-react-shape";
+import { Graph, Node } from "@antv/x6";
+function TableNode({ node, graph }: { node: Node; graph: Graph }) {
+  return (
+    <div className="w-full h-full border border-1px border-solid border-#333 flex flex-col">
+      <div className="header border-b-solid border-b-1px border-b-#333 truncate bg-#fafafa text-center py-4px">
+        user_name(用户信息)
+      </div>
+      <div className="bg-#fafafa flex-1">
+        <div className="primary-key">主键信息</div>
+        <div className="field-info">字段列表</div>
+      </div>
+    </div>
+  );
+}
+
+register({
+  shape: "table-node",
+  component: TableNode,
+  width: 200,
+  height: 120,
+});

+ 73 - 0
apps/er-designer/src/models/erModel.tsx

@@ -0,0 +1,73 @@
+import { useEffect, useMemo, useRef, useState } from "react";
+import { Graph } from "@antv/x6";
+import { History } from "@antv/x6-plugin-history";
+import { Transform } from "@antv/x6-plugin-transform";
+import "@/components/TableNode";
+export default function erModel() {
+  const graphRef = useRef<Graph>();
+  const [graph, setGraph] = useState<Graph>();
+  const initGraph = (container: HTMLElement) => {
+    const instance = new Graph({
+      container,
+      width: document.documentElement.clientWidth,
+      height: document.documentElement.clientHeight,
+      autoResize: true,
+      async: false,
+      mousewheel: {
+        enabled: true,
+        modifiers: "ctrl",
+        minScale: 0.2,
+        maxScale: 2,
+      },
+      connecting: {
+        connectionPoint: "anchor",
+      },
+      grid: {
+        visible: true,
+      },
+      background: {
+        color: "#F2F7FA",
+      },
+    });
+
+    instance.use(new History());
+    instance.use(new Transform());
+
+    instance.addNode({
+      shape: "rect",
+      x: 100,
+      y: 100,
+      width: 100,
+      height: 100,
+      attrs: {
+        body: {
+          stroke: "#8f8f8f",
+          strokeWidth: 1,
+          fill: "#fff",
+        },
+      },
+    });
+
+    instance.addNode({
+      shape: "table-node",
+      x: 300,
+      y: 100,
+      width: 200,
+      height: 200,
+    })
+
+    graphRef.current = instance;
+  };
+
+  useEffect(() => {
+    if (graphRef.current) {
+      console.log(1111, graphRef.current)
+      setGraph(graphRef.current);
+    }
+  }, [graphRef.current]);
+
+  return {
+    initGraph,
+    graph,
+  };
+}

+ 94 - 0
apps/er-designer/src/pages/er/components/DataModel.tsx

@@ -0,0 +1,94 @@
+import React from "react";
+import { Input, Tree, TreeDataNode } from "antd";
+import { SearchOutlined } from "@ant-design/icons";
+export default function DataModel() {
+  const treeData: TreeDataNode[] = [
+    {
+      key: "0",
+      title: "制造业销售管理系统",
+      style: {
+        fontWeight: 'bold'
+      },
+      icon: (
+        <svg className="icon h-20px w-20px m-r-8px" aria-hidden="true">
+          <use xlinkHref="#icon-model"></use>
+        </svg>
+      ),
+      children: [
+        {
+          key: "0-1",
+          title: "表",
+          icon: <i className="iconfont icon-biaogebeifen"/>,
+          style: {
+            color: '#666'
+          },
+          children: [
+            {
+              key: "0-0-0",
+              title: "生产计划",
+            },
+          ],
+        },
+        {
+          key: "0-2",
+          title: "主题域",
+          icon: <i className="iconfont icon-zhutiyu"/>,
+          style: {
+            color: '#666'
+          },
+          children: [
+            {
+              key: "0-0-0",
+              title: "生产计划",
+            },
+          ],
+        },
+        {
+          key: "0-3",
+          title: "关系",
+          icon: <i className="iconfont icon-kinship-full"/>,
+          style: {
+            color: '#666'
+          },
+          children: [
+            {
+              key: "0-0-0",
+              title: "生产计划",
+            },
+          ],
+        },
+        {
+          key: "0-4",
+          title: "备注",
+          icon: <i className="iconfont icon-24_beizhu"/>,
+          style: {
+            color: '#666'
+          },
+          children: [
+            {
+              key: "0-0-0",
+              title: "生产计划",
+            },
+          ],
+        },
+      ],
+    },
+  ];
+
+  const setSearchValue = (value: string) => {
+    
+  };
+
+  return (
+    <div className="p-4px">
+      <Input
+        placeholder="请输入关键字"
+        onChange={(e) => setSearchValue(e.target.value)}
+        prefix={<SearchOutlined/>}
+        size="small"
+        className="mb-4px"
+      />
+      <Tree treeData={treeData} showIcon blockNode defaultExpandedKeys={["0"]} />
+    </div>
+  );
+}

+ 152 - 0
apps/er-designer/src/pages/er/components/Menu.tsx

@@ -0,0 +1,152 @@
+import React from "react";
+import { Button, Tooltip, Divider } from "antd";
+export default function Menu() {
+  return (
+    <div className="flex items-center leading-24px py-4px rel10tive">
+      {/* <div className="group">
+        <div className="flex items-center">
+          <Tooltip title="新建主表">
+            <div className="btn flex flex-col items-center cursor-pointer py-4px px-10px hover:bg-gray-200">
+              <svg className="icon h-32px w-24px" aria-hidden="true">
+                <use xlinkHref="#icon-jurassic_add-form"></use>
+              </svg>
+              <div className="text-14px color-#666">主表</div>
+            </div>
+          </Tooltip>
+          <Tooltip title="新建子表">
+            <div className="btn flex flex-col items-center cursor-pointer py-4px px-10px hover:bg-gray-200 opacity-50">
+              <svg className="icon h-32px w-24px" aria-hidden="true">
+                <use xlinkHref="#icon-jurassic_add-form"></use>
+              </svg>
+              <div className="text-14px color-#666">子表</div>
+            </div>
+          </Tooltip>
+        </div>
+        <div className="text-12px color-#666 text-center">
+          文件
+        </div>
+      </div>
+
+      <Divider type="vertical"/> */}
+
+      <div className="group">
+        <div className="flex items-center">
+          <Tooltip title="撤销上一步操作">
+            <div className="btn flex flex-col items-center cursor-pointer py-4px px-10px hover:bg-gray-200">
+              <svg className="icon h-32px w-24px" aria-hidden="true">
+                <use xlinkHref="#icon-undo"></use>
+              </svg>
+              <div className="text-14px color-#666">撤销</div>
+            </div>
+          </Tooltip>
+          <Tooltip title="恢复上一步操作">
+            <div className="btn flex flex-col items-center cursor-pointer py-4px px-10px hover:bg-gray-200 opacity-50">
+              <svg className="icon h-32px w-24px" aria-hidden="true">
+                <use xlinkHref="#icon-redo"></use>
+              </svg>
+              <div className="text-14px color-#666">恢复</div>
+            </div>
+          </Tooltip>
+        </div>
+        <div className="text-12px color-#666 text-center">
+          编辑
+        </div>
+      </div>
+
+      <div className="h-80px border-transparent border-r-1px border-solid border-r-#eee m-x-8px"></div>
+
+      <div className="group">
+        <div className="flex items-center">
+          <Tooltip title="创建数据表">
+            <div className="btn flex flex-col items-center cursor-pointer py-4px px-10px hover:bg-gray-200">
+              <svg className="icon h-32px w-24px" aria-hidden="true">
+                <use xlinkHref="#icon-biaoge"></use>
+              </svg>
+              <div className="text-14px color-#666">实体/表</div>
+            </div>
+          </Tooltip>
+          <Tooltip title="创建备注">
+            <div className="btn flex flex-col items-center cursor-pointer py-4px px-10px hover:bg-gray-200">
+              <svg className="icon h-32px w-24px" aria-hidden="true">
+                <use xlinkHref="#icon-beizhu"></use>
+              </svg>
+              <div className="text-14px color-#666">备注</div>
+            </div>
+          </Tooltip>
+          <Tooltip title="创建图框">
+            <div className="btn flex flex-col items-center cursor-pointer py-4px px-10px hover:bg-gray-200">
+              <svg className="icon h-32px w-24px" aria-hidden="true">
+                <use xlinkHref="#icon-ditukuangxuan"></use>
+              </svg>
+              <div className="text-14px color-#666">图框</div>
+            </div>
+          </Tooltip>
+          <div className="h-50px border-transparent border-r-1px border-solid border-r-#eee m-x-8px"></div>
+          <Tooltip title="创建一对一关系">
+            <div className="btn flex flex-col items-center cursor-pointer py-4px px-10px hover:bg-gray-200">
+              <svg className="icon h-32px w-24px" aria-hidden="true">
+                <use xlinkHref="#icon-zhujian"></use>
+              </svg>
+              <div className="text-14px color-#666">主键</div>
+            </div>
+          </Tooltip>
+          <Tooltip title="创建一对多关系">
+            <div className="btn flex flex-col items-center cursor-pointer py-4px px-10px hover:bg-gray-200">
+              <svg className="icon h-32px w-24px" aria-hidden="true">
+                <use xlinkHref="#icon-feizhujian"></use>
+              </svg>
+              <div className="text-14px color-#666">非主键</div>
+            </div>
+          </Tooltip>
+          <Tooltip title="创建多对多关系">
+            <div className="btn flex flex-col items-center cursor-pointer py-4px px-10px hover:bg-gray-200">
+              <svg className="icon h-32px w-24px" aria-hidden="true">
+                <use xlinkHref="#icon-duoduiduo"></use>
+              </svg>
+              <div className="text-14px color-#666">多对多</div>
+            </div>
+          </Tooltip>
+        </div>
+        <div className="text-12px color-#666 text-center">
+          实体与关系
+        </div>
+      </div>
+
+      <div className="h-80px border-transparent border-r-1px border-solid border-r-#eee m-x-8px"></div>
+
+      <div className="group">
+        <div className="flex items-center">
+          <Tooltip title="显示">
+            <div className="btn flex flex-col items-center cursor-pointer py-4px px-10px hover:bg-gray-200">
+              <svg className="icon h-32px w-24px" aria-hidden="true">
+                <use xlinkHref="#icon-shitibiao-"></use>
+              </svg>
+              <div className="text-14px color-#666">显示</div>
+            </div>
+          </Tooltip>
+          <Tooltip title="大小">
+            <div className="btn flex flex-col items-center cursor-pointer py-4px px-10px hover:bg-gray-200">
+              <svg className="icon h-32px w-24px" aria-hidden="true">
+                <use xlinkHref="#icon-daxiao"></use>
+              </svg>
+              <div className="text-14px color-#666">大小</div>
+            </div>
+          </Tooltip>
+          <Tooltip title="样式">
+            <div className="btn flex flex-col items-center cursor-pointer py-4px px-10px hover:bg-gray-200">
+              <svg className="icon h-32px w-24px" aria-hidden="true">
+                <use xlinkHref="#icon-yangshi"></use>
+              </svg>
+              <div className="text-14px color-#666">样式</div>
+            </div>
+          </Tooltip>
+        </div>
+        <div className="text-12px color-#666 text-center">
+          显示样式
+        </div>
+      </div>
+
+      <div className="h-80px border-transparent border-r-1px border-solid border-r-#eee m-x-8px"></div>
+    </div>
+  );
+}

+ 27 - 0
apps/er-designer/src/pages/er/components/Navigator.tsx

@@ -0,0 +1,27 @@
+import React, { useEffect } from "react";
+import { useModel } from "umi";
+import { MiniMap } from "@antv/x6-plugin-minimap";
+export default function Navigator() {
+  const { graph } = useModel("erModel");
+  const mapRef = React.useRef<HTMLDivElement>(null);
+
+  useEffect(() => {
+    console.log("graph", graph);
+    if (graph && mapRef.current) {
+      graph.use(
+        new MiniMap({
+          container: mapRef.current,
+          width: 330,
+          height: 210,
+          padding: 10,
+        })
+      );
+    }
+  }, [graph, mapRef.current]);
+
+  return (
+    <div>
+      <div ref={mapRef}></div>
+    </div>
+  );
+}

+ 36 - 66
apps/er-designer/src/pages/er/index.tsx

@@ -1,77 +1,47 @@
-import React from 'react';
-import { LaptopOutlined, NotificationOutlined, UserOutlined } from '@ant-design/icons';
-import type { MenuProps } from 'antd';
-import { Breadcrumb, Layout, Menu, theme } from 'antd';
+import React, { useEffect } from "react";
+import { Layout } from "antd";
+import Menu from "./components/Menu";
+import DataModel from "./components/DataModel";
+import Navigator from "./components/Navigator";
+import { useModel } from "umi";
 
 const { Header, Content, Sider } = Layout;
 
-const items1: MenuProps['items'] = ['1', '2', '3'].map((key) => ({
-  key,
-  label: `nav ${key}`,
-}));
-
-const items2: MenuProps['items'] = [UserOutlined, LaptopOutlined, NotificationOutlined].map(
-  (icon, index) => {
-    const key = String(index + 1);
-
-    return {
-      key: `sub${key}`,
-      icon: React.createElement(icon),
-      label: `subnav ${key}`,
-
-      children: new Array(4).fill(null).map((_, j) => {
-        const subKey = index * 4 + j + 1;
-        return {
-          key: subKey,
-          label: `option${subKey}`,
-        };
-      }),
-    };
-  },
-);
-
 const App: React.FC = () => {
-  const {
-    token: { colorBgContainer, borderRadiusLG },
-  } = theme.useToken();
+  const containerRef = React.useRef(null);
+  const { initGraph } = useModel("erModel");
+
+  useEffect(() => {
+    if (containerRef.current) {
+      initGraph?.(containerRef.current);
+    }
+  }, [containerRef.current]);
 
   return (
-    <Layout>
-      <Header style={{ display: 'flex', alignItems: 'center' }}>
-        <div className="demo-logo" />
-        <Menu
-          theme="dark"
-          mode="horizontal"
-          defaultSelectedKeys={['2']}
-          items={items1}
-          style={{ flex: 1, minWidth: 0 }}
-        />
+    <Layout className="h-100vh">
+      <Header className="bg-white h-100px border-b-1px border-b-solid border-b-gray-200">
+        <Menu />
       </Header>
       <Layout>
-        <Sider width={200} style={{ background: colorBgContainer }}>
-          <Menu
-            mode="inline"
-            defaultSelectedKeys={['1']}
-            defaultOpenKeys={['sub1']}
-            style={{ height: '100%', borderRight: 0 }}
-            items={items2}
-          />
+        <Sider
+          width={360}
+          style={{ background: "#fff", borderRight: "1px solid #f0f0f0" }}
+        >
+          <div className="flex flex-col h-full">
+            <div className="flex-1">
+              <div className="header px-10px py-4px bg-#ddd">数据模型</div>
+              <DataModel />
+            </div>
+
+            <div className="flex-1 bg-#eee">
+              <div className="header px-10px py-4px bg-#ddd">导航</div>
+              <Navigator />
+            </div>
+          </div>
         </Sider>
-        <Layout style={{ padding: '0 24px 24px' }}>
-          <Breadcrumb
-            items={[{ title: 'Home' }, { title: 'List' }, { title: 'App' }]}
-            style={{ margin: '16px 0' }}
-          />
-          <Content
-            style={{
-              padding: 24,
-              margin: 0,
-              minHeight: 280,
-              background: colorBgContainer,
-              borderRadius: borderRadiusLG,
-            }}
-          >
-            Content
+        <Layout>
+          <Content>
+            <div id="graph-container" ref={containerRef}></div>
           </Content>
         </Layout>
       </Layout>
@@ -79,4 +49,4 @@ const App: React.FC = () => {
   );
 };
 
-export default App;
+export default App;