Commit 5e42e69d by zhaochengxiang

Merge branch 'model-compare-v2' into 'master'

Model compare v2

See merge request !14
parents 80e8725c 749e6a25
...@@ -24,6 +24,4 @@ npm-debug.log* ...@@ -24,6 +24,4 @@ npm-debug.log*
yarn-debug.log* yarn-debug.log*
yarn-error.log* yarn-error.log*
package-lock.json
yarn.lock
/center-home /center-home
\ No newline at end of file
...@@ -224,6 +224,11 @@ export class App extends React.Component { ...@@ -224,6 +224,11 @@ export class App extends React.Component {
<Route path={'/center-home/data-model-action'} component={EditModel} exact /> <Route path={'/center-home/data-model-action'} component={EditModel} exact />
<Route path={'/center-home/asset-detail'} component={AssetDetailPage} exact /> <Route path={'/center-home/asset-detail'} component={AssetDetailPage} exact />
<Route path={'/center-home/model-review-detail'} component={ModelReviewDetail} /> <Route path={'/center-home/model-review-detail'} component={ModelReviewDetail} />
<Route path={'/data-model-product/physics-model'} component={Model} exact />
<Route path={'/data-model-product/physics-model-branch'} component={Model} exact />
<Route path={'/data-model-product/admin/model-specification'} component={ModelConfig} exact />
</Switch> </Switch>
</Router> </Router>
</AppContext.Provider> </AppContext.Provider>
......
...@@ -210,6 +210,10 @@ export function* dataModelRollback(payload) { ...@@ -210,6 +210,10 @@ export function* dataModelRollback(payload) {
return yield call(datamodelerService.dataModelRollback, payload); return yield call(datamodelerService.dataModelRollback, payload);
} }
export function* compareOtherModel(payload) {
return yield call(datamodelerService.compareOtherModel, payload);
}
export function* getDataModelLocation(payload) { export function* getDataModelLocation(payload) {
return yield call(datamodelerService.getDataModelLocation, payload); return yield call(datamodelerService.getDataModelLocation, payload);
} }
...@@ -250,6 +254,18 @@ export function* heartbeat() { ...@@ -250,6 +254,18 @@ export function* heartbeat() {
return yield call(datamodelerService.heartbeat); return yield call(datamodelerService.heartbeat);
} }
export function* exportTableDDLAbstractList(payload) {
return yield call(datamodelerService.exportTableDDLAbstractList, payload);
}
export function* getExportTableDDL(payload) {
return yield call(datamodelerService.getExportTableDDL, payload);
}
export function* downloadExportTableDDLListZip(payload) {
return yield call(datamodelerService.downloadExportTableDDLListZip, payload);
}
export function* validateDataModel(payload) { export function* validateDataModel(payload) {
return yield call(datamodelerService.validateDataModel, payload); return yield call(datamodelerService.validateDataModel, payload);
} }
...@@ -346,6 +362,10 @@ export function* getPrivilegeAdmin() { ...@@ -346,6 +362,10 @@ export function* getPrivilegeAdmin() {
return yield call(datamodelerService.getPrivilegeAdmin); return yield call(datamodelerService.getPrivilegeAdmin);
} }
export function* getCatalogAdmin() {
return yield call(datamodelerService.getCatalogAdmin);
}
export function* getPrivilegeBranchAdmin() { export function* getPrivilegeBranchAdmin() {
return yield call(datamodelerService.getPrivilegeBranchAdmin); return yield call(datamodelerService.getPrivilegeBranchAdmin);
} }
...@@ -592,6 +612,50 @@ export function* getCompareJobResultDeployModels(payload) { ...@@ -592,6 +612,50 @@ export function* getCompareJobResultDeployModels(payload) {
return yield call(datamodelerService.getCompareJobResultDeployModels, payload) return yield call(datamodelerService.getCompareJobResultDeployModels, payload)
} }
export function* getCompareJobResultMetadataNotMatchCatalog(payload) {
return yield call(datamodelerService.getCompareJobResultMetadataNotMatchCatalog, payload)
}
export function* getCompareJobResultMetadataPartialMatchCatalog(payload) {
return yield call(datamodelerService.getCompareJobResultMetadataPartialMatchCatalog, payload)
}
export function* getCompareJobResultMetadataPerfectMatchCatalog(payload) {
return yield call(datamodelerService.getCompareJobResultMetadataPerfectMatchCatalog, payload)
}
export function* getCompareJobResultModelNotMatchCatalog(payload) {
return yield call(datamodelerService.getCompareJobResultModelNotMatchCatalog, payload)
}
export function* getCompareJobResultModelPartialMatchCatalog(payload) {
return yield call(datamodelerService.getCompareJobResultModelPartialMatchCatalog, payload)
}
export function* getCompareJobResultModelPerfectMatchCatalog(payload) {
return yield call(datamodelerService.getCompareJobResultModelPerfectMatchCatalog, payload)
}
export function* getCompareJobResultModelCount(payload) {
const deployWaitingCount = yield call(datamodelerService.getCompareJobResultDeployWaitingTotalCount, payload)
const deployedCount = yield call(datamodelerService.getCompareJobResultDeployedTotalCount, payload)
const offlineCount = yield call(datamodelerService.getCompareJobResultOfflineTotalCount, payload)
return { totalCount: (deployWaitingCount+deployedCount+offlineCount), deployWaitingCount, deployedCount, offlineCount }
}
export function* getCompareJobResultDeployWaitingModelPage(payload) {
return yield call(datamodelerService.getCompareJobResultDeployWaitingModelPage, payload)
}
export function* getCompareJobResultDeployedModelPage(payload) {
return yield call(datamodelerService.getCompareJobResultDeployedModelPage, payload)
}
export function* getCompareJobResultOfflineModelPage(payload) {
return yield call(datamodelerService.getCompareJobResultOfflineModelPage, payload)
}
export function* addDataTypeConfig(payload) { export function* addDataTypeConfig(payload) {
return yield call(datamodelerService.addDataTypeConfig, payload) return yield call(datamodelerService.addDataTypeConfig, payload)
} }
...@@ -711,3 +775,7 @@ export function* getOwner(payload) { ...@@ -711,3 +775,7 @@ export function* getOwner(payload) {
export function* setOwner(payload) { export function* setOwner(payload) {
return yield call(datamodelerService.setOwner, payload) return yield call(datamodelerService.setOwner, payload)
} }
export function* findAssociationMetadataByModelId(payload) {
return yield call(datamodelerService.findAssociationMetadataByModelId, payload)
}
\ No newline at end of file
import { PostFile, GetJSON, PostJSON, Post, Get, Delete, Delete1, Delete2 } from "../util/axios" import { PostFile, GetJSON, PostJSON, Post, Get, Delete, Delete1, Delete2, callFetchRaw } from "../util/axios"
export function loadDataModelCatalog() { export function loadDataModelCatalog() {
return GetJSON("/datamodeler/easyDataModelerCURD/loadDataModelCatalog"); return GetJSON("/datamodeler/easyDataModelerCURD/loadDataModelCatalog");
...@@ -201,6 +201,10 @@ export function dataModelRollback(payload) { ...@@ -201,6 +201,10 @@ export function dataModelRollback(payload) {
return PostJSON("/datamodeler/easyDataModelerCURD/reset", payload); return PostJSON("/datamodeler/easyDataModelerCURD/reset", payload);
} }
export function compareOtherModel(payload) {
return PostJSON("/datamodeler/easyDataModelerCURD/compareOtherModel", payload);
}
export function ddlGenerators() { export function ddlGenerators() {
return GetJSON("/datamodeler/easyDataModelerExport/ddlGenerators"); return GetJSON("/datamodeler/easyDataModelerExport/ddlGenerators");
} }
...@@ -237,6 +241,18 @@ export function heartbeat() { ...@@ -237,6 +241,18 @@ export function heartbeat() {
return Get("/datamodeler/easyDataModelerExport/heartbeat"); return Get("/datamodeler/easyDataModelerExport/heartbeat");
} }
export function exportTableDDLAbstractList(payload) {
return PostJSON("/datamodeler/easyDataModelerExport/exportTableDDLAbstractList", payload);
}
export function getExportTableDDL(payload) {
return Post("/datamodeler/easyDataModelerExport/getExportTableDDL", payload);
}
export function downloadExportTableDDLListZip(payload) {
return callFetchRaw("post","/datamodeler/easyDataModelerExport/downloadExportTableDDLListZip", payload);
}
export function validateDataModel(payload) { export function validateDataModel(payload) {
return PostJSON("/datamodeler/easyDataModelerConstraint/validateDataModel", payload); return PostJSON("/datamodeler/easyDataModelerConstraint/validateDataModel", payload);
} }
...@@ -297,6 +313,10 @@ export function getPrivilegeAdmin() { ...@@ -297,6 +313,10 @@ export function getPrivilegeAdmin() {
return Get("/datamodeler/easyDataModelerPrivilegeProvider/getAdmin"); return Get("/datamodeler/easyDataModelerPrivilegeProvider/getAdmin");
} }
export function getCatalogAdmin() {
return Get("/datamodeler/easyDataModelerPrivilegeProvider/getCatalogAdmin");
}
export function getPrivilegeBranchAdmin() { export function getPrivilegeBranchAdmin() {
return Get("/datamodeler/easyDataModelerPrivilegeProvider/getBranchCreation"); return Get("/datamodeler/easyDataModelerPrivilegeProvider/getBranchCreation");
} }
...@@ -537,6 +557,55 @@ export function getCompareJobResultDeployModels(payload) { ...@@ -537,6 +557,55 @@ export function getCompareJobResultDeployModels(payload) {
return PostJSON("/datamodeler/easyDataModelModelCompareJobResult/deployModelByResultItemIds", payload) return PostJSON("/datamodeler/easyDataModelModelCompareJobResult/deployModelByResultItemIds", payload)
} }
export function getCompareJobResultMetadataNotMatchCatalog(payload) {
return GetJSON("/datamodeler/easyDataModelModelCompareJobResult/getMetadataNotMatchCatalog", payload)
}
export function getCompareJobResultMetadataPartialMatchCatalog(payload) {
return GetJSON("/datamodeler/easyDataModelModelCompareJobResult/getMetadataPartialMatchCatalog", payload)
}
export function getCompareJobResultMetadataPerfectMatchCatalog(payload) {
return GetJSON("/datamodeler/easyDataModelModelCompareJobResult/getMetadataPerfectMatchCatalog", payload)
}
export function getCompareJobResultModelNotMatchCatalog(payload) {
return GetJSON("/datamodeler/easyDataModelModelCompareJobResult/getModelNotMatchCatalog", payload)
}
export function getCompareJobResultModelPartialMatchCatalog(payload) {
return GetJSON("/datamodeler/easyDataModelModelCompareJobResult/getModelPartialMatchCatalog", payload)
}
export function getCompareJobResultModelPerfectMatchCatalog(payload) {
return GetJSON("/datamodeler/easyDataModelModelCompareJobResult/getModelPerfectMatchCatalog", payload)
}
export function getCompareJobResultDeployWaitingTotalCount(payload) {
return GetJSON("/datamodeler/easyDataModelModelCompareJobResult/getDeployWaitingTotalCount", payload)
}
export function getCompareJobResultDeployWaitingModelPage(payload) {
return GetJSON("/datamodeler/easyDataModelModelCompareJobResult/getDeployWaitingModelPage", payload)
}
export function getCompareJobResultDeployedTotalCount(payload) {
return GetJSON("/datamodeler/easyDataModelModelCompareJobResult/getDeployedTotalCount", payload)
}
export function getCompareJobResultDeployedModelPage(payload) {
return GetJSON("/datamodeler/easyDataModelModelCompareJobResult/getDeployedModelPage", payload)
}
export function getCompareJobResultOfflineTotalCount(payload) {
return GetJSON("/datamodeler/easyDataModelModelCompareJobResult/getOfflineTotalCount", payload)
}
export function getCompareJobResultOfflineModelPage(payload) {
return GetJSON("/datamodeler/easyDataModelModelCompareJobResult/getOfflineModelPage", payload)
}
/*type config*/ /*type config*/
export function addDataTypeConfig(payload) { export function addDataTypeConfig(payload) {
return PostJSON("/datamodeler/easyDataModelerColumnDataType/addDataTypeConfig", payload) return PostJSON("/datamodeler/easyDataModelerColumnDataType/addDataTypeConfig", payload)
...@@ -661,3 +730,8 @@ export function getOwner(payload) { ...@@ -661,3 +730,8 @@ export function getOwner(payload) {
export function setOwner(payload) { export function setOwner(payload) {
return PostJSON("/datamodeler/easyDataModelerCooperation/setDataModelOwner", payload) return PostJSON("/datamodeler/easyDataModelerCooperation/setDataModelOwner", payload)
} }
//模型和元数据关联
export function findAssociationMetadataByModelId(payload) {
return GetJSON("/datamodeler/easyDataModelerMetadataAssociation/findAssociationByModelId", payload)
}
\ No newline at end of file
...@@ -66,10 +66,11 @@ function FC<RowType extends object = any>({ width, maxHeight, pageSize, pageNum, ...@@ -66,10 +66,11 @@ function FC<RowType extends object = any>({ width, maxHeight, pageSize, pageNum,
useEffect(() => { useEffect(() => {
if (!!columns && tableWidth > 0) { if (!!columns && tableWidth > 0) {
const contentWidth = getWidth(width ?? tableWidth,extraColWidth) const contentWidth = getWidth(width ?? tableWidth,extraColWidth)
setDefaultWidth(columns, contentWidth) setDefaultWidth(columns, contentWidth)
paddingCol.current.width = 0 paddingCol.current.width = 0
const cols = columns const newCols = columns
.map((col, index) => { .map((col, index) => {
const render = getRender(col); const render = getRender(col);
const colWidth = col.width ?? 100; const colWidth = col.width ?? 100;
...@@ -83,7 +84,7 @@ function FC<RowType extends object = any>({ width, maxHeight, pageSize, pageNum, ...@@ -83,7 +84,7 @@ function FC<RowType extends object = any>({ width, maxHeight, pageSize, pageNum,
}), }),
}; };
}) })
setCols(cols) setCols(newCols)
} }
}, [columns, tableWidth, width, extraColWidth]) }, [columns, tableWidth, width, extraColWidth])
......
import { AxiosResponse } from "axios"
export default function (res: AxiosResponse<any>) {
const blob = res.data
const headers = res.headers
let tempName = headers["content-disposition"]
?.split(";")?.[1]
?.split("filename=")?.[1]?.split(".")?.[0].replace(/"/g, "");
tempName = decodeURI(tempName);
// const blob = new Blob([content], { type: 'application/octet-stream' })
var url = (window.URL && window.URL.createObjectURL) ? window.URL.createObjectURL(blob) : window.webkitURL.createObjectURL(blob);
const link = document.createElement('a');
link.style.display = 'none';
link.href = url;
link.setAttribute('download', tempName); //or any other extension
document.body.appendChild(link);
link.click();
URL.revokeObjectURL(link.href) // 释放URL 对象
document.body.removeChild(link)
}
\ No newline at end of file
...@@ -16,7 +16,6 @@ export const PermitCheckOut = 'permitCheckOut'; ...@@ -16,7 +16,6 @@ export const PermitCheckOut = 'permitCheckOut';
export const StateId = 'sid'; export const StateId = 'sid';
export const VersionId = 'vid'; export const VersionId = 'vid';
export const TemplateId = 'tid'; export const TemplateId = 'tid';
export const Holder = 'holder';
export const ReadOnly = 'readOnly'; export const ReadOnly = 'readOnly';
export const ApprovalId = 'approvalId'; export const ApprovalId = 'approvalId';
export const ApprovalType = 'approvalType'; export const ApprovalType = 'approvalType';
......
...@@ -563,3 +563,11 @@ export function isChromeVersionLessThan80() { ...@@ -563,3 +563,11 @@ export function isChromeVersionLessThan80() {
return false; return false;
} }
export function openMetadataDetail(id) {
window.open(`/center-home/metadetail?mid=${encodeURIComponent(id)}&action=metadetail&type=detail&manager=false&activekey=1`);
}
export function openModelDetail(id) {
window.open(`/data-govern/data-model-action?action=detail&mid=${id}`);
}
\ No newline at end of file
...@@ -116,11 +116,9 @@ const FC = (props) => { ...@@ -116,11 +116,9 @@ const FC = (props) => {
<div className='edit-header'> <div className='edit-header'>
<span style={{ fontSize: 16, fontWeight: 'bold', color: '#fff' }}>{title}</span> <span style={{ fontSize: 16, fontWeight: 'bold', color: '#fff' }}>{title}</span>
</div> </div>
<div className='edit-container'> <div className='edit-container' style={{ padding: '10px 10px 0' }}>
<div className='edit-container-card' style={{ padding: '20px 20px 0' }}>
<EditAssets ref={editAssetsRef} action={action} type={type} ids={ids} elementIds={elementIds} /> <EditAssets ref={editAssetsRef} action={action} type={type} ids={ids} elementIds={elementIds} />
</div> </div>
</div>
<div className='edit-footer'> <div className='edit-footer'>
{ {
action === 'edit' ? <Space> action === 'edit' ? <Space>
...@@ -459,7 +457,7 @@ export const EditAssets = React.forwardRef(function ({ action, type, ids, elemen ...@@ -459,7 +457,7 @@ export const EditAssets = React.forwardRef(function ({ action, type, ids, elemen
<div ref={tableRef}> <div ref={tableRef}>
<Form form={form} component={false}> <Form form={form} component={false}>
<Table <Table
maxHeight='calc(100vh - 245px)' maxHeight='calc(100vh - 213px)'
loading={loading} loading={loading}
columns={columns} columns={columns}
dataSource={tableData} dataSource={tableData}
......
...@@ -2,25 +2,25 @@ import React, { useState, useEffect, useRef, useMemo } from 'react'; ...@@ -2,25 +2,25 @@ import React, { useState, useEffect, useRef, useMemo } from 'react';
import { Form, Button, Space, Tooltip, Modal, Select, Input } from 'antd'; import { Form, Button, Space, Tooltip, Modal, Select, Input } from 'antd';
import LocalStorage from 'local-storage'; import LocalStorage from 'local-storage';
import { useMount, useUnmount } from 'ahooks'; import { useMount, useUnmount } from 'ahooks';
import { LeftCircleFilled, RightCircleFilled, QuestionCircleFilled } from '@ant-design/icons'; import { LeftCircleFilled, RightCircleFilled } from '@ant-design/icons';
import copy from "copy-to-clipboard"; import copy from "copy-to-clipboard";
import ResizeObserver from 'rc-resize-observer';
import ImportAction from './ImportAction'; import ImportAction from './ImportAction';
import CatalogModal from './CatalogModal'; import CatalogModal from './CatalogModal';
import { dispatchLatest, dispatch } from '../../../../model'; import { dispatchLatest, dispatch } from '../../../../model';
import { getQueryParam, isSzseEnv, showMessage, showNotifaction } from '../../../../util'; import { getQueryParam, isSzseEnv, showMessage, showNotifaction } from '../../../../util';
import { Action, CatalogId, ModelerId, Hints, ModelerData, PermitCheckOut, Editable, StateId, VersionId, Holder, DDL, ReadOnly, BranchId, ApprovalId, ApprovalType, TaskId } from '../../../../util/constant'; import { Action, CatalogId, ModelerId, Hints, ModelerData, PermitCheckOut, Editable, StateId, VersionId, DDL, ReadOnly, BranchId, ApprovalId, ApprovalType, TaskId } from '../../../../util/constant';
import HistoryAndVersionDrawer from './HistoryAndVersionDrawer'; import HistoryAndVersionDrawer from './HistoryAndVersionDrawer';
import { EditModelContext } from './ContextManage'; import { EditModelContext } from './ContextManage';
import EditInherited from './EditInherited'; import EditInherited from './EditInherited';
import { ImportActionHeaderSubject } from './ImportActionManage'; import { ImportActionHeaderSubject } from './ImportActionManage';
import PermissionButton from '../../../../util/Component/PermissionButton'; import PermissionButton from '../../../../util/Component/PermissionButton';
import RecatalogModal from './RecatalogModal'; import RecatalogModal from './RecatalogModal';
import ExportModel from './ExportOtherModal'; import ExportOptions from './export-options';
import ExportDDLModel from './ExportDDLModal'; import ApprovalTip from './approval-tip';
import './EditModel.less'; import './EditModel.less';
import { getTimeProps } from 'antd/lib/date-picker/generatePicker';
const EditModel = (props) => { const EditModel = (props) => {
...@@ -44,15 +44,13 @@ const EditModel = (props) => { ...@@ -44,15 +44,13 @@ const EditModel = (props) => {
const [exportParams, setExportParams] = useState({ const [exportParams, setExportParams] = useState({
visible: false visible: false
}) })
const [exportDDLParams, setExportDDLParams] = useState({ const [containerHeight, setContainerHeight] = useState(0)
visible: false,
})
const actionRef = useRef(''); const actionRef = useRef('');
const attrIsEditingRef = useRef(false); const attrIsEditingRef = useRef(false);
const indexIsEditingRef = useRef(false); const indexIsEditingRef = useRef(false);
const { action, catalogId, modelerId, hints, roughModelerData, permitCheckOut, editable, stateId, versionId, holder, ddl, readOnly, branchId, approvalId, approvalType, taskId } = actionData; const { action, catalogId, modelerId, hints, roughModelerData, permitCheckOut, editable, stateId, versionId, ddl, readOnly, branchId, approvalId, approvalType, taskId } = actionData;
const [form] = Form.useForm(); const [form] = Form.useForm();
const [modal, contextHolder] = Modal.useModal() const [modal, contextHolder] = Modal.useModal()
...@@ -68,7 +66,6 @@ const EditModel = (props) => { ...@@ -68,7 +66,6 @@ const EditModel = (props) => {
const _editable = getQueryParam(Editable, props.location.search); const _editable = getQueryParam(Editable, props.location.search);
const _stateId = getQueryParam(StateId, props.location.search); const _stateId = getQueryParam(StateId, props.location.search);
const _versionId = getQueryParam(VersionId, props.location.search); const _versionId = getQueryParam(VersionId, props.location.search);
const _holder = getQueryParam(Holder, props.location.search);
const _ddl = getQueryParam(DDL, props.location.search); const _ddl = getQueryParam(DDL, props.location.search);
const _readOnly = getQueryParam(ReadOnly, props.location.search); const _readOnly = getQueryParam(ReadOnly, props.location.search);
const _branchId = getQueryParam(BranchId, props.location.search) const _branchId = getQueryParam(BranchId, props.location.search)
...@@ -88,7 +85,7 @@ const EditModel = (props) => { ...@@ -88,7 +85,7 @@ const EditModel = (props) => {
judgeAttributeRepeat(_roughModelerData.easyDataModelerDataModelAttributes); judgeAttributeRepeat(_roughModelerData.easyDataModelerDataModelAttributes);
} }
setActionData({ action: _action, catalogId: _catalogId, modelerId: _modelerId, hints: _hints, roughModelerData: _roughModelerData, permitCheckOut: (_permitCheckOut==='true'), editable: (_editable==='true'), stateId: _stateId, versionId: _versionId, holder: _holder, ddl: _ddl, readOnly: _readOnly, branchId: _branchId, approvalId: _approvalId, approvalType: _approvalType, taskId: _taskId }); setActionData({ action: _action, catalogId: _catalogId, modelerId: _modelerId, hints: _hints, roughModelerData: _roughModelerData, permitCheckOut: (_permitCheckOut==='true'), editable: (_editable==='true'), stateId: _stateId, versionId: _versionId, ddl: _ddl, readOnly: _readOnly, branchId: _branchId, approvalId: _approvalId, approvalType: _approvalType, taskId: _taskId });
actionRef.current = _action; actionRef.current = _action;
if (_approvalId) { if (_approvalId) {
...@@ -295,7 +292,7 @@ const EditModel = (props) => { ...@@ -295,7 +292,7 @@ const EditModel = (props) => {
} }
const edit = () => { const edit = () => {
setActionData({ ...actionData, action: 'edit' }); setActionData({ ...actionData, action: 'edit', permitCheckOut: modelerData?.permitCheckOut });
actionRef.current = 'edit'; actionRef.current = 'edit';
} }
...@@ -353,31 +350,8 @@ const EditModel = (props) => { ...@@ -353,31 +350,8 @@ const EditModel = (props) => {
setExportParams({ visible: true }) setExportParams({ visible: true })
} }
const onExportCancel = (val) => { const onExportInfo = () => {
setExportParams({ visible: false }) window.open(`/api/datamodeler/easyDataModelerExport/modelBaseDataExcel?ids=${modelerData?.id}`);
let _id = modelerData?.id
if (val === 'ddl') {
setExportDDLParams({ visible: true })
} else if (val === 'erwin') {
dispatch({
type: 'datamodel.exportERWinString',
payload: {
ids: _id,
},
callback: data => {
copy(JSON.stringify(data));
showNotifaction('提示', 'Erwin信息已成功复制到剪贴板', 5);
}
});
} else if (val === 'excel') {
window.open(`/api/datamodeler/easyDataModelerExport/excel?ids=${_id}`);
} else if (val === 'word') {
window.open(`/api/datamodeler/easyDataModelerExport/word/template?ids=${_id}`);
} else if (val === 'basicExcel') {
window.open(`/api/datamodeler/easyDataModelerExport/modelBaseDataExcel?ids=${_id}`);
}
} }
const onChangeCatalog = () => { const onChangeCatalog = () => {
...@@ -458,19 +432,18 @@ const EditModel = (props) => { ...@@ -458,19 +432,18 @@ const EditModel = (props) => {
</Space> </Space>
) )
} else if (action === 'detail') { } else if (action === 'detail') {
let editTip = '', deleteTip = '', editDisabled = false;
let editTip = '', deleteTip = '';
if (modelerData?.inheritedFromEasyDataModelerDataModel) { if (modelerData?.inheritedFromEasyDataModelerDataModel) {
editTip = '请到对应的当前表修改'; editTip = '请到对应的当前表修改';
editDisabled = true;
} else { } else {
if (!editable && stateId !== '4') { if (modelerData?.state?.id === '4') {
if (stateId === '2') { if (!modelerData?.permitCheckOut) {
editTip = '待发布的模型不允许编辑'; editTip = `${modelerData?.holder||''}正在编辑中, 不允许再编辑`;
} editDisabled = true;
} }
} else if (!modelerData?.editable) {
if (!permitCheckOut && stateId === '4') { editDisabled = true;
editTip = `${holder||''}正在编辑中, 不允许再编辑`;
} }
} }
...@@ -513,6 +486,16 @@ const EditModel = (props) => { ...@@ -513,6 +486,16 @@ const EditModel = (props) => {
> >
导出 导出
</PermissionButton> </PermissionButton>
<PermissionButton
type='primary'
onClick={onExportInfo}
ghost
permissionKey='导出模型信息'
defaultPermission={branchId?true:undefined}
permissions={privilege?.optionList?.filter(item => item.enabled)?.map(item => item.name)}
>
导出模型信息
</PermissionButton>
{ {
!branchId && <PermissionButton !branchId && <PermissionButton
type='primary' type='primary'
...@@ -546,7 +529,6 @@ const EditModel = (props) => { ...@@ -546,7 +529,6 @@ const EditModel = (props) => {
return; return;
} }
setActionData({ ...actionData, action: 'edit-inherited' });
setEditInheritedParms({visible: true, modelerData}); setEditInheritedParms({visible: true, modelerData});
}} }}
ghost ghost
...@@ -560,7 +542,7 @@ const EditModel = (props) => { ...@@ -560,7 +542,7 @@ const EditModel = (props) => {
<PermissionButton <PermissionButton
type='primary' type='primary'
onClick={edit} onClick={edit}
disabled={ modelerData?.inheritedFromEasyDataModelerDataModel?true:((stateId==='4')?!permitCheckOut:!editable)} disabled={editDisabled}
tip={editTip} tip={editTip}
tooltipPlacement='topRight' tooltipPlacement='topRight'
permissionKey='编辑' permissionKey='编辑'
...@@ -584,7 +566,7 @@ const EditModel = (props) => { ...@@ -584,7 +566,7 @@ const EditModel = (props) => {
onClick={onHistory} onClick={onHistory}
ghost ghost
permissionKey='历史版本' permissionKey='历史版本'
defaultPermission={branchId?true:undefined} defaultPermission={(branchId||approvalId)?true:undefined}
permissions={privilege?.optionList?.filter(item => item.enabled)?.map(item => item.name)} permissions={privilege?.optionList?.filter(item => item.enabled)?.map(item => item.name)}
> >
历史版本 历史版本
...@@ -603,15 +585,18 @@ const EditModel = (props) => { ...@@ -603,15 +585,18 @@ const EditModel = (props) => {
actionsBtn = <PhysicalModelApprovalBottom actionsBtn = <PhysicalModelApprovalBottom
loading={loadingApprovalData} loading={loadingApprovalData}
data={approvalData} data={approvalData}
modelerData={modelerData}
id={currentApprovalModelId} id={currentApprovalModelId}
taskId={taskId} taskId={taskId}
type={approvalType} type={approvalType}
onExport={() => { onExport={() => {
setExportParams({ visible: true }) setExportParams({ visible: true })
}} }}
onExportInfo={onExportInfo}
onHistory={() => { onHistory={() => {
setHistoryAndVersionDrawerVisible(true) setHistoryAndVersionDrawerVisible(true)
}} }}
onEdit={edit}
onChange={(val) => { onChange={(val) => {
setApprovalModelId(val) setApprovalModelId(val)
}} }}
...@@ -619,50 +604,6 @@ const EditModel = (props) => { ...@@ -619,50 +604,6 @@ const EditModel = (props) => {
getApprovalDetail() getApprovalDetail()
}} }}
/> />
} else {
actionsBtn = (
<Space>
<PermissionButton
type='primary'
onClick={onHistory}
ghost
permissionKey='历史版本'
permissions={modelerData?.optionList?.filter(item => item.enabled)?.map(item => item.name)}
>
历史版本
</PermissionButton>
{
!isSzseEnv && !modelerData?.inheritedFromEasyDataModelerDataModel && <PermissionButton
type='primary'
onClick={() => {
if (importActionRef.current && importActionRef.current.isLoading()) {
showMessage("warn", '正在加载中,请稍后!');
return;
}
setActionData({ ...actionData, action: 'edit-inherited' });
setEditInheritedParms({visible: true, modelerData});
}}
ghost
permissionKey='编辑'
permissions={modelerData?.optionList?.filter(item => item.enabled)?.map(item => item.name)}
>
编辑历史存储形式
</PermissionButton>
}
{
!modelerData?.inheritedFromEasyDataModelerDataModel && editable && <PermissionButton
type='primary'
tooltipPlacement='topRight'
onClick={edit}
permissionKey='编辑'
permissions={modelerData?.optionList?.filter(item => item.enabled)?.map(item => item.name)}
>
编辑
</PermissionButton>
}
</Space>
);
} }
} else if (action === 'detail-version') { } else if (action === 'detail-version') {
actionsBtn = ( actionsBtn = (
...@@ -683,11 +624,27 @@ const EditModel = (props) => { ...@@ -683,11 +624,27 @@ const EditModel = (props) => {
<div className='edit-header'> <div className='edit-header'>
<span style={{ fontSize: 16, fontWeight: 'bold', color: '#fff' }}>{title}</span> <span style={{ fontSize: 16, fontWeight: 'bold', color: '#fff' }}>{title}</span>
</div> </div>
<ResizeObserver
onResize={({ height }) => {
setContainerHeight(height)
}}
>
<div className='edit-container'> <div className='edit-container'>
<div className='edit-container-card'> <div className='edit-container-card'>
<ImportAction ref={importActionRef} hints={hints} onChange={onActionChange} action={action} modelerId={modelerId} catalogId={catalogId} ddl={ddl} form={form} terms={terms} roughModelerData={roughModelerData} permitCheckOut={permitCheckOut} stateId={stateId} versionId={versionId} branchId={branchId} autoTabKey={autoTabKey} approvalModelId={currentApprovalModelId} approvalId={approvalId} {...props} /> <ImportAction
ref={importActionRef} hints={hints} action={action}
modelerId={modelerId} catalogId={catalogId} ddl={ddl} form={form}
terms={terms} roughModelerData={roughModelerData}
permitCheckOut={permitCheckOut}
versionId={versionId} branchId={branchId} autoTabKey={autoTabKey}
approvalModelId={currentApprovalModelId} approvalId={approvalId}
height={containerHeight}
onChange={onActionChange}
{...props}
/>
</div> </div>
</div> </div>
</ResizeObserver>
<div className='edit-footer'> <div className='edit-footer'>
{actionsBtn} {actionsBtn}
</div> </div>
...@@ -714,19 +671,11 @@ const EditModel = (props) => { ...@@ -714,19 +671,11 @@ const EditModel = (props) => {
visible={historyAndVersionDrawerVisible} visible={historyAndVersionDrawerVisible}
onCancel={onHistoryAndVersionDrawerCancel} onCancel={onHistoryAndVersionDrawerCancel}
/> />
<ExportModel <ExportOptions
{...exportParams} {...exportParams}
onCancel={onExportCancel} ids={modelerData?.id?[modelerData?.id]:[]}
/>
<ExportDDLModel
{...exportDDLParams}
reference='exportDDL'
ids={[modelerData?.id]}
names={[modelerData?.name]}
onCancel={() => { onCancel={() => {
setExportDDLParams({ setExportParams({ visible: false })
visible: false
})
}} }}
/> />
<EditInherited <EditInherited
...@@ -735,6 +684,8 @@ const EditModel = (props) => { ...@@ -735,6 +684,8 @@ const EditModel = (props) => {
onCancel={(refresh = false) => { onCancel={(refresh = false) => {
setEditInheritedParms({visible: false, modelerData: undefined}); setEditInheritedParms({visible: false, modelerData: undefined});
if (refresh) { if (refresh) {
//刷新模型详情
setActionData({ ...actionData, action: 'edit-inherited' });
setActionData({ ...actionData, action: 'detail' }); setActionData({ ...actionData, action: 'detail' });
actionRef.current = 'detail'; actionRef.current = 'detail';
} }
...@@ -748,10 +699,9 @@ const EditModel = (props) => { ...@@ -748,10 +699,9 @@ const EditModel = (props) => {
export default EditModel; export default EditModel;
const PhysicalModelApprovalBottom = ({ loading, type, data, id, taskId, onChange, onOk, onHistory, onExport }) => { const PhysicalModelApprovalBottom = ({ loading, type, data, modelerData, id, taskId, onChange, onOk, onHistory, onExport, onExportInfo, onEdit }) => {
const [item, setItem] = useState() const [item, setItem] = useState()
const [waiting, setWaiting] = useState(false) const [waiting, setWaiting] = useState(false)
const [tip, setTip] = useState()
useEffect(() => { useEffect(() => {
if (data && id) { if (data && id) {
...@@ -762,12 +712,6 @@ const PhysicalModelApprovalBottom = ({ loading, type, data, id, taskId, onChange ...@@ -762,12 +712,6 @@ const PhysicalModelApprovalBottom = ({ loading, type, data, id, taskId, onChange
} }
}, [data, id]) }, [data, id])
useEffect(() => {
if (type) {
getTip()
}
}, [type])
const [index, canPrev, canNext] = useMemo(() => { const [index, canPrev, canNext] = useMemo(() => {
if (data && id) { if (data && id) {
const index = (data?.modelInfoList??[]).findIndex(item => item.id === id) const index = (data?.modelInfoList??[]).findIndex(item => item.id === id)
...@@ -779,26 +723,22 @@ const PhysicalModelApprovalBottom = ({ loading, type, data, id, taskId, onChange ...@@ -779,26 +723,22 @@ const PhysicalModelApprovalBottom = ({ loading, type, data, id, taskId, onChange
return [0, false, false] return [0, false, false]
}, [data, id]) }, [data, id])
const getTip = () => {
dispatch({
type: 'datamodel.getPhysicalModelApprovalTip',
payload: {
type
},
callback: data => {
setTip(data?.tip)
}
})
}
const onExportClick = () => { const onExportClick = () => {
onExport?.() onExport?.()
} }
const onExportInfoClick = () => {
onExportInfo?.()
}
const onHistoryClick = () => { const onHistoryClick = () => {
onHistory?.() onHistory?.()
} }
const onEditClick = () => {
onEdit?.()
}
const onPrevClick = () => { const onPrevClick = () => {
if (data?.modelInfoList[index-1]) { if (data?.modelInfoList[index-1]) {
onChange?.(data?.modelInfoList[index-1].id) onChange?.(data?.modelInfoList[index-1].id)
...@@ -845,22 +785,28 @@ const PhysicalModelApprovalBottom = ({ loading, type, data, id, taskId, onChange ...@@ -845,22 +785,28 @@ const PhysicalModelApprovalBottom = ({ loading, type, data, id, taskId, onChange
}) })
} }
let editTip = '', editDisabled = false;
if (modelerData?.state?.id === '4') {
if (!modelerData?.permitCheckOut) {
editTip = `${modelerData?.holder||''}正在编辑中, 不允许再编辑`;
editDisabled = true;
}
} else if (!modelerData?.editable) {
editDisabled = true;
}
return ( return (
<div className='flex' style={{ width: '100%', justifyContent: 'space-between' }}> <div className='flex' style={{ width: '100%', justifyContent: 'space-between' }}>
<Space> <Space>
<Button type='primary' ghost onClick={onExportClick}>导出</Button> <Button type='primary' ghost onClick={onExportClick}>导出</Button>
<Button type='primary' ghost onClick={onExportInfoClick}>导出模型信息</Button>
<Button type='primary' ghost onClick={onHistoryClick}>历史版本</Button> <Button type='primary' ghost onClick={onHistoryClick}>历史版本</Button>
<Tooltip title={editTip}>
<Button type='primary' onClick={onEditClick} disabled={editDisabled}>编辑</Button>
</Tooltip>
</Space> </Space>
<Space> <Space>
{ <ApprovalTip type={type} />
tip && <Tooltip title={
<div>
{(tip??'').split('\n').map((item, index) => (<div key={index}>{item}</div>))}
</div>}
>
<QuestionCircleFilled style={{ fontSize: 18, color: '#3B80D6' }} />
</Tooltip>
}
<Tooltip title={((type==='rule')&&!item?.designReviewPass) ? '设计评审不通过' : ''}> <Tooltip title={((type==='rule')&&!item?.designReviewPass) ? '设计评审不通过' : ''}>
<Select <Select
value={(type==='design')?item?.designReviewPass:item?.standardReviewPass} value={(type==='design')?item?.designReviewPass:item?.standardReviewPass}
...@@ -888,11 +834,12 @@ const PhysicalModelApprovalBottom = ({ loading, type, data, id, taskId, onChange ...@@ -888,11 +834,12 @@ const PhysicalModelApprovalBottom = ({ loading, type, data, id, taskId, onChange
</Tooltip> </Tooltip>
<Tooltip title={((type==='rule')&&!item?.designReviewPass) ? '设计评审不通过' : ''}> <Tooltip title={((type==='rule')&&!item?.designReviewPass) ? '设计评审不通过' : ''}>
<Form.Item style={{ marginBottom: 0 }}> <Form.Item style={{ marginBottom: 0 }}>
<Input <Input.TextArea
rows={1}
value={(type==='design')?item?.designReviewComment:item?.standardReviewComment} value={(type==='design')?item?.designReviewComment:item?.standardReviewComment}
disabled={(type==='rule')&&!item?.designReviewPass} disabled={(type==='rule')&&!item?.designReviewPass}
placeholder='请输入评审意见' placeholder='请输入评审意见'
style={{ width: 500 }} style={{ width: 700 }}
allowClear allowClear
onChange={(e) => { onChange={(e) => {
setItem(prev => { setItem(prev => {
......
.edit-model { .edit-model {
display: flex;
flex-direction: column;
overflow: hidden;
height: 100vh;
.edit-header { .edit-header {
display: flex; display: flex;
flex: none;
width: 100%; width: 100%;
height: 44px; height: 44px;
padding: 0 15px; padding: 0 15px;
background-color: #464d6e; background-color: #464d6e;
align-items: center; align-items: center;
// position: fixed;
justify-content: space-between; justify-content: space-between;
border-bottom: 1px solid #EFEFEF;
// z-index: 100;
} }
.edit-container { .edit-container {
flex: 1;
background: #F2F2F2; background: #F2F2F2;
height: calc(100vh - 44px - 64px);
overflow: hidden; overflow: hidden;
} }
.edit-container-card { .edit-container-card {
margin: 10px; margin: 10px;
height: calc(100vh - 44px - 64px - 20px); overflow: hidden;
// background: #fff;
} }
.edit-footer { .edit-footer {
display: flex; display: flex;
bottom: 0; flex: none;
width: 100%; width: 100%;
height: 64px; min-height: 64px;
position: fixed; max-height: 500px;
justify-content: flex-end; justify-content: flex-end;
opacity: 0.9;
background: #fff; background: #fff;
box-shadow: 0 -1px 4px 0 #e5e9ea; box-shadow: 0 -1px 4px 0 #e5e9ea;
padding: 0 20px; padding: 10px 20px;
} }
} }
\ No newline at end of file
import React, { useState } from 'react';
import { Modal, Button, Form, Radio } from 'antd';
const exportModes = [
{ name: '导出DDL', key: 'ddl' },
{ name: '导出Erwin', key: 'erwin' },
{ name: '导出Excel', key: 'excel' },
{ name: '导出Word', key: 'word' },
{ name: '导出模型信息', key: 'basicExcel' },
]
const ExportOtherModal = (props) => {
const { visible, onCancel } = props;
const [ modeKey, setModeKey ] = useState('');
const [ form ] = Form.useForm();
const onModeChange = (e) => {
setModeKey(e.target?.value);
}
const onOk = async() => {
try {
await form.validateFields();
reset();
onCancel && onCancel(modeKey);
} catch (errInfo) {
console.log('Validate Failed:', errInfo);
}
}
const cancel = () => {
reset();
onCancel && onCancel();
}
const reset = () => {
form.resetFields();
setModeKey('');
}
const footer = [
<Button
key="0"
onClick={cancel}
>
取消
</Button>,
<Button
key="1"
type="primary"
onClick={onOk}
>
确定
</Button>,
];
return (
<Modal
forceRender
visible={visible}
title='模型导出'
width={700}
onCancel={cancel}
footer={footer}
>
<Form form={form}>
<Form.Item
name='mode'
label='导出方式'
rules={[
{
required: true,
message: '请选择导出方式',
},
]}
>
<Radio.Group onChange={onModeChange} value={modeKey}>
{
exportModes.map((item, index) => {
return (
<Radio
value={item.key}
key={index}
>
{item.name}
</Radio>
);
})
}
</Radio.Group>
</Form.Item>
</Form>
</Modal>
)
}
export default ExportOtherModal;
\ No newline at end of file
...@@ -19,7 +19,7 @@ import './ImportAction.less' ...@@ -19,7 +19,7 @@ import './ImportAction.less'
export const importActionSubject = new Subject() export const importActionSubject = new Subject()
const ImportAction = React.forwardRef((props, ref) => { const ImportAction = React.forwardRef((props, ref) => {
const { action, hints, onChange, form, modelerId, terms, ddl, roughModelerData, versionId, permitCheckOut, catalogId, branchId, approvalModelId, approvalId, reference = '' } = props; const { action, hints, onChange, form, modelerId, terms, ddl, roughModelerData, versionId, permitCheckOut, catalogId, branchId, approvalModelId, approvalId, reference = '', height } = props;
const [ constraints, setConstraints ] = useState([]); const [ constraints, setConstraints ] = useState([]);
const [ constraint, setConstraint ] = useState({}); const [ constraint, setConstraint ] = useState({});
...@@ -47,13 +47,9 @@ const ImportAction = React.forwardRef((props, ref) => { ...@@ -47,13 +47,9 @@ const ImportAction = React.forwardRef((props, ref) => {
useEffect(() =>{ useEffect(() =>{
if ((action||'')==='') return; if ((action||'')==='') return;
if ((!mountRef.current && action === 'edit' && !permitCheckOut) || action === 'edit-inherited') { if ((!mountRef.current&&action === 'edit'&&!permitCheckOut)
return; || action==='edit-inherited'
} || approvalId) {
//流程打开模型详情的情况下,id由-1变成-2,会再次触发获取模型详情.这里直接return掉
if (approvalId || (!mountRef.current&&action === 'flow')) {
return; return;
} }
...@@ -110,38 +106,22 @@ const ImportAction = React.forwardRef((props, ref) => { ...@@ -110,38 +106,22 @@ const ImportAction = React.forwardRef((props, ref) => {
useEffect(() => { useEffect(() => {
if (approvalModelId) { if (approvalModelId) {
if (action==='edit') {
form?.resetFields();
}
if (action === 'detail'|| action ==='flow') {
//把数据表结构中的数据清空,解决性能问题 //把数据表结构中的数据清空,解决性能问题
const newModelerData = { ...modelerData, easyDataModelerDataModelAttributes: [] }; const newModelerData = { ...modelerData, easyDataModelerDataModelAttributes: [] };
onChange && onChange(newModelerData); onChange && onChange(newModelerData);
setModelerData(newModelerData); setModelerData(newModelerData);
modelerDataRef.current = newModelerData; modelerDataRef.current = newModelerData;
setLoading(true);
dispatch({
type: 'datamodel.getAllConstraints',
callback: data => {
setConstraints(data);
dispatch({
type: 'datamodel.getDataModelWithRecommendedDefinitionAndTermDiscovery',
payload: {
id: approvalModelId,
},
callback: data => {
setLoading(false);
getExtraData(data);
},
error: () => {
setLoading(false);
}
})
},
error: () => {
setLoading(false);
} }
})
getCurrentDataModel()
} }
}, [approvalModelId]) }, [approvalModelId, action])
useEffect(() => { useEffect(() => {
if (constraint?.name) { if (constraint?.name) {
...@@ -274,7 +254,8 @@ const ImportAction = React.forwardRef((props, ref) => { ...@@ -274,7 +254,8 @@ const ImportAction = React.forwardRef((props, ref) => {
} }
const getCurrentDataModel = () => { const getCurrentDataModel = () => {
if ((modelerId||'') === '') { if ((action!=='flow'&&!modelerId)
|| (action === 'flow'&&!approvalModelId)) {
setLoading(false); setLoading(false);
return; return;
} }
...@@ -289,6 +270,9 @@ const ImportAction = React.forwardRef((props, ref) => { ...@@ -289,6 +270,9 @@ const ImportAction = React.forwardRef((props, ref) => {
type = 'datamodel.modelCopy'; type = 'datamodel.modelCopy';
} else if (action === 'flow') { } else if (action === 'flow') {
type = 'datamodel.getDataModelWithRecommendedDefinitionAndTermDiscovery'; type = 'datamodel.getDataModelWithRecommendedDefinitionAndTermDiscovery';
params = {
id: approvalModelId,
}
} else if (action === 'detail-version') { } else if (action === 'detail-version') {
type = 'datamodel.getDataModelByVersionId'; type = 'datamodel.getDataModelByVersionId';
params = { params = {
...@@ -647,7 +631,7 @@ const ImportAction = React.forwardRef((props, ref) => { ...@@ -647,7 +631,7 @@ const ImportAction = React.forwardRef((props, ref) => {
} }
</Tabs> </Tabs>
} }
<div ref={setContainer} style={{ height: action==='edit-inherite-modal'?'60vh':(reference!=='full-search'?'calc(100vh - 44px - 64px - 66px)': '100%'), overflow: 'auto' }}> <div ref={setContainer} style={{ height: action==='edit-inherite-modal'?'60vh':(reference==='full-search'?'100%':(height-66)), overflow: 'auto' }}>
<ImportActionHeader <ImportActionHeader
form={form} form={form}
editable={action!=='detail'&&action!=='flow'&&action!=='detail-version'&&action!=='edit-inherited'} editable={action!=='detail'&&action!=='flow'&&action!=='detail-version'&&action!=='edit-inherited'}
......
...@@ -109,7 +109,7 @@ const FC = (props) => { ...@@ -109,7 +109,7 @@ const FC = (props) => {
</Form> </Form>
) : ( ) : (
<Descriptions column={3}> <Descriptions column={3}>
<Descriptions.Item label={<div style={{ textAlign: 'right', width: 85 }} >维护历史</div>} > <Descriptions.Item label={<div style={{ textAlign: 'right', width: 106 }} >维护历史</div>} >
<div style={{ maxHeight: 70, overflow: 'auto' }}> <div style={{ maxHeight: 70, overflow: 'auto' }}>
{ {
(maintenanceRecords||[]).map((record, index) => { (maintenanceRecords||[]).map((record, index) => {
......
...@@ -2,10 +2,12 @@ import React from "react" ...@@ -2,10 +2,12 @@ import React from "react"
import { Button, Descriptions, Space, Popover } from "antd" import { Button, Descriptions, Space, Popover } from "antd"
import { DownOutlined, UpOutlined, QuestionCircleOutlined } from '@ant-design/icons' import { DownOutlined, UpOutlined, QuestionCircleOutlined } from '@ant-design/icons'
import { Action, ModelerId, PermitCheckOut, Editable, StateId, Holder, ReadOnly } from '../../../../util/constant' import { Action, ModelerId, PermitCheckOut, Editable, StateId, ReadOnly } from '../../../../util/constant'
import { importActionSubject } from "./ImportAction" import { importActionSubject } from "./ImportAction"
import { dispatch } from '../../../../model';
import './ImportActionHeader.less' import './ImportActionHeader.less'
import { openMetadataDetail } from "../../../../util"
export const inheritanceHistoricalType = 'historical' export const inheritanceHistoricalType = 'historical'
export const inheritanceZipperType = 'zipper' export const inheritanceZipperType = 'zipper'
...@@ -14,6 +16,7 @@ const FC = (props) => { ...@@ -14,6 +16,7 @@ const FC = (props) => {
const { modelerData, action } = props const { modelerData, action } = props
const [isCollapse, setCollapse] = React.useState(true) const [isCollapse, setCollapse] = React.useState(true)
const [relationModelerDatas, setRelationModelerDatas] = React.useState([]) const [relationModelerDatas, setRelationModelerDatas] = React.useState([])
const [associationMetadata, setMetadata] = React.useState()
React.useEffect(() => { React.useEffect(() => {
const $importActionSubject = importActionSubject.subscribe((props) => { const $importActionSubject = importActionSubject.subscribe((props) => {
...@@ -54,6 +57,24 @@ const FC = (props) => { ...@@ -54,6 +57,24 @@ const FC = (props) => {
//eslint-disable-next-line react-hooks/exhaustive-deps //eslint-disable-next-line react-hooks/exhaustive-deps
}, [modelerData]) }, [modelerData])
React.useEffect(() => {
if (modelerData?.id) {
getAssociationMetadata()
}
}, [modelerData?.id])
const getAssociationMetadata = () => {
dispatch({
type: 'datamodel.findAssociationMetadataByModelId',
payload: {
easyDataModelerDataModelId: modelerData?.id,
},
callback: data => {
setMetadata(data)
}
})
}
return ( return (
<div className='model-import-action-relation'> <div className='model-import-action-relation'>
<Space> <Space>
...@@ -77,7 +98,23 @@ const FC = (props) => { ...@@ -77,7 +98,23 @@ const FC = (props) => {
!isCollapse && <Descriptions className='mt-3' column={3}> !isCollapse && <Descriptions className='mt-3' column={3}>
<Descriptions.Item <Descriptions.Item
label={ label={
<div style={{ textAlign: 'right', width: 100 }}> <div style={{ textAlign: 'right', width: 106 }}>
元数据
</div>}
>
<span>
{
!associationMetadata?.metadataPath ? '暂无信息' : <a className='mr-3' onClick={() => {
openMetadataDetail(associationMetadata?.metadataId)
}}>
{associationMetadata?.metadataPath}
</a>
}
</span>
</Descriptions.Item>
<Descriptions.Item
label={
<div style={{ textAlign: 'right', width: 106 }}>
历史存储形式 历史存储形式
</div>} </div>}
> >
...@@ -86,7 +123,7 @@ const FC = (props) => { ...@@ -86,7 +123,7 @@ const FC = (props) => {
relationModelerDatas?.length===0 ? '暂无信息' : relationModelerDatas?.length===0 ? '暂无信息' :
relationModelerDatas?.map((item, index) => ( relationModelerDatas?.map((item, index) => (
<a className='mr-3' key={index} onClick={() => { <a className='mr-3' key={index} onClick={() => {
window.open(`/data-govern/data-model-action?${Action}=detail&${ModelerId}=${item.id}&${PermitCheckOut}=${item.permitCheckOut||false}&${Editable}=${item.editable||false}&${StateId}=${item.state?.id||''}&${Holder}=${item.holder||''}&${ReadOnly}=false`); window.open(`/data-govern/data-model-action?${Action}=detail&${ModelerId}=${item.id}&${PermitCheckOut}=${item.permitCheckOut||false}&${Editable}=${item.editable||false}&${StateId}=${item.state?.id||''}&${ReadOnly}=false`);
}}> }}>
{item.cnName} {item.cnName}
</a> </a>
......
import React, { useState, useCallback, useRef, useEffect, useContext, useMemo } from 'react'; import React, { useState, useCallback, useRef, useEffect, useContext, useMemo } from 'react';
import { Input, Form, Typography, Button, Select, Row, Col, Popover, Checkbox, Tooltip, Table, Pagination, Space } from 'antd'; import { Input, Form, Typography, Button, Select, Row, Col, Popover, Checkbox, Tooltip, Pagination, Space } from 'antd';
import { CheckOutlined, PlusOutlined, QuestionCircleOutlined, DeleteOutlined } from '@ant-design/icons'; import { CheckOutlined, PlusOutlined, QuestionCircleOutlined, DeleteOutlined } from '@ant-design/icons';
import { DndProvider, useDrag, useDrop } from 'react-dnd'; import { DndProvider, useDrag, useDrop } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend'; import { HTML5Backend } from 'react-dnd-html5-backend';
...@@ -18,6 +18,7 @@ import Suggest from './suggest'; ...@@ -18,6 +18,7 @@ import Suggest from './suggest';
import { AttentionSvg, UnAttentionSvg } from './ModelSvg'; import { AttentionSvg, UnAttentionSvg } from './ModelSvg';
import { EditModelContext } from './ContextManage'; import { EditModelContext } from './ContextManage';
import { ValidateTip } from './ImportActionHeader'; import { ValidateTip } from './ImportActionHeader';
import Table from '../../ResizeableTable';
import './ImportActionHeader.less'; import './ImportActionHeader.less';
import './ImportActionTable.less'; import './ImportActionTable.less';
...@@ -32,36 +33,6 @@ const MENU_ID = 'model-attribute-menu'; ...@@ -32,36 +33,6 @@ const MENU_ID = 'model-attribute-menu';
export const InputDebounce = DebounceInput(300)(Input); export const InputDebounce = DebounceInput(300)(Input);
const ResizeableHeaderCell = props => {
const { onResize, width, onClick, ...restProps } = props;
if (!width) {
return <th {...restProps} />;
}
return (
<Resizable
width={width}
height={0}
handle={
<span
className="react-resizable-handle"
onClick={(e) => {
e.stopPropagation();
}}
/>
}
onResize={onResize}
draggableOpts={{ enableUserSelectHack: false }}
>
<th
onClick={onClick}
{...restProps}
/>
</Resizable>
);
};
export const DatatypeInput = ({ value = {}, datatypes, onChange }) => { export const DatatypeInput = ({ value = {}, datatypes, onChange }) => {
const onNameChange = (value) => { const onNameChange = (value) => {
...@@ -453,7 +424,6 @@ export const ImportActionTable = (props) => { ...@@ -453,7 +424,6 @@ export const ImportActionTable = (props) => {
dataIndex: 'definition', dataIndex: 'definition',
editable: true, editable: true,
ellipsis: true, ellipsis: true,
width: 200,
render: (text, record, __) => { render: (text, record, __) => {
return ( return (
<React.Fragment> <React.Fragment>
...@@ -1029,30 +999,6 @@ export const ImportActionTable = (props) => { ...@@ -1029,30 +999,6 @@ export const ImportActionTable = (props) => {
} }
} }
const handleResize = index => (e, { size }) => {
const nextColumns = [...columns];
nextColumns[index] = {
...nextColumns[index],
width: size.width,
};
setColumns(nextColumns);
};
const resizeColumns = () => {
return (
columns.map((column, index) => {
return {
...column,
onHeaderCell: column => ({
width: column.width,
onResize: handleResize(index),
}),
};
})
);
}
return ( return (
<div className='model-import-action-table' id='model-import-action-table'> <div className='model-import-action-table' id='model-import-action-table'>
<div className='d-flex mb-3' style={{ justifyContent: 'space-between', alignItems: 'center' }}> <div className='d-flex mb-3' style={{ justifyContent: 'space-between', alignItems: 'center' }}>
...@@ -1101,9 +1047,6 @@ export const ImportActionTable = (props) => { ...@@ -1101,9 +1047,6 @@ export const ImportActionTable = (props) => {
<Form form={form} component={false} onValuesChange={onValuesChange}> <Form form={form} component={false} onValuesChange={onValuesChange}>
<Table <Table
components={{ components={{
header: {
cell: ResizeableHeaderCell,
},
body: { body: {
cell: EditableCell, cell: EditableCell,
//编辑或者搜索状态下不允许拖动 //编辑或者搜索状态下不允许拖动
...@@ -1167,16 +1110,10 @@ export const ImportActionTable = (props) => { ...@@ -1167,16 +1110,10 @@ export const ImportActionTable = (props) => {
return rowParams; return rowParams;
}} }}
dataSource={filterPageData||[]} dataSource={filterPageData||[]}
columns={resizeColumns()} columns={columns??[]}
size='small' size='small'
rowKey='iid' rowKey='iid'
pagination={false} pagination={false}
sticky
scroll={{
x: 1500,
//解决屏幕尺寸窄时,字段不好横向拖动的问题
y: tableWidth>1500?'100%':630,
}}
/> />
</Form> </Form>
</DndProvider> </DndProvider>
......
...@@ -12,6 +12,7 @@ import { useContextMenu, Menu as RcMenu, Item as RcItem } from "react-contexify" ...@@ -12,6 +12,7 @@ import { useContextMenu, Menu as RcMenu, Item as RcItem } from "react-contexify"
import PermissionRcItem from '../../../../util/Component/PermissionRcItem'; import PermissionRcItem from '../../../../util/Component/PermissionRcItem';
import TagCell from './tag-help'; import TagCell from './tag-help';
import BranchModelSync from './branch-model-sync'; import BranchModelSync from './branch-model-sync';
import ModelCompareSelectModel from './model-compare-select-model';
import './ModelTable.less'; import './ModelTable.less';
import 'react-contexify/dist/ReactContexify.css'; import 'react-contexify/dist/ReactContexify.css';
...@@ -19,6 +20,14 @@ import produce from "immer"; ...@@ -19,6 +20,14 @@ import produce from "immer";
const { Paragraph, Text } = Typography; const { Paragraph, Text } = Typography;
export const stateColorDic = {
草稿: '#DE7777',
评审中: '#779BDE',
评审通过: '#7CDEBF',
已上线: '#4C7813',
已下线: '#AAAAAA',
}
const ModelNameColumn = (props) => { const ModelNameColumn = (props) => {
const { text, record, detailItem } = props; const { text, record, detailItem } = props;
const [ data, setData ] = useState(record); const [ data, setData ] = useState(record);
...@@ -149,6 +158,10 @@ const ModelTable = (props) => { ...@@ -149,6 +158,10 @@ const ModelTable = (props) => {
visible: false, visible: false,
item: undefined, item: undefined,
}); });
const [modelCompareSelectModelParams, setModelCompareSelectModelParams] = useState({
visible: false,
item: undefined,
})
const expandedDataMapRef = useRef(new Map()); const expandedDataMapRef = useRef(new Map());
const shouldScrollRef = useRef(false); const shouldScrollRef = useRef(false);
...@@ -235,18 +248,9 @@ const ModelTable = (props) => { ...@@ -235,18 +248,9 @@ const ModelTable = (props) => {
sortable: true, sortable: true,
resizable: true, resizable: true,
formatter(props) { formatter(props) {
let color = '';
if (props.row.state?.id === '1') {
color = '#DE7777';
} else if (props.row.state?.id === '2') {
color = '#779BDE';
} else if (props.row.state?.id === '4') {
color = '#77DEBF';
}
return ( return (
<span> <span>
<span style={{ display: 'inline-block', width: 10, height: 10, borderRadius: 5, marginRight: 5, backgroundColor: color }}></span> <span style={{ display: 'inline-block', width: 10, height: 10, borderRadius: 5, marginRight: 5, backgroundColor: stateColorDic[props.row.state?.cnName]??'#77DEBF' }}></span>
<span>{props.row.state?.cnName||''}</span> <span>{props.row.state?.cnName||''}</span>
</span> </span>
); );
...@@ -668,6 +672,11 @@ const ModelTable = (props) => { ...@@ -668,6 +672,11 @@ const ModelTable = (props) => {
detailItem(data) detailItem(data)
} }
}) })
} else if (key === 'model-compare') {
setModelCompareSelectModelParams({
visible: true,
item: currentItem,
})
} }
} }
...@@ -680,10 +689,6 @@ const ModelTable = (props) => { ...@@ -680,10 +689,6 @@ const ModelTable = (props) => {
} else { } else {
if (!currentItem?.editable && currentItem?.state?.id!=='4') { if (!currentItem?.editable && currentItem?.state?.id!=='4') {
disableEdit = true; disableEdit = true;
if (currentItem?.state?.id === '2') {
editTip = '待发布的模型不允许编辑';
}
} }
if (!currentItem?.permitCheckOut && currentItem?.state?.id==='4') { if (!currentItem?.permitCheckOut && currentItem?.state?.id==='4') {
...@@ -705,23 +710,9 @@ const ModelTable = (props) => { ...@@ -705,23 +710,9 @@ const ModelTable = (props) => {
return ( return (
<div> <div>
<div className='flex' style={{ height: 20, alignItems: 'center', marginBottom: 12 }}>
<Paragraph style={{ overflow: 'hidden' }}>
<Text className='title-color' ellipsis={true}>
总数:
<Text className='text-color'>{(tableData||[]).length}</Text>
</Text>
</Paragraph>
<Paragraph style={{ overflow: 'hidden', marginLeft: 20 }}>
<Text className='title-color' ellipsis={true}>
已选数:
<Text className='text-color'>{summarySelectedCount}</Text>
</Text>
</Paragraph>
</div>
<DataGrid <DataGrid
gridRef={gridRef} gridRef={gridRef}
style={{ blockSize: 'calc(100vh - 94px - 37px - 57px - 24px - 32px)' }} style={{ blockSize: 'calc(100vh - 121px - 152px )' }}
checkable checkable
columns={columns} columns={columns}
// rows={Array.from({ length: 10000 }).map((_, i) => ({ // rows={Array.from({ length: 10000 }).map((_, i) => ({
...@@ -779,6 +770,13 @@ const ModelTable = (props) => { ...@@ -779,6 +770,13 @@ const ModelTable = (props) => {
> >
复制模型 复制模型
</PermissionRcItem> </PermissionRcItem>
{/* <PermissionRcItem
id='model-compare'
defaultPermission={true}
onClick={handleItemClick}
>
模型对比
</PermissionRcItem> */}
{ {
view !== 'branch' && <PermissionRcItem view !== 'branch' && <PermissionRcItem
id='auth-transfer' id='auth-transfer'
...@@ -857,6 +855,15 @@ const ModelTable = (props) => { ...@@ -857,6 +855,15 @@ const ModelTable = (props) => {
} }
}} }}
/> />
<ModelCompareSelectModel
{...modelCompareSelectModelParams}
onCancel={() => {
setModelCompareSelectModelParams({
visible: false,
item: undefined,
})
}}
/>
{ contextHolder } { contextHolder }
</div> </div>
); );
......
...@@ -40,7 +40,7 @@ const ModelTree = (props) => { ...@@ -40,7 +40,7 @@ const ModelTree = (props) => {
id: MENU_ID, id: MENU_ID,
}); });
const { onSelect, onViewChange, refrence='', importStockModel, keyword, searchProperties, setRootNode, onDirRefresh } = props; const { onSelect, onViewChange, refrence='', importStockModel, keyword, searchProperties, setRootNode, setNode, onDirRefresh } = props;
const { user } = useContext(AppContext); const { user } = useContext(AppContext);
const [ loading, setLoading ] = useState(false); const [ loading, setLoading ] = useState(false);
...@@ -62,6 +62,7 @@ const ModelTree = (props) => { ...@@ -62,6 +62,7 @@ const ModelTree = (props) => {
const [ dataList, setDataList ] = useState([]); const [ dataList, setDataList ] = useState([]);
const [options, setOptions] = useState([]); const [options, setOptions] = useState([]);
const [isAdmin, setAdmin] = useState(false); const [isAdmin, setAdmin] = useState(false);
const [isCatalogAdmin, setCatalogAdmin] = useState(false);
const [isBranchAdmin, setBranchAdmin] = useState(false); const [isBranchAdmin, setBranchAdmin] = useState(false);
const [loadingRoot, setLoadingRoot] = useState(false); const [loadingRoot, setLoadingRoot] = useState(false);
...@@ -74,6 +75,7 @@ const ModelTree = (props) => { ...@@ -74,6 +75,7 @@ const ModelTree = (props) => {
useEffect(() => { useEffect(() => {
getShowSyncAndDomains(); getShowSyncAndDomains();
getPrivilegeAdmin(); getPrivilegeAdmin();
getCatalogAdmin();
getPrivilegeBranchAdmin(); getPrivilegeBranchAdmin();
//eslint-disable-next-line react-hooks/exhaustive-deps //eslint-disable-next-line react-hooks/exhaustive-deps
}, []) }, [])
...@@ -117,6 +119,10 @@ const ModelTree = (props) => { ...@@ -117,6 +119,10 @@ const ModelTree = (props) => {
//eslint-disable-next-line react-hooks/exhaustive-deps //eslint-disable-next-line react-hooks/exhaustive-deps
}, [ keyword, searchProperties, viewSelectedKey, item, prevItem]) }, [ keyword, searchProperties, viewSelectedKey, item, prevItem])
useEffect(() => {
setNode?.(item)
}, [item])
const haveStockImportPermission = useMemo(() => { const haveStockImportPermission = useMemo(() => {
return (item?.optionList||[]).findIndex(option => option.name==='存量模型导入'&&option.enabled) !== -1 return (item?.optionList||[]).findIndex(option => option.name==='存量模型导入'&&option.enabled) !== -1
}, [item]) }, [item])
...@@ -149,6 +155,15 @@ const ModelTree = (props) => { ...@@ -149,6 +155,15 @@ const ModelTree = (props) => {
}); });
} }
const getCatalogAdmin = () => {
dispatch({
type: 'datamodel.getCatalogAdmin',
callback: data => {
setCatalogAdmin(data==='true'?true:false);
}
});
}
const getPrivilegeBranchAdmin = () => { const getPrivilegeBranchAdmin = () => {
dispatch({ dispatch({
type: 'datamodel.getPrivilegeBranchAdmin', type: 'datamodel.getPrivilegeBranchAdmin',
...@@ -646,7 +661,7 @@ const ModelTree = (props) => { ...@@ -646,7 +661,7 @@ const ModelTree = (props) => {
</Dropdown> </Dropdown>
{ {
((viewSelectedKey==='dir'&&isAdmin) || (viewSelectedKey==='branch'&&(isAdmin||isBranchAdmin))) && ( ((viewSelectedKey==='dir'&&(isAdmin||isCatalogAdmin)) || (viewSelectedKey==='branch'&&(isAdmin||isBranchAdmin))) && (
<Tooltip title={(viewSelectedKey==='dir')?"新增目录":'新增项目'}> <Tooltip title={(viewSelectedKey==='dir')?"新增目录":'新增项目'}>
<PlusOutlined className='default' onClick={add} style={{ fontSize:16,cursor:'pointer' }} /> <PlusOutlined className='default' onClick={add} style={{ fontSize:16,cursor:'pointer' }} />
</Tooltip> </Tooltip>
...@@ -665,7 +680,7 @@ const ModelTree = (props) => { ...@@ -665,7 +680,7 @@ const ModelTree = (props) => {
</Tooltip> </Tooltip>
{ {
(viewSelectedKey==='dir' && isAdmin) && !isSetRootId && ( (viewSelectedKey==='dir'&&(isAdmin||isCatalogAdmin)) && !isSetRootId && (
<Dropdown overlay={syncMenu} placement="bottomLeft"> <Dropdown overlay={syncMenu} placement="bottomLeft">
<Tooltip title="同步目录"> <Tooltip title="同步目录">
<SyncOutlined className='default' style={{ fontSize:16,cursor:'pointer' }} /> <SyncOutlined className='default' style={{ fontSize:16,cursor:'pointer' }} />
...@@ -675,7 +690,7 @@ const ModelTree = (props) => { ...@@ -675,7 +690,7 @@ const ModelTree = (props) => {
} }
{ {
(viewSelectedKey==='dir' && isAdmin) && isSetRootId && ( (viewSelectedKey==='dir'&&(isAdmin||isCatalogAdmin)) && isSetRootId && (
<Tooltip title="同步目录" className='ml-2'> <Tooltip title="同步目录" className='ml-2'>
<SyncOutlined className='default' style={{ fontSize:16,cursor:'pointer' }} onClick={sync} /> <SyncOutlined className='default' style={{ fontSize:16,cursor:'pointer' }} onClick={sync} />
</Tooltip> </Tooltip>
...@@ -683,7 +698,7 @@ const ModelTree = (props) => { ...@@ -683,7 +698,7 @@ const ModelTree = (props) => {
} }
{ {
(viewSelectedKey==='dir' && !isAdmin) && <React.Fragment> (viewSelectedKey==='dir'&&!isAdmin&&!isCatalogAdmin) && <React.Fragment>
<div style={{ width: 16 }}></div> <div style={{ width: 16 }}></div>
<div style={{ width: 16 }}></div> <div style={{ width: 16 }}></div>
</React.Fragment> </React.Fragment>
...@@ -735,7 +750,7 @@ const ModelTree = (props) => { ...@@ -735,7 +750,7 @@ const ModelTree = (props) => {
return <span title={nodeData?.remark||''}>{nodeData?.name||''}</span>; return <span title={nodeData?.remark||''}>{nodeData?.name||''}</span>;
}} }}
onRightClick={({event, node}) => { onRightClick={({event, node}) => {
if ((viewSelectedKey==='dir'&&isAdmin) || (viewSelectedKey === 'branch'&&(node?.deletable||node?.editable))) { if ((viewSelectedKey==='dir'&&(isAdmin||isCatalogAdmin)) || (viewSelectedKey === 'branch'&&(node?.deletable||node?.editable))) {
setCurrentRightClickDir(node); setCurrentRightClickDir(node);
displayMenu(event); displayMenu(event);
} }
......
...@@ -6,14 +6,12 @@ import { formatVersionDate, showMessage } from '../../../../util'; ...@@ -6,14 +6,12 @@ import { formatVersionDate, showMessage } from '../../../../util';
import VersionCompareHeader from './VersionCompareHeader'; import VersionCompareHeader from './VersionCompareHeader';
import VersionCompareTable from './VersionCompareTable'; import VersionCompareTable from './VersionCompareTable';
import VersionCompareIndex from './VersionCompareIndex'; import VersionCompareIndex from './VersionCompareIndex';
import FilterColumnAction from './FilterColumnAction';
import VersionDdlAlter from './version-ddl-alter'; import VersionDdlAlter from './version-ddl-alter';
import './VersionCompare.less'; import './VersionCompare.less';
const { Text, Paragraph } = Typography; const { Text, Paragraph } = Typography;
const { Option } = Select; const { Option } = Select;
export const defaultColumnTitles = ['序号', '中文名称', '英文名称', '类型', '业务含义'];
const VersionCompare = (props) => { const VersionCompare = (props) => {
...@@ -27,8 +25,6 @@ const VersionCompare = (props) => { ...@@ -27,8 +25,6 @@ const VersionCompare = (props) => {
const [ compareData, setCompareData ] = useState(null); const [ compareData, setCompareData ] = useState(null);
const [ loadingCompare, setLoadingCompare ] = useState(false); const [ loadingCompare, setLoadingCompare ] = useState(false);
const [ onlyShowChange, setOnlyShowChange ] = useState(true); const [ onlyShowChange, setOnlyShowChange ] = useState(true);
const [ attrFilterColumns, setAttrFilterColumns ] = useState([]);
const [ attrSelectedTitles, setAttrSelectedTitles ] = useState(defaultColumnTitles);
const [ddlAlterParams, setAlterParams] = useState({ const [ddlAlterParams, setAlterParams] = useState({
visible: false, visible: false,
id: undefined, id: undefined,
...@@ -37,8 +33,6 @@ const VersionCompare = (props) => { ...@@ -37,8 +33,6 @@ const VersionCompare = (props) => {
incVersion: undefined, incVersion: undefined,
}) })
const attrColumnsRef = useRef([]);
useEffect(() => { useEffect(() => {
if ((id||'') !== '') { if ((id||'') !== '') {
getVersions(); getVersions();
...@@ -132,51 +126,6 @@ const VersionCompare = (props) => { ...@@ -132,51 +126,6 @@ const VersionCompare = (props) => {
callback: data => { callback: data => {
setLoadingCompare(false); setLoadingCompare(false);
setCompareData(data); setCompareData(data);
const newAttrOptionColumns = [];
(data?.heads?.columnHead||[]).forEach((item, index) => {
newAttrOptionColumns.push({
title: item||'',
dataIndex: `column${index}`,
render: (attrValue, record, index) => {
let stateClassName = '';
if (attrValue?.state==='ADD' || attrValue?.state==='UPDATE') {
stateClassName = 'add';
} else if (attrValue?.state === 'DELETE') {
stateClassName = 'delete';
}
return (
<Paragraph>
<Tooltip title={attrValue?.value||''}>
<Text className={stateClassName} ellipsis={true}>{attrValue?.value||''}</Text>
</Tooltip>
</Paragraph>
);
},
width: (item==='序号')?60: 150,
ellipsis: true,
option: true,
});
})
const newAttrColumns = [...newAttrOptionColumns, {
title: <FilterColumnAction columns={newAttrOptionColumns} defaultSelectedKeys={defaultColumnTitles} onChange={onFilterChange} />,
dataIndex: 'columnFilter',
render: (_, record, index) => {
return '';
},
width: 40,
ellipsis: true,
option: false
}];
attrColumnsRef.current = newAttrColumns;
const newFilterColumns = newAttrColumns.filter(column => column.option===false || attrSelectedTitles.indexOf(column.title) !== -1);
setAttrFilterColumns(newFilterColumns);
}, },
error: () => { error: () => {
setLoadingCompare(false); setLoadingCompare(false);
...@@ -184,13 +133,6 @@ const VersionCompare = (props) => { ...@@ -184,13 +133,6 @@ const VersionCompare = (props) => {
}) })
} }
const onFilterChange = (values) => {
const newFilterColumns = attrColumnsRef.current.filter(column => column.option===false || values.indexOf(column.title) !== -1);
setAttrSelectedTitles(values);
setAttrFilterColumns(newFilterColumns);
}
const onAlterClick = () => { const onAlterClick = () => {
setAlterParams({ setAlterParams({
visible: true, visible: true,
...@@ -257,20 +199,7 @@ const VersionCompare = (props) => { ...@@ -257,20 +199,7 @@ const VersionCompare = (props) => {
<div className='py-5'> <div className='py-5'>
<Spin spinning={loadingCompare} > <Spin spinning={loadingCompare} >
{ <CompareDetail data={compareData} />
compareData && <div className='flex'>
<div style={{ flex: 1, borderRight: '1px solid #EFEFEF', paddingRight: 10, overflow: 'hidden'}}>
<VersionCompareHeader data={compareData} />
<VersionCompareTable data={compareData} columns={attrFilterColumns} />
<VersionCompareIndex data={compareData} />
</div>
<div style={{ flex: 1, paddingLeft: 10, overflow: 'hidden'}}>
<VersionCompareHeader data={compareData} direction='right' />
<VersionCompareTable data={compareData} columns={attrFilterColumns} direction='right' />
<VersionCompareIndex data={compareData} direction='right'/>
</div>
</div>
}
</Spin> </Spin>
</div> </div>
<VersionDdlAlter <VersionDdlAlter
...@@ -290,3 +219,35 @@ const VersionCompare = (props) => { ...@@ -290,3 +219,35 @@ const VersionCompare = (props) => {
} }
export default VersionCompare; export default VersionCompare;
export const CompareDetail = ({ data }) => {
const [selectedColumnTitles, setSelectedColumnTitles] = React.useState()
return (
<>
{
data && <div className='flex'>
<div style={{ flex: 1, borderRight: '1px solid #EFEFEF', paddingRight: 10, overflow: 'hidden'}}>
<VersionCompareHeader data={data} />
<VersionCompareTable
data={data}
selectedColumnTitles={selectedColumnTitles}
onFilterChange={(val) => setSelectedColumnTitles(val)}
/>
<VersionCompareIndex data={data} />
</div>
<div style={{ flex: 1, paddingLeft: 10, overflow: 'hidden'}}>
<VersionCompareHeader data={data} direction='right' />
<VersionCompareTable
data={data}
selectedColumnTitles={selectedColumnTitles}
onFilterChange={(val) => setSelectedColumnTitles(val)}
direction='right'
/>
<VersionCompareIndex data={data} direction='right'/>
</div>
</div>
}
</>
)
}
\ No newline at end of file
import { useEffect, useState } from 'react'; import React from 'react';
import { Typography, Table } from 'antd'; import { Typography, Table, Tooltip } from 'antd';
import FilterColumnAction from './FilterColumnAction';
const { Title } = Typography; const { Title } = Typography;
export const defaultColumnTitles = ['序号', '中文名称', '英文名称', '类型', '业务含义'];
const VersionCompareTable = (props) => { const VersionCompareTable = (props) => {
const { data, direction = 'left', selectedColumnTitles = defaultColumnTitles, onFilterChange } = props;
const [columns, setColumns] = React.useState()
const { data, direction = 'left', columns } = props; React.useEffect(() => {
const [ tableData, setTableData ] = useState([]); const newColumns = [];
for (const [index, item] of (data?.heads?.columnHead||[]).entries()) {
newColumns.push({
title: item||'',
dataIndex: `column${index}`,
render: (attrValue, record, index) => {
useEffect(() => { let stateClassName = '';
const newTableData = []; if (attrValue?.state==='ADD' || attrValue?.state==='UPDATE') {
let columnValue = []; stateClassName = 'add';
if (direction==='left') { } else if (attrValue?.state === 'DELETE') {
columnValue = data?.left?.columnValue||[]; stateClassName = 'delete';
} else if (direction==='right') { }
columnValue = data?.right?.columnValue||[];
return (
<Typography.Paragraph>
<Tooltip title={attrValue?.value||''}>
<Typography.Text className={stateClassName} ellipsis={true}>{attrValue?.value||''}</Typography.Text>
</Tooltip>
</Typography.Paragraph>
);
},
width: (item==='序号')?60: 150,
ellipsis: true,
option: true,
});
} }
(columnValue||[]).forEach((attrItem) => { setColumns([...newColumns, {
let newAttrItem = {}; title: <FilterColumnAction columns={newColumns} defaultSelectedKeys={defaultColumnTitles} onChange={(val) => onFilterChange?.(val)} />,
(attrItem||[]).forEach((item, index) => { dataIndex: 'columnFilter',
newAttrItem[`column${index}`] = item; render: (_, record, index) => {
}) return '';
newTableData.push(newAttrItem); },
}) width: 40,
ellipsis: true,
option: false
}])
}, [data])
const _columns = React.useMemo(() => {
return (columns??[]).filter(column => column.option===false || (selectedColumnTitles??[]).indexOf(column.title) !== -1)
}, [selectedColumnTitles, columns])
const tableData = React.useMemo(() => {
const newTableData = [];
let columnValue = (direction==='left') ? data?.left?.columnValue : data?.right?.columnValue;
setTableData(newTableData); for (const item of columnValue??[]) {
let newItem = {};
for (const [index, _item] of item.entries()) {
newItem[`column${index}`] = _item;
}
newTableData.push(newItem);
}
//eslint-disable-next-line react-hooks/exhaustive-deps return newTableData
}, [ data ]) }, [direction, data])
return ( return (
<div> <div>
...@@ -38,7 +79,7 @@ const VersionCompareTable = (props) => { ...@@ -38,7 +79,7 @@ const VersionCompareTable = (props) => {
</Typography> </Typography>
</div> </div>
<Table <Table
columns={columns||[]} columns={_columns||[]}
dataSource={tableData} dataSource={tableData}
pagination={false} pagination={false}
/> />
......
import React from "react"
import { Modal, Button } from 'antd'
import Draggable from 'react-draggable'
import { QuestionCircleFilled } from '@ant-design/icons'
import { dispatch } from '../../../../model'
const FC = (props) => {
const { type } = props
const [tip, setTip] = React.useState()
const [visible, setVisible] = React.useState(false)
const [disabled, setDisabled] = React.useState(true)
const [bounds, setBounds] = React.useState({ left: 0, top: 0, bottom: 0, right: 0 })
const draggleRef = React.useRef()
React.useEffect(() => {
getTip()
}, [])
const title = React.useMemo(() => {
return type==='design' ? '设计评审提示' : '规范评审提示'
}, [type])
const getTip = () => {
dispatch({
type: 'datamodel.getPhysicalModelApprovalTip',
payload: {
type
},
callback: data => {
setTip(data?.tip)
}
})
}
const handleCancel = (e) => {
setVisible(false)
}
const onStart = (_event, uiData) => {
const { clientWidth, clientHeight } = window.document.documentElement;
const targetRect = draggleRef.current?.getBoundingClientRect();
if (!targetRect) {
return;
}
setBounds({
left: -targetRect.left + uiData.x,
right: clientWidth - (targetRect.right - uiData.x),
top: -targetRect.top + uiData.y,
bottom: clientHeight - (targetRect.bottom - uiData.y),
});
}
return (
<>
{
tip && <Button icon={<QuestionCircleFilled style={{ fontSize: 18, color: '#3B80D6' }} />} onClick={() => {
setVisible(true)
}} />
}
<Modal
title={
<div
style={{
width: '100%',
cursor: 'move',
}}
onMouseOver={() => {
if (disabled) {
setDisabled(false);
}
}}
onMouseOut={() => {
setDisabled(true);
}}
// fix eslintjsx-a11y/mouse-events-have-key-events
// https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/master/docs/rules/mouse-events-have-key-events.md
onFocus={() => {}}
onBlur={() => {}}
// end
>
{ title }
</div>
}
visible={visible}
onCancel={handleCancel}
footer={null}
bodyStyle={{ padding: '15px', overflowX: 'auto', maxHeight: '80vh' }}
modalRender={modal => (
<Draggable
disabled={disabled}
bounds={bounds}
onStart={(event, uiData) => onStart(event, uiData)}
>
<div ref={draggleRef}>{modal}</div>
</Draggable>
)}
>
<div>
{ (tip??'').split('\n').map((item, index) => (<div key={index}>{item}</div>)) }
</div>
</Modal>
</>
)
}
export default FC
\ No newline at end of file
...@@ -2,11 +2,7 @@ import React from 'react' ...@@ -2,11 +2,7 @@ import React from 'react'
import { Modal, Button, Spin, Form, Checkbox, Typography, Tooltip, Row, Col, Tree, } from 'antd' import { Modal, Button, Spin, Form, Checkbox, Typography, Tooltip, Row, Col, Tree, } from 'antd'
import { dispatch } from '../../../../model' import { dispatch } from '../../../../model'
import VersionCompareHeader from './VersionCompareHeader' import { CompareDetail } from './VersionCompare'
import VersionCompareTable from './VersionCompareTable'
import VersionCompareIndex from './VersionCompareIndex'
import { defaultColumnTitles } from './VersionCompare'
import FilterColumnAction from './FilterColumnAction'
import './VersionCompare.less' import './VersionCompare.less'
import { formatDate } from '../../../../util' import { formatDate } from '../../../../util'
...@@ -61,7 +57,7 @@ const FC = (props) => { ...@@ -61,7 +57,7 @@ const FC = (props) => {
onCancel={() => { close() }} onCancel={() => { close() }}
> >
<Spin spinning={waiting}> <Spin spinning={waiting}>
<Diff item={item} /> <Basic item={item} />
</Spin> </Spin>
</Modal> </Modal>
) )
...@@ -69,12 +65,10 @@ const FC = (props) => { ...@@ -69,12 +65,10 @@ const FC = (props) => {
export default FC export default FC
const Diff = ({ item }) => { const Basic = ({ item }) => {
const [loading, setLoading] = React.useState(false) const [loading, setLoading] = React.useState(false)
const [data, setData] = React.useState() const [data, setData] = React.useState()
const [onlyShowChange, setOnlyShowChange] = React.useState(true) const [onlyShowChange, setOnlyShowChange] = React.useState(true)
const [attrSelectedTitles, setAttrSelectedTitles] = React.useState(defaultColumnTitles)
const [attrColumns, setAttrColumns] = React.useState()
React.useEffect(() => { React.useEffect(() => {
if (item) { if (item) {
...@@ -82,59 +76,6 @@ const Diff = ({ item }) => { ...@@ -82,59 +76,6 @@ const Diff = ({ item }) => {
} }
}, [item, onlyShowChange]) }, [item, onlyShowChange])
React.useEffect(() => {
if (data) {
const newAttrOptionColumns = [];
(data?.heads?.columnHead||[]).forEach((item, index) => {
newAttrOptionColumns.push({
title: item||'',
dataIndex: `column${index}`,
render: (attrValue, record, index) => {
let stateClassName = '';
if (attrValue?.state==='ADD' || attrValue?.state==='UPDATE') {
stateClassName = 'add';
} else if (attrValue?.state === 'DELETE') {
stateClassName = 'delete';
}
return (
<Typography.Paragraph>
<Tooltip title={attrValue?.value||''}>
<Typography.Text className={stateClassName} ellipsis={true}>{attrValue?.value||''}</Typography.Text>
</Tooltip>
</Typography.Paragraph>
);
},
width: (item==='序号')?60: 150,
ellipsis: true,
option: true,
});
});
const newAttrColumns = [...newAttrOptionColumns, {
title: <FilterColumnAction columns={newAttrOptionColumns} defaultSelectedKeys={defaultColumnTitles} onChange={onFilterChange} />,
dataIndex: 'columnFilter',
render: (_, record, index) => {
return '';
},
width: 40,
ellipsis: true,
option: false
}];
setAttrColumns(newAttrColumns)
}
}, [data])
const attrFilterColumns = React.useMemo(() => {
return (attrColumns??[]).filter(column => column.option===false || (attrSelectedTitles??[]).indexOf(column.title) !== -1)
}, [attrSelectedTitles, attrColumns])
const onFilterChange = (values) => {
setAttrSelectedTitles(values)
}
const onOnlyShowChange = (e) => { const onOnlyShowChange = (e) => {
setOnlyShowChange(e.target.checked) setOnlyShowChange(e.target.checked)
} }
...@@ -176,20 +117,7 @@ const Diff = ({ item }) => { ...@@ -176,20 +117,7 @@ const Diff = ({ item }) => {
<div className='py-5'> <div className='py-5'>
<Spin spinning={loading} > <Spin spinning={loading} >
{ <CompareDetail data={data} />
data && <div className='flex'>
<div style={{ flex: 1, borderRight: '1px solid #EFEFEF', paddingRight: 10, overflow: 'hidden'}}>
<VersionCompareHeader data={data} />
<VersionCompareTable data={data} columns={attrFilterColumns} />
<VersionCompareIndex data={data} />
</div>
<div style={{ flex: 1, paddingLeft: 10, overflow: 'hidden'}}>
<VersionCompareHeader data={data} direction='right' />
<VersionCompareTable data={data} columns={attrFilterColumns} direction='right' />
<VersionCompareIndex data={data} direction='right'/>
</div>
</div>
}
</Spin> </Spin>
</div> </div>
</div> </div>
......
...@@ -203,22 +203,7 @@ const Basic = React.forwardRef(function ({ type, items }, ref) { ...@@ -203,22 +203,7 @@ const Basic = React.forwardRef(function ({ type, items }, ref) {
setLoadingTreeData(false) setLoadingTreeData(false)
setRootNode(data) setRootNode(data)
const newTreeData = produce(data?.subCatalogs??[], draft => { const newTreeData = data?.subCatalogs??[]
const setNode = (g) => {
g.key = g.id;
g.title = g.name;
g.children = [];
(g.subCatalogs??[]).forEach((child) => {
setNode(child)
g.children.push(child)
});
}
draft.forEach((child) => {
setNode(child)
})
})
setTreeData(newTreeData) setTreeData(newTreeData)
if ((newTreeData??[]).length > 0) { if ((newTreeData??[]).length > 0) {
setNode(newTreeData[0]) setNode(newTreeData[0])
......
import React from "react"
import { Modal, Button, Row, Col, Space, Select, Input, Form, Tooltip, Typography, Spin, Checkbox } from 'antd'
import { SettingFilled } from '@ant-design/icons';
import { dispatch } from '../../../../model'
import { formatVersionDate } from "../../../../util"
import download from "../../../../util/Component/download"
import { config } from "rxjs"
const ddlFilterInfo = {
: {
tableDrop: 'DROP',
tableCreate: 'CREATE',
tableAlert: 'ALERT',
tableSchema: '表物理位置',
tableCnName: '表中文名',
tableRemark: '表描述',
},
KEY: {
primaryKey: '主键',
primaryKeyConstraint: '主键约束',
},
字段: {
columnCnName: '中文名',
columnRemark: '字段描述',
},
索引: {
index: '索引',
indexRemark: '注释',
}
}
const notAlertSpecialInfo = [
{ key: 'tableCreate', defaultValue: true },
{ key: 'tableAlert', defaultValue: false },
]
const alertSpecialInfo = [
{ key: 'tableCreate', defaultValue: false },
{ key: 'tableAlert', defaultValue: true },
{ key: 'tableDrop', defaultValue: false },
]
const FC = (props) => {
const { ids, visible, onCancel } = props
const [downloading, setDownloading] = React.useState(false)
const basicRef = React.useRef()
const close = () => {
setDownloading(false)
onCancel?.()
}
const onDownload = () => {
setDownloading(true)
dispatch({
type: 'datamodel.downloadExportTableDDLListZip',
payload: {
data: basicRef.current.configs??[],
headers: {
'Content-Type': 'application/json',
},
responseType: 'blob',
},
callback: (data) => {
setDownloading(false)
download(data)
},
error: () => {
setDownloading(false)
}
})
}
const footer = React.useMemo(() => {
return [
<Button key={'cancel'}
onClick={() => close()}
>取消</Button>,
<Button key={'save'} type='primary'
loading={downloading}
disabled={downloading}
onClick={() => onDownload()}
>导出</Button>
]
}, [close, onDownload, downloading])
return (
<Modal
visible={visible}
footer={footer}
width='90%'
bodyStyle={{ padding: '15px', height: '80vh', overflow: 'auto' }}
title='DDL导出详情'
centered destroyOnClose
onCancel={() => { close() }}
>
<Basic ref={basicRef} ids={ids} />
</Modal>
)
}
export default FC
const Basic = React.forwardRef(function ({ ids }, ref) {
const [loading, setLoading] = React.useState(false)
const [data, setData] = React.useState()
const [configs, setConfigs] = React.useState()
const [currentConfig, setConfig] = React.useState()
const [loadingDDLGenerators, setLoadingDDLGenerators] = React.useState(false)
const [ddlGenerators, setDDLGenerators] = React.useState()
React.useImperativeHandle(ref, () => ({
configs,
}), [configs])
React.useEffect(() => {
if ((ids??[]).length > 0) {
exportTableDDLAbstractList()
}
}, [ids])
React.useEffect(() => {
getDDLGenerators()
}, [])
React.useEffect(() => {
let ddlFilter = {}
for (const key in ddlFilterInfo) {
for (const _key in ddlFilterInfo[key]) {
ddlFilter[_key] = true
const index = notAlertSpecialInfo.findIndex(item => item.key === _key)
if (index !== -1) {
ddlFilter[_key] = notAlertSpecialInfo[index].defaultValue
}
}
}
const newConfigs = (data??[]).map(item => ({
alertDLL: false, //false全量 true增量
...item,
ddlFilter,
}))
setConfigs(newConfigs)
if ((newConfigs??[]).length > 0) {
setConfig(newConfigs[0])
}
}, [data])
const exportTableDDLAbstractList = () => {
setLoading(true)
dispatch({
type: 'datamodel.exportTableDDLAbstractList',
payload: {
data: ids,
},
callback: (data) => {
setLoading(false)
setData(data)
},
error: () => {
setLoading(false)
}
})
}
const getDDLGenerators = (needDB = false) => {
setLoadingDDLGenerators(true)
dispatch({
type: 'datamodel.ddlGenerators',
callback: data => {
setLoadingDDLGenerators(false)
setDDLGenerators(data)
},
error: () => {
setLoadingDDLGenerators(false)
}
})
}
return (
<Spin spinning={loading}>
<Row gutter={15}>
<Col flex='350px' style={{ height: 'calc(80vh - 30px)', overflow: 'auto' }}>
{ data?.map((item, index) => (
<Row gutter={10} key={index} className={index!==0?'mt-2':''} align='middle'>
<Col flex='1' style={{ overflow: 'hidden' }}>
<Tooltip title={item.modelEnName}>
<Typography.Text ellipsis={true}>
<a style={{ color: (item.easyDataModelerDataModelId===currentConfig?.easyDataModelerDataModelId)?'#196AD2':'#262626' }}
onClick={() => {
const index = (configs??[]).findIndex(_item => item.easyDataModelerDataModelId === _item.easyDataModelerDataModelId)
if (index !== -1) {
setConfig(configs[index])
}
}}
>
{item.modelEnName}
</a>
</Typography.Text>
</Tooltip>
</Col>
<Col flex='none'>
<Select defaultValue={item.dbType}
style={{ width: 120 }}
loading={loadingDDLGenerators}
onChange={(val) => {
const index = (configs??[]).findIndex(_item => item.easyDataModelerDataModelId === _item.easyDataModelerDataModelId)
if (index !== -1) {
const newConfig = { ...configs[index], dbType: val }
setConfigs((prev) => {
const newConfigs = [...prev??[]]
newConfigs.splice(index, 1, newConfig)
return newConfigs
})
if (configs[index].easyDataModelerDataModelId === currentConfig?.easyDataModelerDataModelId) {
setConfig(newConfig)
}
}
}}
>
{
ddlGenerators?.map(item => (
<Select.Option key={item.name} value={item.name}>{item.name}</Select.Option>
))
}
</Select>
</Col>
<Col flex='none'>
<Select defaultValue={false}
style={{ width: 80 }}
onChange={(val) => {
const index = (configs??[]).findIndex(_item => item.easyDataModelerDataModelId === _item.easyDataModelerDataModelId)
if (index !== -1) {
const newConfig = { ...configs[index], alertDLL: val, leftVersionId: null, rightVersionId: null }
const info = val ? alertSpecialInfo : notAlertSpecialInfo
for (const item of info) {
newConfig.ddlFilter[item.key] = item.defaultValue
}
setConfigs((prev) => {
const newConfigs = [...prev??[]]
newConfigs.splice(index, 1, newConfig)
return newConfigs
})
if (configs[index].easyDataModelerDataModelId === currentConfig?.easyDataModelerDataModelId) {
setConfig(newConfig)
}
}
}}
>
<Select.Option value={false}>全量</Select.Option>
{ item.supportAlertDLL && <Select.Option value={true}>增量</Select.Option> }
</Select>
</Col>
</Row>
)) }
</Col>
<Col flex='1' style={{ overflow: 'hidden' }}>
<DDLDetail config={currentConfig} setConfig={(val) => {
const index = (configs??[]).findIndex(item => item.easyDataModelerDataModelId === val?.easyDataModelerDataModelId)
if (index !== -1) {
setConfigs((prev) => {
const newConfigs = [...prev??[]]
newConfigs.splice(index, 1, val)
return newConfigs
})
setConfig(val)
}
}} />
</Col>
</Row>
</Spin>
)
})
const DDLDetail = ({ config, setConfig }) => {
const [loadingVersions, setLoadingVersions] = React.useState(false)
const [versions, setVersions] = React.useState()
const [incVersions, setIncVersions] = React.useState()
const [loadingDDL, setLoadingDDL] = React.useState(false)
const [ddl, setDDL] = React.useState()
const [ddlFilterParams, setDDLFilterParams] = React.useState({
visible: false,
})
const prevModelIdRef = React.useRef()
React.useEffect(() => {
if (config?.easyDataModelerDataModelId && config?.easyDataModelerDataModelId!==prevModelIdRef.current) {
prevModelIdRef.current = config?.easyDataModelerDataModelId
getVersions()
} else {
initConfigVersionId()
}
}, [config?.alertDLL, config?.easyDataModelerDataModelId])
React.useEffect(() => {
if ((config?.alertDLL&&config?.leftVersionId&&config?.rightVersionId)
|| (!config?.alertDLL&&config?.leftVersionId)) {
getDDL()
}
}, [config?.alertDLL, config?.dbType, config?.leftVersionId, config?.rightVersionId, config?.ddlFilter])
const getVersions = () => {
setLoadingVersions(true)
setVersions()
dispatch({
type: 'datamodel.getVersions',
payload: {
params: {
id: config?.easyDataModelerDataModelId,
}
},
callback: data => {
setLoadingVersions(false)
const newData = []
for (const [index, item] of (data??[]).entries()) {
let name = item.name??''
name = name + '_' + formatVersionDate(item.ts)
if ((index === 0&&item.id !== '-1')
|| (index === 1&&data[0].id === '-1')) {
name = name+'(当前版本)'
}
newData.push({ ...item, name })
}
setVersions(newData)
initConfigVersionId()
},
error: () => {
setLoadingVersions(false)
}
})
}
const initConfigVersionId = () => {
setVersions(prevVersions => {
if (config?.alertDLL) {
//增量
if ((prevVersions??[]).length > 1) {
setIncVersions((prevVersions??[]).slice(0, 1))
if (!config?.leftVersionId && !config?.rightVersionId) {
setConfig?.({
...config,
leftVersionId: prevVersions[1].id,
rightVersionId: prevVersions[0].id,
})
}
}
} else {
//全量
if ((prevVersions??[]).length > 0) {
if (!config?.leftVersionId) {
setConfig?.({
...config,
leftVersionId: prevVersions[0].id,
rightVersionId: null,
})
}
}
}
return prevVersions
})
}
const getDDL = () => {
setLoadingDDL(true)
dispatch({
type: 'datamodel.getExportTableDDL',
payload: {
data: config,
},
callback: data => {
setLoadingDDL(false)
setDDL(data)
},
error: () => {
setLoadingDDL(false)
}
})
}
const onBasicChange = (val) => {
setConfig({
...config,
leftVersionId: val,
rightVersionId: null,
})
const index = (versions??[]).findIndex(item => item.id === val)
if (index !== -1) {
setIncVersions((versions||[]).slice(0, index))
}
}
const onIncChange = (val) => {
setConfig({
...config,
rightVersionId: val,
})
}
return (
<>
<div className='flex' style={{ justifyContent: 'space-between' }}>
<Form layout='inline'>
<Form.Item label='基线版本'>
<Select loading={loadingVersions} value={(versions??[]).length>0?config?.leftVersionId:undefined} style={{ width: 300 }} onChange={onBasicChange} >
{
versions?.map((item, index) => (
<Select.Option key={index} value={item.id} disabled={config?.alertDLL&&index===0}>
<Tooltip title={(config?.alertDLL&&index===0)?'最近版本只能在增量版本中被选中':''}>
{item.name}
</Tooltip>
</Select.Option>
))
}
</Select>
</Form.Item>
{
config?.alertDLL && <Form.Item label='增量版本' style={{ marginRight: 5 }}>
<Select value={(incVersions??[]).length>0?config?.rightVersionId:undefined} style={{ width: 300 }} disabled={!config?.leftVersionId} onChange={onIncChange}>
{
(incVersions||[]).map((item, index) => (
<Select.Option key={index} value={item.id}>{item.name}</Select.Option>
))
}
</Select>
</Form.Item>
}
</Form>
<Button icon={<SettingFilled />} onClick={() => { setDDLFilterParams({ visible: true }) }} />
</div>
<div className='mt-2'>
<Spin spinning={loadingDDL}>
<Input.TextArea value={ddl??''} style={{ height: 'calc(80vh - 70px)', resize: 'none' }} />
</Spin>
</div>
<DDLFilter
{...ddlFilterParams}
config={config}
setConfig={setConfig}
onCancel={() => {
setDDLFilterParams({ visible: false })
}}
/>
</>
)
}
const DDLFilter = (props) => {
const { visible, onCancel, config, setConfig } = props
const [form] = Form.useForm()
React.useEffect(() => {
if (visible) {
form.setFieldsValue(config?.ddlFilter)
}
}, [config, visible])
const close = () => {
onCancel?.()
}
const save = () => {
setConfig?.({ ...config, ddlFilter: form?.getFieldsValue() })
close()
}
const footer = React.useMemo(() => {
return [
<Button key='cancel'
onClick={() => close()}
>取消</Button>,
<Button key='save' type='primary'
onClick={() => save()}
>确定</Button>
]
}, [close, save])
const onValuesChange = (changedValues, allValues) => {
if (changedValues.hasOwnProperty('primaryKeyConstraint')) {
if (changedValues['primaryKeyConstraint']) {
form?.setFieldsValue({ primaryKey: true })
}
}
if (changedValues.hasOwnProperty('primaryKey')) {
if (!changedValues['primaryKey']) {
form?.setFieldsValue({ primaryKeyConstraint: false })
}
}
if (changedValues.hasOwnProperty('indexRemark')) {
if (changedValues['indexRemark']) {
form?.setFieldsValue({ index: true })
}
}
if (changedValues.hasOwnProperty('index')) {
if (!changedValues['index']) {
form?.setFieldsValue({ indexRemark: false })
}
}
}
return (
<Modal
visible={visible}
footer={footer}
bodyStyle={{ padding: '15px 15px 0px', overflowX: 'auto', maxHeight: '80vh' }}
title=''
width={600}
closable={false}
centered destroyOnClose
onCancel={() => { close() }}
>
<Form
form={form}
labelCol={{ span: 4 }}
wrapperCol={{ span: 20 }}
autoComplete="off"
onValuesChange={onValuesChange}
>
{
Object.keys(ddlFilterInfo).map((key, index) => {
const item = ddlFilterInfo[key]
return (
<Form.Item
key={key}
label={`对于${key}`}
style={{ marginBottom: 15 }}
>
<Row>
{
Object.keys(item).map((_key, index) => {
let disabled = false
const info = (config?.alertDLL) ? alertSpecialInfo : notAlertSpecialInfo
const _index = info.findIndex(item => item.key === _key)
disabled = (_index !== -1)
return (
<Col span={6} key={index}>
<Form.Item name={_key}
style={{ marginBottom: 0 }}
valuePropName="checked"
>
<Checkbox disabled={disabled}>
{item[_key]}
</Checkbox>
</Form.Item>
</Col>
)
})
}
</Row>
</Form.Item>
)
})
}
</Form>
</Modal>
)
}
\ No newline at end of file
import React from 'react';
import { Modal, Button, Form, Radio } from 'antd';
import copy from "copy-to-clipboard"
import { dispatch } from '../../../../model'
import { showNotifaction } from '../../../../util'
import ExportDDL from './export-ddl'
const exportModes = [
{ name: '导出DDL', key: 'ddl' },
{ name: '导出Erwin', key: 'erwin' },
{ name: '导出Excel', key: 'excel' },
{ name: '导出Word', key: 'word' },
]
const FC = (props) => {
const { ids, visible, onCancel } = props
const [exportDDLParams, setExportDDLParams] = React.useState({
visible: false
})
const basicRef = React.useRef()
const [form] = Form.useForm()
const save = async() => {
try {
await basicRef.current.validate()
const modeKey = basicRef.current.modeKey
if (modeKey === 'ddl') {
setExportDDLParams({ visible: true })
} if (modeKey === 'erwin') {
dispatch({
type: 'datamodel.exportERWinString',
payload: {
ids: (ids??[]).toString(),
},
callback: data => {
copy(JSON.stringify(data))
showNotifaction('提示', 'Erwin信息已成功复制到剪贴板', 5)
}
})
} else if (modeKey === 'excel') {
window.open(`/api/datamodeler/easyDataModelerExport/excel?ids=${(ids??[]).toString()}`)
} else if (modeKey === 'word') {
window.open(`/api/datamodeler/easyDataModelerExport/word/template?ids=${(ids??[]).toString()}`)
}
} catch (errInfo) {
console.log('Validate Failed:', errInfo);
}
}
const close = () => {
onCancel?.()
}
const footer = React.useMemo(() => {
return [
<Button key='cancel'
onClick={() => close()}
>取消</Button>,
<Button key='save' type='primary'
onClick={() => save()}
>保存</Button>
]
}, [close, save])
return (
<>
<Modal
visible={visible}
title='模型导出'
width={540}
footer={footer}
destroyOnClose
onCancel={() => { close() }}
>
<Basic ref={basicRef} />
</Modal>
<ExportDDL
{...exportDDLParams}
ids={ids}
onCancel={() => {
setExportDDLParams({ visible: false })
}}
/>
</>
)
}
export default FC
const Basic = React.forwardRef(function ({}, ref) {
const [modeKey, setModeKey] = React.useState()
const [form] = Form.useForm()
React.useImperativeHandle(ref, () => ({
validate: async () => {
return await form.validateFields()
},
modeKey
}), [form, modeKey])
const onModeChange = (e) => {
setModeKey(e.target?.value)
}
return (
<Form form={form}>
<Form.Item name='mode' label='导出方式'
rules={[{ required: true, message: '请选择导出方式' }]}
>
<Radio.Group onChange={onModeChange} value={modeKey}>
{
exportModes.map((item) => (
<Radio value={item.key} key={item.key}>
{item.name}
</Radio>
))
}
</Radio.Group>
</Form.Item>
</Form>
)
})
\ No newline at end of file
...@@ -2,11 +2,7 @@ import React from 'react' ...@@ -2,11 +2,7 @@ import React from 'react'
import { Modal, Button, Spin, Form, Checkbox, Typography, Tooltip, Row, Col, Tree, } from 'antd' import { Modal, Button, Spin, Form, Checkbox, Typography, Tooltip, Row, Col, Tree, } from 'antd'
import { dispatch } from '../../../../model' import { dispatch } from '../../../../model'
import VersionCompareHeader from './VersionCompareHeader' import { CompareDetail } from './VersionCompare'
import VersionCompareTable from './VersionCompareTable'
import VersionCompareIndex from './VersionCompareIndex'
import { defaultColumnTitles } from './VersionCompare'
import FilterColumnAction from './FilterColumnAction'
import './merge-to-master.less' import './merge-to-master.less'
import './VersionCompare.less' import './VersionCompare.less'
...@@ -119,8 +115,6 @@ export const Diff = ({ item }) => { ...@@ -119,8 +115,6 @@ export const Diff = ({ item }) => {
const [loading, setLoading] = React.useState(false) const [loading, setLoading] = React.useState(false)
const [data, setData] = React.useState() const [data, setData] = React.useState()
const [onlyShowChange, setOnlyShowChange] = React.useState(true) const [onlyShowChange, setOnlyShowChange] = React.useState(true)
const [attrSelectedTitles, setAttrSelectedTitles] = React.useState(defaultColumnTitles)
const [attrColumns, setAttrColumns] = React.useState()
React.useEffect(() => { React.useEffect(() => {
if (item) { if (item) {
...@@ -128,59 +122,6 @@ export const Diff = ({ item }) => { ...@@ -128,59 +122,6 @@ export const Diff = ({ item }) => {
} }
}, [item, onlyShowChange]) }, [item, onlyShowChange])
React.useEffect(() => {
if (data) {
const newAttrOptionColumns = [];
(data?.heads?.columnHead||[]).forEach((item, index) => {
newAttrOptionColumns.push({
title: item||'',
dataIndex: `column${index}`,
render: (attrValue, record, index) => {
let stateClassName = '';
if (attrValue?.state==='ADD' || attrValue?.state==='UPDATE') {
stateClassName = 'add';
} else if (attrValue?.state === 'DELETE') {
stateClassName = 'delete';
}
return (
<Typography.Paragraph>
<Tooltip title={attrValue?.value||''}>
<Typography.Text className={stateClassName} ellipsis={true}>{attrValue?.value||''}</Typography.Text>
</Tooltip>
</Typography.Paragraph>
);
},
width: (item==='序号')?60: 150,
ellipsis: true,
option: true,
});
});
const newAttrColumns = [...newAttrOptionColumns, {
title: <FilterColumnAction columns={newAttrOptionColumns} defaultSelectedKeys={defaultColumnTitles} onChange={onFilterChange} />,
dataIndex: 'columnFilter',
render: (_, record, index) => {
return '';
},
width: 40,
ellipsis: true,
option: false
}];
setAttrColumns(newAttrColumns)
}
}, [data])
const attrFilterColumns = React.useMemo(() => {
return (attrColumns??[]).filter(column => column.option===false || (attrSelectedTitles??[]).indexOf(column.title) !== -1)
}, [attrSelectedTitles, attrColumns])
const onFilterChange = (values) => {
setAttrSelectedTitles(values)
}
const onOnlyShowChange = (e) => { const onOnlyShowChange = (e) => {
setOnlyShowChange(e.target.checked) setOnlyShowChange(e.target.checked)
} }
...@@ -221,20 +162,7 @@ export const Diff = ({ item }) => { ...@@ -221,20 +162,7 @@ export const Diff = ({ item }) => {
<div className='py-5'> <div className='py-5'>
<Spin spinning={loading} > <Spin spinning={loading} >
{ <CompareDetail data={data} />
data && <div className='flex'>
<div style={{ flex: 1, borderRight: '1px solid #EFEFEF', paddingRight: 10, overflow: 'hidden'}}>
<VersionCompareHeader data={data} />
<VersionCompareTable data={data} columns={attrFilterColumns} />
<VersionCompareIndex data={data} />
</div>
<div style={{ flex: 1, paddingLeft: 10, overflow: 'hidden'}}>
<VersionCompareHeader data={data} direction='right' />
<VersionCompareTable data={data} columns={attrFilterColumns} direction='right' />
<VersionCompareIndex data={data} direction='right'/>
</div>
</div>
}
</Spin> </Spin>
</div> </div>
</div> </div>
......
import React from 'react'
import { Modal, Button, Row, Col, Spin, Tree, Input, Pagination, Tooltip, Typography, Space, Select, } from 'antd'
import { useDebounceEffect } from "ahooks"
import { dispatch } from '../../../../model'
import Table from '../../ResizeableTable'
import produce from 'immer'
import { paginate, showMessage } from '../../../../util'
import ModelCompare from './model-compare'
import './branch-select-model.less'
const FC = ({ visible, item, onCancel }) => {
const [animated, setAnimated] = React.useState(true)
const [modelCompareParams, setModelCompareParams] = React.useState({
visible: false,
leftItem: undefined,
rightItem: undefined
})
const basicRef = React.useRef()
React.useEffect(() => {
if (visible) {
setTimeout(() => {
setAnimated(false)
}, 300)
}
}, [visible])
const close = () => {
setAnimated(true)
onCancel?.()
}
const save = () => {
const selectedRows = basicRef.current.selectedRows??[]
if ((selectedRows??[]).length === 0) {
showMessage('warn', '请选选择模型')
return
}
setModelCompareParams({
visible: true,
leftItem: item,
rightItem: selectedRows[0]
})
}
const footer = React.useMemo(() => {
return [
<Button key='cancel'
onClick={() => close()}
>取消</Button>,
<Button key='save' type='primary'
onClick={() => save()}
>确定</Button>
]
}, [close, save])
return (
<>
<Modal
title='模型对比设置'
visible={visible}
footer={footer}
width='80%'
bodyStyle={{ padding: '15px', overflowX: 'auto', height: '80vh' }}
centered destroyOnClose
onCancel={() => { close() }}
>
{ !animated && <Basic ref={basicRef} /> }
</Modal>
<ModelCompare
{...modelCompareParams}
onCancel={() => {
setModelCompareParams({
visible: false,
leftItem: undefined,
rightItem: undefined,
})
}}
/>
</>
)
}
export default FC
const Basic = React.forwardRef(function ({}, ref) {
const [args, setArgs] = React.useState({
keyword: undefined,
})
const [pagination, setPagination] = React.useState({
page: 1,
size: 20,
})
const [loadingTreeData, setLoadingTreeData] = React.useState(false)
const [treeData, setTreeData] = React.useState()
const [node, setNode] = React.useState()
const [loading, setLoading] = React.useState(false)
const [data, setData] = React.useState()
const [selectedRows, setSelectedRows] = React.useState()
const [expandedKeys, setExpandedKeys] = React.useState([])
const [autoExpandParent, setAutoExpandParent] = React.useState(false)
React.useImperativeHandle(ref, () => ({
selectedRows
}), [selectedRows])
React.useEffect(() => {
getTreeData()
}, [])
React.useEffect(() => {
setPagination({...pagination, page: 1})
}, [node])
useDebounceEffect(() => {
if (node) {
getDataModels()
}
}, [node, args], { wait: 300 })
const treeData1 = React.useMemo(() => {
if (treeData) {
const newTreeData = produce(treeData, draft => {
const setNode = (g) => {
g.key = g.id;
g.title = g.name;
g.children = [];
(g.subCatalogs??[]).forEach((child) => {
setNode(child)
g.children.push(child)
});
}
draft.forEach((child) => {
setNode(child)
})
})
return newTreeData
}
return undefined
}, [treeData])
const setArgsByParams = React.useCallback((params) => {
setArgs((prev) => {
return {...prev, ...params}
})
}, [])
const [tableData, total] = React.useMemo(() => {
const newData= [...data??[]]
return [paginate(newData, pagination.page, pagination.size), (newData??[]).length]
}, [data, pagination])
const columns = [
{
title: '模型名称',
dataIndex: 'name',
render: (text, record) => {
return <Tooltip title={text}>
<Typography.Text ellipsis={true}>{text}</Typography.Text>
</Tooltip>
}
},
{
title: '中文名称',
dataIndex: 'cnName',
render: (text, record) => {
return <Tooltip title={text}>
<Typography.Text ellipsis={true}>{text}</Typography.Text>
</Tooltip>
}
},
{
title: '描述',
dataIndex: 'remark',
render: (text, record) => {
return <Tooltip title={text}>
<Typography.Text ellipsis={true}>{text}</Typography.Text>
</Tooltip>
}
},
{
title: '创建人',
dataIndex: 'editor',
render: (text, record) => {
return <Tooltip title={text}>
<Typography.Text ellipsis={true}>{text}</Typography.Text>
</Tooltip>
}
},
]
const getTreeData = () => {
setLoadingTreeData(true)
dispatch({
type: 'datamodel.refreshDataModelCatalog',
callback: (data) => {
setLoadingTreeData(false)
const newTreeData = data?.subCatalogs??[]
setTreeData(newTreeData)
if ((newTreeData??[]).length > 0) {
setNode(newTreeData[0])
}
},
error: () => {
setLoadingTreeData(false)
}
})
}
const getDataModels = () => {
setLoading(true)
if (args.keyword) {
} else {
dispatch({
type: 'datamodel.getCurrentDataModelCatalog',
payload: {
easyDataModelerCatalogId: node?.id,
},
callback: (data) => {
setLoading(false)
setData(data?.easyDataModelerDataModels)
},
error: () => {
setLoading(false)
}
})
}
}
const onTreeExpand = (expandedKeys) => {
setExpandedKeys(expandedKeys)
setAutoExpandParent(false)
}
const onTreeSelect = (selectedKeys, { selectedNodes }) => {
if (selectedKeys.length === 0 || selectedNodes.length === 0) {
return
}
setNode(selectedNodes[0])
}
const onChange = (val) => {
setSelectedRows(val)
}
return (
<div className='branch-select-model'>
<Row>
<Col span={4}>
<Spin spinning={loadingTreeData}>
<Tree
className='tree'
showLine
showIcon={false}
autoExpandParent={autoExpandParent}
treeData={treeData1}
selectedKeys={node?[node.id]:[]}
expandedKeys={expandedKeys}
onSelect={onTreeSelect}
onExpand={onTreeExpand}
/>
</Spin>
</Col>
<Col span={20}>
<div
style={{
display: 'flex',
padding: '0px 0px 15px',
alignItems: 'center',
justifyContent: 'flex-end',
}}>
<Input size="middle"
placeholder="请输入关键字搜索"
value={args.keyword}
bordered={true} allowClear
onChange={(e) => {
setArgsByParams({ keyword: e.target.value })
setPagination({ ...pagination, page: 1 })
}}
style={{ width: 200 }}
/>
</div>
<Table
size='small'
extraColWidth={32}
rowKey='id'
loading={loading}
columns={columns}
dataSource={tableData??[]}
pagination={false}
rowSelection={{
type: 'radio',
selectedRowKeys: (selectedRows??[]).map(item => item.id),
onChange: (selectedRowKeys, selectedRows) => {
setSelectedRows(selectedRows)
},
}}
scroll={{ y: 'calc(80vh - 170px)' }}
/>
{
total!==0 && <Pagination
style={{
textAlign: 'center',
marginTop: 15,
}}
showSizeChanger
onChange={(page,size) => {
setPagination({page, size})
}}
current={pagination.page}
pageSize={pagination.size}
defaultCurrent={1}
total={total}
showTotal={(total) => `共${total??0}项`}
/>
}
</Col>
</Row>
</div>
)
})
\ No newline at end of file
import React from 'react'
import { Modal, Checkbox, Spin } from 'antd'
import { CompareDetail } from './VersionCompare'
import { dispatch } from '../../../../model'
const FC = ({ visible, leftItem, rightItem, onCancel }) => {
const close = () => {
onCancel?.()
}
return (
<Modal
title='模型对比'
visible={visible}
footer={null}
width='70%'
bodyStyle={{ padding: '15px', overflowX: 'auto', height: '80vh' }}
centered destroyOnClose
onCancel={() => { close() }}
>
<Basic leftItem={leftItem} rightItem={rightItem} />
</Modal>
)
}
export default FC
const Basic = ({ leftItem, rightItem }) => {
const [loading, setLoading] = React.useState(false)
const [data, setData] = React.useState()
const [onlyShowChange, setOnlyShowChange] = React.useState(true)
React.useEffect(() => {
if (leftItem && rightItem) {
compareOtherModel()
}
}, [leftItem, rightItem, onlyShowChange])
const onOnlyShowChange = (e) => {
setOnlyShowChange(e.target.checked)
}
const compareOtherModel = () => {
setLoading(true)
dispatch({
type: 'datamodel.compareOtherModel',
payload: {
params: {
leftEasyDataModelerDataModelId: leftItem?.id,
rightEasyDataModelerDataModelId: rightItem?.id,
includeSame: !onlyShowChange,
}
},
callback: data => {
setLoading(false)
setData(data)
},
error: () => {
setLoading(false)
}
})
}
return (
<div className='model-version-compare'>
<div className='flex'>
<div style={{ flex: 1, paddingRight: 10, overflow: 'hidden'}}>
{`模型:${leftItem?.path}/${leftItem?.name}`}
</div>
<div style={{ flex: 1, paddingLeft: 10, overflow: 'hidden'}}>
<div className='flex' style={{ justifyContent: 'space-between', alignItems: 'center' }}>
<span>{`模型:${rightItem?.path}/${rightItem?.name}`}</span>
<Checkbox onChange={onOnlyShowChange} checked={onlyShowChange}>
仅显示差异
</Checkbox>
</div>
</div>
</div>
<div className='py-5'>
<Spin spinning={loading} >
<CompareDetail data={data} />
</Spin>
</div>
</div>
)
}
\ No newline at end of file
import React from 'react'
import { Descriptions, Row, Col, Typography, Tooltip, Space } from 'antd'
import { stateColorDic } from './ModelTable'
const FC = (props) => {
const { data, node } = props
const [deployedCount, deployWaitingCount, releaseWaitingCount, offlineCount] = React.useMemo(() => {
return [
(data??[]).filter(item => item.state?.cnName === '已上线').length,
(data??[]).filter(item => item.state?.cnName === '评审通过').length,
(data??[]).filter(item => item.state?.cnName === '评审中').length,
(data??[]).filter(item => item.state?.cnName === '已下线').length,
]
}, [data])
return (
<div style={{ height: 80, padding: '10px 20px' }}>
<Row gutter={10}>
<Col flex='1' style={{ overflow: 'hidden' }}>
<Tooltip title={node?.title}>
<Typography.Text ellipsis={true} style={{ fontWeight: 500, fontSize: 16 }}>
{node?.title}
</Typography.Text>
</Tooltip>
</Col>
<Col flex='0 0 auto'>
<Space size={50}>
<StateItem title='已上线' count={deployedCount} />
<StateItem title='评审通过' count={deployWaitingCount} />
<StateItem title='评审中' count={releaseWaitingCount} />
<StateItem title='已下线' count={offlineCount} />
</Space>
</Col>
</Row>
<div style={{ marginTop: 10 }}>
<Tooltip title={node?.remark}>
<Typography.Text ellipsis={true}>
{node?.remark}
</Typography.Text>
</Tooltip>
</div>
</div>
)
}
export default FC
const StateItem = ({ title, count }) => {
return (
<Space size={10}>
<div style={{ width: 10, height: 10, borderRadius: 5, backgroundColor: stateColorDic[title]??'#77DEBF' }}></div>
<span>{title}</span>
<span style={{ color: stateColorDic[title]??'#77DEBF' }}>{count}</span>
</Space>
)
}
\ No newline at end of file
...@@ -6,7 +6,7 @@ import { useDebounceEffect } from 'ahooks' ...@@ -6,7 +6,7 @@ import { useDebounceEffect } from 'ahooks'
import { isSzseEnv, showMessage, showNotifaction } from '../../../../util' import { isSzseEnv, showMessage, showNotifaction } from '../../../../util'
import Table from '../../../../util/Component/Table' import Table from '../../../../util/Component/Table'
import produce from 'immer' import produce from 'immer'
import { Action, CatalogId, Editable, Holder, ModelerId, PermitCheckOut, ReadOnly, StateId } from '../../../../util/constant' import { Action, CatalogId, Editable, ModelerId, PermitCheckOut, ReadOnly, StateId } from '../../../../util/constant'
import { dispatch } from '../../../../model' import { dispatch } from '../../../../model'
const FC = (props) => { const FC = (props) => {
...@@ -75,7 +75,7 @@ const FC = (props) => { ...@@ -75,7 +75,7 @@ const FC = (props) => {
visible={visible} visible={visible}
footer={footer} footer={footer}
width='80%' width='80%'
bodyStyle={{ padding: '15px', overflowX: 'auto', maxHeight: '80vh' }} bodyStyle={{ padding: '15px', overflowX: 'auto', height: '80vh' }}
title='模型送审' title='模型送审'
centered destroyOnClose centered destroyOnClose
onCancel={() => { close() }} onCancel={() => { close() }}
...@@ -327,7 +327,7 @@ const List = React.forwardRef(function ({ items }, ref) { ...@@ -327,7 +327,7 @@ const List = React.forwardRef(function ({ items }, ref) {
<Typography.Paragraph ellipsis={{ <Typography.Paragraph ellipsis={{
rows: 3, rows: 3,
}}><a onClick={() => { }}><a onClick={() => {
window.open(`/data-govern/data-model-action?${Action}=detail&${ModelerId}=${record.id}&${PermitCheckOut}=${record.permitCheckOut||false}&${Editable}=${record.editable||false}&${StateId}=${record.state?.id||''}&${Holder}=${record.holder||''}&${ReadOnly}=false`); window.open(`/data-govern/data-model-action?${Action}=detail&${ModelerId}=${record.id}&${PermitCheckOut}=${record.permitCheckOut||false}&${Editable}=${record.editable||false}&${StateId}=${record.state?.id||''}&${ReadOnly}=false`);
}}>{text}</a></Typography.Paragraph> }}>{text}</a></Typography.Paragraph>
</Tooltip> </Tooltip>
) )
......
...@@ -4,7 +4,7 @@ import { ArrowsAltOutlined, ShrinkOutlined } from '@ant-design/icons'; ...@@ -4,7 +4,7 @@ import { ArrowsAltOutlined, ShrinkOutlined } from '@ant-design/icons';
import { dispatch } from '../../../../model' import { dispatch } from '../../../../model'
import Table from '../../../../util/Component/Table' import Table from '../../../../util/Component/Table'
import { checkMenuAdmit, inputWidth, isSzseEnv, showMessage } from '../../../../util' import { checkMenuAdmit, inputWidth, isSzseEnv, openMetadataDetail, showMessage } from '../../../../util'
import { useDebounceEffect } from 'ahooks' import { useDebounceEffect } from 'ahooks'
const topN = 20 const topN = 20
...@@ -138,7 +138,7 @@ const FC = (props) => { ...@@ -138,7 +138,7 @@ const FC = (props) => {
useDebounceEffect(()=>{ useDebounceEffect(()=>{
setColumns(isCompact?[...cols].slice(0, 4):[...cols]) setColumns(isCompact?[...cols].slice(0, 4):[...cols])
}, [cols, isCompact], { wait: 100 }) }, [isCompact], { wait: 100 })
const onSourceClick = (id, name) => { const onSourceClick = (id, name) => {
const timestamp = new Date().getTime(); const timestamp = new Date().getTime();
...@@ -150,7 +150,7 @@ const FC = (props) => { ...@@ -150,7 +150,7 @@ const FC = (props) => {
id id
}, },
callback: data => { callback: data => {
window.open(`/center-home/metadetail?mid=${encodeURIComponent(data?._id)}&action=metadetail&type=detail&manager=false&activekey=1&name=${encodeURIComponent(name||'')}`); openMetadataDetail(data?._id)
} }
}) })
} else { } else {
......
...@@ -10,12 +10,12 @@ import ModelTable from './Component/ModelTable'; ...@@ -10,12 +10,12 @@ import ModelTable from './Component/ModelTable';
import ImportModal from './Component/ImportModal'; import ImportModal from './Component/ImportModal';
import ImportStockWordDrawer from './Component/ImportStockWordDrawer'; import ImportStockWordDrawer from './Component/ImportStockWordDrawer';
import ExportDDLModal from './Component/ExportDDLModal'; import ExportDDLModal from './Component/ExportDDLModal';
import ExportOtherModal from './Component/ExportOtherModal'; import ExportOptions from './Component/export-options';
import RecatalogModal from './Component/RecatalogModal'; import RecatalogModal from './Component/RecatalogModal';
import HistoryAndVersionDrawer from './Component/HistoryAndVersionDrawer'; import HistoryAndVersionDrawer from './Component/HistoryAndVersionDrawer';
import { showMessage, showNotifaction, inputWidth, DeleteTipModal } from '../../../util'; import { showMessage, showNotifaction, inputWidth, DeleteTipModal } from '../../../util';
import { dispatch, dispatchLatestHomepage } from '../../../model'; import { dispatch, dispatchLatestHomepage } from '../../../model';
import { Action, CatalogId, ModelerId, Hints, ModelerData, PermitCheckOut, Editable, StateId, Holder, DDL, ReadOnly, BranchId } from '../../../util/constant'; import { Action, CatalogId, ModelerId, Hints, ModelerData, PermitCheckOut, Editable, StateId, DDL, ReadOnly, BranchId } from '../../../util/constant';
import { AppContext } from '../../../App'; import { AppContext } from '../../../App';
import DebounceInput from './Component/DebounceInput'; import DebounceInput from './Component/DebounceInput';
import ColSettingModal from './Component/ColSettingModal'; import ColSettingModal from './Component/ColSettingModal';
...@@ -27,6 +27,7 @@ import StartFlow from './Component/start-flow' ...@@ -27,6 +27,7 @@ import StartFlow from './Component/start-flow'
import MergeToMaster from './Component/merge-to-master'; import MergeToMaster from './Component/merge-to-master';
import AuthTransfer from './Component/auth-transfer'; import AuthTransfer from './Component/auth-transfer';
import AuthShare from './Component/auth-share'; import AuthShare from './Component/auth-share';
import NodeIntroduction from './Component/node-introduction';
import './index.less'; import './index.less';
...@@ -42,7 +43,7 @@ class Model extends React.Component { ...@@ -42,7 +43,7 @@ class Model extends React.Component {
importModalVisible: false, importModalVisible: false,
importStockWordDrawerVisible: false, importStockWordDrawerVisible: false,
exportDDLModalVisible: false, exportDDLModalVisible: false,
exportOtherModalVisible: false, exportOptionsVisible: false,
recatalogModalVisible: false, recatalogModalVisible: false,
historyAndVersionDrawerVisible: false, historyAndVersionDrawerVisible: false,
catalogId: '', catalogId: '',
...@@ -81,6 +82,7 @@ class Model extends React.Component { ...@@ -81,6 +82,7 @@ class Model extends React.Component {
}, },
batchAddTagChange: false, batchAddTagChange: false,
rootNode: undefined, rootNode: undefined,
node: undefined,
branchAddParams: { branchAddParams: {
visible: false, visible: false,
}, },
...@@ -115,7 +117,7 @@ class Model extends React.Component { ...@@ -115,7 +117,7 @@ class Model extends React.Component {
componentDidUpdate(prevProps, prevState) { componentDidUpdate(prevProps, prevState) {
const { selectModelerIds, tableData, catalogId, permissions, currentView } = this.state; const { selectModelerIds, tableData, catalogId, permissions, currentView } = this.state;
if (selectModelerIds !== prevState.selectModelerIds || tableData !== prevState.tableData) { if (selectModelerIds !== prevState.selectModelerIds || tableData !== prevState.tableData) {
let canExport = true, canStartFlow = true, canChangeCatalog = true, canDelete = true, canBatchAddTag = true, canBatchJoin = true; let canExport = true, canExportInfo = true, canStartFlow = true, canChangeCatalog = true, canDelete = true, canBatchAddTag = true, canBatchJoin = true;
//分支管理返回的模型 都是有权限的 //分支管理返回的模型 都是有权限的
if (currentView !== 'branch') { if (currentView !== 'branch') {
selectModelerIds?.forEach(id => { selectModelerIds?.forEach(id => {
...@@ -129,6 +131,8 @@ class Model extends React.Component { ...@@ -129,6 +131,8 @@ class Model extends React.Component {
modelItem.optionList?.forEach(item => { modelItem.optionList?.forEach(item => {
if (item.name === '导出' && item.enabled === false) { if (item.name === '导出' && item.enabled === false) {
canExport = false; canExport = false;
} else if (item.name === '导出模型信息' && item.enabled === false) {
canExportInfo = false;
} else if (item.name === '送审' && (item.enabled===false||modelItem.state?.id!=='1')) { } else if (item.name === '送审' && (item.enabled===false||modelItem.state?.id!=='1')) {
canStartFlow = false; canStartFlow = false;
} else if (item.name === '变更目录' && (item.enabled===false||!modelItem.supportRecatalog)) { } else if (item.name === '变更目录' && (item.enabled===false||!modelItem.supportRecatalog)) {
...@@ -165,6 +169,7 @@ class Model extends React.Component { ...@@ -165,6 +169,7 @@ class Model extends React.Component {
this.setState({ this.setState({
canExport, canExport,
canExportInfo,
canStartFlow, canStartFlow,
canChangeCatalog, canChangeCatalog,
canDelete, canDelete,
...@@ -384,7 +389,7 @@ class Model extends React.Component { ...@@ -384,7 +389,7 @@ class Model extends React.Component {
currentBranchId = catalogId currentBranchId = catalogId
} }
window.open(`/data-govern/data-model-action?${Action}=${importModalAction}&${CatalogId}=${currentCatalogId}&${ModelerId}=${modelerId}&${PermitCheckOut}=${record.permitCheckOut||false}&${Editable}=${record.editable||false}&${StateId}=${record.state?.id||''}&${Holder}=${record.holder||''}&${ReadOnly}=${readOnly}&${BranchId}=${currentBranchId}`); window.open(`/data-govern/data-model-action?${Action}=${importModalAction}&${CatalogId}=${currentCatalogId}&${ModelerId}=${modelerId}&${PermitCheckOut}=${record.permitCheckOut||false}&${Editable}=${record.editable||false}&${StateId}=${record.state?.id||''}&${ReadOnly}=${readOnly}&${BranchId}=${currentBranchId}`);
}); });
} }
...@@ -501,10 +506,6 @@ class Model extends React.Component { ...@@ -501,10 +506,6 @@ class Model extends React.Component {
this.setState({ exportDDLModalVisible: true, selectModelerNames: _selectModelerNames, exportDDLModalReference: 'exportDDL' }); this.setState({ exportDDLModalVisible: true, selectModelerNames: _selectModelerNames, exportDDLModalReference: 'exportDDL' });
} }
onExportOtherBtnClick = () => {
this.setState({ exportOtherModalVisible: true });
}
startFlow = () => { startFlow = () => {
const { selectModelerIds, tableData } = this.state; const { selectModelerIds, tableData } = this.state;
if ((selectModelerIds||[]).length === 0) { if ((selectModelerIds||[]).length === 0) {
...@@ -651,33 +652,6 @@ class Model extends React.Component { ...@@ -651,33 +652,6 @@ class Model extends React.Component {
this.setState({ exportDDLModalVisible: false }); this.setState({ exportDDLModalVisible: false });
} }
onExportOtherModalCancel = (key='') => {
const { selectModelerIds } = this.state;
this.setState({ exportOtherModalVisible: false }, () => {
if (key === 'ddl') {
this.onExportDDLBtnClick();
} else if (key === 'erwin') {
dispatch({
type: 'datamodel.exportERWinString',
payload: {
ids: selectModelerIds.join(','),
},
callback: data => {
copy(JSON.stringify(data));
showNotifaction('提示', 'Erwin信息已成功复制到剪贴板', 5);
}
});
} else if (key === 'excel') {
window.open(`/api/datamodeler/easyDataModelerExport/excel?ids=${selectModelerIds.join(',')}`);
} else if (key === 'word') {
window.open(`/api/datamodeler/easyDataModelerExport/word/template?ids=${selectModelerIds.join(',')}`);
} else if (key === 'basicExcel') {
window.open(`/api/datamodeler/easyDataModelerExport/modelBaseDataExcel?ids=${selectModelerIds.join(',')}`);
}
});
}
onRecatalogModalCancel = (refresh = false) => { onRecatalogModalCancel = (refresh = false) => {
this.setState({ recatalogModalVisible: false }); this.setState({ recatalogModalVisible: false });
if (refresh) { if (refresh) {
...@@ -738,7 +712,7 @@ class Model extends React.Component { ...@@ -738,7 +712,7 @@ class Model extends React.Component {
} }
render() { render() {
const { importModalVisible, catalogId, loadingTableData, selectModelerIds, keyword, filterTableData, selectModelerNames, exportDDLModalVisible, exportOtherModalVisible, importStockWordDrawerVisible , loadingStates, modelStates, currentModelState, currentView, recatalogModalVisible, exportDDLModalReference, currentModel, offset, historyAndVersionDrawerVisible, modelerId, expandTree, showDeleteTip, colSettingModalVisible, visibleColNames, canExport, canStartFlow, canChangeCatalog, canDelete, canAdd } = this.state; const { importModalVisible, catalogId, loadingTableData, selectModelerIds, keyword, filterTableData, selectModelerNames, exportDDLModalVisible, exportOptionsVisible, importStockWordDrawerVisible , loadingStates, modelStates, currentModelState, currentView, recatalogModalVisible, exportDDLModalReference, currentModel, offset, historyAndVersionDrawerVisible, modelerId, expandTree, showDeleteTip, colSettingModalVisible, visibleColNames, canExport, canExportInfo, canStartFlow, canChangeCatalog, canDelete, canAdd } = this.state;
const classes = classNames('data-model', { const classes = classNames('data-model', {
'data-model-collapse': !expandTree 'data-model-collapse': !expandTree
...@@ -773,6 +747,9 @@ class Model extends React.Component { ...@@ -773,6 +747,9 @@ class Model extends React.Component {
setRootNode={(val) => { setRootNode={(val) => {
this.setState({ rootNode: val }) this.setState({ rootNode: val })
}} }}
setNode={(val) => {
this.setState({ node: val })
}}
onDirRefresh={() => { onDirRefresh={() => {
//刷新目录时,重新获取目录权限 //刷新目录时,重新获取目录权限
this.getPermissions() this.getPermissions()
...@@ -785,10 +762,12 @@ class Model extends React.Component { ...@@ -785,10 +762,12 @@ class Model extends React.Component {
</div> </div>
</div> </div>
<div className='right'> <div className='right'>
<NodeIntroduction node={this.state.node} data={filterTableData} />
<div style={{ height: 10, background: '#f0f2f5' }} />
<div <div
className='d-flex p-3' className='flex'
style={{ style={{
borderBottom: '1px solid #EFEFEF', padding: '10px 20px',
justifyContent: 'space-between', justifyContent: 'space-between',
alignItems: 'center' alignItems: 'center'
}} }}
...@@ -818,13 +797,26 @@ class Model extends React.Component { ...@@ -818,13 +797,26 @@ class Model extends React.Component {
<PermissionButton <PermissionButton
defaultPermission={canExport} defaultPermission={canExport}
tip={(selectModelerIds||[]).length===0?'请先选择模型':''} tip={(selectModelerIds||[]).length===0?'请先选择模型':''}
onClick={this.onExportOtherBtnClick} onClick={() => {
this.setState({ exportOptionsVisible: true })
}}
disabled={(selectModelerIds||[]).length===0} disabled={(selectModelerIds||[]).length===0}
> >
导出 导出
</PermissionButton> </PermissionButton>
<PermissionButton <PermissionButton
defaultPermission={canExportInfo}
tip={(selectModelerIds||[]).length===0?'请先选择模型':''}
onClick={() => {
window.open(`/api/datamodeler/easyDataModelerExport/modelBaseDataExcel?ids=${selectModelerIds.join(',')}`);
}}
disabled={(selectModelerIds||[]).length===0}
>
导出模型信息
</PermissionButton>
<PermissionButton
defaultPermission={canStartFlow} defaultPermission={canStartFlow}
tip={startFlowTip} tip={startFlowTip}
onClick={this.startFlow} onClick={this.startFlow}
...@@ -932,7 +924,7 @@ class Model extends React.Component { ...@@ -932,7 +924,7 @@ class Model extends React.Component {
</Space> </Space>
</div> </div>
<div className='p-3'> <div style={{ padding: '0 20px' }}>
<Spin spinning={loadingTableData}> <Spin spinning={loadingTableData}>
<ModelTable <ModelTable
loading={loadingTableData} loading={loadingTableData}
...@@ -987,9 +979,12 @@ class Model extends React.Component { ...@@ -987,9 +979,12 @@ class Model extends React.Component {
onCancel={this.onExportDDLModalCancel} onCancel={this.onExportDDLModalCancel}
/> />
<ExportOtherModal <ExportOptions
visible={exportOtherModalVisible} visible={exportOptionsVisible}
onCancel={this.onExportOtherModalCancel} ids={selectModelerIds}
onCancel={() => {
this.setState({ exportOptionsVisible: false })
}}
/> />
<RecatalogModal <RecatalogModal
......
...@@ -10,10 +10,23 @@ ...@@ -10,10 +10,23 @@
overflow: hidden; overflow: hidden;
} }
.tree-toggle-wrap { .right {
flex: 1;
overflow: hidden;
}
}
.data-model-collapse {
.left {
width: 0 !important;
}
}
.tree-toggle-wrap {
position: relative; position: relative;
width: 20px; width: 15px;
height: 100%; height: 100%;
background: #f0f2f5;
.tree-toggle { .tree-toggle {
display: flex; display: flex;
...@@ -23,7 +36,7 @@ ...@@ -23,7 +36,7 @@
right: 0; right: 0;
background: #f2f5fc; background: #f2f5fc;
position: absolute; position: absolute;
top: calc(50% - 40px); top: 50%;
width: 12px; width: 12px;
height: 80px; height: 80px;
border-radius: 0 12px 12px 0; border-radius: 0 12px 12px 0;
...@@ -31,16 +44,4 @@ ...@@ -31,16 +44,4 @@
transform: translateY(-50%); transform: translateY(-50%);
cursor: pointer; cursor: pointer;
} }
}
.right {
flex: 1;
overflow: hidden;
}
}
.data-model-collapse {
.left {
width: 0 !important;
}
} }
\ No newline at end of file
import React from 'react' import React from 'react'
import { Modal, Tabs, Button, Tooltip, Typography, Row, Col, Descriptions } from 'antd' import { Modal, Tabs, Button, Tooltip, Typography, Row, Col, Descriptions, TreeSelect, Space } from 'antd'
import { dispatch } from '../../../model' import { dispatch } from '../../../model'
import Table from '../../../util/Component/Table' import Table from '../../../util/Component/Table'
import { defaultPage } from '../../../util/hooks/page' import { defaultPage } from '../../../util/hooks/page'
import { generateUUID, paginate, showMessage } from '../../../util' import { generateUUID, inputWidth, openMetadataDetail, openModelDetail, paginate, showMessage } from '../../../util'
import './result-detail.less' import './result-detail.less'
import produce from 'immer'
const FC = (props) => { const FC = (props) => {
const { visible, item, onCancel} = props const { visible, item, onCancel} = props
...@@ -49,11 +50,41 @@ const FC = (props) => { ...@@ -49,11 +50,41 @@ const FC = (props) => {
export default FC export default FC
const Basic = ({ item }) => { const Basic = ({ item }) => {
const [totalCount, setTotalCount] = React.useState('')
const [deployWaitingCount, setDeployWaitingCount] = React.useState('')
const [deployedCount, setDeployedCount] = React.useState('')
const [offlineCount, setOfflineCount] = React.useState('')
const [modelListParams, setModelListParams] = React.useState({
visible: false,
type: undefined,
})
React.useEffect(() => {
if (item?.id) {
getModelCount()
}
}, [item])
const getModelCount = () => {
dispatch({
type: 'datamodel.getCompareJobResultModelCount',
payload: {
resultId: item?.id,
},
callback: data => {
setTotalCount(data?.totalCount)
setDeployWaitingCount(data?.deployWaitingCount)
setDeployedCount(data?.deployedCount)
setOfflineCount(data?.offlineCount)
}
})
}
return ( return (
<div> <div>
<Descriptions column={3}> <Descriptions column={3}>
<Descriptions.Item label='任务名称'>{item?.jobName}</Descriptions.Item> <Descriptions.Item label='任务名称'>{item?.jobName}</Descriptions.Item>
<Descriptions.Item label='模型评审通过时间'>{item?.endTs?new Date(item?.endTs).toLocaleString():''}</Descriptions.Item> {/* <Descriptions.Item label='模型评审通过时间'>{item?.endTs?new Date(item?.endTs).toLocaleString():''}</Descriptions.Item> */}
<Descriptions.Item label='任务执行时间'>{item?.beginTs?new Date(item?.beginTs).toLocaleString():''}</Descriptions.Item> <Descriptions.Item label='任务执行时间'>{item?.beginTs?new Date(item?.beginTs).toLocaleString():''}</Descriptions.Item>
</Descriptions> </Descriptions>
<Descriptions column={5}> <Descriptions column={5}>
...@@ -68,10 +99,191 @@ const Basic = ({ item }) => { ...@@ -68,10 +99,191 @@ const Basic = ({ item }) => {
<Descriptions.Item label='差异元数据数'>{item?.metadataPartialMatchCount}</Descriptions.Item> <Descriptions.Item label='差异元数据数'>{item?.metadataPartialMatchCount}</Descriptions.Item>
<Descriptions.Item label='未匹配元数据数'>{item?.metadataNotMatchCount}</Descriptions.Item> <Descriptions.Item label='未匹配元数据数'>{item?.metadataNotMatchCount}</Descriptions.Item>
</Descriptions> </Descriptions>
<Descriptions column={1}>
<Descriptions.Item label=''>
<span>{`本次任务更新模型${totalCount}个,`}</span>
<span>更新为已上线:</span>
{
deployedCount ? <a onClick={() => {
setModelListParams({
visible: true,
type: 'deployed',
})
}}>{deployedCount}</a> : <span>{deployedCount}</span>
}
<span>个,</span>
<span>更新为评审通过:</span>
{
deployWaitingCount ? <a onClick={() => {
setModelListParams({
visible: true,
type: 'deployWaiting',
})
}}>{deployWaitingCount}</a> : <span>{deployWaitingCount}</span>
}
<span>个,</span>
<span>更新为已下线:</span>
{
offlineCount ? <a onClick={() => {
setModelListParams({
visible: true,
type: 'offline',
})
}}>{offlineCount}</a> : <span>{offlineCount}</span>
}
<span></span>
</Descriptions.Item>
</Descriptions>
<ModelList
{...modelListParams}
item={item}
onCancel={() => {
setModelListParams({
visible: false,
type: undefined
})
}}
/>
</div> </div>
) )
} }
const ModelList = ({ visible, item, type, onCancel }) => {
const [args, setArgs] = React.useState({
page: defaultPage.pageNum,
size: defaultPage.pageSize,
})
const [loading, setLoading] = React.useState(false)
const [data, setData] = React.useState()
const [total, setTotal] = React.useState(0)
const [animating, setAnimating] = React.useState(true)
React.useEffect(() => {
if (visible) {
setTimeout(() => {
setAnimating(false)
}, 300)
}
}, [visible])
React.useEffect(() => {
if (type && item) {
getList()
}
}, [args, type])
const setArgsByParams = React.useCallback((params) => {
setArgs((prev) => {
return {...prev, ...params}
})
}, [])
const title = React.useMemo(() => {
if (type) {
if (type === 'deployWaiting') {
return '更新为评审通过模型'
} else if (type === 'deployed') {
return '更新为已上线模型'
} else if (type === 'offline') {
return '更新为已下线模型'
}
}
return ''
}, [type])
const cols = [
{
title: '序号',
dataIndex: 'index',
width:60,
render: (_, __, index)=> ((args.page-1)*args.size+index+1)
},
{
title: '模型路径',
dataIndex: 'modelPath',
},
{
title: '模型名称',
dataIndex: 'modelName',
render: (_, record) => {
return (
<Tooltip title={record.modelName}>
<Typography.Text ellipsis={true}>
<a onClick={() => {
openModelDetail(record.modelId)
}}>
{ record.modelName }
</a>
</Typography.Text>
</Tooltip>
)
}
},
]
const getList = () => {
setLoading(true)
let url = ''
if (type === 'deployWaiting') {
url = 'datamodel.getCompareJobResultDeployWaitingModelPage'
} else if (type === 'deployed') {
url = 'datamodel.getCompareJobResultDeployedModelPage'
} else if (type === 'offline') {
url = 'datamodel.getCompareJobResultOfflineModelPage'
}
dispatch({
type: url,
payload: {
resultId: item?.id,
pageNo: args.page,
pageSize: args.size,
},
callback: data => {
setLoading(false)
setData(data?.content)
setTotal(data?.totalElements)
},
error: () => {
setLoading(false)
}
})
}
const close = () => {
setAnimating(true)
onCancel?.()
}
return (
<Modal
visible={visible}
footer={null}
width='50%'
bodyStyle={{ padding: '15px 15px 0', overflowX: 'auto', height: '50vh' }}
title={title}
centered destroyOnClose
onCancel={() => { close() }}
>
{
!animating && <Table
loading={loading}
columns={cols??[]}
dataSource={data??[]}
pageNum={args.page} pageSize={args.size} total={total}
onPaginate={(page, size) => {
setArgsByParams({ page, size })
}}
/>
}
</Modal>
)
}
const Match = ({ item }) => { const Match = ({ item }) => {
const onExportClick = () => { const onExportClick = () => {
window.open(`/api/datamodeler/easyDataModelModelCompareJobResult/exportResultDetails?resultId=${item?.id??''}`); window.open(`/api/datamodeler/easyDataModelModelCompareJobResult/exportResultDetails?resultId=${item?.id??''}`);
...@@ -100,11 +312,18 @@ const PerfectMatch = ({ item }) => { ...@@ -100,11 +312,18 @@ const PerfectMatch = ({ item }) => {
const [args, setArgs] = React.useState({ const [args, setArgs] = React.useState({
page: defaultPage.pageNum, page: defaultPage.pageNum,
size: defaultPage.pageSize, size: defaultPage.pageSize,
metadataPath: undefined,
modelPath: undefined,
}) })
const [loading, setLoading] = React.useState(false) const [loading, setLoading] = React.useState(false)
const [data, setData] = React.useState() const [data, setData] = React.useState()
const [total, setTotal] = React.useState(0)
const [selectedRows, setSelectedRows] = React.useState() const [selectedRows, setSelectedRows] = React.useState()
const [rightRow, setRightRow] = React.useState() const [rightRow, setRightRow] = React.useState()
const [loadingMetadataCatalog, setLoadingMetadataCatalog] = React.useState(false)
const [metadataCatalog, setMetadataCatalog] = React.useState()
const [loadingModelCatalog, setLoadingModelCatalog] = React.useState(false)
const [modelCatalog, setModelCatalog] = React.useState()
const [modal, contextHolder] = Modal.useModal() const [modal, contextHolder] = Modal.useModal()
...@@ -116,15 +335,16 @@ const PerfectMatch = ({ item }) => { ...@@ -116,15 +335,16 @@ const PerfectMatch = ({ item }) => {
React.useEffect(() => { React.useEffect(() => {
if (item) { if (item) {
getList() getMetadataCatalog()
getModelCatalog()
} }
}, [item]) }, [item])
const tableData = React.useMemo(() => { React.useEffect(() => {
let newTableData = [...data??[]] if (item) {
getList()
return paginate(newTableData, args.page, args.size) }
}, [data, args]) }, [args])
const cols = [ const cols = [
{ {
...@@ -154,6 +374,24 @@ const PerfectMatch = ({ item }) => { ...@@ -154,6 +374,24 @@ const PerfectMatch = ({ item }) => {
{ {
title: '模型名称', title: '模型名称',
dataIndex: 'modelName', dataIndex: 'modelName',
render: (_, record) => {
return (
<Tooltip title={record.modelName}>
<Typography.Text ellipsis={true}>
<a onClick={() => {
openModelDetail(record.modelId)
}}>
{ record.modelName }
</a>
</Typography.Text>
</Tooltip>
)
}
},
{
title: '模型更新时间',
dataIndex: 'modelModifiedTs',
render: (_, record) => record.modelModifiedTs?new Date(record.modelModifiedTs).toLocaleString():''
}, },
{ {
title: '元数据路径', title: '元数据路径',
...@@ -172,6 +410,24 @@ const PerfectMatch = ({ item }) => { ...@@ -172,6 +410,24 @@ const PerfectMatch = ({ item }) => {
{ {
title: '元数据名称', title: '元数据名称',
dataIndex: 'metadataName', dataIndex: 'metadataName',
render: (_, record) => {
return (
<Tooltip title={record.metadataName}>
<Typography.Text ellipsis={true}>
<a onClick={() => {
openMetadataDetail(record.metadataId)
}}>
{ record.metadataName }
</a>
</Typography.Text>
</Tooltip>
)
}
},
{
title: '元数据更新时间',
dataIndex: 'metadataModifiedTs',
render: (_, record) => record.metadataModifiedTs?new Date(record.metadataModifiedTs).toLocaleString():''
}, },
{ {
title: '模型状态', title: '模型状态',
...@@ -184,11 +440,16 @@ const PerfectMatch = ({ item }) => { ...@@ -184,11 +440,16 @@ const PerfectMatch = ({ item }) => {
dispatch({ dispatch({
type: 'datamodel.getCompareJobResultPerfectMatchList', type: 'datamodel.getCompareJobResultPerfectMatchList',
payload: { payload: {
resultId: item?.id resultId: item?.id,
metadataPath: args.metadataPath,
modelPath: args.modelPath,
pageNo: args.page,
pageSize: args.size,
}, },
callback: data => { callback: data => {
setLoading(false) setLoading(false)
setData(data) setData(data?.content)
setTotal(data?.totalElements)
}, },
error: () => { error: () => {
setLoading(false) setLoading(false)
...@@ -196,6 +457,40 @@ const PerfectMatch = ({ item }) => { ...@@ -196,6 +457,40 @@ const PerfectMatch = ({ item }) => {
}) })
} }
const getMetadataCatalog = () => {
setLoadingMetadataCatalog(true)
dispatch({
type: 'datamodel.getCompareJobResultMetadataPerfectMatchCatalog',
payload: {
resultId: item?.id
},
callback: data => {
setLoadingMetadataCatalog(false)
setMetadataCatalog(data)
},
error: () => {
setLoadingMetadataCatalog(false)
}
})
}
const getModelCatalog = () => {
setLoadingModelCatalog(true)
dispatch({
type: 'datamodel.getCompareJobResultModelPerfectMatchCatalog',
payload: {
resultId: item?.id
},
callback: data => {
setLoadingModelCatalog(false)
setModelCatalog(data)
},
error: () => {
setLoadingModelCatalog(false)
}
})
}
const onDeployClick = () => { const onDeployClick = () => {
modal.confirm({ modal.confirm({
title:'提示', title:'提示',
...@@ -243,24 +538,40 @@ const PerfectMatch = ({ item }) => { ...@@ -243,24 +538,40 @@ const PerfectMatch = ({ item }) => {
return ( return (
<div> <div>
<div> <div className='flex mb-3' style={{ justifyContent: 'space-between', alignItems: 'center' }}>
<Tooltip title={(selectedRows??[]).length===0?'请选择模型':''}> <Tooltip title={(selectedRows??[]).length===0?'请选择模型':''}>
<Button onClick={onDeployClick} disabled={(selectedRows??[]).length===0}>确定上线</Button> <Button onClick={onDeployClick} disabled={(selectedRows??[]).length===0}>确定上线</Button>
</Tooltip> </Tooltip>
<Space>
<Catalog
placeholder='请选择元数据目录'
loading={loadingMetadataCatalog} data={metadataCatalog} value={args.metadataPath}
onChange={(val) => {
setArgsByParams({ metadataPath: val, metadataPage: 1 })
}}
/>
<Catalog
placeholder='请选择模型目录'
loading={loadingModelCatalog} data={modelCatalog} value={args.modelPath}
onChange={(val) => {
setArgsByParams({ modelPath: val, modelPage: 1 })
}}
/>
</Space>
</div> </div>
<div className='pt-3'> <div>
<Table <Table
extraColWidth={32} extraColWidth={32}
loading={loading} loading={loading}
columns={cols??[]} columns={cols??[]}
dataSource={tableData??[]} dataSource={data??[]}
rowSelection={{ rowSelection={{
selectedRowKeys: (selectedRows??[]).map(item => item.id), selectedRowKeys: (selectedRows??[]).map(item => item.id),
onChange: (selectedRowKeys, selectedRows) => { onChange: (selectedRowKeys, selectedRows) => {
setSelectedRows(selectedRows) setSelectedRows(selectedRows)
}, },
}} }}
pageNum={args.page} pageSize={args.size} total={(data??[]).length} pageNum={args.page} pageSize={args.size} total={total}
onPaginate={(page, size) => { onPaginate={(page, size) => {
setArgsByParams({ page, size }) setArgsByParams({ page, size })
}} }}
...@@ -282,11 +593,18 @@ const PartialMatch = ({ item }) => { ...@@ -282,11 +593,18 @@ const PartialMatch = ({ item }) => {
const [args, setArgs] = React.useState({ const [args, setArgs] = React.useState({
page: defaultPage.pageNum, page: defaultPage.pageNum,
size: defaultPage.pageSize, size: defaultPage.pageSize,
metadataPath: undefined,
modelPath: undefined,
}) })
const [loading, setLoading] = React.useState(false) const [loading, setLoading] = React.useState(false)
const [data, setData] = React.useState() const [data, setData] = React.useState()
const [total, setTotal] = React.useState(0)
const [selectedRows, setSelectedRows] = React.useState() const [selectedRows, setSelectedRows] = React.useState()
const [rightRow, setRightRow] = React.useState() const [rightRow, setRightRow] = React.useState()
const [loadingMetadataCatalog, setLoadingMetadataCatalog] = React.useState(false)
const [metadataCatalog, setMetadataCatalog] = React.useState()
const [loadingModelCatalog, setLoadingModelCatalog] = React.useState(false)
const [modelCatalog, setModelCatalog] = React.useState()
const [modal, contextHolder] = Modal.useModal() const [modal, contextHolder] = Modal.useModal()
...@@ -298,15 +616,16 @@ const PartialMatch = ({ item }) => { ...@@ -298,15 +616,16 @@ const PartialMatch = ({ item }) => {
React.useEffect(() => { React.useEffect(() => {
if (item) { if (item) {
getList() getMetadataCatalog()
getModelCatalog()
} }
}, [item]) }, [item])
const tableData = React.useMemo(() => { React.useEffect(() => {
let newTableData = [...data??[]] if (item) {
getList()
return paginate(newTableData, args.page, args.size) }
}, [data, args]) }, [args])
const cols = [ const cols = [
{ {
...@@ -336,6 +655,24 @@ const PartialMatch = ({ item }) => { ...@@ -336,6 +655,24 @@ const PartialMatch = ({ item }) => {
{ {
title: '模型名称', title: '模型名称',
dataIndex: 'modelName', dataIndex: 'modelName',
render: (_, record) => {
return (
<Tooltip title={record.modelName}>
<Typography.Text ellipsis={true}>
<a onClick={() => {
openModelDetail(record.modelId)
}}>
{ record.modelName }
</a>
</Typography.Text>
</Tooltip>
)
}
},
{
title: '模型更新时间',
dataIndex: 'modelModifiedTs',
render: (_, record) => record.modelModifiedTs?new Date(record.modelModifiedTs).toLocaleString():''
}, },
{ {
title: '元数据路径', title: '元数据路径',
...@@ -354,6 +691,24 @@ const PartialMatch = ({ item }) => { ...@@ -354,6 +691,24 @@ const PartialMatch = ({ item }) => {
{ {
title: '元数据名称', title: '元数据名称',
dataIndex: 'metadataName', dataIndex: 'metadataName',
render: (_, record) => {
return (
<Tooltip title={record.metadataName}>
<Typography.Text ellipsis={true}>
<a onClick={() => {
openMetadataDetail(record.metadataId)
}}>
{ record.metadataName }
</a>
</Typography.Text>
</Tooltip>
)
}
},
{
title: '元数据更新时间',
dataIndex: 'metadataModifiedTs',
render: (_, record) => record.metadataModifiedTs?new Date(record.metadataModifiedTs).toLocaleString():''
}, },
{ {
title: '差异结论', title: '差异结论',
...@@ -387,11 +742,16 @@ const PartialMatch = ({ item }) => { ...@@ -387,11 +742,16 @@ const PartialMatch = ({ item }) => {
dispatch({ dispatch({
type: 'datamodel.getCompareJobResultPartialMatchList', type: 'datamodel.getCompareJobResultPartialMatchList',
payload: { payload: {
resultId: item?.id resultId: item?.id,
metadataPath: args.metadataPath,
modelPath: args.modelPath,
pageNo: args.page,
pageSize: args.size,
}, },
callback: data => { callback: data => {
setLoading(false) setLoading(false)
setData(data) setData(data?.content)
setTotal(data?.totalElements)
}, },
error: () => { error: () => {
setLoading(false) setLoading(false)
...@@ -399,6 +759,40 @@ const PartialMatch = ({ item }) => { ...@@ -399,6 +759,40 @@ const PartialMatch = ({ item }) => {
}) })
} }
const getMetadataCatalog = () => {
setLoadingMetadataCatalog(true)
dispatch({
type: 'datamodel.getCompareJobResultMetadataPartialMatchCatalog',
payload: {
resultId: item?.id
},
callback: data => {
setLoadingMetadataCatalog(false)
setMetadataCatalog(data)
},
error: () => {
setLoadingMetadataCatalog(false)
}
})
}
const getModelCatalog = () => {
setLoadingModelCatalog(true)
dispatch({
type: 'datamodel.getCompareJobResultModelPartialMatchCatalog',
payload: {
resultId: item?.id
},
callback: data => {
setLoadingModelCatalog(false)
setModelCatalog(data)
},
error: () => {
setLoadingModelCatalog(false)
}
})
}
const onDeployClick = () => { const onDeployClick = () => {
modal.confirm({ modal.confirm({
title:'提示', title:'提示',
...@@ -446,24 +840,40 @@ const PartialMatch = ({ item }) => { ...@@ -446,24 +840,40 @@ const PartialMatch = ({ item }) => {
return ( return (
<div> <div>
<div> <div className='flex mb-3' style={{ justifyContent: 'space-between', alignItems: 'center' }}>
<Tooltip title={(selectedRows??[]).length===0?'请选择模型':''}> <Tooltip title={(selectedRows??[]).length===0?'请选择模型':''}>
<Button onClick={onDeployClick} disabled={(selectedRows??[]).length===0}>确定上线</Button> <Button onClick={onDeployClick} disabled={(selectedRows??[]).length===0}>确定上线</Button>
</Tooltip> </Tooltip>
<Space>
<Catalog
placeholder='请选择元数据目录'
loading={loadingMetadataCatalog} data={metadataCatalog} value={args.metadataPath}
onChange={(val) => {
setArgsByParams({ metadataPath: val, metadataPage: 1 })
}}
/>
<Catalog
placeholder='请选择模型目录'
loading={loadingModelCatalog} data={modelCatalog} value={args.modelPath}
onChange={(val) => {
setArgsByParams({ modelPath: val, modelPage: 1 })
}}
/>
</Space>
</div> </div>
<div className='pt-3'> <div>
<Table <Table
extraColWidth={32} extraColWidth={32}
loading={loading} loading={loading}
columns={cols??[]} columns={cols??[]}
dataSource={tableData??[]} dataSource={data??[]}
rowSelection={{ rowSelection={{
selectedRowKeys: (selectedRows??[]).map(item => item.id), selectedRowKeys: (selectedRows??[]).map(item => item.id),
onChange: (selectedRowKeys, selectedRows) => { onChange: (selectedRowKeys, selectedRows) => {
setSelectedRows(selectedRows) setSelectedRows(selectedRows)
}, },
}} }}
pageNum={args.page} pageSize={args.size} total={(data??[]).length} pageNum={args.page} pageSize={args.size} total={total}
onPaginate={(page, size) => { onPaginate={(page, size) => {
setArgsByParams({ page, size }) setArgsByParams({ page, size })
}} }}
...@@ -486,21 +896,36 @@ const NotMatch = ({ item }) => { ...@@ -486,21 +896,36 @@ const NotMatch = ({ item }) => {
const [args, setArgs] = React.useState({ const [args, setArgs] = React.useState({
metadataPage: defaultPage.pageNum, metadataPage: defaultPage.pageNum,
metadataSize: defaultPage.pageSize, metadataSize: defaultPage.pageSize,
metadataPath: undefined,
modelPage: defaultPage.pageNum, modelPage: defaultPage.pageNum,
modelSize: defaultPage.pageSize, modelSize: defaultPage.pageSize,
modelPath: undefined,
}) })
const [loadingMetadata, setLoadingMetadata] = React.useState(false) const [loadingMetadata, setLoadingMetadata] = React.useState(false)
const [metadatas, setMetadatas] = React.useState() const [metadatas, setMetadatas] = React.useState()
const [metadataTotal, setMetadataTotal] = React.useState(0)
const [loadingModel, setLoadingModel] = React.useState(false) const [loadingModel, setLoadingModel] = React.useState(false)
const [models, setModels] = React.useState() const [models, setModels] = React.useState()
const [modelTotal, setModelTotal] = React.useState(0)
const [loadingMetadataCatalog, setLoadingMetadataCatalog] = React.useState(false)
const [metadataCatalog, setMetadataCatalog] = React.useState()
const [loadingModelCatalog, setLoadingModelCatalog] = React.useState(false)
const [modelCatalog, setModelCatalog] = React.useState()
React.useEffect(() => {
if (item) {
getMetadataCatalog()
getModelCatalog()
}
}, [item])
React.useEffect(() => { React.useEffect(() => {
if (item) { if (item) {
getMetadatas() getMetadatas()
getModels() getModels()
} }
}, [item]) }, [args])
const setArgsByParams = React.useCallback((params) => { const setArgsByParams = React.useCallback((params) => {
setArgs((prev) => { setArgs((prev) => {
...@@ -508,13 +933,6 @@ const NotMatch = ({ item }) => { ...@@ -508,13 +933,6 @@ const NotMatch = ({ item }) => {
}) })
}, []) }, [])
const [metadataTableData, modelTableData] = React.useMemo(() => {
let newMetadataTableData = [...metadatas??[]]
let newModelTableData = [...models??[]]
return [paginate(newMetadataTableData, args.metadataPage, args.metadataSize), paginate(newModelTableData, args.modelPage, args.modelSize)]
}, [metadatas, models, args])
const metadataCols = [ const metadataCols = [
{ {
title: '序号', title: '序号',
...@@ -553,6 +971,24 @@ const NotMatch = ({ item }) => { ...@@ -553,6 +971,24 @@ const NotMatch = ({ item }) => {
{ {
title: '元数据名称', title: '元数据名称',
dataIndex: 'metadataName', dataIndex: 'metadataName',
render: (_, record) => {
return (
<Tooltip title={record.metadataName}>
<Typography.Text ellipsis={true}>
<a onClick={() => {
openMetadataDetail(record.metadataId)
}}>
{ record.metadataName }
</a>
</Typography.Text>
</Tooltip>
)
}
},
{
title: '元数据更新时间',
dataIndex: 'metadataModifiedTs',
render: (_, record) => record.metadataModifiedTs?new Date(record.metadataModifiedTs).toLocaleString():''
}, },
] ]
...@@ -570,6 +1006,24 @@ const NotMatch = ({ item }) => { ...@@ -570,6 +1006,24 @@ const NotMatch = ({ item }) => {
{ {
title: '模型名称', title: '模型名称',
dataIndex: 'modelName', dataIndex: 'modelName',
render: (_, record) => {
return (
<Tooltip title={record.modelName}>
<Typography.Text ellipsis={true}>
<a onClick={() => {
openModelDetail(record.modelId)
}}>
{ record.modelName }
</a>
</Typography.Text>
</Tooltip>
)
}
},
{
title: '模型更新时间',
dataIndex: 'modelModifiedTs',
render: (_, record) => record.modelModifiedTs?new Date(record.modelModifiedTs).toLocaleString():''
}, },
] ]
...@@ -578,11 +1032,15 @@ const NotMatch = ({ item }) => { ...@@ -578,11 +1032,15 @@ const NotMatch = ({ item }) => {
dispatch({ dispatch({
type: 'datamodel.getCompareJobResultNotMatchMetadataList', type: 'datamodel.getCompareJobResultNotMatchMetadataList',
payload: { payload: {
resultId: item?.id resultId: item?.id,
metadataPath: args.metadataPath,
pageNo: args.metadataPage,
pageSize: args.metadataSize,
}, },
callback: data => { callback: data => {
setLoadingMetadata(false) setLoadingMetadata(false)
setMetadatas(data) setMetadatas(data?.content)
setMetadataTotal(data?.totalElements)
}, },
error: () => { error: () => {
setLoadingMetadata(false) setLoadingMetadata(false)
...@@ -595,11 +1053,15 @@ const NotMatch = ({ item }) => { ...@@ -595,11 +1053,15 @@ const NotMatch = ({ item }) => {
dispatch({ dispatch({
type: 'datamodel.getCompareJobResultNotMatchModelList', type: 'datamodel.getCompareJobResultNotMatchModelList',
payload: { payload: {
resultId: item?.id resultId: item?.id,
modelPath: args.modelPath,
pageNo: args.modelPage,
pageSize: args.modelSize,
}, },
callback: data => { callback: data => {
setLoadingModel(false) setLoadingModel(false)
setModels(data) setModels(data?.content)
setModelTotal(data?.totalElements)
}, },
error: () => { error: () => {
setLoadingModel(false) setLoadingModel(false)
...@@ -607,16 +1069,59 @@ const NotMatch = ({ item }) => { ...@@ -607,16 +1069,59 @@ const NotMatch = ({ item }) => {
}) })
} }
const getMetadataCatalog = () => {
setLoadingMetadataCatalog(true)
dispatch({
type: 'datamodel.getCompareJobResultMetadataNotMatchCatalog',
payload: {
resultId: item?.id
},
callback: data => {
setLoadingMetadataCatalog(false)
setMetadataCatalog(data)
},
error: () => {
setLoadingMetadataCatalog(false)
}
})
}
const getModelCatalog = () => {
setLoadingModelCatalog(true)
dispatch({
type: 'datamodel.getCompareJobResultModelNotMatchCatalog',
payload: {
resultId: item?.id
},
callback: data => {
setLoadingModelCatalog(false)
setModelCatalog(data)
},
error: () => {
setLoadingModelCatalog(false)
}
})
}
return ( return (
<div style={{ overflow: 'hidden' }}> <div style={{ overflow: 'hidden' }}>
<Row gutter={15}> <Row gutter={15}>
<Col span={12}> <Col span={12}>
<h4>未匹配元数据列表</h4> <div className='flex mb-3' style={{ justifyContent: 'space-between', alignItems: 'center' }}>
<h4 style={{ marginBottom: 0 }}>未匹配元数据列表</h4>
<Catalog
placeholder='请选择元数据目录'
loading={loadingMetadataCatalog} data={metadataCatalog} value={args.metadataPath}
onChange={(val) => {
setArgsByParams({ metadataPath: val, metadataPage: 1 })
}}
/>
</div>
<Table <Table
loading={loadingMetadata} loading={loadingMetadata}
columns={metadataCols??[]} columns={metadataCols??[]}
dataSource={metadataTableData??[]} dataSource={metadatas??[]}
pageNum={args.metadataPage} pageSize={args.metadataSize} total={(metadatas??[]).length} pageNum={args.metadataPage} pageSize={args.metadataSize} total={metadataTotal}
onPaginate={(page, size) => { onPaginate={(page, size) => {
setArgsByParams({ metadataPage: page, metadataSize: size }) setArgsByParams({ metadataPage: page, metadataSize: size })
}} }}
...@@ -626,12 +1131,21 @@ const NotMatch = ({ item }) => { ...@@ -626,12 +1131,21 @@ const NotMatch = ({ item }) => {
/> />
</Col> </Col>
<Col span={12}> <Col span={12}>
<h4>未匹配模型列表</h4> <div className='flex mb-3' style={{ justifyContent: 'space-between', alignItems: 'center' }}>
<h4 style={{ marginBottom: 0 }}>未匹配模型列表</h4>
<Catalog
placeholder='请选择模型目录'
loading={loadingModelCatalog} data={modelCatalog} value={args.modelPath}
onChange={(val) => {
setArgsByParams({ modelPath: val, modelPage: 1 })
}}
/>
</div>
<Table <Table
loading={loadingModel} loading={loadingModel}
columns={modelCols??[]} columns={modelCols??[]}
dataSource={modelTableData??[]} dataSource={models??[]}
pageNum={args.modelPage} pageSize={args.modelSize} total={(models??[]).length} pageNum={args.modelPage} pageSize={args.modelSize} total={modelTotal}
onPaginate={(page, size) => { onPaginate={(page, size) => {
setArgsByParams({ modelPage: page, modelSize: size }) setArgsByParams({ modelPage: page, modelSize: size })
}} }}
...@@ -644,3 +1158,56 @@ const NotMatch = ({ item }) => { ...@@ -644,3 +1158,56 @@ const NotMatch = ({ item }) => {
</div> </div>
) )
} }
const Catalog = ({ value, onChange, data, loading, placeholder }) => {
const [expandedKeys, setExpandedKeys] = React.useState()
React.useEffect(() => {
if ((data?.children??[]).length>0) {
setExpandedKeys([data.children[0].path])
}
}, [data])
const treeData = React.useMemo(() => {
if (data && data.children) {
const newTreeData = produce(data.children, draft => {
const setNode = (g) => {
g.key = g.path
g.title = g.name
g.value = g.path
g.children?.forEach((child) => {
setNode(child)
})
}
draft.forEach((child) => {
setNode(child)
})
})
return newTreeData
}
return undefined
}, [data])
return (
<TreeSelect
showSearch allowClear
treeNodeFilterProp='title'
loading={loading}
value={value}
dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
treeData={treeData}
treeExpandedKeys={expandedKeys}
onTreeExpand={(val) => {
setExpandedKeys(val)
}}
onChange={(val) => {
onChange?.(val)
}}
style={{ width: inputWidth }}
placeholder={placeholder??'请选择目录'}
/>
)
}
\ No newline at end of file
...@@ -5,7 +5,7 @@ import { defaultPage } from '../../../util/hooks/page' ...@@ -5,7 +5,7 @@ import { defaultPage } from '../../../util/hooks/page'
import Table from '../../../util/Component/Table' import Table from '../../../util/Component/Table'
import { dispatch } from '../../../model' import { dispatch } from '../../../model'
import { generateUUID, paginate, showMessage, showNotifaction } from '../../../util' import { generateUUID, paginate, showMessage, showNotifaction } from '../../../util'
import UpdateTask from './update-task' import UpdateTask, { Range } from './update-task'
import ColConfig from './col-config' import ColConfig from './col-config'
import '../AssetTask/index.less' import '../AssetTask/index.less'
...@@ -108,21 +108,12 @@ const FC = (props) => { ...@@ -108,21 +108,12 @@ const FC = (props) => {
dataIndex: 'jobCatalogItems', dataIndex: 'jobCatalogItems',
render: (_, record) => { render: (_, record) => {
return ( return (
<Tooltip title={ <Tooltip title={<Range task={record} type='detail' />}>
<div>
{
(record.jobCatalogItems??[]).map(item => (
<Row key={generateUUID()}>
{`【模型】${(item.modelCatalogNameList??[]).join('/')} -【元数据】${(item.metadataCatalogNameList??[]).join('/')}`}
</Row>
))
}
</div>
}>
<Typography.Text ellipsis={true}> <Typography.Text ellipsis={true}>
{ { (record?.jobModelCatalogItems??[]).length > 0 && '【模型范围】' }
(record.jobCatalogItems??[]).map(item => `【模型】${(item.modelCatalogNameList??[]).join('/')} -【元数据】${(item.metadataCatalogNameList??[]).join('/')}`).toString() { record?.jobModelCatalogItems?.map((item, index) => `${(item.modelCatalogNameList??[]).join('/')}`).toString() }
} { (record?.jobMetadataCatalogItems??[]).length > 0 && '【元数据范围】' }
{ record?.jobMetadataCatalogItems?.map((item, index) => `${(item.metadataCatalogNameList??[]).join('/')}`).toString() }
</Typography.Text> </Typography.Text>
</Tooltip> </Tooltip>
) )
......
import React from 'react' import React from 'react'
import { Button, Modal, Spin, Space, Divider, Form, Row, Col, Input, Select, Checkbox, Radio, Switch, TimePicker, Tooltip, TreeSelect } from 'antd' import { Button, Modal, Spin, Space, Divider, Form, Row, Col, Input, Select, Checkbox, Radio, Switch, TimePicker, Tooltip, TreeSelect, Typography } from 'antd'
import { useClickAway } from 'ahooks' import { useClickAway } from 'ahooks'
import moment from 'moment' import moment from 'moment'
import produce from 'immer' import produce from 'immer'
import { CloseOutlined } from '@ant-design/icons';
import { dispatch } from '../../../model' import { dispatch } from '../../../model'
import Table, { generateId } from '../../../util/Component/Table' import Table, { generateId } from '../../../util/Component/Table'
...@@ -80,7 +81,7 @@ const FC = (props) => { ...@@ -80,7 +81,7 @@ const FC = (props) => {
console.log('strategyRows', strategyRows) console.log('strategyRows', strategyRows)
console.log('scheduleRows', scheduleRows) console.log('scheduleRows', scheduleRows)
if ((rangeRows??[]).length === 0) { if ((rangeRows?.jobModelCatalogItems??[]).length===0 || (rangeRows?.jobMetadataCatalogItems??[]).length===0) {
showMessage('warn', '请先填写对比范围') showMessage('warn', '请先填写对比范围')
return return
} }
...@@ -97,7 +98,7 @@ const FC = (props) => { ...@@ -97,7 +98,7 @@ const FC = (props) => {
payload: { payload: {
data: { data: {
...basicRows, ...basicRows,
jobCatalogItems: rangeRows, ...rangeRows,
strategyItemPropertyTypes: strategyRows, strategyItemPropertyTypes: strategyRows,
jobSchedule: scheduleRows, jobSchedule: scheduleRows,
} }
...@@ -116,7 +117,7 @@ const FC = (props) => { ...@@ -116,7 +117,7 @@ const FC = (props) => {
data: { data: {
...task, ...task,
...basicRows, ...basicRows,
jobCatalogItems: rangeRows, ...rangeRows,
strategyItemPropertyTypes: strategyRows, strategyItemPropertyTypes: strategyRows,
jobSchedule: scheduleRows, jobSchedule: scheduleRows,
} }
...@@ -151,7 +152,7 @@ const FC = (props) => { ...@@ -151,7 +152,7 @@ const FC = (props) => {
visible={visible} visible={visible}
footer={(type==='detail') ? null : footer} footer={(type==='detail') ? null : footer}
width='90%' width='90%'
bodyStyle={{ padding: '15px', overflowX: 'auto', maxHeight: '80vh' }} bodyStyle={{ padding: '15px', overflowX: 'auto', height: '80vh' }}
title={title} title={title}
centered destroyOnClose centered destroyOnClose
onCancel={() => { close() }} onCancel={() => { close() }}
...@@ -390,28 +391,26 @@ const BasicForm = React.forwardRef(function ({ type, task }, ref) { ...@@ -390,28 +391,26 @@ const BasicForm = React.forwardRef(function ({ type, task }, ref) {
const ModelRangeItem = ({ value, onChange }) => { const ModelRangeItem = ({ value, onChange }) => {
const [loading, setLoading] = React.useState(false) const [loading, setLoading] = React.useState(false)
const [treeData, setTreeData] = React.useState() const [treeData, setTreeData] = React.useState()
const [treeSelectedKey, setTreeSelectedKey] = React.useState() const [treeSelectedKeys, setTreeSelectedKeys] = React.useState()
React.useEffect(() => { React.useEffect(() => {
getTreeData() getTreeData()
}, []) }, [])
React.useEffect(() => { React.useEffect(() => {
if (treeSelectedKey) { const newData = []
const treeIndex = (treeList??[]).findIndex(item => item.key === treeSelectedKey) for (const key of treeSelectedKeys??[]) {
const index = (treeList??[]).findIndex(item => item.key === key)
if (treeIndex !== -1) { if (index !== -1) {
onChange?.({ newData.push({
modelCatalogIdList: treeList[treeIndex].ids??[], modelCatalogIdList: treeList[index].ids??[],
modelCatalogNameList: treeList[treeIndex].names??[] modelCatalogNameList: treeList[index].names??[]
}) })
} else {
onChange?.()
} }
} else {
onChange?.()
} }
}, [treeSelectedKey])
onChange?.(newData)
}, [treeSelectedKeys])
const generateList = (data, list, ids, names) => { const generateList = (data, list, ids, names) => {
(data||[]).forEach(node => { (data||[]).forEach(node => {
...@@ -474,17 +473,18 @@ const ModelRangeItem = ({ value, onChange }) => { ...@@ -474,17 +473,18 @@ const ModelRangeItem = ({ value, onChange }) => {
}) })
} }
const onTreeSelect = (selectedKeys) => { const onTreeChange = (selectedKeys) => {
setTreeSelectedKey(selectedKeys) setTreeSelectedKeys(selectedKeys)
} }
return ( return (
<TreeSelect <TreeSelect
multiple
loading={loading} loading={loading}
dropdownStyle={{ maxHeight: 400, overflow: 'auto' }} dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
treeData={treeData1} treeData={treeData1}
value={treeSelectedKey?[treeSelectedKey]:undefined} value={treeSelectedKeys??[]}
onSelect={onTreeSelect} onChange={onTreeChange}
placeholder="请选择模型范围" placeholder="请选择模型范围"
/> />
) )
...@@ -496,7 +496,7 @@ const MetadataRangeItem = ({ value, onChange }) => { ...@@ -496,7 +496,7 @@ const MetadataRangeItem = ({ value, onChange }) => {
const [currentDomainId, setCurrentDomainId] = React.useState() const [currentDomainId, setCurrentDomainId] = React.useState()
const [loadingTreeData, setLoadingTreeData] = React.useState(false) const [loadingTreeData, setLoadingTreeData] = React.useState(false)
const [treeData, setTreeData] = React.useState() const [treeData, setTreeData] = React.useState()
const [treeSelectedKey, setTreeSelectedKey] = React.useState() const [treeSelectedKeys, setTreeSelectedKeys] = React.useState()
React.useEffect(() => { React.useEffect(() => {
getDomains() getDomains()
...@@ -509,28 +509,31 @@ const MetadataRangeItem = ({ value, onChange }) => { ...@@ -509,28 +509,31 @@ const MetadataRangeItem = ({ value, onChange }) => {
}, [currentDomainId]) }, [currentDomainId])
React.useEffect(() => { React.useEffect(() => {
if (currentDomainId && treeSelectedKey) { if (currentDomainId) {
const domainIndex = (domains??[]).findIndex(item => item.domainId === currentDomainId) const newData = []
const treeIndex = (treeList??[]).findIndex(item => item.key === treeSelectedKey)
if (domainIndex !== -1 && treeIndex !== -1) { const domainIndex = (domains??[]).findIndex(item => item.domainId === currentDomainId)
onChange?.({ for (const key of treeSelectedKeys??[]) {
const index = (treeList??[]).findIndex(item => item.key === key)
if (domainIndex!==-1 && index !== -1) {
newData.push({
metadataCatalogIdList: [ metadataCatalogIdList: [
`${domains[domainIndex].domainId}`, `${domains[domainIndex].domainId}`,
...treeList[treeIndex].ids??[], ...treeList[index].ids??[],
], ],
metadataCatalogNameList: [ metadataCatalogNameList: [
domains[domainIndex].domainName, domains[domainIndex].domainName,
...treeList[treeIndex].names??[], ...treeList[index].names??[]
] ]
}) })
} else {
onChange?.()
} }
}
onChange?.(newData)
} else { } else {
onChange?.() onChange?.()
} }
}, [currentDomainId, treeSelectedKey]) }, [currentDomainId, treeSelectedKeys])
const generateList = (data, list, ids, names) => { const generateList = (data, list, ids, names) => {
(data||[]).forEach(node => { (data||[]).forEach(node => {
...@@ -591,7 +594,6 @@ const MetadataRangeItem = ({ value, onChange }) => { ...@@ -591,7 +594,6 @@ const MetadataRangeItem = ({ value, onChange }) => {
item.title = item.catalogName item.title = item.catalogName
item.value = `${item.catalogId}` item.value = `${item.catalogId}`
item.idStr = `${item.catalogId}` item.idStr = `${item.catalogId}`
item.disabled = true
item.children = [] item.children = []
for (let child of item.scopes??[]) { for (let child of item.scopes??[]) {
...@@ -599,7 +601,6 @@ const MetadataRangeItem = ({ value, onChange }) => { ...@@ -599,7 +601,6 @@ const MetadataRangeItem = ({ value, onChange }) => {
child.title = child.scopeName child.title = child.scopeName
child.value = `${item.catalogId}-${child.scopeId}` child.value = `${item.catalogId}-${child.scopeId}`
child.idStr = `${child.scopeId}` child.idStr = `${child.scopeId}`
child.disabled = true
item.children.push(child) item.children.push(child)
} }
} }
...@@ -616,11 +617,11 @@ const MetadataRangeItem = ({ value, onChange }) => { ...@@ -616,11 +617,11 @@ const MetadataRangeItem = ({ value, onChange }) => {
const onDomainChange = (val) => { const onDomainChange = (val) => {
setCurrentDomainId(val) setCurrentDomainId(val)
setTreeData() setTreeData()
setTreeSelectedKey() onChange?.()
} }
const onTreeSelect = (selectedKeys) => { const onTreeChange = (selectedKeys) => {
setTreeSelectedKey(selectedKeys) setTreeSelectedKeys(selectedKeys)
} }
const updateTreeData = (list, key, children) => const updateTreeData = (list, key, children) =>
...@@ -692,7 +693,6 @@ const MetadataRangeItem = ({ value, onChange }) => { ...@@ -692,7 +693,6 @@ const MetadataRangeItem = ({ value, onChange }) => {
value: item._id, value: item._id,
scopeId: node?.scopeId, scopeId: node?.scopeId,
idStr: item._id, idStr: item._id,
disabled: true,
...item ...item
})) }))
...@@ -709,8 +709,6 @@ const MetadataRangeItem = ({ value, onChange }) => { ...@@ -709,8 +709,6 @@ const MetadataRangeItem = ({ value, onChange }) => {
} }
}); });
console.log('treeSelectedKey', treeSelectedKey)
return ( return (
<Row gutter={15}> <Row gutter={15}>
<Col span={6}> <Col span={6}>
...@@ -725,9 +723,10 @@ const MetadataRangeItem = ({ value, onChange }) => { ...@@ -725,9 +723,10 @@ const MetadataRangeItem = ({ value, onChange }) => {
</Col> </Col>
<Col span={18}> <Col span={18}>
<TreeSelect <TreeSelect
multiple
loading={loadingTreeData} loading={loadingTreeData}
value={treeSelectedKey?[treeSelectedKey]:undefined} value={treeSelectedKeys??[]}
onSelect={onTreeSelect} onChange={onTreeChange}
dropdownStyle={{ maxHeight: 400, overflow: 'auto' }} dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
treeData={treeData} treeData={treeData}
loadData={onLoadData} loadData={onLoadData}
...@@ -738,7 +737,7 @@ const MetadataRangeItem = ({ value, onChange }) => { ...@@ -738,7 +737,7 @@ const MetadataRangeItem = ({ value, onChange }) => {
) )
} }
const Range = React.forwardRef(function ({ type, task }, ref) { export const Range = React.forwardRef(function ({ type, task }, ref) {
const [ranges, setRanges] = React.useState() const [ranges, setRanges] = React.useState()
const [isAdding, setAdding] = React.useState(false) const [isAdding, setAdding] = React.useState(false)
...@@ -753,7 +752,10 @@ const Range = React.forwardRef(function ({ type, task }, ref) { ...@@ -753,7 +752,10 @@ const Range = React.forwardRef(function ({ type, task }, ref) {
}), [ranges, isAdding]) }), [ranges, isAdding])
React.useEffect(() => { React.useEffect(() => {
setRanges(task?.jobCatalogItems) setRanges({
jobMetadataCatalogItems: task?.jobMetadataCatalogItems??[],
jobModelCatalogItems: task?.jobModelCatalogItems??[],
})
}, [task]) }, [task])
const onAddClick = () => { const onAddClick = () => {
...@@ -763,21 +765,20 @@ const Range = React.forwardRef(function ({ type, task }, ref) { ...@@ -763,21 +765,20 @@ const Range = React.forwardRef(function ({ type, task }, ref) {
} }
const onOkClick = async () => { const onOkClick = async () => {
rangesRef.current = ranges??[]
if (!isAdding) return if (!isAdding) return
try { try {
const rows = await form.validateFields() const rows = await form.validateFields()
setRanges({
let newRanges = [...ranges??[]] jobMetadataCatalogItems: [
newRanges = [...newRanges, { ...ranges?.jobMetadataCatalogItems??[],
...rows.modelRange, ...rows?.jobMetadataCatalogItems??[]
...rows.metadataRange, ],
}] jobModelCatalogItems: [
...ranges?.jobModelCatalogItems??[],
setRanges(newRanges) ...rows?.jobModelCatalogItems??[]
rangesRef.current = newRanges ]
})
setAdding(false) setAdding(false)
} catch(e) { } catch(e) {
throw new Error() throw new Error()
...@@ -788,11 +789,7 @@ const Range = React.forwardRef(function ({ type, task }, ref) { ...@@ -788,11 +789,7 @@ const Range = React.forwardRef(function ({ type, task }, ref) {
setAdding(false) setAdding(false)
} }
const onRangeDeleteClick = (index) => { rangesRef.current = ranges
const newRanges = [...ranges??[]]
newRanges.splice(index, 1)
setRanges(newRanges)
}
return ( return (
<div> <div>
...@@ -812,8 +809,16 @@ const Range = React.forwardRef(function ({ type, task }, ref) { ...@@ -812,8 +809,16 @@ const Range = React.forwardRef(function ({ type, task }, ref) {
<Col span={10}> <Col span={10}>
<Form.Item <Form.Item
label="数据模型范围" label="数据模型范围"
name="modelRange" name="jobModelCatalogItems"
rules={[{ required: true, message: '请选择模型范围!' }]} rules={[{
validator: (_, value) => {
if ((form.getFieldValue('jobModelCatalogItems')??[]).length===0 && (ranges?.jobModelCatalogItems??[]).length===0) {
return Promise.reject(new Error('请选择模型范围!'))
}
return Promise.resolve();
}
}]}
> >
<ModelRangeItem /> <ModelRangeItem />
</Form.Item> </Form.Item>
...@@ -821,8 +826,16 @@ const Range = React.forwardRef(function ({ type, task }, ref) { ...@@ -821,8 +826,16 @@ const Range = React.forwardRef(function ({ type, task }, ref) {
<Col span={10}> <Col span={10}>
<Form.Item <Form.Item
label="元数据范围" label="元数据范围"
name="metadataRange" name="jobMetadataCatalogItems"
rules={[{ required: true, message: '请选择元数据范围!' }]} rules={[{
validator: (_, value) => {
if ((form.getFieldValue('jobMetadataCatalogItems')??[]).length===0 && (ranges?.jobMetadataCatalogItems??[]).length===0) {
return Promise.reject(new Error('请选择元数据范围!'))
}
return Promise.resolve();
}
}]}
> >
<MetadataRangeItem /> <MetadataRangeItem />
</Form.Item> </Form.Item>
...@@ -836,20 +849,60 @@ const Range = React.forwardRef(function ({ type, task }, ref) { ...@@ -836,20 +849,60 @@ const Range = React.forwardRef(function ({ type, task }, ref) {
</Row> </Row>
</Form> </Form>
} }
<Row gutter={10}>
<Col flex='0 0 110px'>
{ {
(ranges??[]).map((item, index) => { (ranges?.jobModelCatalogItems??[]).length > 0 && <span>【模型范围】</span>
return ( }
<Row className={type==='detail'?'mb-2':'mb-1'} key={generateUUID()} align='middle' gutter={15}>
<Col span={type==='detail'?24:12}>
{`【模型】${(item.modelCatalogNameList??[]).join('/')} -【元数据】${(item.metadataCatalogNameList??[]).join('/')}`}
</Col> </Col>
<Col flex='1' style={{ overflow: 'hidden' }}>
{
type === 'detail' ? <span>{ranges?.jobModelCatalogItems?.map((item) => (item.modelCatalogNameList??[]).join('/')).toString()}</span> : <Space wrap>
{ {
type !== 'detail' && <Button type='link' onClick={() => onRangeDeleteClick(index)}>删除</Button> ranges?.jobModelCatalogItems?.map((item, index) => <Tooltip title={(item.modelCatalogNameList??[]).join('/')}>
<Button key={generateUUID()} size='small' onClick={() => {
const newJobModelCatalogItems = [...ranges?.jobModelCatalogItems??[]]
newJobModelCatalogItems.splice(index, 1)
setRanges({...ranges, jobModelCatalogItems: newJobModelCatalogItems})
}}>
<Typography.Text ellipsis={true} style={{ maxWidth: 600 }}>
{(item.modelCatalogNameList??[]).join('/')}
</Typography.Text>
<CloseOutlined />
</Button>
</Tooltip>
)}
</Space>
} }
</Col>
</Row> </Row>
) <Row className={(type==='detail')?'mt-2':'mt-3'} gutter={10}>
}) <Col flex='0 0 110px'>
{
(ranges?.jobMetadataCatalogItems??[]).length > 0 && <span>【元数据范围】</span>
} }
</Col>
<Col flex='1' style={{ overflow: 'hidden' }}>
{
type === 'detail' ? <span>{ranges?.jobMetadataCatalogItems?.map((item) => (item.metadataCatalogNameList??[]).join('/')).toString()}</span> : <Space wrap>
{
ranges?.jobMetadataCatalogItems?.map((item, index) => <Tooltip title={(item.metadataCatalogNameList??[]).join('/')}>
<Button key={generateUUID()} size='small' onClick={() => {
const newJobMetadataCatalogItems = [...ranges?.jobMetadataCatalogItems??[]]
newJobMetadataCatalogItems.splice(index, 1)
setRanges({...ranges, jobMetadataCatalogItems: newJobMetadataCatalogItems})
}}>
<Typography.Text ellipsis={true} style={{ maxWidth: 600 }}>
{(item.metadataCatalogNameList??[]).join('/')}
</Typography.Text>
<CloseOutlined />
</Button>
</Tooltip>
)}
</Space>
}
</Col>
</Row>
</div> </div>
) )
}) })
......
...@@ -422,6 +422,9 @@ const FC = (props) => { ...@@ -422,6 +422,9 @@ const FC = (props) => {
setSelectedRows(selectedRows) setSelectedRows(selectedRows)
}, },
}} }}
scroll={{
y: readonly?'calc(80vh - 230px)':null
}}
/> />
</div> </div>
<AddRule <AddRule
......
...@@ -36,7 +36,7 @@ const ResizeableHeaderCell = props => { ...@@ -36,7 +36,7 @@ const ResizeableHeaderCell = props => {
}; };
const ResizeableTable = (props) => { const ResizeableTable = (props) => {
const { columns, extraColWidth = 0, ...restProps } = props const { columns, extraColWidth = 0, components, ...restProps } = props
const [tableWidth, setTableWidth] = useState(0) const [tableWidth, setTableWidth] = useState(0)
...@@ -62,10 +62,18 @@ const ResizeableTable = (props) => { ...@@ -62,10 +62,18 @@ const ResizeableTable = (props) => {
useEffect(() => { useEffect(() => {
if (!!columns && tableWidth > 0) { if (!!columns && tableWidth > 0) {
const contentWidth = getWidth(tableWidth, extraColWidth) const contentWidth = getWidth(tableWidth, extraColWidth)
for (const item of columns??[]) {
const index = (cols??[]).findIndex(_item => item.dataIndex === _item.dataIndex && item.title === _item.title)
if (index !== -1) {
item.width = cols[index].width
}
}
setDefaultWidth(columns, contentWidth) setDefaultWidth(columns, contentWidth)
paddingCol.current.width = 0 paddingCol.current.width = 0
const cols = columns const newCols = columns
.map((col, index) => { .map((col, index) => {
const colWidth = col.width ?? 100; const colWidth = col.width ?? 100;
return { return {
...@@ -78,7 +86,7 @@ const ResizeableTable = (props) => { ...@@ -78,7 +86,7 @@ const ResizeableTable = (props) => {
}), }),
}; };
}) })
setCols(cols) setCols(newCols)
} }
}, [columns, tableWidth]) }, [columns, tableWidth])
...@@ -94,7 +102,8 @@ const ResizeableTable = (props) => { ...@@ -94,7 +102,8 @@ const ResizeableTable = (props) => {
components={{ components={{
header: { header: {
cell: ResizeableHeaderCell, cell: ResizeableHeaderCell,
} },
...components,
}} }}
columns={cols1} columns={cols1}
{ ...restProps } { ...restProps }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment