소스 검색

feat: 添加多语言组件、待办弹窗等

liaojiaxing 4 달 전
부모
커밋
85a4bb97c5

+ 97 - 0
apps/er-designer/src/components/LangInput.tsx

@@ -0,0 +1,97 @@
+import React, { useEffect } from "react";
+import { ListLangBySearchKey } from "@/api";
+import { useRequest } from "umi";
+import { AutoComplete } from "antd";
+
+interface LanItem {
+  en: string;
+  key: string;
+  ["zh-CN"]: string;
+}
+export default function LangInput({
+  value,
+  onChange,
+}: {
+  value: string;
+  onChange: (value: any) => void;
+}) {
+  const [lanOptions, setLanOptions] = React.useState<any[]>([]);
+  const [formModel, setFormModel] = React.useState({
+    langText: "",
+    en: "",
+    ["zh-CN"]: "",
+  });
+  const { run } = useRequest(ListLangBySearchKey, {
+    manual: true,
+    onSuccess(data, params) {
+      setLanOptions(
+        (data.result || []).map((item: LanItem) => ({
+          ...item,
+          label: `${item.en || ""}[zh-CN:${item["zh-CN"]}]`,
+          value: item[params[0].searchLan],
+        }))
+      );
+    },
+  });
+
+  const handleSearch = (serchKey: string, searchLan: "en" | "zh-CN") => {
+    run({
+      maxCount: 10,
+      searchKey: serchKey,
+      searchLan,
+    });
+  };
+
+  const handleSelectLang = (option: LanItem) => {
+    setFormModel(() => {
+      return {
+        ...formModel,
+        langText: option.key,
+        en: option?.en,
+        ["zh-CN"]: option?.["zh-CN"],
+      };
+    });
+  };
+
+  const handleChange = (key: "en" | "zh-CN", val: string) => {
+    setFormModel(() => {
+      return {
+        ...formModel,
+        langText: "",
+        [key]: val,
+      };
+    });
+  };
+
+  useEffect(() => {
+    onChange(formModel);
+  }, [formModel]);
+
+  return (
+    <div>
+      <div>
+        <AutoComplete
+          allowClear
+          placeholder="中文"
+          options={lanOptions}
+          onSearch={(val) => handleSearch(val, "zh-CN")}
+          value={formModel["zh-CN"]}
+          onChange={(val) => handleChange("zh-CN", val)}
+          onSelect={(_val, opt) => handleSelectLang(opt)}
+        />
+      </div>
+      <div>
+        <AutoComplete
+          allowClear
+          style={{ width: "100%" }}
+          placeholder="英文"
+          options={lanOptions}
+          onSearch={(val) => handleSearch(val, "en")}
+          value={formModel.en}
+          onChange={(val) => handleChange("en", val)}
+          onSelect={(_val, opt) => handleSelectLang(opt)}
+        />
+      </div>
+    </div>
+  );
+}

+ 99 - 0
apps/er-designer/src/components/LangInputTextarea.tsx

