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*
yarn-debug.log*
yarn-error.log*
package-lock.json
yarn.lock
/center-home
\ No newline at end of file
......@@ -224,6 +224,11 @@ export class App extends React.Component {
<Route path={'/center-home/data-model-action'} component={EditModel} exact />
<Route path={'/center-home/asset-detail'} component={AssetDetailPage} exact />
<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>
</Router>
</AppContext.Provider>
......
......@@ -210,6 +210,10 @@ export function* dataModelRollback(payload) {
return yield call(datamodelerService.dataModelRollback, payload);
}
export function* compareOtherModel(payload) {
return yield call(datamodelerService.compareOtherModel, payload);
}
export function* getDataModelLocation(payload) {
return yield call(datamodelerService.getDataModelLocation, payload);
}
......@@ -250,6 +254,18 @@ export function* 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) {
return yield call(datamodelerService.validateDataModel, payload);
}
......@@ -346,6 +362,10 @@ export function* getPrivilegeAdmin() {
return yield call(datamodelerService.getPrivilegeAdmin);
}
export function* getCatalogAdmin() {
return yield call(datamodelerService.getCatalogAdmin);
}
export function* getPrivilegeBranchAdmin() {
return yield call(datamodelerService.getPrivilegeBranchAdmin);
}
......@@ -592,6 +612,50 @@ export function* 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) {
return yield call(datamodelerService.addDataTypeConfig, payload)
}
......@@ -711,3 +775,7 @@ export function* getOwner(payload) {
export function* 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() {
return GetJSON("/datamodeler/easyDataModelerCURD/loadDataModelCatalog");
......@@ -201,6 +201,10 @@ export function dataModelRollback(payload) {
return PostJSON("/datamodeler/easyDataModelerCURD/reset", payload);
}
export function compareOtherModel(payload) {
return PostJSON("/datamodeler/easyDataModelerCURD/compareOtherModel", payload);
}
export function ddlGenerators() {
return GetJSON("/datamodeler/easyDataModelerExport/ddlGenerators");
}
......@@ -237,6 +241,18 @@ export function 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) {
return PostJSON("/datamodeler/easyDataModelerConstraint/validateDataModel", payload);
}
......@@ -297,6 +313,10 @@ export function getPrivilegeAdmin() {
return Get("/datamodeler/easyDataModelerPrivilegeProvider/getAdmin");
}
export function getCatalogAdmin() {
return Get("/datamodeler/easyDataModelerPrivilegeProvider/getCatalogAdmin");
}
export function getPrivilegeBranchAdmin() {
return Get("/datamodeler/easyDataModelerPrivilegeProvider/getBranchCreation");
}
......@@ -537,6 +557,55 @@ export function getCompareJobResultDeployModels(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*/
export function addDataTypeConfig(payload) {
return PostJSON("/datamodeler/easyDataModelerColumnDataType/addDataTypeConfig", payload)
......@@ -661,3 +730,8 @@ export function getOwner(payload) {
export function setOwner(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,
useEffect(() => {
if (!!columns && tableWidth > 0) {
const contentWidth = getWidth(width ?? tableWidth,extraColWidth)
setDefaultWidth(columns, contentWidth)
paddingCol.current.width = 0
const cols = columns
const newCols = columns
.map((col, index) => {
const render = getRender(col);
const colWidth = col.width ?? 100;
......@@ -83,7 +84,7 @@ function FC<RowType extends object = any>({ width, maxHeight, pageSize, pageNum,
}),
};
})
setCols(cols)
setCols(newCols)
}
}, [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';
export const StateId = 'sid';
export const VersionId = 'vid';
export const TemplateId = 'tid';
export const Holder = 'holder';
export const ReadOnly = 'readOnly';
export const ApprovalId = 'approvalId';
export const ApprovalType = 'approvalType';
......
......@@ -563,3 +563,11 @@ export function isChromeVersionLessThan80() {
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) => {
<div className='edit-header'>
<span style={{ fontSize: 16, fontWeight: 'bold', color: '#fff' }}>{title}</span>
</div>
<div className='edit-container'>
<div className='edit-container-card' style={{ padding: '20px 20px 0' }}>
<div className='edit-container' style={{ padding: '10px 10px 0' }}>
<EditAssets ref={editAssetsRef} action={action} type={type} ids={ids} elementIds={elementIds} />
</div>
</div>
<div className='edit-footer'>
{
action === 'edit' ? <Space>
......@@ -459,7 +457,7 @@ export const EditAssets = React.forwardRef(function ({ action, type, ids, elemen
<div ref={tableRef}>
<Form form={form} component={false}>
<Table
maxHeight='calc(100vh - 245px)'
maxHeight='calc(100vh - 213px)'
loading={loading}
columns={columns}
dataSource={tableData}
......
......@@ -2,25 +2,25 @@ import React, { useState, useEffect, useRef, useMemo } from 'react';
import { Form, Button, Space, Tooltip, Modal, Select, Input } from 'antd';
import LocalStorage from 'local-storage';
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 ResizeObserver from 'rc-resize-observer';
import ImportAction from './ImportAction';
import CatalogModal from './CatalogModal';
import { dispatchLatest, dispatch } from '../../../../model';
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 { EditModelContext } from './ContextManage';
import EditInherited from './EditInherited';
import { ImportActionHeaderSubject } from './ImportActionManage';
import PermissionButton from '../../../../util/Component/PermissionButton';
import RecatalogModal from './RecatalogModal';
import ExportModel from './ExportOtherModal';
import ExportDDLModel from './ExportDDLModal';
import ExportOptions from './export-options';
import ApprovalTip from './approval-tip';
import './EditModel.less';
import { getTimeProps } from 'antd/lib/date-picker/generatePicker';
const EditModel = (props) => {
......@@ -44,15 +44,13 @@ const EditModel = (props) => {
const [exportParams, setExportParams] = useState({
visible: false
})
const [exportDDLParams, setExportDDLParams] = useState({
visible: false,
})
const [containerHeight, setContainerHeight] = useState(0)
const actionRef = useRef('');
const attrIsEditingRef = 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 [modal, contextHolder] = Modal.useModal()
......@@ -68,7 +66,6 @@ const EditModel = (props) => {
const _editable = getQueryParam(Editable, props.location.search);
const _stateId = getQueryParam(StateId, 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 _readOnly = getQueryParam(ReadOnly, props.location.search);
const _branchId = getQueryParam(BranchId, props.location.search)
......@@ -88,7 +85,7 @@ const EditModel = (props) => {
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;
if (_approvalId) {
......@@ -295,7 +292,7 @@ const EditModel = (props) => {
}
const edit = () => {
setActionData({ ...actionData, action: 'edit' });
setActionData({ ...actionData, action: 'edit', permitCheckOut: modelerData?.permitCheckOut });
actionRef.current = 'edit';
}
......@@ -353,31 +350,8 @@ const EditModel = (props) => {
setExportParams({ visible: true })
}
const onExportCancel = (val) => {
setExportParams({ visible: false })
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 onExportInfo = () => {
window.open(`/api/datamodeler/easyDataModelerExport/modelBaseDataExcel?ids=${modelerData?.id}`);
}
const onChangeCatalog = () => {
......@@ -458,19 +432,18 @@ const EditModel = (props) => {
</Space>
)
} else if (action === 'detail') {
let editTip = '', deleteTip = '';
let editTip = '', deleteTip = '', editDisabled = false;
if (modelerData?.inheritedFromEasyDataModelerDataModel) {
editTip = '请到对应的当前表修改';
editDisabled = true;
} else {
if (!editable && stateId !== '4') {
if (stateId === '2') {
editTip = '待发布的模型不允许编辑';
}
if (modelerData?.state?.id === '4') {
if (!modelerData?.permitCheckOut) {
editTip = `${modelerData?.holder||''}正在编辑中, 不允许再编辑`;
editDisabled = true;
}
if (!permitCheckOut && stateId === '4') {
editTip = `${holder||''}正在编辑中, 不允许再编辑`;
} else if (!modelerData?.editable) {
editDisabled = true;
}
}
......@@ -513,6 +486,16 @@ const EditModel = (props) => {
>
导出
</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
type='primary'
......@@ -546,7 +529,6 @@ const EditModel = (props) => {
return;
}
setActionData({ ...actionData, action: 'edit-inherited' });
setEditInheritedParms({visible: true, modelerData});
}}
ghost
......@@ -560,7 +542,7 @@ const EditModel = (props) => {
<PermissionButton
type='primary'
onClick={edit}
disabled={ modelerData?.inheritedFromEasyDataModelerDataModel?true:((stateId==='4')?!permitCheckOut:!editable)}
disabled={editDisabled}
tip={editTip}
tooltipPlacement='topRight'
permissionKey='编辑'
......@@ -584,7 +566,7 @@ const EditModel = (props) => {
onClick={onHistory}
ghost
permissionKey='历史版本'
defaultPermission={branchId?true:undefined}
defaultPermission={(branchId||approvalId)?true:undefined}
permissions={privilege?.optionList?.filter(item => item.enabled)?.map(item => item.name)}
>
历史版本
......@@ -603,15 +585,18 @@ const EditModel = (props) => {
actionsBtn = <PhysicalModelApprovalBottom
loading={loadingApprovalData}
data={approvalData}
modelerData={modelerData}
id={currentApprovalModelId}
taskId={taskId}
type={approvalType}
onExport={() => {
setExportParams({ visible: true })
}}
onExportInfo={onExportInfo}
onHistory={() => {
setHistoryAndVersionDrawerVisible(true)
}}
onEdit={edit}
onChange={(val) => {
setApprovalModelId(val)
}}
......@@ -619,50 +604,6 @@ const EditModel = (props) => {
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') {
actionsBtn = (
......@@ -683,11 +624,27 @@ const EditModel = (props) => {
<div className='edit-header'>
<span style={{ fontSize: 16, fontWeight: 'bold', color: '#fff' }}>{title}</span>
</div>
<ResizeObserver
onResize={({ height }) => {
setContainerHeight(height)
}}
>
<div className='edit-container'>
<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>
</ResizeObserver>
<div className='edit-footer'>
{actionsBtn}
</div>
......@@ -714,19 +671,11 @@ const EditModel = (props) => {
visible={historyAndVersionDrawerVisible}
onCancel={onHistoryAndVersionDrawerCancel}
/>
<ExportModel
<ExportOptions
{...exportParams}
onCancel={onExportCancel}
/>
<ExportDDLModel
{...exportDDLParams}
reference='exportDDL'
ids={[modelerData?.id]}
names={[modelerData?.name]}
ids={modelerData?.id?[modelerData?.id]:[]}
onCancel={() => {
setExportDDLParams({
visible: false
})
setExportParams({ visible: false })
}}
/>
<EditInherited
......@@ -735,6 +684,8 @@ const EditModel = (props) => {
onCancel={(refresh = false) => {
setEditInheritedParms({visible: false, modelerData: undefined});
if (refresh) {
//刷新模型详情
setActionData({ ...actionData, action: 'edit-inherited' });
setActionData({ ...actionData, action: 'detail' });
actionRef.current = 'detail';
}
......@@ -748,10 +699,9 @@ const EditModel = (props) => {
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 [waiting, setWaiting] = useState(false)
const [tip, setTip] = useState()
useEffect(() => {
if (data && id) {
......@@ -762,12 +712,6 @@ const PhysicalModelApprovalBottom = ({ loading, type, data, id, taskId, onChange
}
}, [data, id])
useEffect(() => {
if (type) {
getTip()
}
}, [type])
const [index, canPrev, canNext] = useMemo(() => {
if (data && id) {
const index = (data?.modelInfoList??[]).findIndex(item => item.id === id)
......@@ -779,26 +723,22 @@ const PhysicalModelApprovalBottom = ({ loading, type, data, id, taskId, onChange
return [0, false, false]
}, [data, id])
const getTip = () => {
dispatch({
type: 'datamodel.getPhysicalModelApprovalTip',
payload: {
type
},
callback: data => {
setTip(data?.tip)
}
})
}
const onExportClick = () => {
onExport?.()
}
const onExportInfoClick = () => {
onExportInfo?.()
}
const onHistoryClick = () => {
onHistory?.()
}
const onEditClick = () => {
onEdit?.()
}
const onPrevClick = () => {
if (data?.modelInfoList[index-1]) {
onChange?.(data?.modelInfoList[index-1].id)
......@@ -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 (
<div className='flex' style={{ width: '100%', justifyContent: 'space-between' }}>
<Space>
<Button type='primary' ghost onClick={onExportClick}>导出</Button>
<Button type='primary' ghost onClick={onExportInfoClick}>导出模型信息</Button>
<Button type='primary' ghost onClick={onHistoryClick}>历史版本</Button>
<Tooltip title={editTip}>
<Button type='primary' onClick={onEditClick} disabled={editDisabled}>编辑</Button>
</Tooltip>
</Space>
<Space>
{
tip && <Tooltip title={
<div>
{(tip??'').split('\n').map((item, index) => (<div key={index}>{item}</div>))}
</div>}
>
<QuestionCircleFilled style={{ fontSize: 18, color: '#3B80D6' }} />
</Tooltip>
}
<ApprovalTip type={type} />
<Tooltip title={((type==='rule')&&!item?.designReviewPass) ? '设计评审不通过' : ''}>
<Select
value={(type==='design')?item?.designReviewPass:item?.standardReviewPass}
......@@ -888,11 +834,12 @@ const PhysicalModelApprovalBottom = ({ loading, type, data, id, taskId, onChange
</Tooltip>
<Tooltip title={((type==='rule')&&!item?.designReviewPass) ? '设计评审不通过' : ''}>
<Form.Item style={{ marginBottom: 0 }}>
<Input
<Input.TextArea
rows={1}
value={(type==='design')?item?.designReviewComment:item?.standardReviewComment}
disabled={(type==='rule')&&!item?.designReviewPass}
placeholder='请输入评审意见'
style={{ width: 500 }}
style={{ width: 700 }}
allowClear
onChange={(e) => {
setItem(prev => {
......
.edit-model {
display: flex;
flex-direction: column;
overflow: hidden;
height: 100vh;
.edit-header {
display: flex;
flex: none;
width: 100%;
height: 44px;
padding: 0 15px;
background-color: #464d6e;
align-items: center;
// position: fixed;
justify-content: space-between;
border-bottom: 1px solid #EFEFEF;
// z-index: 100;
}
.edit-container {
flex: 1;
background: #F2F2F2;
height: calc(100vh - 44px - 64px);
overflow: hidden;
}
.edit-container-card {
margin: 10px;
height: calc(100vh - 44px - 64px - 20px);
// background: #fff;
overflow: hidden;
}
.edit-footer {
display: flex;
bottom: 0;
flex: none;
width: 100%;
height: 64px;
position: fixed;
min-height: 64px;
max-height: 500px;
justify-content: flex-end;
opacity: 0.9;
background: #fff;
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'
export const importActionSubject = new Subject()
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 [ constraint, setConstraint ] = useState({});
......@@ -47,13 +47,9 @@ const ImportAction = React.forwardRef((props, ref) => {
useEffect(() =>{
if ((action||'')==='') return;
if ((!mountRef.current && action === 'edit' && !permitCheckOut) || action === 'edit-inherited') {
return;
}
//流程打开模型详情的情况下,id由-1变成-2,会再次触发获取模型详情.这里直接return掉
if (approvalId || (!mountRef.current&&action === 'flow')) {
if ((!mountRef.current&&action === 'edit'&&!permitCheckOut)
|| action==='edit-inherited'
|| approvalId) {
return;
}
......@@ -110,38 +106,22 @@ const ImportAction = React.forwardRef((props, ref) => {
useEffect(() => {
if (approvalModelId) {
if (action==='edit') {
form?.resetFields();
}
if (action === 'detail'|| action ==='flow') {
//把数据表结构中的数据清空,解决性能问题
const newModelerData = { ...modelerData, easyDataModelerDataModelAttributes: [] };
onChange && onChange(newModelerData);
setModelerData(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(() => {
if (constraint?.name) {
......@@ -274,7 +254,8 @@ const ImportAction = React.forwardRef((props, ref) => {
}
const getCurrentDataModel = () => {
if ((modelerId||'') === '') {
if ((action!=='flow'&&!modelerId)
|| (action === 'flow'&&!approvalModelId)) {
setLoading(false);
return;
}
......@@ -289,6 +270,9 @@ const ImportAction = React.forwardRef((props, ref) => {
type = 'datamodel.modelCopy';
} else if (action === 'flow') {
type = 'datamodel.getDataModelWithRecommendedDefinitionAndTermDiscovery';
params = {
id: approvalModelId,
}
} else if (action === 'detail-version') {
type = 'datamodel.getDataModelByVersionId';
params = {
......@@ -647,7 +631,7 @@ const ImportAction = React.forwardRef((props, ref) => {
}
</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
form={form}
editable={action!=='detail'&&action!=='flow'&&action!=='detail-version'&&action!=='edit-inherited'}
......
......@@ -109,7 +109,7 @@ const FC = (props) => {
</Form>
) : (
<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' }}>
{
(maintenanceRecords||[]).map((record, index) => {
......
......@@ -2,10 +2,12 @@ import React from "react"
import { Button, Descriptions, Space, Popover } from "antd"
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 { dispatch } from '../../../../model';
import './ImportActionHeader.less'
import { openMetadataDetail } from "../../../../util"
export const inheritanceHistoricalType = 'historical'
export const inheritanceZipperType = 'zipper'
......@@ -14,6 +16,7 @@ const FC = (props) => {
const { modelerData, action } = props
const [isCollapse, setCollapse] = React.useState(true)
const [relationModelerDatas, setRelationModelerDatas] = React.useState([])
const [associationMetadata, setMetadata] = React.useState()
React.useEffect(() => {
const $importActionSubject = importActionSubject.subscribe((props) => {
......@@ -54,6 +57,24 @@ const FC = (props) => {
//eslint-disable-next-line react-hooks/exhaustive-deps
}, [modelerData])
React.useEffect(() => {
if (modelerData?.id) {
getAssociationMetadata()
}
}, [modelerData?.id])
const getAssociationMetadata = () => {
dispatch({
type: 'datamodel.findAssociationMetadataByModelId',
payload: {
easyDataModelerDataModelId: modelerData?.id,
},
callback: data => {
setMetadata(data)
}
})
}
return (
<div className='model-import-action-relation'>
<Space>
......@@ -77,7 +98,23 @@ const FC = (props) => {
!isCollapse && <Descriptions className='mt-3' column={3}>
<Descriptions.Item
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>}
>
......@@ -86,7 +123,7 @@ const FC = (props) => {
relationModelerDatas?.length===0 ? '暂无信息' :
relationModelerDatas?.map((item, index) => (
<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}
</a>
......
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 { DndProvider, useDrag, useDrop } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend';
......@@ -18,6 +18,7 @@ import Suggest from './suggest';
import { AttentionSvg, UnAttentionSvg } from './ModelSvg';
import { EditModelContext } from './ContextManage';
import { ValidateTip } from './ImportActionHeader';
import Table from '../../ResizeableTable';
import './ImportActionHeader.less';
import './ImportActionTable.less';
......@@ -32,36 +33,6 @@ const MENU_ID = 'model-attribute-menu';
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 }) => {
const onNameChange = (value) => {
......@@ -453,7 +424,6 @@ export const ImportActionTable = (props) => {
dataIndex: 'definition',
editable: true,
ellipsis: true,
width: 200,
render: (text, record, __) => {
return (
<React.Fragment>
......@@ -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 (
<div className='model-import-action-table' id='model-import-action-table'>
<div className='d-flex mb-3' style={{ justifyContent: 'space-between', alignItems: 'center' }}>
......@@ -1101,9 +1047,6 @@ export const ImportActionTable = (props) => {
<Form form={form} component={false} onValuesChange={onValuesChange}>
<Table
components={{
header: {
cell: ResizeableHeaderCell,
},
body: {
cell: EditableCell,
//编辑或者搜索状态下不允许拖动
......@@ -1167,16 +1110,10 @@ export const ImportActionTable = (props) => {
return rowParams;
}}
dataSource={filterPageData||[]}
columns={resizeColumns()}
columns={columns??[]}
size='small'
rowKey='iid'
pagination={false}
sticky
scroll={{
x: 1500,
//解决屏幕尺寸窄时,字段不好横向拖动的问题
y: tableWidth>1500?'100%':630,
}}
/>
</Form>
</DndProvider>
......
......@@ -12,6 +12,7 @@ import { useContextMenu, Menu as RcMenu, Item as RcItem } from "react-contexify"
import PermissionRcItem from '../../../../util/Component/PermissionRcItem';
import TagCell from './tag-help';
import BranchModelSync from './branch-model-sync';
import ModelCompareSelectModel from './model-compare-select-model';
import './ModelTable.less';
import 'react-contexify/dist/ReactContexify.css';
......@@ -19,6 +20,14 @@ import produce from "immer";
const { Paragraph, Text } = Typography;
export const stateColorDic = {
草稿: '#DE7777',
评审中: '#779BDE',
评审通过: '#7CDEBF',
已上线: '#4C7813',
已下线: '#AAAAAA',
}
const ModelNameColumn = (props) => {
const { text, record, detailItem } = props;
const [ data, setData ] = useState(record);
......@@ -149,6 +158,10 @@ const ModelTable = (props) => {
visible: false,
item: undefined,
});
const [modelCompareSelectModelParams, setModelCompareSelectModelParams] = useState({
visible: false,
item: undefined,
})
const expandedDataMapRef = useRef(new Map());
const shouldScrollRef = useRef(false);
......@@ -235,18 +248,9 @@ const ModelTable = (props) => {
sortable: true,
resizable: true,
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 (
<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>
);
......@@ -668,6 +672,11 @@ const ModelTable = (props) => {
detailItem(data)
}
})
} else if (key === 'model-compare') {
setModelCompareSelectModelParams({
visible: true,
item: currentItem,
})
}
}
......@@ -680,10 +689,6 @@ const ModelTable = (props) => {
} else {
if (!currentItem?.editable && currentItem?.state?.id!=='4') {
disableEdit = true;
if (currentItem?.state?.id === '2') {
editTip = '待发布的模型不允许编辑';
}
}
if (!currentItem?.permitCheckOut && currentItem?.state?.id==='4') {
......@@ -705,23 +710,9 @@ const ModelTable = (props) => {
return (
<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
gridRef={gridRef}
style={{ blockSize: 'calc(100vh - 94px - 37px - 57px - 24px - 32px)' }}
style={{ blockSize: 'calc(100vh - 121px - 152px )' }}
checkable
columns={columns}
// rows={Array.from({ length: 10000 }).map((_, i) => ({
......@@ -779,6 +770,13 @@ const ModelTable = (props) => {
>
复制模型
</PermissionRcItem>
{/* <PermissionRcItem
id='model-compare'
defaultPermission={true}
onClick={handleItemClick}
>
模型对比
</PermissionRcItem> */}
{
view !== 'branch' && <PermissionRcItem
id='auth-transfer'
......@@ -857,6 +855,15 @@ const ModelTable = (props) => {
}
}}
/>
<ModelCompareSelectModel
{...modelCompareSelectModelParams}
onCancel={() => {
setModelCompareSelectModelParams({
visible: false,
item: undefined,
})
}}
/>
{ contextHolder }
</div>
);
......
......@@ -40,7 +40,7 @@ const ModelTree = (props) => {
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 [ loading, setLoading ] = useState(false);
......@@ -62,6 +62,7 @@ const ModelTree = (props) => {
const [ dataList, setDataList ] = useState([]);
const [options, setOptions] = useState([]);
const [isAdmin, setAdmin] = useState(false);
const [isCatalogAdmin, setCatalogAdmin] = useState(false);
const [isBranchAdmin, setBranchAdmin] = useState(false);
const [loadingRoot, setLoadingRoot] = useState(false);
......@@ -74,6 +75,7 @@ const ModelTree = (props) => {
useEffect(() => {
getShowSyncAndDomains();
getPrivilegeAdmin();
getCatalogAdmin();
getPrivilegeBranchAdmin();
//eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
......@@ -117,6 +119,10 @@ const ModelTree = (props) => {
//eslint-disable-next-line react-hooks/exhaustive-deps
}, [ keyword, searchProperties, viewSelectedKey, item, prevItem])
useEffect(() => {
setNode?.(item)
}, [item])
const haveStockImportPermission = useMemo(() => {
return (item?.optionList||[]).findIndex(option => option.name==='存量模型导入'&&option.enabled) !== -1
}, [item])
......@@ -149,6 +155,15 @@ const ModelTree = (props) => {
});
}
const getCatalogAdmin = () => {
dispatch({
type: 'datamodel.getCatalogAdmin',
callback: data => {
setCatalogAdmin(data==='true'?true:false);
}
});
}
const getPrivilegeBranchAdmin = () => {
dispatch({
type: 'datamodel.getPrivilegeBranchAdmin',
......@@ -646,7 +661,7 @@ const ModelTree = (props) => {
</Dropdown>
{
((viewSelectedKey==='dir'&&isAdmin) || (viewSelectedKey==='branch'&&(isAdmin||isBranchAdmin))) && (
((viewSelectedKey==='dir'&&(isAdmin||isCatalogAdmin)) || (viewSelectedKey==='branch'&&(isAdmin||isBranchAdmin))) && (
<Tooltip title={(viewSelectedKey==='dir')?"新增目录":'新增项目'}>
<PlusOutlined className='default' onClick={add} style={{ fontSize:16,cursor:'pointer' }} />
</Tooltip>
......@@ -665,7 +680,7 @@ const ModelTree = (props) => {
</Tooltip>
{
(viewSelectedKey==='dir' && isAdmin) && !isSetRootId && (
(viewSelectedKey==='dir'&&(isAdmin||isCatalogAdmin)) && !isSetRootId && (
<Dropdown overlay={syncMenu} placement="bottomLeft">
<Tooltip title="同步目录">
<SyncOutlined className='default' style={{ fontSize:16,cursor:'pointer' }} />
......@@ -675,7 +690,7 @@ const ModelTree = (props) => {
}
{
(viewSelectedKey==='dir' && isAdmin) && isSetRootId && (
(viewSelectedKey==='dir'&&(isAdmin||isCatalogAdmin)) && isSetRootId && (
<Tooltip title="同步目录" className='ml-2'>
<SyncOutlined className='default' style={{ fontSize:16,cursor:'pointer' }} onClick={sync} />
</Tooltip>
......@@ -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>
</React.Fragment>
......@@ -735,7 +750,7 @@ const ModelTree = (props) => {
return <span title={nodeData?.remark||''}>{nodeData?.name||''}</span>;
}}
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);
displayMenu(event);
}
......
......@@ -6,14 +6,12 @@ import { formatVersionDate, showMessage } from '../../../../util';
import VersionCompareHeader from './VersionCompareHeader';
import VersionCompareTable from './VersionCompareTable';
import VersionCompareIndex from './VersionCompareIndex';
import FilterColumnAction from './FilterColumnAction';
import VersionDdlAlter from './version-ddl-alter';
import './VersionCompare.less';
const { Text, Paragraph } = Typography;
const { Option } = Select;
export const defaultColumnTitles = ['序号', '中文名称', '英文名称', '类型', '业务含义'];
const VersionCompare = (props) => {
......@@ -27,8 +25,6 @@ const VersionCompare = (props) => {
const [ compareData, setCompareData ] = useState(null);
const [ loadingCompare, setLoadingCompare ] = useState(false);
const [ onlyShowChange, setOnlyShowChange ] = useState(true);
const [ attrFilterColumns, setAttrFilterColumns ] = useState([]);
const [ attrSelectedTitles, setAttrSelectedTitles ] = useState(defaultColumnTitles);
const [ddlAlterParams, setAlterParams] = useState({
visible: false,
id: undefined,
......@@ -37,8 +33,6 @@ const VersionCompare = (props) => {
incVersion: undefined,
})
const attrColumnsRef = useRef([]);
useEffect(() => {
if ((id||'') !== '') {
getVersions();
......@@ -132,51 +126,6 @@ const VersionCompare = (props) => {
callback: data => {
setLoadingCompare(false);
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: () => {
setLoadingCompare(false);
......@@ -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 = () => {
setAlterParams({
visible: true,
......@@ -257,20 +199,7 @@ const VersionCompare = (props) => {
<div className='py-5'>
<Spin spinning={loadingCompare} >
{
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>
}
<CompareDetail data={compareData} />
</Spin>
</div>
<VersionDdlAlter
......@@ -290,3 +219,35 @@ const VersionCompare = (props) => {
}
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 { Typography, Table } from 'antd';
import React from 'react';
import { Typography, Table, Tooltip } from 'antd';
import FilterColumnAction from './FilterColumnAction';
const { Title } = Typography;
export const defaultColumnTitles = ['序号', '中文名称', '英文名称', '类型', '业务含义'];
const VersionCompareTable = (props) => {
const { data, direction = 'left', selectedColumnTitles = defaultColumnTitles, onFilterChange } = props;
const [columns, setColumns] = React.useState()
const { data, direction = 'left', columns } = props;
const [ tableData, setTableData ] = useState([]);
React.useEffect(() => {
const newColumns = [];
for (const [index, item] of (data?.heads?.columnHead||[]).entries()) {
newColumns.push({
title: item||'',
dataIndex: `column${index}`,
render: (attrValue, record, index) => {
useEffect(() => {
const newTableData = [];
let columnValue = [];
if (direction==='left') {
columnValue = data?.left?.columnValue||[];
} else if (direction==='right') {
columnValue = data?.right?.columnValue||[];
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,
});
}
(columnValue||[]).forEach((attrItem) => {
let newAttrItem = {};
(attrItem||[]).forEach((item, index) => {
newAttrItem[`column${index}`] = item;
})
newTableData.push(newAttrItem);
})
setColumns([...newColumns, {
title: <FilterColumnAction columns={newColumns} defaultSelectedKeys={defaultColumnTitles} onChange={(val) => onFilterChange?.(val)} />,
dataIndex: 'columnFilter',
render: (_, record, index) => {
return '';
},
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
}, [ data ])
return newTableData
}, [direction, data])
return (
<div>
......@@ -38,7 +79,7 @@ const VersionCompareTable = (props) => {
</Typography>
</div>
<Table
columns={columns||[]}
columns={_columns||[]}
dataSource={tableData}
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'
import { Modal, Button, Spin, Form, Checkbox, Typography, Tooltip, Row, Col, Tree, } from 'antd'
import { dispatch } from '../../../../model'
import VersionCompareHeader from './VersionCompareHeader'
import VersionCompareTable from './VersionCompareTable'
import VersionCompareIndex from './VersionCompareIndex'
import { defaultColumnTitles } from './VersionCompare'
import FilterColumnAction from './FilterColumnAction'
import { CompareDetail } from './VersionCompare'
import './VersionCompare.less'
import { formatDate } from '../../../../util'
......@@ -61,7 +57,7 @@ const FC = (props) => {
onCancel={() => { close() }}
>
<Spin spinning={waiting}>
<Diff item={item} />
<Basic item={item} />
</Spin>
</Modal>
)
......@@ -69,12 +65,10 @@ const FC = (props) => {
export default FC
const Diff = ({ item }) => {
const Basic = ({ item }) => {
const [loading, setLoading] = React.useState(false)
const [data, setData] = React.useState()
const [onlyShowChange, setOnlyShowChange] = React.useState(true)
const [attrSelectedTitles, setAttrSelectedTitles] = React.useState(defaultColumnTitles)
const [attrColumns, setAttrColumns] = React.useState()
React.useEffect(() => {
if (item) {
......@@ -82,59 +76,6 @@ const Diff = ({ item }) => {
}
}, [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) => {
setOnlyShowChange(e.target.checked)
}
......@@ -176,20 +117,7 @@ const Diff = ({ item }) => {
<div className='py-5'>
<Spin spinning={loading} >
{
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>
}
<CompareDetail data={data} />
</Spin>
</div>
</div>
......
......@@ -203,22 +203,7 @@ const Basic = React.forwardRef(function ({ type, items }, ref) {
setLoadingTreeData(false)
setRootNode(data)
const newTreeData = produce(data?.subCatalogs??[], 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)
})
})
const newTreeData = data?.subCatalogs??[]
setTreeData(newTreeData)
if ((newTreeData??[]).length > 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'
import { Modal, Button, Spin, Form, Checkbox, Typography, Tooltip, Row, Col, Tree, } from 'antd'
import { dispatch } from '../../../../model'
import VersionCompareHeader from './VersionCompareHeader'
import VersionCompareTable from './VersionCompareTable'
import VersionCompareIndex from './VersionCompareIndex'
import { defaultColumnTitles } from './VersionCompare'
import FilterColumnAction from './FilterColumnAction'
import { CompareDetail } from './VersionCompare'
import './merge-to-master.less'
import './VersionCompare.less'
......@@ -119,8 +115,6 @@ export const Diff = ({ item }) => {
const [loading, setLoading] = React.useState(false)
const [data, setData] = React.useState()
const [onlyShowChange, setOnlyShowChange] = React.useState(true)
const [attrSelectedTitles, setAttrSelectedTitles] = React.useState(defaultColumnTitles)
const [attrColumns, setAttrColumns] = React.useState()
React.useEffect(() => {
if (item) {
......@@ -128,59 +122,6 @@ export const Diff = ({ item }) => {
}
}, [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) => {
setOnlyShowChange(e.target.checked)
}
......@@ -221,20 +162,7 @@ export const Diff = ({ item }) => {
<div className='py-5'>
<Spin spinning={loading} >
{
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>
}
<CompareDetail data={data} />
</Spin>
</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'
import { isSzseEnv, showMessage, showNotifaction } from '../../../../util'
import Table from '../../../../util/Component/Table'
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'
const FC = (props) => {
......@@ -75,7 +75,7 @@ const FC = (props) => {
visible={visible}
footer={footer}
width='80%'
bodyStyle={{ padding: '15px', overflowX: 'auto', maxHeight: '80vh' }}
bodyStyle={{ padding: '15px', overflowX: 'auto', height: '80vh' }}
title='模型送审'
centered destroyOnClose
onCancel={() => { close() }}
......@@ -327,7 +327,7 @@ const List = React.forwardRef(function ({ items }, ref) {
<Typography.Paragraph ellipsis={{
rows: 3,
}}><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>
</Tooltip>
)
......
......@@ -4,7 +4,7 @@ import { ArrowsAltOutlined, ShrinkOutlined } from '@ant-design/icons';
import { dispatch } from '../../../../model'
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'
const topN = 20
......@@ -138,7 +138,7 @@ const FC = (props) => {
useDebounceEffect(()=>{
setColumns(isCompact?[...cols].slice(0, 4):[...cols])
}, [cols, isCompact], { wait: 100 })
}, [isCompact], { wait: 100 })
const onSourceClick = (id, name) => {
const timestamp = new Date().getTime();
......@@ -150,7 +150,7 @@ const FC = (props) => {
id
},
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 {
......
......@@ -10,12 +10,12 @@ import ModelTable from './Component/ModelTable';
import ImportModal from './Component/ImportModal';
import ImportStockWordDrawer from './Component/ImportStockWordDrawer';
import ExportDDLModal from './Component/ExportDDLModal';
import ExportOtherModal from './Component/ExportOtherModal';
import ExportOptions from './Component/export-options';
import RecatalogModal from './Component/RecatalogModal';
import HistoryAndVersionDrawer from './Component/HistoryAndVersionDrawer';
import { showMessage, showNotifaction, inputWidth, DeleteTipModal } from '../../../util';
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 DebounceInput from './Component/DebounceInput';
import ColSettingModal from './Component/ColSettingModal';
......@@ -27,6 +27,7 @@ import StartFlow from './Component/start-flow'
import MergeToMaster from './Component/merge-to-master';
import AuthTransfer from './Component/auth-transfer';
import AuthShare from './Component/auth-share';
import NodeIntroduction from './Component/node-introduction';
import './index.less';
......@@ -42,7 +43,7 @@ class Model extends React.Component {
importModalVisible: false,
importStockWordDrawerVisible: false,
exportDDLModalVisible: false,
exportOtherModalVisible: false,
exportOptionsVisible: false,
recatalogModalVisible: false,
historyAndVersionDrawerVisible: false,
catalogId: '',
......@@ -81,6 +82,7 @@ class Model extends React.Component {
},
batchAddTagChange: false,
rootNode: undefined,
node: undefined,
branchAddParams: {
visible: false,
},
......@@ -115,7 +117,7 @@ class Model extends React.Component {
componentDidUpdate(prevProps, prevState) {
const { selectModelerIds, tableData, catalogId, permissions, currentView } = this.state;
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') {
selectModelerIds?.forEach(id => {
......@@ -129,6 +131,8 @@ class Model extends React.Component {
modelItem.optionList?.forEach(item => {
if (item.name === '导出' && item.enabled === false) {
canExport = false;
} else if (item.name === '导出模型信息' && item.enabled === false) {
canExportInfo = false;
} else if (item.name === '送审' && (item.enabled===false||modelItem.state?.id!=='1')) {
canStartFlow = false;
} else if (item.name === '变更目录' && (item.enabled===false||!modelItem.supportRecatalog)) {
......@@ -165,6 +169,7 @@ class Model extends React.Component {
this.setState({
canExport,
canExportInfo,
canStartFlow,
canChangeCatalog,
canDelete,
......@@ -384,7 +389,7 @@ class Model extends React.Component {
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 {
this.setState({ exportDDLModalVisible: true, selectModelerNames: _selectModelerNames, exportDDLModalReference: 'exportDDL' });
}
onExportOtherBtnClick = () => {
this.setState({ exportOtherModalVisible: true });
}
startFlow = () => {
const { selectModelerIds, tableData } = this.state;
if ((selectModelerIds||[]).length === 0) {
......@@ -651,33 +652,6 @@ class Model extends React.Component {
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) => {
this.setState({ recatalogModalVisible: false });
if (refresh) {
......@@ -738,7 +712,7 @@ class Model extends React.Component {
}
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', {
'data-model-collapse': !expandTree
......@@ -773,6 +747,9 @@ class Model extends React.Component {
setRootNode={(val) => {
this.setState({ rootNode: val })
}}
setNode={(val) => {
this.setState({ node: val })
}}
onDirRefresh={() => {
//刷新目录时,重新获取目录权限
this.getPermissions()
......@@ -785,10 +762,12 @@ class Model extends React.Component {
</div>
</div>
<div className='right'>
<NodeIntroduction node={this.state.node} data={filterTableData} />
<div style={{ height: 10, background: '#f0f2f5' }} />
<div
className='d-flex p-3'
className='flex'
style={{
borderBottom: '1px solid #EFEFEF',
padding: '10px 20px',
justifyContent: 'space-between',
alignItems: 'center'
}}
......@@ -818,13 +797,26 @@ class Model extends React.Component {
<PermissionButton
defaultPermission={canExport}
tip={(selectModelerIds||[]).length===0?'请先选择模型':''}
onClick={this.onExportOtherBtnClick}
onClick={() => {
this.setState({ exportOptionsVisible: true })
}}
disabled={(selectModelerIds||[]).length===0}
>
导出
</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}
tip={startFlowTip}
onClick={this.startFlow}
......@@ -932,7 +924,7 @@ class Model extends React.Component {
</Space>
</div>
<div className='p-3'>
<div style={{ padding: '0 20px' }}>
<Spin spinning={loadingTableData}>
<ModelTable
loading={loadingTableData}
......@@ -987,9 +979,12 @@ class Model extends React.Component {
onCancel={this.onExportDDLModalCancel}
/>
<ExportOtherModal
visible={exportOtherModalVisible}
onCancel={this.onExportOtherModalCancel}
<ExportOptions
visible={exportOptionsVisible}
ids={selectModelerIds}
onCancel={() => {
this.setState({ exportOptionsVisible: false })
}}
/>
<RecatalogModal
......
......@@ -10,10 +10,23 @@
overflow: hidden;
}
.tree-toggle-wrap {
.right {
flex: 1;
overflow: hidden;
}
}
.data-model-collapse {
.left {
width: 0 !important;
}
}
.tree-toggle-wrap {
position: relative;
width: 20px;
width: 15px;
height: 100%;
background: #f0f2f5;
.tree-toggle {
display: flex;
......@@ -23,7 +36,7 @@
right: 0;
background: #f2f5fc;
position: absolute;
top: calc(50% - 40px);
top: 50%;
width: 12px;
height: 80px;
border-radius: 0 12px 12px 0;
......@@ -31,16 +44,4 @@
transform: translateY(-50%);
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 { 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 Table from '../../../util/Component/Table'
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 produce from 'immer'
const FC = (props) => {
const { visible, item, onCancel} = props
......@@ -49,11 +50,41 @@ const FC = (props) => {
export default FC
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 (
<div>
<Descriptions column={3}>
<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>
<Descriptions column={5}>
......@@ -68,10 +99,191 @@ const Basic = ({ item }) => {
<Descriptions.Item label='差异元数据数'>{item?.metadataPartialMatchCount}</Descriptions.Item>
<Descriptions.Item label='未匹配元数据数'>{item?.metadataNotMatchCount}</Descriptions.Item>
</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>
)
}
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 onExportClick = () => {
window.open(`/api/datamodeler/easyDataModelModelCompareJobResult/exportResultDetails?resultId=${item?.id??''}`);
......@@ -100,11 +312,18 @@ const PerfectMatch = ({ item }) => {
const [args, setArgs] = React.useState({
page: defaultPage.pageNum,
size: defaultPage.pageSize,
metadataPath: undefined,
modelPath: undefined,
})
const [loading, setLoading] = React.useState(false)
const [data, setData] = React.useState()
const [total, setTotal] = React.useState(0)
const [selectedRows, setSelectedRows] = 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()
......@@ -116,15 +335,16 @@ const PerfectMatch = ({ item }) => {
React.useEffect(() => {
if (item) {
getList()
getMetadataCatalog()
getModelCatalog()
}
}, [item])
const tableData = React.useMemo(() => {
let newTableData = [...data??[]]
return paginate(newTableData, args.page, args.size)
}, [data, args])
React.useEffect(() => {
if (item) {
getList()
}
}, [args])
const cols = [
{
......@@ -154,6 +374,24 @@ const PerfectMatch = ({ item }) => {
{
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>
)
}
},
{
title: '模型更新时间',
dataIndex: 'modelModifiedTs',
render: (_, record) => record.modelModifiedTs?new Date(record.modelModifiedTs).toLocaleString():''
},
{
title: '元数据路径',
......@@ -172,6 +410,24 @@ const PerfectMatch = ({ item }) => {
{
title: '元数据名称',
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: '模型状态',
......@@ -184,11 +440,16 @@ const PerfectMatch = ({ item }) => {
dispatch({
type: 'datamodel.getCompareJobResultPerfectMatchList',
payload: {
resultId: item?.id
resultId: item?.id,
metadataPath: args.metadataPath,
modelPath: args.modelPath,
pageNo: args.page,
pageSize: args.size,
},
callback: data => {
setLoading(false)
setData(data)
setData(data?.content)
setTotal(data?.totalElements)
},
error: () => {
setLoading(false)
......@@ -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 = () => {
modal.confirm({
title:'提示',
......@@ -243,24 +538,40 @@ const PerfectMatch = ({ item }) => {
return (
<div>
<div>
<div className='flex mb-3' style={{ justifyContent: 'space-between', alignItems: 'center' }}>
<Tooltip title={(selectedRows??[]).length===0?'请选择模型':''}>
<Button onClick={onDeployClick} disabled={(selectedRows??[]).length===0}>确定上线</Button>
</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 className='pt-3'>
<div>
<Table
extraColWidth={32}
loading={loading}
columns={cols??[]}
dataSource={tableData??[]}
dataSource={data??[]}
rowSelection={{
selectedRowKeys: (selectedRows??[]).map(item => item.id),
onChange: (selectedRowKeys, selectedRows) => {
setSelectedRows(selectedRows)
},
}}
pageNum={args.page} pageSize={args.size} total={(data??[]).length}
pageNum={args.page} pageSize={args.size} total={total}
onPaginate={(page, size) => {
setArgsByParams({ page, size })
}}
......@@ -282,11 +593,18 @@ const PartialMatch = ({ item }) => {
const [args, setArgs] = React.useState({
page: defaultPage.pageNum,
size: defaultPage.pageSize,
metadataPath: undefined,
modelPath: undefined,
})
const [loading, setLoading] = React.useState(false)
const [data, setData] = React.useState()
const [total, setTotal] = React.useState(0)
const [selectedRows, setSelectedRows] = 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()
......@@ -298,15 +616,16 @@ const PartialMatch = ({ item }) => {
React.useEffect(() => {
if (item) {
getList()
getMetadataCatalog()
getModelCatalog()
}
}, [item])
const tableData = React.useMemo(() => {
let newTableData = [...data??[]]
return paginate(newTableData, args.page, args.size)
}, [data, args])
React.useEffect(() => {
if (item) {
getList()
}
}, [args])
const cols = [
{
......@@ -336,6 +655,24 @@ const PartialMatch = ({ item }) => {
{
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>
)
}
},
{
title: '模型更新时间',
dataIndex: 'modelModifiedTs',
render: (_, record) => record.modelModifiedTs?new Date(record.modelModifiedTs).toLocaleString():''
},
{
title: '元数据路径',
......@@ -354,6 +691,24 @@ const PartialMatch = ({ item }) => {
{
title: '元数据名称',
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: '差异结论',
......@@ -387,11 +742,16 @@ const PartialMatch = ({ item }) => {
dispatch({
type: 'datamodel.getCompareJobResultPartialMatchList',
payload: {
resultId: item?.id
resultId: item?.id,
metadataPath: args.metadataPath,
modelPath: args.modelPath,
pageNo: args.page,
pageSize: args.size,
},
callback: data => {
setLoading(false)
setData(data)
setData(data?.content)
setTotal(data?.totalElements)
},
error: () => {
setLoading(false)
......@@ -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 = () => {
modal.confirm({
title:'提示',
......@@ -446,24 +840,40 @@ const PartialMatch = ({ item }) => {
return (
<div>
<div>
<div className='flex mb-3' style={{ justifyContent: 'space-between', alignItems: 'center' }}>
<Tooltip title={(selectedRows??[]).length===0?'请选择模型':''}>
<Button onClick={onDeployClick} disabled={(selectedRows??[]).length===0}>确定上线</Button>
</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 className='pt-3'>
<div>
<Table
extraColWidth={32}
loading={loading}
columns={cols??[]}
dataSource={tableData??[]}
dataSource={data??[]}
rowSelection={{
selectedRowKeys: (selectedRows??[]).map(item => item.id),
onChange: (selectedRowKeys, selectedRows) => {
setSelectedRows(selectedRows)
},
}}
pageNum={args.page} pageSize={args.size} total={(data??[]).length}
pageNum={args.page} pageSize={args.size} total={total}
onPaginate={(page, size) => {
setArgsByParams({ page, size })
}}
......@@ -486,21 +896,36 @@ const NotMatch = ({ item }) => {
const [args, setArgs] = React.useState({
metadataPage: defaultPage.pageNum,
metadataSize: defaultPage.pageSize,
metadataPath: undefined,
modelPage: defaultPage.pageNum,
modelSize: defaultPage.pageSize,
modelPath: undefined,
})
const [loadingMetadata, setLoadingMetadata] = React.useState(false)
const [metadatas, setMetadatas] = React.useState()
const [metadataTotal, setMetadataTotal] = React.useState(0)
const [loadingModel, setLoadingModel] = React.useState(false)
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(() => {
if (item) {
getMetadatas()
getModels()
}
}, [item])
}, [args])
const setArgsByParams = React.useCallback((params) => {
setArgs((prev) => {
......@@ -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 = [
{
title: '序号',
......@@ -553,6 +971,24 @@ const NotMatch = ({ item }) => {
{
title: '元数据名称',
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 }) => {
{
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>
)
}
},
{
title: '模型更新时间',
dataIndex: 'modelModifiedTs',
render: (_, record) => record.modelModifiedTs?new Date(record.modelModifiedTs).toLocaleString():''
},
]
......@@ -578,11 +1032,15 @@ const NotMatch = ({ item }) => {
dispatch({
type: 'datamodel.getCompareJobResultNotMatchMetadataList',
payload: {
resultId: item?.id
resultId: item?.id,
metadataPath: args.metadataPath,
pageNo: args.metadataPage,
pageSize: args.metadataSize,
},
callback: data => {
setLoadingMetadata(false)
setMetadatas(data)
setMetadatas(data?.content)
setMetadataTotal(data?.totalElements)
},
error: () => {
setLoadingMetadata(false)
......@@ -595,11 +1053,15 @@ const NotMatch = ({ item }) => {
dispatch({
type: 'datamodel.getCompareJobResultNotMatchModelList',
payload: {
resultId: item?.id
resultId: item?.id,
modelPath: args.modelPath,
pageNo: args.modelPage,
pageSize: args.modelSize,
},
callback: data => {
setLoadingModel(false)
setModels(data)
setModels(data?.content)
setModelTotal(data?.totalElements)
},
error: () => {
setLoadingModel(false)
......@@ -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 (
<div style={{ overflow: 'hidden' }}>
<Row gutter={15}>
<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
loading={loadingMetadata}
columns={metadataCols??[]}
dataSource={metadataTableData??[]}
pageNum={args.metadataPage} pageSize={args.metadataSize} total={(metadatas??[]).length}
dataSource={metadatas??[]}
pageNum={args.metadataPage} pageSize={args.metadataSize} total={metadataTotal}
onPaginate={(page, size) => {
setArgsByParams({ metadataPage: page, metadataSize: size })
}}
......@@ -626,12 +1131,21 @@ const NotMatch = ({ item }) => {
/>
</Col>
<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
loading={loadingModel}
columns={modelCols??[]}
dataSource={modelTableData??[]}
pageNum={args.modelPage} pageSize={args.modelSize} total={(models??[]).length}
dataSource={models??[]}
pageNum={args.modelPage} pageSize={args.modelSize} total={modelTotal}
onPaginate={(page, size) => {
setArgsByParams({ modelPage: page, modelSize: size })
}}
......@@ -644,3 +1158,56 @@ const NotMatch = ({ item }) => {
</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'
import Table from '../../../util/Component/Table'
import { dispatch } from '../../../model'
import { generateUUID, paginate, showMessage, showNotifaction } from '../../../util'
import UpdateTask from './update-task'
import UpdateTask, { Range } from './update-task'
import ColConfig from './col-config'
import '../AssetTask/index.less'
......@@ -108,21 +108,12 @@ const FC = (props) => {
dataIndex: 'jobCatalogItems',
render: (_, record) => {
return (
<Tooltip title={
<div>
{
(record.jobCatalogItems??[]).map(item => (
<Row key={generateUUID()}>
{`【模型】${(item.modelCatalogNameList??[]).join('/')} -【元数据】${(item.metadataCatalogNameList??[]).join('/')}`}
</Row>
))
}
</div>
}>
<Tooltip title={<Range task={record} type='detail' />}>
<Typography.Text ellipsis={true}>
{
(record.jobCatalogItems??[]).map(item => `【模型】${(item.modelCatalogNameList??[]).join('/')} -【元数据】${(item.metadataCatalogNameList??[]).join('/')}`).toString()
}
{ (record?.jobModelCatalogItems??[]).length > 0 && '【模型范围】' }
{ record?.jobModelCatalogItems?.map((item, index) => `${(item.modelCatalogNameList??[]).join('/')}`).toString() }
{ (record?.jobMetadataCatalogItems??[]).length > 0 && '【元数据范围】' }
{ record?.jobMetadataCatalogItems?.map((item, index) => `${(item.metadataCatalogNameList??[]).join('/')}`).toString() }
</Typography.Text>
</Tooltip>
)
......
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 moment from 'moment'
import produce from 'immer'
import { CloseOutlined } from '@ant-design/icons';
import { dispatch } from '../../../model'
import Table, { generateId } from '../../../util/Component/Table'
......@@ -80,7 +81,7 @@ const FC = (props) => {
console.log('strategyRows', strategyRows)
console.log('scheduleRows', scheduleRows)
if ((rangeRows??[]).length === 0) {
if ((rangeRows?.jobModelCatalogItems??[]).length===0 || (rangeRows?.jobMetadataCatalogItems??[]).length===0) {
showMessage('warn', '请先填写对比范围')
return
}
......@@ -97,7 +98,7 @@ const FC = (props) => {
payload: {
data: {
...basicRows,
jobCatalogItems: rangeRows,
...rangeRows,
strategyItemPropertyTypes: strategyRows,
jobSchedule: scheduleRows,
}
......@@ -116,7 +117,7 @@ const FC = (props) => {
data: {
...task,
...basicRows,
jobCatalogItems: rangeRows,
...rangeRows,
strategyItemPropertyTypes: strategyRows,
jobSchedule: scheduleRows,
}
......@@ -151,7 +152,7 @@ const FC = (props) => {
visible={visible}
footer={(type==='detail') ? null : footer}
width='90%'
bodyStyle={{ padding: '15px', overflowX: 'auto', maxHeight: '80vh' }}
bodyStyle={{ padding: '15px', overflowX: 'auto', height: '80vh' }}
title={title}
centered destroyOnClose
onCancel={() => { close() }}
......@@ -390,28 +391,26 @@ const BasicForm = React.forwardRef(function ({ type, task }, ref) {
const ModelRangeItem = ({ value, onChange }) => {
const [loading, setLoading] = React.useState(false)
const [treeData, setTreeData] = React.useState()
const [treeSelectedKey, setTreeSelectedKey] = React.useState()
const [treeSelectedKeys, setTreeSelectedKeys] = React.useState()
React.useEffect(() => {
getTreeData()
}, [])
React.useEffect(() => {
if (treeSelectedKey) {
const treeIndex = (treeList??[]).findIndex(item => item.key === treeSelectedKey)
if (treeIndex !== -1) {
onChange?.({
modelCatalogIdList: treeList[treeIndex].ids??[],
modelCatalogNameList: treeList[treeIndex].names??[]
const newData = []
for (const key of treeSelectedKeys??[]) {
const index = (treeList??[]).findIndex(item => item.key === key)
if (index !== -1) {
newData.push({
modelCatalogIdList: treeList[index].ids??[],
modelCatalogNameList: treeList[index].names??[]
})
} else {
onChange?.()
}
} else {
onChange?.()
}
}, [treeSelectedKey])
onChange?.(newData)
}, [treeSelectedKeys])
const generateList = (data, list, ids, names) => {
(data||[]).forEach(node => {
......@@ -474,17 +473,18 @@ const ModelRangeItem = ({ value, onChange }) => {
})
}
const onTreeSelect = (selectedKeys) => {
setTreeSelectedKey(selectedKeys)
const onTreeChange = (selectedKeys) => {
setTreeSelectedKeys(selectedKeys)
}
return (
<TreeSelect
multiple
loading={loading}
dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
treeData={treeData1}
value={treeSelectedKey?[treeSelectedKey]:undefined}
onSelect={onTreeSelect}
value={treeSelectedKeys??[]}
onChange={onTreeChange}
placeholder="请选择模型范围"
/>
)
......@@ -496,7 +496,7 @@ const MetadataRangeItem = ({ value, onChange }) => {
const [currentDomainId, setCurrentDomainId] = React.useState()
const [loadingTreeData, setLoadingTreeData] = React.useState(false)
const [treeData, setTreeData] = React.useState()
const [treeSelectedKey, setTreeSelectedKey] = React.useState()
const [treeSelectedKeys, setTreeSelectedKeys] = React.useState()
React.useEffect(() => {
getDomains()
......@@ -509,28 +509,31 @@ const MetadataRangeItem = ({ value, onChange }) => {
}, [currentDomainId])
React.useEffect(() => {
if (currentDomainId && treeSelectedKey) {
const domainIndex = (domains??[]).findIndex(item => item.domainId === currentDomainId)
const treeIndex = (treeList??[]).findIndex(item => item.key === treeSelectedKey)
if (currentDomainId) {
const newData = []
if (domainIndex !== -1 && treeIndex !== -1) {
onChange?.({
const domainIndex = (domains??[]).findIndex(item => item.domainId === currentDomainId)
for (const key of treeSelectedKeys??[]) {
const index = (treeList??[]).findIndex(item => item.key === key)
if (domainIndex!==-1 && index !== -1) {
newData.push({
metadataCatalogIdList: [
`${domains[domainIndex].domainId}`,
...treeList[treeIndex].ids??[],
...treeList[index].ids??[],
],
metadataCatalogNameList: [
domains[domainIndex].domainName,
...treeList[treeIndex].names??[],
...treeList[index].names??[]
]
})
} else {
onChange?.()
}
}
onChange?.(newData)
} else {
onChange?.()
}
}, [currentDomainId, treeSelectedKey])
}, [currentDomainId, treeSelectedKeys])
const generateList = (data, list, ids, names) => {
(data||[]).forEach(node => {
......@@ -591,7 +594,6 @@ const MetadataRangeItem = ({ value, onChange }) => {
item.title = item.catalogName
item.value = `${item.catalogId}`
item.idStr = `${item.catalogId}`
item.disabled = true
item.children = []
for (let child of item.scopes??[]) {
......@@ -599,7 +601,6 @@ const MetadataRangeItem = ({ value, onChange }) => {
child.title = child.scopeName
child.value = `${item.catalogId}-${child.scopeId}`
child.idStr = `${child.scopeId}`
child.disabled = true
item.children.push(child)
}
}
......@@ -616,11 +617,11 @@ const MetadataRangeItem = ({ value, onChange }) => {
const onDomainChange = (val) => {
setCurrentDomainId(val)
setTreeData()
setTreeSelectedKey()
onChange?.()
}
const onTreeSelect = (selectedKeys) => {
setTreeSelectedKey(selectedKeys)
const onTreeChange = (selectedKeys) => {
setTreeSelectedKeys(selectedKeys)
}
const updateTreeData = (list, key, children) =>
......@@ -692,7 +693,6 @@ const MetadataRangeItem = ({ value, onChange }) => {
value: item._id,
scopeId: node?.scopeId,
idStr: item._id,
disabled: true,
...item
}))
......@@ -709,8 +709,6 @@ const MetadataRangeItem = ({ value, onChange }) => {
}
});
console.log('treeSelectedKey', treeSelectedKey)
return (
<Row gutter={15}>
<Col span={6}>
......@@ -725,9 +723,10 @@ const MetadataRangeItem = ({ value, onChange }) => {
</Col>
<Col span={18}>
<TreeSelect
multiple
loading={loadingTreeData}
value={treeSelectedKey?[treeSelectedKey]:undefined}
onSelect={onTreeSelect}
value={treeSelectedKeys??[]}
onChange={onTreeChange}
dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
treeData={treeData}
loadData={onLoadData}
......@@ -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 [isAdding, setAdding] = React.useState(false)
......@@ -753,7 +752,10 @@ const Range = React.forwardRef(function ({ type, task }, ref) {
}), [ranges, isAdding])
React.useEffect(() => {
setRanges(task?.jobCatalogItems)
setRanges({
jobMetadataCatalogItems: task?.jobMetadataCatalogItems??[],
jobModelCatalogItems: task?.jobModelCatalogItems??[],
})
}, [task])
const onAddClick = () => {
......@@ -763,21 +765,20 @@ const Range = React.forwardRef(function ({ type, task }, ref) {
}
const onOkClick = async () => {
rangesRef.current = ranges??[]
if (!isAdding) return
try {
const rows = await form.validateFields()
let newRanges = [...ranges??[]]
newRanges = [...newRanges, {
...rows.modelRange,
...rows.metadataRange,
}]
setRanges(newRanges)
rangesRef.current = newRanges
setRanges({
jobMetadataCatalogItems: [
...ranges?.jobMetadataCatalogItems??[],
...rows?.jobMetadataCatalogItems??[]
],
jobModelCatalogItems: [
...ranges?.jobModelCatalogItems??[],
...rows?.jobModelCatalogItems??[]
]
})
setAdding(false)
} catch(e) {
throw new Error()
......@@ -788,11 +789,7 @@ const Range = React.forwardRef(function ({ type, task }, ref) {
setAdding(false)
}
const onRangeDeleteClick = (index) => {
const newRanges = [...ranges??[]]
newRanges.splice(index, 1)
setRanges(newRanges)
}
rangesRef.current = ranges
return (
<div>
......@@ -812,8 +809,16 @@ const Range = React.forwardRef(function ({ type, task }, ref) {
<Col span={10}>
<Form.Item
label="数据模型范围"
name="modelRange"
rules={[{ required: true, message: '请选择模型范围!' }]}
name="jobModelCatalogItems"
rules={[{
validator: (_, value) => {
if ((form.getFieldValue('jobModelCatalogItems')??[]).length===0 && (ranges?.jobModelCatalogItems??[]).length===0) {
return Promise.reject(new Error('请选择模型范围!'))
}
return Promise.resolve();
}
}]}
>
<ModelRangeItem />
</Form.Item>
......@@ -821,8 +826,16 @@ const Range = React.forwardRef(function ({ type, task }, ref) {
<Col span={10}>
<Form.Item
label="元数据范围"
name="metadataRange"
rules={[{ required: true, message: '请选择元数据范围!' }]}
name="jobMetadataCatalogItems"
rules={[{
validator: (_, value) => {
if ((form.getFieldValue('jobMetadataCatalogItems')??[]).length===0 && (ranges?.jobMetadataCatalogItems??[]).length===0) {
return Promise.reject(new Error('请选择元数据范围!'))
}
return Promise.resolve();
}
}]}
>
<MetadataRangeItem />
</Form.Item>
......@@ -836,20 +849,60 @@ const Range = React.forwardRef(function ({ type, task }, ref) {
</Row>
</Form>
}
<Row gutter={10}>
<Col flex='0 0 110px'>
{
(ranges??[]).map((item, index) => {
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('/')}`}
(ranges?.jobModelCatalogItems??[]).length > 0 && <span>【模型范围】</span>
}
</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 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>
)
})
......
......@@ -422,6 +422,9 @@ const FC = (props) => {
setSelectedRows(selectedRows)
},
}}
scroll={{
y: readonly?'calc(80vh - 230px)':null
}}
/>
</div>
<AddRule
......
......@@ -36,7 +36,7 @@ const ResizeableHeaderCell = props => {
};
const ResizeableTable = (props) => {
const { columns, extraColWidth = 0, ...restProps } = props
const { columns, extraColWidth = 0, components, ...restProps } = props
const [tableWidth, setTableWidth] = useState(0)
......@@ -62,10 +62,18 @@ const ResizeableTable = (props) => {
useEffect(() => {
if (!!columns && tableWidth > 0) {
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)
paddingCol.current.width = 0
const cols = columns
const newCols = columns
.map((col, index) => {
const colWidth = col.width ?? 100;
return {
......@@ -78,7 +86,7 @@ const ResizeableTable = (props) => {
}),
};
})
setCols(cols)
setCols(newCols)
}
}, [columns, tableWidth])
......@@ -94,7 +102,8 @@ const ResizeableTable = (props) => {
components={{
header: {
cell: ResizeableHeaderCell,
}
},
...components,
}}
columns={cols1}
{ ...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