liaojiaxing 4 місяців тому
батько
коміт
8da366b4c5

+ 11 - 0
apps/er-designer/src/api/dataModel.ts

@@ -42,3 +42,14 @@ export const DeleteDataModel = (data: { id: string }) => {
     data,
   });
 };
+
+/**
+ * 模型详情
+ * @param id
+ */
+export const GetDataModelDetail = (data: { id: string }) => {
+  return request("/api/erDiagram/dataModel/detail", {
+    method: "POST",
+    data,
+  });
+};

+ 32 - 12
apps/er-designer/src/components/AddModel.tsx

@@ -1,24 +1,30 @@
-import React from "react";
+import React, { useRef } from "react";
 import { Modal, Input, Form, message } from "antd";
 import { SaveDataModel } from "@/api";
 import { TableType } from "@/enum";
+import { ProjectInfo } from "@/type";
+import { DEFAULT_SETTING } from "@/constants";
 
 export default React.forwardRef(function AddModel(
-  props: { onChange?: () => {} },
+  props: { onChange?: (id: string) => void },
   ref
 ) {
   const [open, setOpen] = React.useState(false);
   const [form] = Form.useForm();
   const type = React.useRef<TableType>();
+  const isAdd = useRef(true);
   React.useImperativeHandle(ref, () => {
     return {
       open: (tableType: TableType) => {
+        isAdd.current = true;
         type.current = tableType;
         setOpen(true);
         form.resetFields();
       },
-      close: () => {
-        setOpen(false);
+      edit: (info: ProjectInfo) => {
+        isAdd.current = false;
+        form.setFieldsValue(info);
+        setOpen(true);
       },
     };
   });
@@ -26,13 +32,24 @@ export default React.forwardRef(function AddModel(
   const handleSubmit = () => {
     form.validateFields().then(async (values) => {
       console.log(values, message);
-      await SaveDataModel({
-        ...values,
-        type: type.current,
-      });
-      message.success("创建成功");
-      props.onChange?.();
-      setOpen(false);
+      if (isAdd.current) {
+        const res = await SaveDataModel({
+          ...values,
+          type: type.current,
+          setting: JSON.stringify(DEFAULT_SETTING),
+        });
+        message.success("创建成功");
+        props.onChange?.(res?.result);
+        setOpen(false);
+      } else {
+        const res = await SaveDataModel({
+          ...values,
+          type: type.current,
+        });
+        message.success("创建成功");
+        props.onChange?.(res?.result);
+        setOpen(false);
+      }
     });
   };
 
@@ -44,7 +61,10 @@ export default React.forwardRef(function AddModel(
       onCancel={() => setOpen(false)}
       onOk={handleSubmit}
     >
-      <Form layout="vertical" form={form}>
+      <Form<{ name: string; description: string }>
+        layout="vertical"
+        form={form}
+      >
         <Form.Item
           label="模型名称"
           name="name"

+ 14 - 1
apps/er-designer/src/constants/index.ts

@@ -45,4 +45,17 @@ export const DEFAULT_SYSTEM_VARIABLES = [
   {id: '3', name: "UserCode", type: "String"},
   {id: '4', name: "SystemDate", type: "Date"},
   {id: '5', name: "?", type: "运行中的参数"},
-]
+];
+
+/**
+ * 默认设置
+ */
+export const DEFAULT_SETTING = {
+  showMenu: true,
+  showSidebar: true,
+  showColumnDetail: true,
+  showGrid: true,
+  showRelation: true,
+  autoUpdate: false,
+  tableWidth: 220,
+};

+ 8 - 1
apps/er-designer/src/layouts/index.tsx

@@ -2,6 +2,11 @@ import { Outlet } from 'umi';
 import './index.less';
 import { useSearchParams } from 'umi';
 import { useEffect } from 'react';
+import zhCN from 'antd/locale/zh_CN';
+import dayjs from 'dayjs';
+import { ConfigProvider } from "antd";
+
+dayjs.locale('zh-cn')
 
 export default function Layout() {
   const [searchParams] = useSearchParams();
@@ -14,6 +19,8 @@ export default function Layout() {
     }
   }, [searchParams]);
   return (
-    <Outlet />
+    <ConfigProvider locale={zhCN}>
+      <Outlet />
+    </ConfigProvider>
   );
 }

+ 47 - 24
apps/er-designer/src/models/erModel.tsx

@@ -22,22 +22,13 @@ import type {
 } from "@/type";
 import { RelationLineType } from "@/enum";
 import { render } from "./renderer";
+import { DEFAULT_SETTING } from "@/constants";
 
 import "@/components/TableNode";
 import "@/components/TopicNode";
 import "@/components/NoticeNode";
 import { message } from "antd";
 
-const DEFAULT_SETTING = {
-  showMenu: true,
-  showSidebar: true,
-  showColumnDetail: true,
-  showGrid: true,
-  showRelation: true,
-  autoUpdate: false,
-  tableWidth: 220,
-};
-
 export default function erModel() {
   const graphRef = useRef<Graph>();
   const [graph, setGraph] = useState<Graph>();
@@ -55,7 +46,7 @@ export default function erModel() {
   );
   const [saveTime, setSaveTime] = useState<string>();
   const [project, setProjectInfo] = useState<ProjectInfo>({
-    id: "9bb35adc-0b2f-450f-b137-de39ce06fe9f",
+    id: "",
     name: "新建模型",
     directory: "",
     type: 3,
@@ -79,6 +70,16 @@ export default function erModel() {
     useSessionStorageState("relation-active");
 
   const timer = useRef<any>();
+  const saveData = (info: ProjectInfo) => {
+    // 提交服务器
+    // 清除定时器
+    clearTimeout(timer.current);
+    timer.current = setTimeout(() => {
+      SaveDataModel(info);
+      // 格式化当前时间
+      setSaveTime(dayjs().format("YYYY-MM-DD HH:mm:ss"));
+    }, 500);
+  };
 
   /**
    * 统一修改数据
@@ -92,14 +93,40 @@ export default function erModel() {
     isInit?: boolean,
     ingoreRender?: boolean
   ) => {
-    if (isInit) {
+    if (isInit && typeof info === "object" && typeof info !== null) {
       historyRef.current = [];
       activeIndex.current = 0;
+      // 配置
+      info.setting = info?.setting
+        ? JSON.parse(info.setting as unknown as string)
+        : { ...DEFAULT_SETTING };
+      // json字符串转换
+      info.tables.forEach(tableItem => {
+        tableItem.table.style = JSON.parse(tableItem.table.style as unknown as string);
+      });
+      info.relations.forEach(relationItem => {
+        relationItem.style = JSON.parse(
+          relationItem.style as unknown as string
+        );
+      });
+      info.topicAreas.forEach(topicAreaItem => {
+        topicAreaItem.style = JSON.parse(
+          topicAreaItem.style as unknown as string
+        );
+      });
+      info.remarkInfos.forEach(remarkItem => {
+        remarkItem.style = JSON.parse(
+          remarkItem.style as unknown as string
+        );
+      });
     }
 
     if (info && typeof info === "function") {
       setProjectInfo((state) => {
         const result = info(state);
+        if (!isInit) {
+          saveData(result);
+        }
         graphRef.current && !ingoreRender && render(graphRef.current, result);
         // 添加记录
         if (!ingoreHistory) {
@@ -124,17 +151,9 @@ export default function erModel() {
           activeIndex.current -= 1;
         }
       }
-    }
-
-    // 提交服务器
-    if (!isInit) {
-      // 清除定时器
-      clearTimeout(timer.current);
-      timer.current = setTimeout(() => {
-        SaveDataModel(project);
-        // 格式化当前时间
-        setSaveTime(dayjs().format("YYYY-MM-DD HH:mm:ss"));
-      }, 500);
+      if (!isInit) {
+        saveData(info);
+      }
     }
   };
 
@@ -222,6 +241,10 @@ export default function erModel() {
       },
     });
 
+    if (project.id) {
+      render(instance, project);
+    }
+
     instance.use(new Snapline({ enabled: true }));
     instance.use(
       new Transform({
@@ -369,7 +392,7 @@ export default function erModel() {
     const x = area?.center.x || 300;
     const y = area?.center.y || 300;
     // 数据表类型动态路由传参
-    const newTable = createTable(project.type || 3, project.id,  parentId);
+    const newTable = createTable(project.type || 3, project.id, parentId);
     newTable.table.style.x = x;
     newTable.table.style.y = y;
 

+ 1 - 1
apps/er-designer/src/pages/detail/components/ER.tsx

@@ -25,7 +25,7 @@ export default function ER(props: {
   return (
     <div className="w-full h-full relative">
       <div className="w-full h-full" ref={contianerRef} />
-      {props.showNavigator && <Navigator />}
+      {props.showNavigator && <Navigator key="detail"/>}
       {props.isFullScreen && (
         <div className="absolute top-32px right-32px z-2">
           <div className="left bg-#fff shadow-md p-x-4px p-y-4px flex items-center gap-8px">

+ 174 - 134
apps/er-designer/src/pages/detail/index.tsx

@@ -7,26 +7,33 @@ import {
   Input,
   Tooltip,
   Empty,
+  Spin,
 } from "antd";
 import type { DescriptionsProps, MenuProps } from "antd";
 import { PlusOutlined, SearchOutlined } from "@ant-design/icons";
 import TableEdit from "@/components/TableEdit";
 import ER from "./components/ER";
 import AddTable from "./components/AddTable";
-import { useModel, history } from "umi";
+import { useModel, history, useRequest, useParams } from "umi";
+import { GetDataModelDetail } from "@/api";
 import NoData from "@/assets/no-data.png";
-import { TableItemType } from "@/type";
+import { ProjectInfo, TableItemType } from "@/type";
 import { useFullscreen } from "ahooks";
+import AddModel from "@/components/AddModel";
 
 const { Content, Header } = Layout;
 export default function index() {
   const [active, setActive] = useState(0);
   const [showNavigator, setShowNavigator] = useState(true);
   const addTableRef = useRef<{ open: () => void }>();
-  const { project, setProject, setPlayModeEnable, graph } = useModel("erModel");
+  const { project, setProject, setPlayModeEnable, exitPlayMode, graph } =
+    useModel("erModel");
   const [searchKeyword, setSearchKeyword] = useState("");
-  const [selectKey, setSelectKey] = useState<string>(project.tables?.[0]?.table?.id || '');
+  const [selectKey, setSelectKey] = useState<string>(
+    project.tables?.[0]?.table?.id || ""
+  );
   const erRef = useRef<HTMLDivElement>(null);
+  const addModelRef = useRef<{ edit: (info: ProjectInfo) => void }>();
   const [isFullscreen, { enterFullscreen, exitFullscreen }] =
     useFullscreen(erRef);
 
@@ -34,37 +41,60 @@ export default function index() {
     setPlayModeEnable(true);
   }, [project]);
 
+  const params = useParams();
+
+  const { run, loading } = useRequest(GetDataModelDetail, {
+    manual: true,
+    onSuccess: (res) => {
+      console.log("模型详情:", res);
+      const result = res?.result;
+      if (result) {
+        setProject(
+          result,
+          false,
+          true
+        );
+      }
+    },
+  });
+
+  useEffect(() => {
+    if (params?.id) {
+      run({ id: params.id });
+    }
+  }, []);
+
   const descItems: DescriptionsProps["items"] = useMemo(() => {
     return [
       {
         key: "1",
         label: "模型名称",
-        children: project?.name || '-',
+        children: project?.name || "-",
       },
       {
         key: "2",
         label: "创建用户",
-        children: project?.createdBy || '-',
+        children: project?.createdBy || "-",
       },
       {
         key: "3",
         label: "创建时间",
-        children: project?.createTime || '-',
+        children: project?.createTime || "-",
       },
       {
         key: "4-1",
         label: "更新用户",
-        children: project?.updatedBy || '-',
+        children: project?.updatedBy || "-",
       },
       {
         key: "4",
         label: "更新时间",
-        children: project?.updateTime || '-',
+        children: project?.updateTime || "-",
       },
       {
         key: "5",
         label: "发布状态",
-        children: project?.publishStatus || '-',
+        children: project?.publishStatus || "-",
       },
       {
         key: "6",
@@ -74,7 +104,7 @@ export default function index() {
       {
         key: "7",
         label: "描述",
-        children: project?.description || '-',
+        children: project?.description || "-",
       },
     ];
   }, [project]);
@@ -128,14 +158,16 @@ export default function index() {
 
   const handleEnterFullscreen = () => {
     enterFullscreen();
-  }
+  };
   const handleExitFullscreen = () => {
     exitFullscreen();
-  }
+  };
 
   const handleEnterEdit = () => {
-    history.push(`/er/${project.id}`)
-  }
+    exitPlayMode();
+    const enterpriseCode = sessionStorage.getItem("enterpriseCode");
+    history.push(`/er/${project.id}?enterpriseCode=${enterpriseCode}`);
+  };
 
   const extra = (
     <div className="flex gap-12px">
@@ -143,13 +175,13 @@ export default function index() {
         <i className="iconfont icon-tongbu text-12px" />
         一键同步
       </a>
-      <a>
+      <a onClick={() => project.id && addModelRef.current?.edit(project)}>
         <i className="iconfont icon-bianji text-12px" />
-        修改
+        基础信息
       </a>
-      <a onClick={() => handleEnterEdit}>
+      <a onClick={handleEnterEdit}>
         <i className="iconfont icon-bianji text-12px" />
-        进入编辑
+        模型编辑
       </a>
       <a>
         <i className="iconfont icon-moban text-14px" />
@@ -159,36 +191,38 @@ export default function index() {
   );
 
   return (
-    <Layout className="h-100vh flex flex-col bg-#fafafa p-12px">
-      <Header
-        className="shadow-sm"
-        style={{
-          backgroundColor: "#fff",
-          padding: 16,
-          height: "auto",
-          borderRadius: 8,
-          // boxShadow: "0 2px 4px rgba(0, 0, 0, 0.15)",
-          marginBottom: 12,
-        }}
-      >
-        <Descriptions
-          title={
-            <span>
-              <svg className="iconfont w-14px h-14px m-r-4px">
-                <use xlinkHref="#icon-shujumoxing" />
-              </svg>
-              模型详情
-            </span>
-          }
-          items={descItems}
-          extra={extra}
-        ></Descriptions>
-      </Header>
+    <Spin spinning={loading}>
+      <AddModel ref={addModelRef} />
+      <Layout className="h-100vh flex flex-col bg-#fafafa p-12px">
+        <Header
+          className="shadow-sm"
+          style={{
+            backgroundColor: "#fff",
+            padding: 16,
+            height: "auto",
+            borderRadius: 8,
+            // boxShadow: "0 2px 4px rgba(0, 0, 0, 0.15)",
+            marginBottom: 12,
+          }}
+        >
+          <Descriptions
+            title={
+              <span>
+                <svg className="iconfont w-14px h-14px m-r-4px">
+                  <use xlinkHref="#icon-shujumoxing" />
+                </svg>
+                模型详情
+              </span>
+            }
+            items={descItems}
+            extra={extra}
+          ></Descriptions>
+        </Header>
 
-      <Content className="flex-1 overflow-auto flex gap-12px">
-        <div className="left w-300px shrink-0 h-full shadow-sm bg-#fff rounded-8px">
-          <div
-            className="
+        <Content className="flex-1 overflow-auto flex gap-12px">
+          <div className="left w-300px shrink-0 h-full shadow-sm bg-#fff rounded-8px">
+            <div
+              className="
               flex 
               justify-between
               header-tit 
@@ -200,99 +234,105 @@ export default function index() {
               border-b-1px 
               border-b-#eee 
               border-b-solid"
-          >
-            <div>
-              <svg className="iconfont w-14px h-14px m-r-4px">
-                <use xlinkHref="#icon-biaoge" />
-              </svg>
-              <span>数据表</span>
-            </div>
-            <div>
-              <Input
-                placeholder="搜索"
-                className="w-100px m-r-4px"
-                suffix={<SearchOutlined />}
-                value={searchKeyword}
-                onChange={(e) => setSearchKeyword(e.target.value)}
-              />
-              <Tooltip title="添加数据表">
-                <Button
-                  type="primary"
-                  icon={<PlusOutlined />}
-                  onClick={() => addTableRef.current?.open()}
-                ></Button>
-              </Tooltip>
+            >
+              <div>
+                <svg className="iconfont w-14px h-14px m-r-4px">
+                  <use xlinkHref="#icon-biaoge" />
+                </svg>
+                <span>数据表</span>
+              </div>
+              <div>
+                <Input
+                  placeholder="搜索"
+                  className="w-100px m-r-4px"
+                  suffix={<SearchOutlined />}
+                  value={searchKeyword}
+                  onChange={(e) => setSearchKeyword(e.target.value)}
+                />
+                <Tooltip title="添加数据表">
+                  <Button
+                    type="primary"
+                    icon={<PlusOutlined />}
+                    onClick={() => addTableRef.current?.open()}
+                  ></Button>
+                </Tooltip>
+              </div>
             </div>
+            <Menu
+              mode="inline"
+              items={tableData}
+              selectedKeys={[selectKey]}
+              onSelect={({ key }) => setSelectKey(key)}
+            />
+            {!tableData.length && (
+              <Empty image={NoData} description="点击+添加数据表!" />
+            )}
           </div>
-          <Menu
-            mode="inline"
-            items={tableData}
-            selectedKeys={[selectKey]}
-            onSelect={({ key }) => setSelectKey(key)}
-          />
-          {!tableData.length && (
-            <Empty image={NoData} description="点击+添加数据表!" />
-          )}
-        </div>
-        <div className="right flex-1 overflow-hidden h-full shadow-sm bg-#fff rounded-8px p-12px flex flex-col">
-          <div className="head flex justify-between">
-            <div className="left flex gap-8px">
-              <Button
-                type={active === 0 ? "primary" : "default"}
-                onClick={() => setActive(0)}
-              >
-                ER图
-              </Button>
-              <Button
-                type={active === 1 ? "primary" : "default"}
-                onClick={() => setActive(1)}
-              >
-                实体
-              </Button>
+          <div className="right flex-1 overflow-hidden h-full shadow-sm bg-#fff rounded-8px p-12px flex flex-col">
+            <div className="head flex justify-between">
+              <div className="left flex gap-8px">
+                <Button
+                  type={active === 0 ? "primary" : "default"}
+                  onClick={() => setActive(0)}
+                >
+                  ER图
+                </Button>
+                <Button
+                  type={active === 1 ? "primary" : "default"}
+                  onClick={() => setActive(1)}
+                >
+                  实体
+                </Button>
+              </div>
+              <div className="right flex gap-8px m-b-12px">
+                {active === 0 ? (
+                  <>
+                    <Input placeholder="搜索" suffix={<SearchOutlined />} />
+                    <Button
+                      type={showNavigator ? "primary" : "default"}
+                      onClick={() => setShowNavigator(!showNavigator)}
+                      icon={<i className="iconfont icon-xiaoditu text-14px" />}
+                    >
+                      导航
+                    </Button>
+                    <Button
+                      type="primary"
+                      icon={
+                        <i className="iconfont icon-quanping_o text-14px" />
+                      }
+                      onClick={handleEnterFullscreen}
+                    >
+                      全屏
+                    </Button>
+                  </>
+                ) : (
+                  <>
+                    <Input placeholder="搜索" suffix={<SearchOutlined />} />
+                  </>
+                )}
+              </div>
             </div>
-            <div className="right flex gap-8px m-b-12px">
+            <div className="content w-full flex-1 overflow-auto">
               {active === 0 ? (
-                <>
-                  <Input placeholder="搜索" suffix={<SearchOutlined />} />
-                  <Button
-                    type={showNavigator ? "primary" : "default"}
-                    onClick={() => setShowNavigator(!showNavigator)}
-                    icon={<i className="iconfont icon-xiaoditu text-14px" />}
-                  >
-                    导航
-                  </Button>
-                  <Button
-                    type="primary"
-                    icon={<i className="iconfont icon-quanping_o text-14px" />}
-                    onClick={handleEnterFullscreen}
-                  >
-                    全屏
-                  </Button>
-                </>
+                <div
+                  className="er w-full h-full bg-#ccc overflow-hidden"
+                  ref={erRef}
+                >
+                  <ER
+                    showNavigator={showNavigator}
+                    isFullScreen={isFullscreen}
+                    onExitFullscreen={handleExitFullscreen}
+                    onChangeShowNavigator={setShowNavigator}
+                  />
+                </div>
               ) : (
-                <>
-                  <Input placeholder="搜索" suffix={<SearchOutlined />} />
-                </>
+                <TableEdit data={currentTable?.tableColumnList || []} />
               )}
             </div>
           </div>
-          <div className="content w-full flex-1 overflow-auto">
-            {active === 0 ? (
-              <div className="er w-full h-full bg-#ccc overflow-hidden" ref={erRef}>
-                <ER
-                  showNavigator={showNavigator}
-                  isFullScreen={isFullscreen}
-                  onExitFullscreen={handleExitFullscreen}
-                  onChangeShowNavigator={setShowNavigator}
-                />
-              </div>
-            ) : (
-              <TableEdit data={currentTable?.tableColumnList || []} />
-            )}
-          </div>
-        </div>
-      </Content>
-      <AddTable ref={addTableRef} onChange={handleAddTable} />
-    </Layout>
+        </Content>
+        <AddTable ref={addTableRef} onChange={handleAddTable} />
+      </Layout>
+    </Spin>
   );
 }

+ 3 - 1
apps/er-designer/src/pages/er/components/Navigator.tsx

@@ -9,10 +9,12 @@ export default function Navigator() {
     defaultValue: true,
     listenStorageChange: true,
   });
+  const miniMap = React.useRef<any>();
 
   useEffect(() => {
     if (graph && mapRef.current) {
-      graph.use(
+      if(miniMap.current) return;
+      miniMap.current = graph.use(
         new MiniMap({
           container: mapRef.current,
           width: mapRef.current.offsetWidth || 300,

+ 124 - 100
apps/er-designer/src/pages/er/index.tsx

@@ -1,5 +1,5 @@
 import React, { useEffect } from "react";
-import { Input, Layout, Tabs, ConfigProvider } from "antd";
+import { Input, Layout, Tabs, ConfigProvider, Spin } from "antd";
 import type { TabsProps } from "antd/lib";
 import Toolbar from "./components/Toolbar";
 import TablePanel from "./components/TablePanel";
@@ -8,16 +8,17 @@ import ThemePanel from "./components/ThemePanel";
 import RemarkPanel from "./components/RemarkPanel";
 import Navigator from "./components/Navigator";
 import Menu from "./components/Menu";
-import { useModel } from "umi";
+import { useModel, useRequest, useParams } from "umi";
 import "./index.less";
 import { useSessionStorageState } from "ahooks";
 import { EnvironmentOutlined, FullscreenExitOutlined, UnorderedListOutlined } from "@ant-design/icons";
+import { GetDataModelDetail } from "@/api";
 
 const { Header, Content, Sider } = Layout;
 
 const App: React.FC = () => {
   const containerRef = React.useRef(null);
-  const { initGraph, project, playModeEnable, exitPlayMode } =
+  const { initGraph, project, playModeEnable, exitPlayMode, setProject } =
     useModel("erModel");
   const [tabActiveKey, setTabActiveKey] = useSessionStorageState(
     "tabs-active-key",
@@ -27,11 +28,32 @@ const App: React.FC = () => {
     }
   );
   const [show, setShow] = useSessionStorageState('show-navigator');
+  const params = useParams();
+
+  const { run, loading } = useRequest(GetDataModelDetail, {
+    manual: true,
+    onSuccess: (res) => {
+      console.log("模型详情:", res);
+      const result = res?.result;
+      if (result) {
+        setProject(
+          result,
+          false,
+          true
+        );
+      }
+    },
+  });
 
   useEffect(() => {
     if (containerRef.current) {
       initGraph(containerRef.current);
     }
+    if(!project.id && params?.id) {
+      run({
+        id: params.id
+      });
+    }
   }, []);
 
   const tabItems: TabsProps["items"] = [
@@ -58,115 +80,117 @@ const App: React.FC = () => {
   ];
 
   return (
-    <Layout className="h-100vh">
-      {!playModeEnable && (
-        <Header
-          className="bg-white h-100px border-b-1px border-b-solid border-b-gray-200 p-x-0 flex flex-col"
-          style={{
-            height: project.setting.showMenu ? "100px" : "32px",
-            transition: "all 0.3s ease-in-out",
-          }}
-        >
-          <div
-            className="grid"
+    <Spin spinning={loading}>
+      <Layout className="h-100vh">
+        {!playModeEnable && (
+          <Header
+            className="bg-white h-100px border-b-1px border-b-solid border-b-gray-200 p-x-0 flex flex-col"
             style={{
-              gridTemplateRows: project.setting.showMenu ? "1fr" : "0fr",
-              transition: "all 0.3s",
+              height: project.setting.showMenu ? "100px" : "32px",
+              transition: "all 0.3s ease-in-out",
             }}
           >
-            <div className="overflow-hidden">
-              <Menu />
-            </div>
-          </div>
-          <Toolbar />
-        </Header>
-      )}
-      <Layout>
-        {!playModeEnable && (
-          <Sider
-            width={project.setting.showSidebar ? 360 : 0}
-            style={{ background: "#fff", borderRight: "1px solid #eee" }}
-          >
-            <ConfigProvider
-              theme={{
-                components: {
-                  Tabs: {
-                    colorPrimary: "#000",
-                  },
-                },
+            <div
+              className="grid"
+              style={{
+                gridTemplateRows: project.setting.showMenu ? "1fr" : "0fr",
+                transition: "all 0.3s",
               }}
             >
-              <Tabs
-                animated
-                activeKey={tabActiveKey}
-                onChange={setTabActiveKey}
-                items={tabItems}
-                centered
-              />
-            </ConfigProvider>
-          </Sider>
+              <div className="overflow-hidden">
+                <Menu />
+              </div>
+            </div>
+            <Toolbar />
+          </Header>
         )}
         <Layout>
-          <Content>
-            <div id="graph-container" ref={containerRef}></div>
-            <Navigator />
-            {
-              playModeEnable && <div className="absolute top-32px right-32px z-2">
-              <div className="left bg-#fff shadow-md p-x-4px p-y-4px flex items-center gap-8px">
-                <div
-                  className="
-                  rounded-4px 
-                  cus-btn 
-                  w-32px 
-                  h-32px
-                  bg-#eee 
-                  flex-none 
-                  text-center 
-                  leading-32px 
-                  cursor-pointer 
-                  hover:bg-#ddd"
-                >
-                  <UnorderedListOutlined />
-                </div>
-                <div
-                  className="
-                  rounded-4px 
-                  cus-btn 
-                  w-32px 
-                  h-32px
-                  bg-#eee 
-                  flex-none 
-                  text-center 
-                  leading-32px 
-                  cursor-pointer 
-                  hover:bg-#ddd"
+          {!playModeEnable && (
+            <Sider
+              width={project.setting.showSidebar ? 360 : 0}
+              style={{ background: "#fff", borderRight: "1px solid #eee" }}
+            >
+              <ConfigProvider
+                theme={{
+                  components: {
+                    Tabs: {
+                      colorPrimary: "#000",
+                    },
+                  },
+                }}
+              >
+                <Tabs
+                  animated
+                  activeKey={tabActiveKey}
+                  onChange={setTabActiveKey}
+                  items={tabItems}
+                  centered
+                />
+              </ConfigProvider>
+            </Sider>
+          )}
+          <Layout>
+            <Content>
+              <div id="graph-container" ref={containerRef}></div>
+              <Navigator key="editor"/>
+              {
+                playModeEnable && <div className="absolute top-32px right-32px z-2">
+                <div className="left bg-#fff shadow-md p-x-4px p-y-4px flex items-center gap-8px">
+                  <div
+                    className="
+                    rounded-4px 
+                    cus-btn 
+                    w-32px 
+                    h-32px
+                    bg-#eee 
+                    flex-none 
+                    text-center 
+                    leading-32px 
+                    cursor-pointer 
+                    hover:bg-#ddd"
+                  >
+                    <UnorderedListOutlined />
+                  </div>
+                  <div
+                    className="
+                    rounded-4px 
+                    cus-btn 
+                    w-32px 
+                    h-32px
+                    bg-#eee 
+                    flex-none 
+                    text-center 
+                    leading-32px 
+                    cursor-pointer 
+                    hover:bg-#ddd"
 
-                >
-                  <EnvironmentOutlined onClick={() => setShow(!show)}/>
-                </div>
-                <div
-                  className="
-                  rounded-4px 
-                  cus-btn 
-                  w-32px 
-                  h-32px
-                  bg-#eee 
-                  flex-none 
-                  text-center 
-                  leading-32px 
-                  cursor-pointer 
-                  hover:bg-#ddd"
-                  onClick={() => exitPlayMode()}
-                >
-                  <FullscreenExitOutlined />
+                  >
+                    <EnvironmentOutlined onClick={() => setShow(!show)}/>
+                  </div>
+                  <div
+                    className="
+                    rounded-4px 
+                    cus-btn 
+                    w-32px 
+                    h-32px
+                    bg-#eee 
+                    flex-none 
+                    text-center 
+                    leading-32px 
+                    cursor-pointer 
+                    hover:bg-#ddd"
+                    onClick={() => exitPlayMode()}
+                  >
+                    <FullscreenExitOutlined />
+                  </div>
                 </div>
               </div>
-            </div>
-            }
-          </Content>
+              }
+            </Content>
+          </Layout>
         </Layout>
       </Layout>
-    </Layout>
+    </Spin>
   );
 };
 

+ 10 - 1
apps/er-designer/src/pages/home/All.tsx

@@ -268,6 +268,15 @@ export default function All({
     setOpen(false);
   };
 
+  const handleAddChange = (id: string) => {
+    refresh();
+    const { origin, pathname } = window.location;
+    const enterpriseCode = sessionStorage.getItem("enterpriseCode");
+    window.open(
+      `${origin}${pathname}#/detail/${id}?enterpriseCode=${enterpriseCode}`
+    );
+  }
+
   const handleMove = (targetFolderId: string) => {
     if (!moveSource.current || targetFolderId === moveSource.current?.id)
       return;
@@ -315,7 +324,7 @@ export default function All({
       >
         <div className="flex justify-between items-center m-b-12px">
           <Breadcrumb items={breadcrumbData} />
-          <AddModel ref={addModelRef} />
+          <AddModel ref={addModelRef} onChange={handleAddChange}/>
           <div className="right flex items-center gap-12px">
             <Button
               type="primary"

+ 1 - 1
apps/er-designer/src/pages/home/ProjectCard.tsx

@@ -140,7 +140,7 @@ export default function ProjectCard({
             <span className="text-12px font-normal color-#999">更新于:{record.updateTime}</span>
           </span>
         }
-        description={`${record.description}`}
+        description={<span className="block truncate">{record.description || '暂无描述'}</span>}
       />
     </Card>
   );