liaojiaxing 3 месяцев назад
Родитель
Сommit
7bee73ff04

+ 84 - 60
apps/er-designer/src/models/erModel.tsx

@@ -43,14 +43,19 @@ export default function erModel() {
   const [graph, setGraph] = useState<Graph>();
   const historyRef = useRef<ProjectInfo[]>([]);
   const activeIndex = useRef(0);
-  const [_isFullscreen, { enterFullscreen, exitFullscreen }] = useFullscreen(document.body);
-  const [playModeEnable, setPlayModeEnable] = useSessionStorageState('playModeEnable', {
-    defaultValue: false,
-    listenStorageChange: true
-  });
+  const [_isFullscreen, { enterFullscreen, exitFullscreen }] = useFullscreen(
+    document.body
+  );
+  const [playModeEnable, setPlayModeEnable] = useSessionStorageState(
+    "playModeEnable",
+    {
+      defaultValue: false,
+      listenStorageChange: true,
+    }
+  );
   const [saveTime, setSaveTime] = useState<string>();
   const [project, setProjectInfo] = useState<ProjectInfo>({
-    id: "1",
+    id: "9bb35adc-0b2f-450f-b137-de39ce06fe9f",
     name: "新建模型",
     directory: "",
     type: 3,
@@ -64,7 +69,7 @@ export default function erModel() {
     remarkInfos: [],
     todos: [],
     setting: {
-      ...DEFAULT_SETTING
+      ...DEFAULT_SETTING,
     },
   });
 
@@ -73,6 +78,8 @@ export default function erModel() {
   const [_relationActive, setRelationActive] =
     useSessionStorageState("relation-active");
 
+  const timer = useRef<any>();
+
   /**
    * 统一修改数据
    * @param info 模型信息
@@ -120,10 +127,14 @@ export default function erModel() {
     }
 
     // 提交服务器
-    if(!isInit) {
-      SaveDataModel(project);
-      // 格式化当前时间
-      setSaveTime(dayjs().format('YYYY-MM-DD HH:mm:ss'));
+    if (!isInit) {
+      // 清除定时器
+      clearTimeout(timer.current);
+      timer.current = setTimeout(() => {
+        SaveDataModel(project);
+        // 格式化当前时间
+        setSaveTime(dayjs().format("YYYY-MM-DD HH:mm:ss"));
+      }, 500);
     }
   };
 
@@ -139,7 +150,11 @@ export default function erModel() {
    * 初始化画布
    * @param container
    */
-  const initGraph = (container: HTMLElement, width?: number, height?: number) => {
+  const initGraph = (
+    container: HTMLElement,
+    width?: number,
+    height?: number
+  ) => {
     const instance = new Graph({
       container,
       width: width || document.documentElement.clientWidth,
@@ -256,43 +271,46 @@ export default function erModel() {
       }
     );
 
-    instance.on("node:moved", throttle((args) => {
-      console.log('moved', args);
-      const data = args.node.data;
-      if(data.isTable) {
-        updateTable({
-          ...data,
-          table: {
-            ...data.table,
+    instance.on(
+      "node:moved",
+      throttle((args) => {
+        console.log("moved", args);
+        const data = args.node.data;
+        if (data.isTable) {
+          updateTable({
+            ...data,
+            table: {
+              ...data.table,
+              style: {
+                ...data.table.style,
+                x: args.x,
+                y: args.y,
+              },
+            },
+          });
+        }
+        if (data.isTopicArea) {
+          updateTopicArea({
+            ...data,
             style: {
-              ...data.table.style,
+              ...data.style,
               x: args.x,
               y: args.y,
-            }
-          }
-        })
-      }
-      if(data.isTopicArea) {
-        updateTopicArea({
-          ...data,
-          style: {
-            ...data.style,
-            x: args.x,
-            y: args.y,
-          }
-        })
-      }
-      if(data.isRemark) {
-        updateRemark({
-          ...data,
-          style: {
-            ...data.style,
-            x: args.x,
-            y: args.y,
-          }
-        })
-      }
-    }, 500));
+            },
+          });
+        }
+        if (data.isRemark) {
+          updateRemark({
+            ...data,
+            style: {
+              ...data.style,
+              x: args.x,
+              y: args.y,
+            },
+          });
+        }
+      }, 500)
+    );
 
     instance.bindKey("ctrl+z", onUndo);
     instance.bindKey("ctrl+y", onRedo);
@@ -303,7 +321,7 @@ export default function erModel() {
     instance.bindKey("ctrl+down", () => {
       const scale = instance.zoom() - 0.1;
       instance.zoomTo(scale < 0.2 ? 0.2 : scale);
-    })
+    });
     instance.bindKey("ctrl+up", () => {
       const scale = instance.zoom() + 0.1;
       instance.zoomTo(scale > 2 ? 2 : scale);
@@ -351,7 +369,7 @@ export default function erModel() {
     const x = area?.center.x || 300;
     const y = area?.center.y || 300;
     // 数据表类型动态路由传参
-    const newTable = createTable(project.type || 3, parentId);
+    const newTable = createTable(project.type || 3, project.id,  parentId);
     newTable.table.style.x = x;
     newTable.table.style.y = y;
 
@@ -365,8 +383,8 @@ export default function erModel() {
     }
     setProject({
       ...project,
-      tables: list
-    })
+      tables: list,
+    });
     setTabActiveKey("1");
   };
 
@@ -384,7 +402,7 @@ export default function erModel() {
           }
           return item;
         }),
-      }
+      };
     });
   };
 
