Browse Source

perf: 重构数据模型同步功能

liaojiaxing 2 months ago
parent
commit
decb5dbce1

+ 30 - 1
apps/er-designer/src/components/AddModel.tsx

@@ -1,15 +1,19 @@
 import React, { useRef } from "react";
-import { Modal, Input, Form, message } from "antd";
+import { Modal, Input, Form, message, Select } from "antd";
 import { SaveDataModel } from "@/api";
 import { TableType } from "@/enum";
 import { ProjectInfo } from "@/type";
 import { DEFAULT_SETTING } from "@/constants";
+import CustomColorPicker from "@/components/CustomColorPicker";
 
 export default React.forwardRef(function AddModel(
   props: { onChange?: (val: string | ProjectInfo) => void },
   ref
 ) {
   const [open, setOpen] = React.useState(false);
+  const [loading, setLoading] = React.useState(false);
+  const [color, setColor] = React.useState<string>();
+  const [tableOptions, setTableOptions] = React.useState<any[]>([]);
   const [form] = Form.useForm();
   const type = React.useRef<TableType>();
   const editInfo = useRef<ProjectInfo>();
@@ -82,6 +86,31 @@ export default React.forwardRef(function AddModel(
         <Form.Item name="description" label="模型描述">
           <Input.TextArea placeholder="请输入模型描述" />
         </Form.Item>
+        <Form.Item
+            label="选择表"
+            name="table"
+          >
+            <Select
+              mode="multiple"
+              placeholder="请选择"
+              loading={loading}
+              options={tableOptions}
+            />
+          </Form.Item>
+          <Form.Item label="颜色" name="color">
+            <CustomColorPicker onChange={setColor}>
+              {color ? (
+                <div
+                  className="rounded-4px cus-btn w-32px h-32px bg-#eee flex-none cursor-pointer shadow-inner"
+                  style={{ background: color || "#eee" }}
+                ></div>
+              ) : (
+                <span className="bg-#eee px-5px py-3px rounded-4px cursor-pointer text-12px text-#666">
+                  随机生成,点击可选择颜色
+                </span>
+              )}
+            </CustomColorPicker>
+          </Form.Item>
       </Form>
     </Modal>
   );

+ 3 - 2
apps/er-designer/src/components/DiffTable.tsx

@@ -252,7 +252,7 @@ export default function DiffTable({
         <div className="left rounded-8px border border-1px border-solid border-#979797 overflow-hidden">
           <div className="h-120px p-y-10px p-l-20px">
             <DescComp
-              title="当前模型"
+              title="当前模型"
               detail={sourceTable.table}
               editable={{
                 onSave: async (keypath, newInfo, oriInfo) => {
@@ -271,6 +271,7 @@ export default function DiffTable({
           <Table
             pagination={false}
             columns={baseColumns}
+            rowKey={'id'}
             dataSource={sourceTable.tableColumnList || []}
           />
         </div>
@@ -287,7 +288,7 @@ export default function DiffTable({
         <div className="right rounded-8px border border-1px border-solid border-#979797 overflow-hidden">
           <div className="h-120px p-y-10px p-l-20px">
             <DescComp
-              title="远程数据表"
+              title="数据表"
               detail={tableDetailRes?.result?.[0] || {}}
             />
           </div>

+ 249 - 57
apps/er-designer/src/components/SyncModal.tsx

@@ -1,5 +1,13 @@
-import { forwardRef, useEffect, useImperativeHandle, useMemo, useState } from "react";
-import { Modal, Collapse, message, Empty } from "antd";
+import {
+  forwardRef,
+  Key,
+  useEffect,
+  useImperativeHandle,
+  useMemo,
+  useState,
+} from "react";
+import { Modal, Collapse, Empty, Steps, Result, Button, Spin } from "antd";
+import { ProTable } from "@ant-design/pro-components";
 import DiffTable from "./DiffTable";
 import { useModel, useRequest } from "umi";
 import { PushDataModelTable } from "@/api/dataModel";
@@ -7,16 +15,36 @@ import { GetAllDesignTables } from "@/api";
 import { TableItemType } from "@/type";
 import NoData from "@/assets/no-data.png";
 
-export default forwardRef(function SyncModal(props: {onPush: () => void}, ref) {
+export default forwardRef(function SyncModal(
+  props: { onPush: () => void },
+  ref
+) {
   const [open, setOpen] = useState(false);
   const { project } = useModel("erModel");
   const { data, loading, run } = useRequest(GetAllDesignTables, {
     manual: true,
   });
   const [tableList, setTableList] = useState<TableItemType[]>(project?.tables);
+  const [step, setStep] = useState(0);
+
+  // 选中需要同步的表
+  const [selectedRowKeys, setSelectedRowKeys] = useState<Key[]>([]);
+  const [selectedRows, setSelectedRows] = useState<any[]>([]);
+  const [okLoading, setOkLoading] = useState(false);
+  const [resultStatus, setResultStatus] = useState<"success" | "error">(
+    "success"
+  );
+
+  useEffect(() => {
+    if (step === 0 && tableList.length) {
+      setSelectedRowKeys(tableList.map((item) => item.table.id));
+      setSelectedRows(tableList);
+    }
+  }, [tableList, step]);
 
   useImperativeHandle(ref, () => ({
     open: () => {
+      setStep(0);
       setOpen(true);
       run();
     },
@@ -34,7 +62,7 @@ export default forwardRef(function SyncModal(props: {onPush: () => void}, ref) {
       dataTable: any;
     }[] = [];
     const tables = data?.result?.appBusinessTables || [];
-    tableList.forEach((tableItem) => {
+    selectedRows.forEach((tableItem) => {
       const dataTable = tables.find(
         (item: any) =>
           item.schemaName === tableItem.table.schemaName ||
@@ -49,35 +77,187 @@ export default forwardRef(function SyncModal(props: {onPush: () => void}, ref) {
     });
 
     return list;
-  }, [tableList, data]);
+  }, [selectedRows, data]);
 
+  // 差异对比更新当前模型数据
   const handleUpdateTable = (tableItem: TableItemType) => {
-    setTableList(
-      tableList.map((item) =>
+    setSelectedRows(
+      selectedRows.map((item) =>
         item.table.id === tableItem.table.id ? tableItem : item
       )
     );
-  }
+  };
+
+  const Step1Comp = () => {
+    return (
+      <>
+        <ProTable
+          title={() => "请选择需要推送的表"}
+          dataSource={tableList.map((item) => item.table)}
+          rowKey="id"
+          search={false}
+          pagination={false}
+          options={false}
+          size="small"
+          rowSelection={{
+            selectedRowKeys,
+            onChange(selectedRowKeys) {
+              setSelectedRowKeys(selectedRowKeys);
+              setSelectedRows(
+                tableList.filter((item) =>
+                  selectedRowKeys.includes(item.table.id)
+                )
+              );
+            },
+          }}
+          columns={[
+            {
+              title: "类型",
+              dataIndex: "type",
+              valueType: "select",
+              valueEnum: {
+                3: "业务表",
+                2: "流程表",
+              },
+            },
+            {
+              title: "编码",
+              dataIndex: "schemaName",
+              valueType: "text",
+            },
+            {
+              title: "别名",
+              dataIndex: "aliasName",
+              valueType: "text",
+            },
+            {
+              title: "名称",
+              dataIndex: "langNameList",
+              render: (_, record) => {
+                return (
+                  record.langNameList?.find(
+                    (item: any) => item.name === "zh-CN"
+                  )?.value || "-"
+                );
+              },
+            },
+            {
+              title: "描述",
+              dataIndex: "langDescriptionList",
+              render: (_, record) => {
+                return (
+                  record?.langDescriptionList?.find(
+                    (item: any) => item.name === "zh-CN"
+                  )?.value || "-"
+                );
+              },
+            },
+          ]}
+        />
+      </>
+    );
+  };
+
+  const Step2Comp = () => {
+    const list = selectedRows.filter(({ table }) =>
+      existTableList.find((item) => item.modelTable.table.id === table.id)
+    );
+    return (
+      <>
+        <div className="text-14px font-bold m-y-12px">
+          提示:共选择了
+          <span className="color-green">{selectedRows.length}</span>
+          张表格数据。
+          {existTableList.length ? (
+            <>
+              其中有
+              <span className="color-#faad14">{existTableList.length}</span>
+              张表存在相同的编码或别名,可对比确认后提交。
+            </>
+          ) : null}
+        </div>
+
+        <Collapse
+          items={list.map((item) => {
+            const { table } = item;
+            const name = table.langNameList?.find(
+              (item: any) => item.name === "zh-CN"
+            )?.value;
+            const existTable = existTableList.find(
+              (item) => item.modelTable.table.id === table.id
+            );
+            return {
+              key: table.id,
+              label: (
+                <span
+                  style={{
+                    color: existTable ? "#faad14" : "",
+                  }}
+                >{`${table.schemaName}${name ? `(${name})` : ""}`}</span>
+              ),
+              children: (
+                <DiffTable
+                  sourceTable={item}
+                  targetTableId={existTable?.dataTable?.id}
+                  onChange={handleUpdateTable}
+                />
+              ),
+            };
+          })}
+        />
+      </>
+    );
+  };
+
+  const Step3Comp = () => {
+    return (
+      <div className="flex justify-center items-center h-full">
+        {okLoading ? (
+          <Spin spinning={okLoading} tip="正在同步数据...">
+            <div className="w-500px h-full"></div>
+          </Spin>
+        ) : (
+          <>
+            {resultStatus === "success" ? (
+              <Result
+                status="success"
+                title="数据同步完成!"
+                subTitle="请查看数据表的数据信息"
+              />
+            ) : (
+              <Result
+                status="error"
+                title="数据同步失败!"
+                subTitle="请检查数据表的数据信息"
+              />
+            )}
+          </>
+        )}
+      </div>
+    );
+  };
 
-  const [okLoading, setOkLoading] = useState(false);
   const handleSubmit = async () => {
+    setStep(2);
     try {
       setOkLoading(true);
-      await PushDataModelTable(tableList);
-      message.success("同步推送完成");
+      await PushDataModelTable(selectedRows);
+      // message.success("同步推送完成");
+      setResultStatus("success");
       setOpen(false);
       props.onPush?.();
+    } catch (err) {
+      setResultStatus("error");
     } finally {
       setOkLoading(false);
     }
-  }
+  };
 
   return (
     <Modal
       title="数据同步推送"
       width={"100%"}
       open={open}
-      okText="同步"
       loading={loading}
       style={{
         top: 10,
@@ -91,55 +271,67 @@ export default forwardRef(function SyncModal(props: {onPush: () => void}, ref) {
       }}
       okButtonProps={{
         loading: okLoading,
-        disabled: !tableList.length
+        disabled: !selectedRows.length,
       }}
       onCancel={() => setOpen(false)}
-      onOk={handleSubmit}
-    >
-      <div className="text-14px font-bold m-y-12px">
-        提示:将当前模型表推送至数据表,一共
-        <span className="color-green">{tableList.length}</span>
-        张表格数据。
-        {existTableList.length ? (
+      footer={(_, { CancelBtn }) => {
+        return (
           <>
-            其中有<span className="color-#faad14">{existTableList.length}</span>
-            张表存在相同的编码或别名,可对比确认后提交。
+            <CancelBtn />
+            {step === 0 && (
+              <Button
+                type="primary"
+                disabled={!selectedRows.length}
+                onClick={() => setStep(step + 1)}
+              >
+                下一步
+              </Button>
+            )}
+            {step > 0 && (
+              <Button onClick={() => setStep(step - 1)}>上一步</Button>
+            )}
+            {step === 1 && (
+              <Button type="primary" onClick={handleSubmit}>
+                开始同步
+              </Button>
+            )}
           </>
-        ) : null}
+        );
+      }}
+    >
+      <div className="h-full flex flex-col overflow-hidden">
+        <div className="py-12px px-30px">
+          <Steps
+            current={step}
+            progressDot
+            items={[
+              {
+                title: "选择数据表",
+                description: "选择需要同步的表",
+              },
+              {
+                title: "差异对比",
+                description: "数据模型与数据表进行对比",
+              },
+              {
+                title: "推送",
+                description: "推送到数据表",
+              },
+            ]}
+          />
+        </div>
+        <div className="flex-1 overflow-auto">
+          {step === 0 && <Step1Comp />}
+          {step === 1 && <Step2Comp />}
+          {step === 2 && <Step3Comp />}
+        </div>
       </div>
-      <Collapse
-        items={tableList.map((item) => {
-          const { table } = item;
-          const name = table.langNameList?.find(
-            (item) => item.name === "zh-CN"
-          )?.value;
-          const existTable = existTableList.find(
-            (item) => item.modelTable.table.id === table.id
-          );
-          return {
-            key: table.id,
-            label: (
-              <span
-                style={{
-                  color: existTable
-                    ? "#faad14"
-                    : "",
-                }}
-              >{`${table.schemaName}${name ? `(${name})` : ""}`}</span>
-            ),
-            children: (
-              <DiffTable
-                sourceTable={item}
-                targetTableId={existTable?.dataTable?.id}
-                onChange={handleUpdateTable}
-              />
-            ),
-          };
-        })}
-      />
-      {
-        !tableList.length && <Empty image={NoData} description="当前模型表为空,请添加后再进行同步!"/>
-      }
+      {!tableList.length && (
+        <Empty
+          image={NoData}
+          description="当前模型表为空,请添加后再进行同步!"
+        />
+      )}
     </Modal>
   );
 });

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

@@ -394,7 +394,6 @@ export default forwardRef(function AddTable(
           const newTables = await Promise.all(result);
 
           props.onChange(newTables);
-          console.log(newTables);
           form1.resetFields();
           setOpen(false);
         } catch (err) {

+ 10 - 5
apps/er-designer/src/pages/er/components/RelationPanel.tsx

@@ -8,6 +8,7 @@ import {
   Popconfirm,
   Popover,
   Select,
+  Tooltip,
 } from "antd";
 import React from "react";
 import { RELATION_TYPE_OPTIONS } from "@/constants";
@@ -129,12 +130,16 @@ export default function RelationPanel() {
               }}
             >
               <Form layout="vertical" className="overflow-hidden">
-                <div className="flex justify-between">
-                  <Form.Item label="主键">
-                    {getPrimaryColumn(item)?.table?.schemaName}
+                <div className="flex justify-between gap-25px">
+                  <Form.Item className="w-120px" label="主键">
+                    <Tooltip title={getPrimaryColumn(item)?.table?.schemaName}>
+                      <div className="truncate">{getPrimaryColumn(item)?.table?.schemaName}</div>
+                    </Tooltip>
                   </Form.Item>
-                  <Form.Item label="外键">
-                    {getForeignColumn(item)?.table?.schemaName}
+                  <Form.Item className="w-120px" label="外键">
+                    <Tooltip title={getForeignColumn(item)?.table?.schemaName}>
+                      <div className="truncate">{getForeignColumn(item)?.table?.schemaName}</div>
+                    </Tooltip>
                   </Form.Item>
                   <Popover
                     trigger="click"