@@ -0,0 +1,99 @@
+import React, { useEffect } from "react";
+import { ListLangBySearchKey } from "@/api";
+import { useRequest } from "umi";
+import { AutoComplete, Input } from "antd";
+
+interface LanItem {
+  en: string;
+  key: string;
+  ["zh-CN"]: string;
+}
+export default function LangInputTextarea({
+  value,
+  onChange,
+}: {
+  value: string;
+  onChange: (value: any) => void;
+}) {
+  const [lanOptions, setLanOptions] = React.useState<any[]>([]);
+  const [formModel, setFormModel] = React.useState({
+    langText: "",
+    en: "",
+    ["zh-CN"]: "",
+  });
+  const { run } = useRequest(ListLangBySearchKey, {
+    manual: true,
+    onSuccess(data, params) {
+      setLanOptions(
+        (data.result || []).map((item: LanItem) => ({
+          ...item,
+          label: `${item.en || ""}[zh-CN:${item["zh-CN"]}]`,
+          value: item[params[0].searchLan],
+        }))
+      );
+    },
+  });
+
+  const handleSearch = (serchKey: string, searchLan: "en" | "zh-CN") => {
+    run({
+      maxCount: 10,
+      searchKey: serchKey,
+      searchLan,
+    });
+  };
+
+  const handleSelectLang = (option: LanItem) => {
+    setFormModel(() => {
+      return {
+        ...formModel,
+        langText: option.key,
+        en: option?.en,
+        ["zh-CN"]: option?.["zh-CN"],
+      };
+    });
+  };
+
+  const handleChange = (key: "en" | "zh-CN", val: string) => {
+    setFormModel(() => {
+      return {
+        ...formModel,
+        langText: "",
+        [key]: val,
+      };
+    });
+  };
+
+  useEffect(() => {
+    onChange(formModel);
+  }, [formModel]);
+
+  return (
+    <div>
+      <div>
+        <AutoComplete
+          allowClear
+          options={lanOptions}
+          onSearch={(val) => handleSearch(val, "zh-CN")}
+          value={formModel["zh-CN"]}
+          onChange={(val) => handleChange("zh-CN", val)}
+          onSelect={(_val, opt) => handleSelectLang(opt)}
+        >
+          <Input.TextArea placeholder="中文..." value={formModel["zh-CN"]}/>
+        </AutoComplete>
+      </div>
+      <div>
+        <AutoComplete
+          allowClear
+          style={{ width: "100%" }}
+          options={lanOptions}
+          onSearch={(val) => handleSearch(val, "en")}
+          value={formModel.en}
+          onChange={(val) => handleChange("en", val)}
+          onSelect={(_val, opt) => handleSelectLang(opt)}
+        >
+          <Input.TextArea placeholder="英文..." value={formModel.en}/>
+        </AutoComplete>
+      </div>
+    </div>
+  );
+}

+ 2 - 2
apps/er-designer/src/constants/index.ts