@@ -449,7 +467,7 @@ export default function erModel() {
           }
           return item;
         }),
-      }
+      };
     });
   };
 
@@ -493,10 +511,12 @@ export default function erModel() {
    * 修改备注
    */
   const updateRemark = (remark: RemarkInfo) => {
-    console.log(remark)
+    console.log(remark);
     setProject((state) => ({
       ...(state || {}),
-      remarkInfos: state.remarkInfos.map((item) => item.id === remark.id ? remark : item),
+      remarkInfos: state.remarkInfos.map((item) =>
+        item.id === remark.id ? remark : item
+      ),
     }));
   };
 
@@ -580,9 +600,9 @@ export default function erModel() {
       foreignTable: target.tableId,
       relationType: 1,
       style: {
-        color: '#333',
+        color: "#333",
         lineType: RelationLineType.Solid,
-        width: 1
+        width: 1,
       },
     };
     setProject((state) => {
@@ -687,7 +707,9 @@ export default function erModel() {
       if (data?.isRemark) {
         setProject({
           ...project,
-          remarkInfos: project.remarkInfos.filter((item) => item.id !== cell.id),
+          remarkInfos: project.remarkInfos.filter(
+            (item) => item.id !== cell.id
+          ),
         });
       }
     }
@@ -799,7 +821,9 @@ export default function erModel() {
       if (data?.isRemark) {
         setProject({
           ...project,
-          remarkInfos: project.remarkInfos.filter((item) => item.id !== cell[0].id),
+          remarkInfos: project.remarkInfos.filter(
+            (item) => item.id !== cell[0].id
+          ),
         });
       }
     }
@@ -814,7 +838,7 @@ export default function erModel() {
     setTimeout(() => {
       graphRef.current?.centerContent();
     }, 100);
-  }; 
+  };
 
   /**
    * 退出演示模式
@@ -855,6 +879,6 @@ export default function erModel() {
     playModeEnable,
     setPlayModeEnable,
     exitPlayMode,
-    saveTime
+    saveTime,
   };
 }

+ 5 - 1
apps/er-designer/src/pages/detail/index.tsx

@@ -133,6 +133,10 @@ export default function index() {
     exitFullscreen();
   }
 
+  const handleEnterEdit = () => {
+    history.push(`/er/${project.id}`)
+  }
+
   const extra = (
     <div className="flex gap-12px">
       <a>
@@ -143,7 +147,7 @@ export default function index() {
         <i className="iconfont icon-bianji text-12px" />
         修改
       </a>
-      <a onClick={() => history.push(`/er/${project.id}`)}>
+      <a onClick={() => handleEnterEdit}>
         <i className="iconfont icon-bianji text-12px" />
         进入编辑
       </a>

+ 37 - 14
apps/er-designer/src/pages/er/components/ColumnItem.tsx

@@ -16,6 +16,7 @@ import { DATA_TYPE_OPTIONS } from "@/constants";
 import { useSortable } from "@dnd-kit/sortable";
 import { CSS } from "@dnd-kit/utilities";
 import LangInput from "@/components/LangInput";
+import { validateColumnCode } from "@/utils/validator";
 
 export default function ColumnItem({
   column,
@@ -40,6 +41,15 @@ export default function ColumnItem({
     transition,
   };
 
+  const validMsg = (val?: string) => {
+    if (!val) return "编码不能为空";
+    if (val.length >= 50) {
+      return "编码长度不能超过50";
+    }
+    const regex = /^[a-z][a-z0-9_]*$/;
+    if (!regex.test(val)) return "编码只能包含字母和数字, 必须小写字母开头!";
+  };
+
   return (
     <div
       key={column.id}
@@ -55,13 +65,21 @@ export default function ColumnItem({
         {...listeners}
       />
       <Tooltip title="字段编码">
-        <Input
-          placeholder="编码"
-          defaultValue={column.schemaName}
-          className="flex-1"
-          disabled={column.isPreDefined}
-          onChange={(e) => onChange("schemaName", e.target.value)}
-        />
+        <Popover
+          title={
+            <span className="text-red">{validMsg(column.schemaName)}</span>
+          }
+          trigger={["focus"]}
+        >
+          <Input
+            placeholder="编码"
+            defaultValue={column.schemaName}
+            className="flex-1"
+            disabled={column.isPreDefined}
+            onChange={(e) => onChange("schemaName", e.target.value)}
+            status={validMsg(column.schemaName) ? "error" : ''}
+          />
+        </Popover>
       </Tooltip>
       <Tooltip title="字段类型">
         <Select
@@ -85,11 +103,13 @@ export default function ColumnItem({
             flex-none 
             text-center 
             leading-32px 
-            cursor-pointer 
             hover:bg-#ddd"
-          style={
-            column.isRequired ? { background: "#1677ff", color: "#fff" } : {}
-          }
+          style={{
+            ...(column.isRequired
+              ? { background: "#1677ff", color: "#fff" }
+              : {}),
+            cursor: column.isPreDefined ? "not-allowed" : "pointer",
+          }}
           onClick={() =>
             !column.isPreDefined && onChange("isRequired", !column.isRequired)
           }
@@ -110,9 +130,12 @@ export default function ColumnItem({
             leading-32px 
             cursor-pointer 
             hover:bg-#ddd"
-          style={
-            column.isUnique ? { background: "#1677ff", color: "#fff" } : {}
-          }
+          style={{
+            ...(column.isUnique
+              ? { background: "#1677ff", color: "#fff" }
+              : {}),
+            cursor: column.isPreDefined ? "not-allowed" : "pointer",
+          }}
           onClick={() =>
             !column.isPreDefined && onChange("isUnique", !column.isUnique)
           }

+ 34 - 10
apps/er-designer/src/pages/er/components/TableItem.tsx

@@ -19,7 +19,11 @@ import { createColumn } from "@/utils";
 import { useModel } from "umi";
 import { DndContext } from "@dnd-kit/core";
 import type { DragEndEvent } from "@dnd-kit/core";
-import { SortableContext, arrayMove, verticalListSortingStrategy } from "@dnd-kit/sortable";
+import {
+  SortableContext,
+  arrayMove,
+  verticalListSortingStrategy,
+} from "@dnd-kit/sortable";
 import { restrictToParentElement } from "@dnd-kit/modifiers";
 import ColumnItem from "./ColumnItem";
 import LangInput from "@/components/LangInput";
@@ -96,10 +100,12 @@ export default function TableItem({
   const handleDragEnd = (dragItem: DragEndEvent) => {
     const { active, over } = dragItem;
     if (!active || !over) return; // 处理边界情况
-    
-    const activeIndex = tableColumnList.findIndex((item) => item.id === active.id);
+
+    const activeIndex = tableColumnList.findIndex(
+      (item) => item.id === active.id
+    );
     const overIndex = tableColumnList.findIndex((item) => item.id === over.id);
-    
+
     const newList = arrayMove(tableColumnList, activeIndex, overIndex);
     setList(newList);
     onChange({
@@ -107,7 +113,7 @@ export default function TableItem({
       tableColumnList: newList,
       isTable: true,
     });
-  }
+  };
 
   return (
     <div
@@ -150,10 +156,20 @@ export default function TableItem({
               <div className="w-200px" onClick={(e) => e.stopPropagation()}>
                 <Form layout="vertical">
                   <Form.Item label="表名称" name="pkName">
-                    <LangInput value="" onChange={() => {}}/>
+                    <LangInput
+                      value={table.langNameList}
+                      onChange={(lang) => {
+                        handleTableChange("langNameList", lang);
+                      }}
+                    />
                   </Form.Item>
                   <Form.Item label="描述" name="pkName">
-                    <LangInputTextarea value="" onChange={() => {}}/>
+                    <LangInputTextarea
+                      value={table.langDescriptionList}
+                      onChange={(lang) => {
+                        handleTableChange("langDescriptionList", lang);
+                      }}
+                    />
                   </Form.Item>
                 </Form>
               </div>
@@ -259,14 +275,22 @@ export default function TableItem({
 
           <div className="column-content border-solid border-1px border-#e4e4e4 border-x-none p-y-10px">
             {/* 字段内容 */}
-            <DndContext onDragEnd={handleDragEnd} modifiers={[restrictToParentElement]}>
-              <SortableContext items={list.map(item => item.id)} strategy={verticalListSortingStrategy}>
+            <DndContext
+              onDragEnd={handleDragEnd}
+              modifiers={[restrictToParentElement]}
+            >
+              <SortableContext
+                items={list.map((item) => item.id)}
+                strategy={verticalListSortingStrategy}
+              >
                 {list.map((column, index) => {
                   return (
                     <ColumnItem
                       key={column.id}
                       column={column}
-                      onChange={(key, val) => handleChangeColumn(index, key, val)}
+                      onChange={(key, val) =>
+                        handleChangeColumn(index, key, val)
+                      }
                       onDelete={handleDeleteColumn}
                     />
                   );

+ 2 - 2
apps/er-designer/src/type.d.ts

@@ -12,7 +12,7 @@ export interface ColumnItem {
   isUnique?: boolean;
   displayOrder?: number;
   // 表id
-  businessTableId?: string;
+  tableId?: string;
   // 描述
   memo?: string;
   alignment?: string;
@@ -131,7 +131,7 @@ export interface RemarkInfo {
  * 表数据
  */
 export type TableItemType = {
-  table: ViewTable & {openSync: boolean; style: Record<string, any>},
+  table: ViewTable & {openSync: boolean; style: Record<string, any>, dataModelId: string},
   tableColumnList: ColumnItem[],
   isTable: boolean;
 };

+ 17 - 16
apps/er-designer/src/utils/index.ts

@@ -12,7 +12,7 @@ export function uuid() {
   });
 }
 
-export const createTable = (tableType: TableType, parentId?: string): TableItemType => {
+export const createTable = (tableType: TableType, dataModelId: string, parentId?: string): TableItemType => {
   const tableId = uuid();
   const tableColumnList: ColumnItem[] = [];
   if(tableType === TableType.FlowTable) {
@@ -33,6 +33,7 @@ export const createTable = (tableType: TableType, parentId?: string): TableItemT
       id: tableId,
       isDeleted: false,
       langName: "",
+      dataModelId,
       parentBusinessTableId: parentId || "",
       schemaName: "new_table",
       type: 1,
@@ -69,7 +70,7 @@ export const createColumn = (tableId?: string): ColumnItem => {
     isPreDefined: false,
     defaultValue: "",
     displayOrder: 0,
-    businessTableId: tableId || "",
+    tableId: tableId || "",
     memo: "",
     whereInputType: "",
     whereInputContent: "",
@@ -93,7 +94,7 @@ export const createBasePredefinedField = (tableId: string): ColumnItem[] => {
       type: DataType.Nvarchar,
       displayOrder: 1,
       maxLength: 50,
-      businessTableId: tableId,
+      tableId,
       isRequired: true,
       isUnique: false,
       isPreDefined: true,
@@ -109,7 +110,7 @@ export const createBasePredefinedField = (tableId: string): ColumnItem[] => {
       type: DataType.DateTime,
       displayOrder: 9001,
       maxLength: 0,
-      businessTableId: tableId,
+      tableId,
       isRequired: true,
       isUnique: false,
       isPreDefined: true,
@@ -126,7 +127,7 @@ export const createBasePredefinedField = (tableId: string): ColumnItem[] => {
       type: DataType.Nvarchar,
       displayOrder: 9002,
       maxLength: 50,
-      businessTableId: tableId,
+      tableId,
       isRequired: true,
       isUnique: false,
       isPreDefined: true,
@@ -143,7 +144,7 @@ export const createBasePredefinedField = (tableId: string): ColumnItem[] => {
       type: DataType.DateTime,
       displayOrder: 9003,
       maxLength: 0,
-      businessTableId: tableId,
+      tableId,
       isRequired: false,
       isUnique: false,
       isPreDefined: true,
@@ -160,7 +161,7 @@ export const createBasePredefinedField = (tableId: string): ColumnItem[] => {
       type: DataType.Nvarchar,
       displayOrder: 9004,
       maxLength: 50,
-      businessTableId: tableId,
+      tableId,
       isRequired: false,
       isUnique: false,
       isPreDefined: true,
@@ -177,7 +178,7 @@ export const createBasePredefinedField = (tableId: string): ColumnItem[] => {
       type: DataType.DateTime,
       displayOrder: 9005,
       maxLength: 0,
-      businessTableId: tableId,
+      tableId,
       isRequired: false,
       isUnique: false,
       isPreDefined: true,
@@ -193,7 +194,7 @@ export const createBasePredefinedField = (tableId: string): ColumnItem[] => {
       type: DataType.Nvarchar,
       displayOrder: 9006,
       maxLength: 50,
-      businessTableId: tableId,
+      tableId,
       isRequired: false,
       isUnique: false,
       isPreDefined: true,
@@ -210,7 +211,7 @@ export const createBasePredefinedField = (tableId: string): ColumnItem[] => {
       type: DataType.Bit,
       displayOrder: 9007,
       maxLength: 0,
-      businessTableId: tableId,
+      tableId,
       isRequired: false,
       isUnique: false,
       isPreDefined: true,
@@ -237,7 +238,7 @@ export const createFlowPredefinedFields = (
       type: DataType.Nvarchar,
       displayOrder: 1,
       maxLength: 50,
-      businessTableId: tableId,
+      tableId,
       isRequired: true,
       isUnique: false,
       isPreDefined: true,
@@ -253,7 +254,7 @@ export const createFlowPredefinedFields = (
       type: DataType.Nvarchar,
       displayOrder: 2,
       maxLength: 50,
-      businessTableId: tableId,
+      tableId,
       isRequired: true,
       isUnique: false,
       isPreDefined: true,
@@ -270,7 +271,7 @@ export const createFlowPredefinedFields = (
             type: DataType.Nvarchar,
             displayOrder: 3,
             maxLength: 50,
-            businessTableId: tableId,
+            tableId,
             isRequired: true,
             isUnique: false,
             isPreDefined: true,
@@ -285,7 +286,7 @@ export const createFlowPredefinedFields = (
             type: DataType.Int,
             displayOrder: 4,
             maxLength: 0,
-            businessTableId: tableId,
+            tableId,
             isRequired: false,
             isUnique: false,
             isPreDefined: true,
@@ -299,7 +300,7 @@ export const createFlowPredefinedFields = (
             schemaName: "creationTime",
             type: DataType.DateTime,
             displayOrder: 5,
-            businessTableId: tableId,
+            tableId,
             isRequired: true,
             isUnique: false,
             isPreDefined: true,
@@ -315,7 +316,7 @@ export const createFlowPredefinedFields = (
             schemaName: "gridOrder",
             type: DataType.Int,
             displayOrder: 3,
-            businessTableId: tableId,
+            tableId,
             isRequired: false,
             isUnique: false,
             isPreDefined: true,