Prechádzať zdrojové kódy

perf: 优化快捷键,新增快捷键帮助

liaojiaxing 3 mesiacov pred
rodič
commit
e49ac767c4

+ 27 - 17
apps/er-designer/src/models/erModel.tsx

@@ -68,10 +68,13 @@ export default function erModel() {
     useSessionStorageState("tabs-active-key");
   const [_relationActive, setRelationActive] =
     useSessionStorageState("relation-active");
-  const [tableActive, setTableActive] = useSessionStorageState<string>('table-active', {
+  const [tableActive, setTableActive] = useSessionStorageState<string>(
+    "table-active",
+    {
       defaultValue: "",
-      listenStorageChange: true
-    });
+      listenStorageChange: true,
+    }
+  );
 
   const timer = useRef<any>();
   const saveData = (info: ProjectInfo) => {
@@ -696,12 +699,14 @@ export default function erModel() {
    */
   const onClean = () => {
     setProject(
-      {
-        ...project,
-        tables: [],
-        relations: [],
-        topicAreas: [],
-        remarkInfos: [],
+      (project) => {
+        return {
+          ...project,
+          tables: [],
+          relations: [],
+          topicAreas: [],
+          remarkInfos: [],
+        };
       },
       true,
       true
@@ -709,7 +714,7 @@ export default function erModel() {
     graph?.clearCells();
   };
 
-  const [clipboardCache, setClipboardCache] = useState<any>(null);
+  const clipboardCache = useRef<any>(null);
 
   /**
    * 剪切
@@ -719,7 +724,7 @@ export default function erModel() {
     if (cells?.[0]?.isNode()) {
       const cell = cells[0];
       const data = cell.data;
-      setClipboardCache(data);
+      clipboardCache.current = data;
       // 表
       if (data?.isTable) {
         const childTableIds = project.tables
@@ -768,7 +773,8 @@ export default function erModel() {
     if (cells?.[0]?.isNode()) {
       const cell = cells[0];
       const data = cell.data;
-      setClipboardCache(data);
+      clipboardCache.current = data;
+      message.success("已复制");
     }
   };
 
@@ -776,8 +782,8 @@ export default function erModel() {
    * 粘贴
    */
   const onPaste = () => {
-    if (clipboardCache) {
-      const data = clipboardCache;
+    if (clipboardCache.current) {
+      const data = clipboardCache.current;
       // 表格
       if (data?.isTable) {
         const tableId = uuid();
@@ -785,6 +791,8 @@ export default function erModel() {
           ...data,
           table: {
             ...data.table,
+            schemaName: data.table.schemaName + '_copy',
+            aliasName: data.table.aliasName + 'Copy',
             id: tableId,
             style: {
               ...data.table.style,
@@ -796,7 +804,7 @@ export default function erModel() {
             return {
               ...item,
               id: uuid(),
-              parentBusinessTableId: tableId,
+              tableId,
             };
           }),
         };
@@ -810,6 +818,7 @@ export default function erModel() {
         const topicAreaId = uuid();
         const newTopicArea = {
           ...data,
+          name: data.name + '_copy',
           id: topicAreaId,
           style: {
             ...data.style,
@@ -827,6 +836,7 @@ export default function erModel() {
         const remarkId = uuid();
         const newRemark = {
           ...data,
+          name: data.name + '_copy',
           id: remarkId,
           style: {
             ...data.style,
@@ -921,7 +931,7 @@ export default function erModel() {
           setProjectInfo({
             ...state,
             coverImage: res?.result?.[0]?.id,
-          })
+          });
           setSaveTime(dayjs().format("YYYY-MM-DD HH:mm:ss"));
           message.success("保存成功");
         },
@@ -971,6 +981,6 @@ export default function erModel() {
     saveTime,
     onSave,
     tableActive,
-    setTableActive
+    setTableActive,
   };
 }

+ 52 - 11
apps/er-designer/src/pages/er/components/Menu.tsx

@@ -1,6 +1,14 @@
 import React, { useEffect, useState, useRef } from "react";
-import { Button, Dropdown, Input, Modal, Switch, InputNumber } from "antd";
-import type { DropDownProps, MenuProps } from "antd";
+import {
+  Button,
+  Dropdown,
+  Input,
+  Modal,
+  Switch,
+  InputNumber,
+  Drawer,
+} from "antd";
+import type { MenuProps } from "antd";
 import { useModel } from "umi";
 import { useFullscreen } from "ahooks";
 import AddModel from "@/components/AddModel";
@@ -27,8 +35,11 @@ export default function Menu() {
   const [isFullscreen, { toggleFullscreen }] = useFullscreen(document.body);
   const [openKey, setOpenKey] = useState("");
   const [open, setOpen] = useState(false);
+  const [openFastKey, setOpenFastKey] = useState(false);
   const [name, setName] = useState(project?.name);
-  const addModelRef = useRef<{ open: (type: TableType, folder: string) => void }>();
+  const addModelRef = useRef<{
+    open: (type: TableType, folder: string) => void;
+  }>();
 
   useEffect(() => {
     setName(project.name);
@@ -51,7 +62,7 @@ export default function Menu() {
 
   const handleChangeName = () => {
     name.trim() && setProject({ ...project, name });
-  }
+  };
 
   const handleZoom = (value: number) => {
     if (value < 0.2) {
@@ -323,12 +334,24 @@ export default function Menu() {
       label: "帮助",
       type: "group",
       children: [
-        { key: "1-1", label: "快捷键" },
+        { key: "1-1", label: "快捷键", onClick: () => setOpenFastKey(true) },
         { key: "1-2", label: "操作说明" },
       ],
     },
   ];
 
+  const fastKeyList = [
+    { key: "CTRL+Z", label: "撤销" },
+    { key: "CTRL+Y", label: "重做" },
+    { key: "CTRL+C", label: "复制" },
+    { key: "CTRL+V", label: "粘贴" },
+    { key: "CTRL+X", label: "剪切" },
+    { key: "DELETE", label: "删除" },
+    { key: "CTRL+UP", label: "放大" },
+    { key: "CTRL+DOWN", label: "缩小" },
+    { key: "CTRL+S", label: "保存" },
+  ];
+
   const handleOpenChange = (
     nextOpen: boolean,
     info: { source: "trigger" | "menu" },
@@ -351,6 +374,24 @@ export default function Menu() {
     <>
       <div className="flex-1 flex items-center">
         {contextHolder}
+        <Drawer
+          placement="right"
+          title="快捷键"
+          open={openFastKey}
+          onClose={() => setOpenFastKey(false)}
+        >
+          {fastKeyList.map((item, index) => {
+            return (
+              <div
+                key={index}
+                className="flex items-center leading-32px odd:bg-gray-100 p-x-8px"
+              >
+                <div className="flex-1 font-bold text-16px">{item.key}</div>
+                <div className="flex-1 text-right color-#666">{item.label}</div>
+              </div>
+            );
+          })}
+        </Drawer>
         <div className="logo h-48px m-l-12px">
           <svg className="icon h-48px w-48px" aria-hidden="true">
             <use xlinkHref="#icon-shujujianmo"></use>
@@ -366,11 +407,11 @@ export default function Menu() {
               onBlur={handleChangeName}
               onPressEnter={handleChangeName}
             />
-            {
-              saveTime && <div className="bg-#eee text-12px leading-20px color-#666 rounded-4px p-x-4px p-y-2px">
-              上次保存时间:{saveTime}
-            </div>
-            }
+            {saveTime && (
+              <div className="bg-#eee text-12px leading-20px color-#666 rounded-4px p-x-4px p-y-2px">
+                上次保存时间:{saveTime}
+              </div>
+            )}
           </div>
           <div className="flex">
             {menuData.map((item) => {
@@ -413,7 +454,7 @@ export default function Menu() {
           onChange={(value) => handleChangeSetting("tableWidth", value)}
         />
       </Modal>
-      <AddModel ref={addModelRef} onChange={handleAddAfter}/>
+      <AddModel ref={addModelRef} onChange={handleAddAfter} />
     </>
   );
 }

+ 0 - 1
package.json

@@ -59,7 +59,6 @@
     "antd": "^5.23.0",
     "axios": "^1.7.7",
     "dayjs": "^1.11.13",
-    "html2canvas": "^1.4.1",
     "insert-css": "^2.0.0",
     "lodash-es": "^4.17.21",
     "react-draggable": "^4.4.6",

+ 166 - 35
pnpm-lock.yaml

@@ -131,9 +131,6 @@ importers:
       dayjs:
         specifier: ^1.11.13
         version: 1.11.13
-      html2canvas:
-        specifier: ^1.4.1
-        version: 1.4.1
       insert-css:
         specifier: ^2.0.0
         version: 2.0.0
@@ -148,7 +145,7 @@ importers:
         version: 2.0.1(@codemirror/language@6.10.3)(@codemirror/state@6.4.1)(@codemirror/view@6.34.1)
       umi:
         specifier: ^4.3.18
-        version: 4.3.19(@babel/core@7.25.2)(@types/react@18.3.5)(eslint@8.57.0)(prettier@3.3.3)(react-dom@18.3.1)(react@18.3.1)(stylelint@14.16.1)(typescript@5.5.4)(webpack@5.94.0)
+        version: 4.3.19(@babel/core@7.25.2)(eslint@8.57.0)(prettier@3.3.3)(react-dom@18.3.1)(react@18.3.1)(stylelint@14.16.1)(typescript@5.5.4)(webpack@5.94.0)
       unocss:
         specifier: ^0.62.3
         version: 0.62.3(postcss@8.4.45)(vite@5.4.3)
@@ -2833,6 +2830,20 @@ packages:
       - react-dom
     dev: false
 
+  /@floating-ui/react-dom-interactions@0.3.1(react-dom@18.3.1)(react@18.3.1):
+    resolution: {integrity: sha512-tP2KEh7EHJr5hokSBHcPGojb+AorDNUf0NYfZGg/M+FsMvCOOsSEeEF0O1NDfETIzDnpbHnCs0DuvCFhSMSStg==}
+    deprecated: Package renamed to @floating-ui/react
+    dependencies:
+      '@floating-ui/react-dom': 0.6.3(react-dom@18.3.1)(react@18.3.1)
+      aria-hidden: 1.2.4
+      point-in-polygon: 1.1.0
+      use-isomorphic-layout-effect: 1.1.2(react@18.3.1)
+    transitivePeerDependencies:
+      - '@types/react'
+      - react
+      - react-dom
+    dev: false
+
   /@floating-ui/react-dom@0.6.3(@types/react@18.3.5)(react-dom@18.3.1)(react@18.3.1):
     resolution: {integrity: sha512-hC+pS5D6AgS2wWjbmSQ6UR6Kpy+drvWGJIri6e1EDGADTPsCaa4KzCgmCczHrQeInx9tqs81EyDmbKJYY2swKg==}
     peerDependencies:
@@ -2847,6 +2858,20 @@ packages:
       - '@types/react'
     dev: false
 
+  /@floating-ui/react-dom@0.6.3(react-dom@18.3.1)(react@18.3.1):
+    resolution: {integrity: sha512-hC+pS5D6AgS2wWjbmSQ6UR6Kpy+drvWGJIri6e1EDGADTPsCaa4KzCgmCczHrQeInx9tqs81EyDmbKJYY2swKg==}
+    peerDependencies:
+      react: '>=16.8.0'
+      react-dom: '>=16.8.0'
+    dependencies:
+      '@floating-ui/dom': 0.4.5
+      react: 18.3.1
+      react-dom: 18.3.1(react@18.3.1)
+      use-isomorphic-layout-effect: 1.1.2(react@18.3.1)
+    transitivePeerDependencies:
+      - '@types/react'
+    dev: false
+
   /@formatjs/intl-displaynames@1.2.10:
     resolution: {integrity: sha512-GROA2RP6+7Ouu0WnHFF78O5XIU7pBfI19WM1qm93l6MFWibUk67nCfVCK3VAYJkLy8L8ZxjkYT11VIAfvSz8wg==}
     dependencies:
@@ -5096,6 +5121,66 @@ packages:
       - webpack-plugin-serve
     dev: false
 
+  /@umijs/preset-umi@4.3.19(typescript@5.5.4)(webpack@5.94.0):
+    resolution: {integrity: sha512-un/1R06c92nv9uuTRRChDOu3wjFwUU4YhTvXnlvzzj5BmqLaUF/miHapvTn0CTviDVl0UTEL/wEZ8bmDnqTj1Q==}
+    dependencies:
+      '@iconify/utils': 2.1.1
+      '@svgr/core': 6.5.1
+      '@umijs/ast': 4.3.19
+      '@umijs/babel-preset-umi': 4.3.19
+      '@umijs/bundler-esbuild': 4.3.19
+      '@umijs/bundler-mako': 0.8.8
+      '@umijs/bundler-utils': 4.3.19
+      '@umijs/bundler-vite': 4.3.19(postcss@8.4.45)
+      '@umijs/bundler-webpack': 4.3.19(typescript@5.5.4)(webpack@5.94.0)
+      '@umijs/core': 4.3.19
+      '@umijs/did-you-know': 1.0.3
+      '@umijs/es-module-parser': 0.0.7
+      '@umijs/history': 5.3.1
+      '@umijs/mfsu': 4.3.19
+      '@umijs/plugin-run': 4.3.19
+      '@umijs/renderer-react': 4.3.19(react-dom@18.3.1)(react@18.3.1)
+      '@umijs/server': 4.3.19
+      '@umijs/ui': 3.0.1
+      '@umijs/utils': 4.3.19
+      '@umijs/zod2ts': 4.3.19
+      babel-plugin-dynamic-import-node: 2.3.3
+      babel-plugin-react-compiler: 0.0.0-experimental-c23de8d-20240515
+      click-to-react-component: 1.1.0(react-dom@18.3.1)(react@18.3.1)
+      core-js: 3.34.0
+      current-script-polyfill: 1.0.0
+      enhanced-resolve: 5.9.3
+      fast-glob: 3.2.12
+      html-webpack-plugin: 5.5.0(webpack@5.94.0)
+      less-plugin-resolve: 1.0.2
+      path-to-regexp: 1.7.0
+      postcss: 8.4.45
+      postcss-prefix-selector: 1.16.0(postcss@8.4.45)
+      react: 18.3.1
+      react-dom: 18.3.1(react@18.3.1)
+      react-router: 6.3.0(react@18.3.1)
+      react-router-dom: 6.3.0(react-dom@18.3.1)(react@18.3.1)
+      regenerator-runtime: 0.13.11
+    transitivePeerDependencies:
+      - '@types/node'
+      - '@types/react'
+      - '@types/webpack'
+      - lightningcss
+      - rollup
+      - sass
+      - sockjs-client
+      - stylus
+      - sugarss
+      - supports-color
+      - terser
+      - type-fest
+      - typescript
+      - webpack
+      - webpack-dev-server
+      - webpack-hot-middleware
+      - webpack-plugin-serve
+    dev: false
+
   /@umijs/react-refresh-webpack-plugin@0.5.11(react-refresh@0.14.0)(webpack@5.94.0):
     resolution: {integrity: sha512-RtFvB+/GmjRhpHcqNgnw8iWZpTlxOnmNxi8eDcecxMmxmSgeDj25LV0jr4Q6rOhv3GTIfVGBhkwz+khGT5tfmg==}
     engines: {node: '>= 10.13'}
@@ -6185,11 +6270,6 @@ packages:
     resolution: {integrity: sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==}
     dev: false
 
-  /base64-arraybuffer@1.0.2:
-    resolution: {integrity: sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==}
-    engines: {node: '>= 0.6.0'}
-    dev: false
-
   /base64-js@1.5.1:
     resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
 
@@ -6633,6 +6713,20 @@ packages:
       - react-dom
     dev: false
 
+  /click-to-react-component@1.1.0(react-dom@18.3.1)(react@18.3.1):
+    resolution: {integrity: sha512-/DjZemufS1BkxyRgZL3r7HXVVOFRWVQi5Xd4EBnjxZMwrHEh0OlUVA2N9CjXkZ0x8zMf8dL1cKnnx+xUWUg4VA==}
+    peerDependencies:
+      react: '>=16.8.0'
+    dependencies:
+      '@floating-ui/react-dom-interactions': 0.3.1(react-dom@18.3.1)(react@18.3.1)
+      htm: 3.1.1
+      react: 18.3.1
+      react-merge-refs: 1.1.0
+    transitivePeerDependencies:
+      - '@types/react'
+      - react-dom
+    dev: false
+
   /client-only@0.0.1:
     resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==}
 
@@ -6949,12 +7043,6 @@ packages:
       postcss-selector-parser: 6.1.2
     dev: false
 
-  /css-line-break@2.1.0:
-    resolution: {integrity: sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==}
-    dependencies:
-      utrie: 1.0.2
-    dev: false
-
   /css-loader@6.7.1(webpack@5.94.0):
     resolution: {integrity: sha512-yB5CNFa14MbPJcomwNh3wLThtkZgcNyI2bNMRt8iE5Z8Vwl7f8vQXFAzn2HDOJvtDq2NTZBUGMSUNNyrv3/+cw==}
     engines: {node: '>= 12.13.0'}
@@ -9292,14 +9380,6 @@ packages:
       webpack: 5.94.0(esbuild@0.23.1)
     dev: false
 
-  /html2canvas@1.4.1:
-    resolution: {integrity: sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==}
-    engines: {node: '>=8.0.0'}
-    dependencies:
-      css-line-break: 2.1.0
-      text-segmentation: 1.0.3
-    dev: false
-
   /htmlparser2@6.1.0:
     resolution: {integrity: sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==}
     dependencies:
@@ -14008,12 +14088,6 @@ packages:
       minimatch: 3.1.2
     dev: false
 
-  /text-segmentation@1.0.3:
-    resolution: {integrity: sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==}
-    dependencies:
-      utrie: 1.0.2
-    dev: false
-
   /text-table@0.2.0:
     resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==}
 
@@ -14437,6 +14511,57 @@ packages:
       - webpack-plugin-serve
     dev: false
 
+  /umi@4.3.19(@babel/core@7.25.2)(eslint@8.57.0)(prettier@3.3.3)(react-dom@18.3.1)(react@18.3.1)(stylelint@14.16.1)(typescript@5.5.4)(webpack@5.94.0):
+    resolution: {integrity: sha512-RXIn3+NokatSuqB7jw1XXk0kQlKz8tRcIuv0gQ0CKAc/ltyseMnePEg7DgBFazvBS7s23oVIP8e4oTHHiLO+KA==}
+    engines: {node: '>=14'}
+    hasBin: true
+    dependencies:
+      '@babel/runtime': 7.23.6
+      '@umijs/bundler-utils': 4.3.19
+      '@umijs/bundler-webpack': 4.3.19(typescript@5.5.4)(webpack@5.94.0)
+      '@umijs/core': 4.3.19
+      '@umijs/lint': 4.3.19(eslint@8.57.0)(stylelint@14.16.1)(typescript@5.5.4)
+      '@umijs/preset-umi': 4.3.19(typescript@5.5.4)(webpack@5.94.0)
+      '@umijs/renderer-react': 4.3.19(react-dom@18.3.1)(react@18.3.1)
+      '@umijs/server': 4.3.19
+      '@umijs/test': 4.3.19(@babel/core@7.25.2)
+      '@umijs/utils': 4.3.19
+      prettier-plugin-organize-imports: 3.2.4(prettier@3.3.3)(typescript@5.5.4)
+      prettier-plugin-packagejson: 2.4.3(prettier@3.3.3)
+    transitivePeerDependencies:
+      - '@babel/core'
+      - '@types/node'
+      - '@types/react'
+      - '@types/webpack'
+      - '@volar/vue-language-plugin-pug'
+      - '@volar/vue-typescript'
+      - eslint
+      - jest
+      - lightningcss
+      - postcss-html
+      - postcss-jsx
+      - postcss-less
+      - postcss-markdown
+      - postcss-scss
+      - prettier
+      - react
+      - react-dom
+      - rollup
+      - sass
+      - sockjs-client
+      - stylelint
+      - stylus
+      - sugarss
+      - supports-color
+      - terser
+      - type-fest
+      - typescript
+      - webpack
+      - webpack-dev-server
+      - webpack-hot-middleware
+      - webpack-plugin-serve
+    dev: false
+
   /unbox-primitive@1.0.2:
     resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==}
     dependencies:
@@ -14575,6 +14700,18 @@ packages:
       react: 18.3.1
     dev: false
 
+  /use-isomorphic-layout-effect@1.1.2(react@18.3.1):
+    resolution: {integrity: sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==}
+    peerDependencies:
+      '@types/react': '*'
+      react: ^16.8.0 || ^17.0.0 || ^18.0.0
+    peerDependenciesMeta:
+      '@types/react':
+        optional: true
+    dependencies:
+      react: 18.3.1
+    dev: false
+
   /use-sync-external-store@1.2.0(react@18.3.1):
     resolution: {integrity: sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==}
     peerDependencies:
@@ -14631,12 +14768,6 @@ packages:
     engines: {node: '>= 0.4.0'}
     dev: false
 
-  /utrie@1.0.2:
-    resolution: {integrity: sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==}
-    dependencies:
-      base64-arraybuffer: 1.0.2
-    dev: false
-
   /v8-compile-cache-lib@3.0.1:
     resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==}
     dev: true