@@ -21,10 +21,10 @@ export const DATA_TYPE_OPTIONS: DataTypeOption[] = [
  * 表类型
  */
 export const TABLE_TYPE_OPTIONS = [
-  {label: "系统表", value: 1},
+  // {label: "系统表", value: 1},
   {label: "流程表", value: 2},
   {label: "业务表", value: 3},
-  {label: "视图", value: 4},
+  // {label: "视图", value: 4},
 ];
 
 /**

+ 41 - 50
apps/er-designer/src/models/erModel.tsx

@@ -17,7 +17,7 @@ import type {
   TopicAreaInfo,
 } from "@/type";
 import { uuid } from "@/utils";
-import { RelationLineType, RelationType } from "@/enum";
+import { DataType, RelationLineType, RelationType } from "@/enum";
 import { render } from "./renderer";
 import { useSessionStorageState } from "ahooks";
 
@@ -34,10 +34,8 @@ export default function erModel() {
   const [project, setProjectInfo] = useState<ProjectInfo>({
     id: "1",
     name: "项目1",
-    folderId: "root",
-    creationTime: "2024-12-12 12:12:12",
-    creatorUser: "管理员",
-    lastModificationTime: "2024-12-12 12:12:12",
+    directory: "",
+    type: 3,
     description: "模型描述",
     isTemplate: false,
     industry: "互联网",
@@ -45,8 +43,9 @@ export default function erModel() {
     tables: [],
     relations: [],
     topicAreas: [],
-    remarks: [],
+    remarkInfos: [],
     history: [],
+    todos: [],
     setting: {
       showMenu: true,
       showSidebar: true,
@@ -120,11 +119,12 @@ export default function erModel() {
    * 初始化画布
    * @param container
    */
-  const initGraph = (container: HTMLElement) => {
+  const initGraph = (container: HTMLElement, width?: number, height?: number) => {
+    console.log(container.clientWidth, container.clientHeight);
     const instance = new Graph({
       container,
-      width: document.documentElement.clientWidth,
-      height: document.documentElement.clientHeight,
+      width: width || document.documentElement.clientWidth,
+      height: height || document.documentElement.clientHeight,
       autoResize: true,
       async: false,
       mousewheel: {
@@ -298,14 +298,11 @@ export default function erModel() {
         aliasName: "newtable",
         creationTime: "",
         creatorUserId: "",
-        displayOrder: 1,
+        displayOrder: true,
         id: tableId,
         isDeleted: false,
         langDescription: "",
         langName: "",
-        name: "",
-        cn_name: "新建表",
-        en_name: "new table",
         parentBusinessTableId: parentId || "",
         schemaName: "new_table",
         type: 1,
@@ -320,38 +317,32 @@ export default function erModel() {
       },
       tableColumnList: [
         {
-          aggregateEnable: false,
-          businessTableId: tableId,
-          charset: "",
-          defaultValue: "",
-          displayEnable: false,
-          displayOrder: 1,
-          groupByEnable: false,
           id: columnId,
-          isAggregateEnable: false,
-          isDisplayEnable: false,
-          isGroupByEnable: false,
-          isLinkEnable: false,
-          isOrderByEnable: false,
-          isPreDefined: true,
-          isRequired: true,
-          isUnique: false,
-          isWhereEnable: false,
-          langName: "120d5c4e-a5d8-4d49-b5f5-492feb37e6c6",
-          linkEnable: false,
-          maxLength: 50,
-          name: "Id",
-          orderByEnable: false,
-          preDefined: true,
+          schemaName: "",
+          type: DataType.Nvarchar,
+          maxLength: 100,
           precision: 0,
-          required: true,
           scale: 0,
-          schemaName: "id",
-          type: 2,
-          unique: false,
-          whereEnable: false,
-          cn_name: "id",
-          en_name: "id",
+          isRequired: false,
+          isUnique: false,
+          isPreDefined: false,
+          defaultValue: "",
+          displayOrder: false,
+          businessTableId: tableId,
+          memo: '',
+          alignment: '',
+          isDisplayEnable: true,
+          isLinkEnable: false,
+          isWhereEnable: false,
+          isOrderByEnable: false,
+          isGroupByEnable: false,
+          isAggregateEnable: false,
+          whereInputType: '',
+          whereInputContent: '',
+          alterId: '',
+          temp_rename: '',
+          charset: '',
+          orderChar: ''
         },
       ],
     };
@@ -467,7 +458,7 @@ export default function erModel() {
     const newRemark = {
       isRemark: true,
       id: remarkId,
-      name: "备注_" + (project.remarks.length + 1),
+      name: "备注_" + (project.remarkInfos.length + 1),
       text: "",
       style: {
         x: 300,
@@ -479,7 +470,7 @@ export default function erModel() {
     };
     setProject({
       ...project,
-      remarks: [...project.remarks, newRemark],
+      remarkInfos: [...project.remarkInfos, newRemark],
     });
   };
 
@@ -489,7 +480,7 @@ export default function erModel() {
   const updateRemark = (remark: RemarkInfo) => {
     setProject((state) => ({
       ...(state || {}),
-      remarks: state.remarks.map((item) => {
+      remarks: state.remarkInfos.map((item) => {
         if (item.id === remark.id) {
           return remark;
         }
@@ -505,7 +496,7 @@ export default function erModel() {
   const deleteRemark = (remarkId: string) => {
     setProject({
       ...project,
-      remarks: project.remarks.filter((item) => item.id !== remarkId),
+      remarkInfos: project.remarkInfos.filter((item) => item.id !== remarkId),
     });
     graphRef.current?.removeCell(remarkId);
   };
@@ -633,7 +624,7 @@ export default function erModel() {
         tables: [],
         relations: [],
         topicAreas: [],
-        remarks: [],
+        remarkInfos: [],
       },
       true,
       true
@@ -681,7 +672,7 @@ export default function erModel() {
       if (data?.isRemark) {
         setProject({
           ...project,
-          remarks: project.remarks.filter((item) => item.id !== cell.id),
+          remarkInfos: project.remarkInfos.filter((item) => item.id !== cell.id),
         });
       }
     }
@@ -763,7 +754,7 @@ export default function erModel() {
         };
         setProject({
           ...project,
-          remarks: [...project.remarks, newRemark],
+          remarkInfos: [...project.remarkInfos, newRemark],
         });
       }
     }
@@ -793,7 +784,7 @@ export default function erModel() {
       if (data?.isRemark) {
         setProject({
           ...project,
-          remarks: project.remarks.filter((item) => item.id !== cell[0].id),
+          remarkInfos: project.remarkInfos.filter((item) => item.id !== cell[0].id),
         });
       }
     }

+ 9 - 0
apps/er-designer/src/pages/detail/components/AddTable.tsx

@@ -0,0 +1,9 @@
+import React from 'react'
+
+export default function AddTable() {
+  return (
+    <div>
+      
+    </div>
+  )
+}

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

@@ -0,0 +1,21 @@
+import React, { useEffect } from "react";
+import { useModel } from "umi";
+import Navigator from "@/pages/er/components/Navigator";
+export default function ER(props: { showNavigator: boolean }) {
+  const contianerRef = React.useRef<HTMLDivElement>(null);
+  const { graph, project, initGraph } = useModel("erModel");
+
+  useEffect(() => {
+    initGraph(
+      contianerRef.current!,
+      contianerRef.current?.clientWidth!,
+      contianerRef.current?.clientHeight!
+    );
+  }, []);
+  return (
+    <div className="w-full h-full relative">
+      <div className="w-full h-full" ref={contianerRef} />
+      {props.showNavigator && <Navigator />}
+    </div>
+  );
+}

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

@@ -3,6 +3,7 @@ import { Layout, Menu, Button, Descriptions, Input, Tooltip } 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";
 
 const { Content, Header } = Layout;
 export default function index() {
@@ -156,7 +157,9 @@ export default function index() {
           <div className="content w-full flex-1">
             {
               active === 0
-              ? <div className="er w-full h-full bg-#ccc"></div>
+              ? <div className="er w-full h-full bg-#ccc">
+                <ER/>
+              </div>
               : <TableEdit/>
             }
           </div>

+ 8 - 23
apps/er-designer/src/pages/er/components/ColumnItem.tsx

@@ -15,6 +15,8 @@ import { ColumnItem as ColumnItemType } from "@/type";
 import { DATA_TYPE_OPTIONS } from "@/constants";
 import { useSortable } from "@dnd-kit/sortable";
 import { CSS } from "@dnd-kit/utilities";
+import LangInput from "@/components/LangInput";
+import LangInputTextarea from "@/components/LangInputTextarea";
 
 export default function ColumnItem({
   column,
@@ -25,6 +27,7 @@ export default function ColumnItem({
   onChange: (key: string, value: any) => void;
   onDelete: (id: string) => void;
 }) {
+  console.log(column);
   const { setNodeRef, attributes, listeners, transform, transition } =
     useSortable({
       id: column.id,
@@ -120,23 +123,12 @@ export default function ColumnItem({
             <Form layout="vertical">
               <Row gutter={8}>
                 <Col span={12}>
-                  <Form.Item label="字段名称" name="pkName">
-                    <Input
-                      className="w-full"
-                      placeholder="中文"
-                      value={column.cn_name}
-                      onChange={(e) => onChange("cn_name", e.target.value)}
-                    />
-                    <Input
-                      className="w-full"
-                      placeholder="英文"
-                      value={column.en_name}
-                      onChange={(e) => onChange("en_name", e.target.value)}
-                    />
+                  <Form.Item label="字段名称">
+                    <LangInput value="" onChange={() => {}}/>
                   </Form.Item>
                 </Col>
                 <Col span={12}>
-                  <Form.Item label="默认值" name="pkName">
+                  <Form.Item label="默认值">
                     <Input
                       className="w-full"
                       placeholder="默认值"
@@ -172,15 +164,8 @@ export default function ColumnItem({
               </Row>
               <Row>
                 <Col span={24}>
-                  <Form.Item label="描述" name="pkName">
-                    <Input.TextArea
-                      className="w-full"
-                      placeholder="描述中文..."
-                    />
-                    <Input.TextArea
-                      className="w-full"
-                      placeholder="描述英文..."
-                    />
+                  <Form.Item label="描述">
+                    <LangInputTextarea value="" onChange={() => {}}/>
                   </Form.Item>
                 </Col>
               </Row>

+ 4 - 4
apps/er-designer/src/pages/er/components/RemarkPanel.tsx

@@ -9,17 +9,17 @@ import { useSessionStorageState } from "ahooks";
 export default function RemarkPanel() {
   const { project, addRemark, deleteRemark, updateRemark } =
     useModel("erModel");
-  const { remarks = [] } = project;
+  const { remarkInfos = [] } = project;
   const [search, setSearch] = React.useState("");
   const [activeKey, setActiveKey] = useSessionStorageState("remark-active", {
     defaultValue: "",
     listenStorageChange: true
   });
-  const [remarkList, setRemarkList] = useState(remarks);
+  const [remarkList, setRemarkList] = useState(remarkInfos);
 
   useEffect(() => {
-    setRemarkList(remarks.filter(item => item.name.includes(search)));
-  }, [remarks, search]);
+    setRemarkList(remarkInfos.filter(item => item.name.includes(search)));
+  }, [remarkInfos, search]);
 
   const handleChange = (index: number, key: keyof RemarkInfo, value: any) => {
     const data = remarkList[index];

+ 29 - 30
apps/er-designer/src/pages/er/components/TableItem.tsx

@@ -23,6 +23,8 @@ import type { DragEndEvent } from "@dnd-kit/core";
 import { SortableContext, arrayMove, verticalListSortingStrategy } from "@dnd-kit/sortable";
 import { restrictToParentElement } from "@dnd-kit/modifiers";
 import ColumnItem from "./ColumnItem";
+import LangInput from "@/components/LangInput";
+import LangInputTextarea from "@/components/LangInputTextarea";
 
 export default function TableItem({
   data,
@@ -79,17 +81,30 @@ export default function TableItem({
     const newColumn: ColumnItemType = {
       id: uuid(),
       schemaName: "",
-      name: "",
-      en_name: "",
-      cn_name: "",
-      type: DataType.Nvarchar,
-      maxLength: 100,
-      precision: 0,
-      scale: 0,
-      isRequired: false,
-      isUnique: false,
-      isPreDefined: false,
-      defaultValue: "",
+          type: DataType.Nvarchar,
+          maxLength: 100,
+          precision: 0,
+          scale: 0,
+          isRequired: false,
+          isUnique: false,
+          isPreDefined: false,
+          defaultValue: "",
+          displayOrder: false,
+          businessTableId: table.id,
+          memo: '',
+          alignment: '',
+          isDisplayEnable: true,
+          isLinkEnable: false,
+          isWhereEnable: false,
+          isOrderByEnable: false,
+          isGroupByEnable: false,
+          isAggregateEnable: false,
+          whereInputType: '',
+          whereInputContent: '',
+          alterId: '',
+          temp_rename: '',
+          charset: '',
+          orderChar: ''
     };
     onChange({
       table,
@@ -153,7 +168,7 @@ export default function TableItem({
         onClick={() => setActive(active === table.id ? "" : table.id)}
       >
         <div className="font-bold truncate">
-          {table.schemaName}({table.cn_name})
+          {table.schemaName}({table.langName})
         </div>
         <div>
           <Popover
@@ -163,26 +178,10 @@ export default function TableItem({
               <div className="w-200px" onClick={(e) => e.stopPropagation()}>
                 <Form layout="vertical">
                   <Form.Item label="表名称" name="pkName">
-                    <Input
-                      className="w-full"
-                      placeholder="中文"
-                      value={table.cn_name}
-                      onChange={(e) =>
-                        handleTableChange("cn_name", e.target.value)
-                      }
-                    />
-                    <Input
-                      className="w-full"
-                      placeholder="英文"
-                      value={table.en_name}
-                      onChange={(e) =>
-                        handleTableChange("en_name", e.target.value)
-                      }
-                    />
+                    <LangInput value="" onChange={() => {}}/>
                   </Form.Item>
                   <Form.Item label="描述" name="pkName">
-                    <Input.TextArea className="w-full" placeholder="中文..." />
-                    <Input.TextArea className="w-full" placeholder="英文..." />
+                    <LangInputTextarea value="" onChange={() => {}}/>
                   </Form.Item>
                 </Form>
               </div>

+ 140 - 0
apps/er-designer/src/pages/er/components/TodoDrawer.tsx

@@ -0,0 +1,140 @@
+import React, { useImperativeHandle, forwardRef } from "react";
+import { DownOutlined, PlusOutlined } from "@ant-design/icons";
+import type { MenuProps } from "antd";
+import { Dropdown, Space, Drawer, Button, Checkbox, Input } from "antd";
+import { useModel } from "umi";
+import { TodoItem } from "@/type";
+import { uuid } from "@/utils";
+
+const TodoDrawer = forwardRef((props: {}, ref) => {
+  const [open, setOpen] = React.useState(false);
+  const { project, setProject } = useModel("erModel");
+
+  useImperativeHandle(ref, () => ({
+    open: () => setOpen(true),
+    close: () => setOpen(false),
+  }));
+
+  const items: MenuProps["items"] = [
+    {
+      key: "1",
+      label: "按优先级排序",
+    },
+    {
+      key: "2",
+      label: "按完成时间排序",
+    },
+    {
+      key: "3",
+      label: "按创建时间排序",
+    },
+  ];
+
+  const levelMap = {
+    0: {
+      label: "无",
+      color: "gray",
+    },
+    1: {
+      label: "低",
+      color: "green",
+    },
+    2: {
+      label: "中",
+      color: "orange",
+    },
+    3: {
+      label: "高",
+      color: "red",
+    },
+  };
+
+  const handleChange = (index: number, key: string, value: any) => {
+    const newTodos = [...project.todos];
+    newTodos[index] = {
+      ...newTodos[index],
+      [key]: value,
+    };
+    setProject({
+      ...project,
+      todos: newTodos,
+    });
+  };
+
+  const addToto = () => {
+    setProject({
+      ...project,
+      todos: [
+        ...project.todos,
+        {
+          id: uuid(),
+          name: "",
+          text: "",
+          isDone: false,
+          level: 1,
+        },
+      ],
+    });
+  }
+
+  return (
+    <Drawer
+      open={open}
+      placement="right"
+      width={400}
+      title={<>待办事项</>}
+      onClose={() => setOpen(false)}
+    >
+      <div className="flex justify-between m-b-12px">
+        <Dropdown menu={{ items }}>
+          <Space>
+            排序方式
+            <DownOutlined />
+          </Space>
+        </Dropdown>
+        <Button type="primary" icon={<PlusOutlined />}>
+          新建待办
+        </Button>
+      </div>
+      {project.todos.map((todo: TodoItem, index: number) => (
+        <div className="todo-item flex border-b-1 border-b-solid border-#eee p-b-12px">
+          <div className="left m-r-12px">
+            <Checkbox
+              checked={todo.isDone}
+              onChange={(e) => {
+                handleChange(index, "isDone", e.target.checked);
+              }}
+            />
+          </div>
+          <div className="right flex-1">
+            <div className="flex m-b-12px">
+              <Input
+                placeholder="标题..."
+                value={todo.name}
+                onChange={(e) => handleChange(index, "name", e.target.value)}
+              />
+              <div className="rounded-4px cus-btn w-32px h-32px bg-#eee flex-none text-center leading-32px cursor-pointer hover:bg-#ddd m-l-12px">
+                <i className="iconfont icon-gengduo" />
+              </div>
+            </div>
+            <div className="m-b-12px">
+              <Input.TextArea
+                placeholder="描述..."
+                value={todo.text}
+                onChange={(e) => handleChange(index, "text", e.target.value)}
+              />
+            </div>
+            <div>
+              优先级:{" "}
+              <span className="inline-block w-20px h-20px bg-red-500 text-center color-#fff leading-16px rounded-4px">
+                高
+              </span>
+            </div>
+          </div>
+        </div>
+      ))}
+    </Drawer>
+  );
+});
+
+export default TodoDrawer;

+ 6 - 2
apps/er-designer/src/pages/er/components/Toolbar.tsx

@@ -2,6 +2,7 @@ import React, { useEffect, useState } from "react";
 import { Button, Tooltip, Divider, Dropdown } from "antd";
 import { DownOutlined } from "@ant-design/icons";
 import { useModel } from "umi";
+import TodoDrawer from "./TodoDrawer";
 export default function Toolbar() {
   const {
     addTable,
@@ -15,6 +16,7 @@ export default function Toolbar() {
     project,
     setProject,
   } = useModel("erModel");
+  const todoRef = React.useRef<{ open: () => void }>();
   const scaleMenu = {
     style: {
       width: 200,
@@ -94,7 +96,9 @@ export default function Toolbar() {
 
   return (
     <div className="flex justify-between bg-#fafafa">
-      <div></div>
+      <div>
+        <TodoDrawer ref={todoRef} />
+      </div>
       <div className="flex items-center py-0px justify-center h-32px">
         <div className="group">
           <div className="flex items-center">
@@ -176,7 +180,7 @@ export default function Toolbar() {
               </div>
             </Tooltip>
             <Tooltip title="待办项">
-              <div className="btn flex flex-col items-center cursor-pointer py-4px px-10px hover:bg-gray-200 opacity-50">
+              <div className="btn flex flex-col items-center cursor-pointer py-4px px-10px hover:bg-gray-200 opacity-50" onClick={() => todoRef.current?.open()}>
                 <svg className="icon h-24px w-24px" aria-hidden="true">
                   <use xlinkHref="#icon-daiban"></use>
                 </svg>

+ 52 - 42
apps/er-designer/src/type.d.ts

@@ -2,38 +2,33 @@
  * 字段结构
  */
 export interface ColumnItem {
-  aggregateEnable?: boolean;
-  businessTableId?: string;
-  charset?: string;
-  cn_name: string;
-  defaultValue: any;
-  displayEnable?: boolean;
-  displayOrder?: number;
-  en_name: string;
-  groupByEnable?: boolean;
-  id: string;
-  isAggregateEnable?: boolean;
-  isDisplayEnable?: boolean;
-  isGroupByEnable?: boolean;
-  isLinkEnable?: boolean;
-  isOrderByEnable?: boolean;
-  isPreDefined?: boolean;
+  langName?: string;
+  schemaName: string;
+  type: number;
+  maxLength: number;
   isRequired: boolean;
   isUnique: boolean;
-  isWhereEnable?: boolean;
-  linkEnable?: boolean;
-  maxLength?: number;
-  name: string;
-  orderByEnable?: boolean;
-  preDefined?: boolean;
-  precision?: number;
-  required?: boolean;
-  scale?: number;
-  schemaName?: string;
-  type: number;
-  unique?: boolean;
-  whereEnable?: boolean;
-  langName?: string;
+  displayOrder: boolean;
+  businessTableId: string;
+  memo: string;
+  alignment: string;
+  isDisplayEnable: boolean;
+  isLinkEnable: boolean;
+  isWhereEnable: boolean;
+  isOrderByEnable: boolean;
+  isGroupByEnable: boolean;
+  isAggregateEnable: boolean;
+  whereInputType: string;
+  whereInputContent: string;
+  precision: number;
+  scale: number;
+  isPreDefined: boolean;
+  defaultValue: string;
+  alterId: string;
+  temp_rename: string;
+  id: string;
+  charset: string;
+  orderChar: string;
 }
 
 /**
@@ -43,18 +38,16 @@ export interface ViewTable {
   aliasName: string;
   creationTime: string;
   creatorUserId: string;
-  displayOrder: number;
+  displayOrder: boolean;
   id: string;
   isDeleted: boolean;
   langDescription: string;
-  langName: string;
-  name: string;
+  langName?: string;
   parentBusinessTableId: string;
   schemaName: string;
-  type: number;
+  type: number; // 1 基础数据 2 流程 3 系统表
   updateTime: string;
-  cn_name: string;
-  en_name: string;
+  langName: string;
 }
 
 /**
@@ -78,6 +71,17 @@ export interface ColumnRelation {
   style: Record<string, any>;
 }
 
+/**
+ * 待办
+ */
+export interface TodoItem {
+  id: string;
+  name: string;
+  text: string;
+  isDone: boolean;
+  level: number;
+}
+
 /**
  * 主题区域信息
  */
@@ -127,14 +131,18 @@ export interface ProjectInfo {
   id: string;
   // 名称
   name: string;
-  // 文件夹ID
-  folderId: string;
+  // 文件夹
+  directory: string;
+  // 模型类型
+  type: number; // 2 流程 3 业务表
   // 创建时间
-  creationTime: string;
+  createTime?: string;
   // 创建者
-  creatorUser: string;
+  createdBy?: string;
+  // 更新者
+  updatedBy?: string;
   // 更新时间
-  lastModificationTime: string;
+  updateTime?: string;
   // 模型说明
   description?: string;
   // 是否模版
@@ -150,9 +158,11 @@ export interface ProjectInfo {
   // 主题区域
   topicAreas: TopicAreaInfo[];
   // 注释节点
-  remarks: RemarkInfo[];
+  remarkInfos: RemarkInfo[];
   // 操作记录
   history: any[];
+  // 待办事项
+  todos: TodoItem[];
   // 画布设置
   setting: {
     // 展示菜单栏