123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514 |
- import React, { useEffect, useMemo, useRef, useState } from "react";
- import {
- Layout,
- Menu,
- Button,
- Descriptions,
- Input,
- Tooltip,
- Empty,
- Spin,
- } from "antd";
- import { ProDescriptions } from "@ant-design/pro-components";
- import type { DescriptionsProps, MenuProps } from "antd";
- import { DownOutlined, PlusOutlined, SearchOutlined } from "@ant-design/icons";
- import TableEdit from "@/components/TableEdit";
- import ER from "./components/ER";
- import AddTable from "./components/AddTable";
- import { useModel, history, useRequest, useParams } from "umi";
- import { GetDataModelDetail } from "@/api";
- import NoData from "@/assets/no-data.png";
- import { ColumnItem, ProjectInfo, TableItemType } from "@/type";
- import { useFullscreen } from "ahooks";
- import AddModel from "@/components/AddModel";
- import insertCss from "insert-css";
- import LangInput from "@/components/LangInput";
- import LangInputTextarea from "@/components/LangInputTextarea";
- import { validateAliasName, validateTableCode } from "@/utils/validator";
- import SyncModal from "@/components/SyncModal";
- 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,
- exitPlayMode,
- updateTable,
- graph,
- } = useModel("erModel");
- const [searchKeyword, setSearchKeyword] = useState("");
- const [selectKey, setSelectKey] = useState<string>(
- project.tables?.[0]?.table?.id || ""
- );
- const erRef = useRef<HTMLDivElement>(null);
- const addModelRef = useRef<{ edit: (info: ProjectInfo) => void }>();
- const syncModalRef = useRef<{ open: () => void }>();
- const [isFullscreen, { enterFullscreen, exitFullscreen }] =
- useFullscreen(erRef);
- const [collapsed, setCollapsed] = useState(false);
- useEffect(() => {
- insertCss(`
- .ant-descriptions-header {
- margin-bottom: ${collapsed ? "0 !important" : "20px !important"};
- }
- `);
- }, [collapsed]);
- useEffect(() => {
- setPlayModeEnable(true);
- graph?.disableKeyboard();
- }, [project, graph]);
- const params = useParams();
- const { run, loading } = useRequest(GetDataModelDetail, {
- manual: true,
- onSuccess: (res) => {
- console.log("模型详情:", res);
- const result = res?.result;
- setSelectKey(result?.tables?.[0]?.table?.id || "");
- 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 || "-",
- },
- {
- key: "2",
- label: "创建用户",
- children: project?.createdByName || "-",
- },
- {
- key: "3",
- label: "创建时间",
- children: project?.createTime || "-",
- },
- {
- key: "4-1",
- label: "更新用户",
- children: project?.updatedBy || "-",
- },
- {
- key: "4",
- label: "更新时间",
- children: project?.updateTime || "-",
- },
- {
- key: "5",
- label: "发布状态",
- children: project?.publishStatus || "-",
- },
- {
- key: "6",
- label: "表数",
- children: `${project.tables.length}`,
- },
- {
- key: "7",
- label: "描述",
- children: project?.description || "-",
- },
- ];
- }, [project]);
- const tableData: MenuProps["items"] = useMemo(() => {
- const { tables } = project;
- const treeList: {
- key: string;
- label: string | React.ReactNode;
- icon: React.ReactNode;
- children?: MenuProps["items"];
- }[] = [];
- tables
- .filter((item) => {
- const tableName = item.table.langNameList?.find(
- (item) => item.name === "zh-CN"
- )?.value;
- return (
- item.table.schemaName.includes(searchKeyword) ||
- tableName?.includes(searchKeyword)
- );
- })
- .forEach((item) => {
- const name = item.table.langNameList?.find(
- (item) => item.name === "zh-CN"
- )?.value;
- const newItem = {
- key: item.table.id,
- label: item.table.schemaName + `${name ? `(${name})` : ""}`,
- icon: <i className="iconfont icon-biaogebeifen" />,
- };
- if (!item.table.parentBusinessTableId) {
- treeList.push(newItem);
- } else {
- const parent = treeList.find(
- (tableItem) => tableItem?.key === item.table.parentBusinessTableId
- );
- if (parent) {
- if (!parent?.children) {
- parent.children = [];
- }
- parent.children?.push(newItem);
- }
- }
- });
- return treeList;
- }, [project, searchKeyword]);
- const currentTable = useMemo(() => {
- return project.tables.find((item) => item.table.id === selectKey);
- }, [project, selectKey]);
- const handleAddTable = (tableItem: TableItemType) => {
- setProject({
- ...project,
- tables: [...project.tables, tableItem],
- });
- setSelectKey(tableItem.table.id);
- };
- const handleEnterFullscreen = () => {
- enterFullscreen();
- };
- const handleExitFullscreen = () => {
- exitFullscreen();
- };
- const handleSelectTable = (key: string) => {
- graph?.resetSelection(key);
- graph?.centerCell(graph.getCellById(key));
- setSelectKey(key);
- };
- const handleEnterEdit = () => {
- exitPlayMode();
- const enterpriseCode = sessionStorage.getItem("enterpriseCode");
- history.push(`/er/${project.id}?enterpriseCode=${enterpriseCode}`);
- };
- // 修改表格字段
- const handleChangeColumn = (columns: readonly ColumnItem[]) => {
- currentTable &&
- updateTable({
- ...currentTable,
- tableColumnList: [...columns],
- });
- };
- // 同步数据表
- const handleSync = () => {
- syncModalRef.current?.open();
- };
- const extra = (
- <div className="flex gap-12px">
- <a onClick={handleSync}>
- <i className="iconfont icon-tongbu text-12px" />
- 数据同步
- </a>
- <a onClick={() => project.id && addModelRef.current?.edit(project)}>
- <i className="iconfont icon-bianji text-12px" />
- 基础信息
- </a>
- <a onClick={handleEnterEdit}>
- <i className="iconfont icon-bianji text-12px" />
- 模型编辑
- </a>
- <a>
- <i className="iconfont icon-moban text-14px" />
- 保存为模板
- </a>
- </div>
- );
- return (
- <Spin spinning={loading}>
- {/* 基础信息修改弹窗 */}
- <AddModel
- ref={addModelRef}
- onChange={(info) => {
- typeof info === "object" && setProject(info);
- }}
- />
- {/* 同步弹窗 */}
- <SyncModal ref={syncModalRef} />
- <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>
- {project?.name || "-"}
- <Button
- type="text"
- className={!collapsed ? "rotate-180" : ""}
- icon={<DownOutlined />}
- onClick={() => setCollapsed(!collapsed)}
- />
- </span>
- }
- items={collapsed ? [] : 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="
- flex
- justify-between
- header-tit
- p-x-16px
- p-y-12px
- text-16px
- font-bold
- text-rgba(0, 0, 0, 0.88)
- 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>
- </div>
- <Menu
- mode="inline"
- items={tableData}
- selectedKeys={[selectKey]}
- onSelect={({ key }) => handleSelectTable(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>
- <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="content w-full flex-1 overflow-auto">
- {active === 0 ? (
- <div
- className="er w-full h-full bg-#ccc overflow-auto"
- ref={erRef}
- >
- <ER
- showNavigator={showNavigator}
- isFullScreen={isFullscreen}
- onExitFullscreen={handleExitFullscreen}
- onChangeShowNavigator={setShowNavigator}
- />
- </div>
- ) : (
- <div>
- <div className="p-y-10px p-l-20px">
- <ProDescriptions
- title={currentTable?.table.schemaName}
- dataSource={currentTable?.table}
- editable={{
- onSave: async (keypath, newInfo, oriInfo) => {
- currentTable &&
- updateTable({
- ...currentTable,
- table: {
- ...currentTable?.table,
- ...newInfo,
- langName: "",
- langDescription: "",
- },
- });
- return true;
- },
- }}
- columns={[
- {
- label: "类型",
- dataIndex: "type",
- valueType: "select",
- valueEnum: {
- 3: "业务表",
- 2: "流程表",
- },
- editable: false,
- },
- {
- label: "编码",
- dataIndex: "schemaName",
- valueType: "text",
- formItemProps: {
- rules: [
- { required: true, message: "请输入编码" },
- validateTableCode,
- ],
- },
- },
- {
- label: "别名",
- dataIndex: "aliasName",
- valueType: "text",
- formItemProps: {
- rules: [
- { required: true, message: "请输入别名" },
- validateAliasName,
- ],
- },
- },
- {
- label: "名称",
- dataIndex: "langNameList",
- render: (_, record) => {
- return (
- record.langNameList?.find(
- (item) => item.name === "zh-CN"
- )?.value || "-"
- );
- },
- renderFormItem: (_schema, config, form) => {
- return (
- <LangInput
- style={{ width: 200 }}
- onChange={(val) =>
- form.setFieldValue("langNameList", val)
- }
- />
- );
- },
- },
- {
- label: "描述",
- dataIndex: "langDescriptionList",
- render: (_, record) => {
- return (
- record?.langDescriptionList?.find(
- (item) => item.name === "zh-CN"
- )?.value || "-"
- );
- },
- renderFormItem: (_schema, config, form) => {
- return (
- <>
- <LangInputTextarea
- value={form.getFieldValue(
- "langDescriptionList"
- )}
- onChange={(val) =>
- form.setFieldValue(
- "langDescriptionList",
- val
- )
- }
- />
- </>
- );
- },
- },
- ]}
- ></ProDescriptions>
- </div>
- <TableEdit
- key={selectKey}
- data={currentTable?.tableColumnList || []}
- tableId={currentTable?.table?.id}
- onChange={handleChangeColumn}
- />
- </div>
- )}
- </div>
- </div>
- </Content>
- <AddTable ref={addTableRef} onChange={handleAddTable} />
- </Layout>
- </Spin>
- );
- }
|