Commit 5851a49f by fanyongjun

4.8

parents f46aab0e 38340f8d
......@@ -10,6 +10,7 @@ import Home from './view/Home';
import Manage from './view/Manage';
import Map from './view/Manage/Map';
import Model from './view/Manage/Model';
import Element from './view/Manage/Element';
export default class App extends React.Component {
render() {
......@@ -23,6 +24,7 @@ export default class App extends React.Component {
<Route path={`${ContextPath}/manage`} component={Manage} />
<Route path={`/center-home/view/modelmap`} component={Map} exact />
<Route path={`/center-home/view/datamodel`} component={Model} exact />
<Route path={`/center-home/view/data-asset-element`} component={Element} exact />
</Switch>
</Router>
</React.Fragment>
......
......@@ -84,5 +84,9 @@ div[id^='__qiankun_microapp_wrapper_'] {
background-color: transparent !important;
}
.yy-card-bordered {
border-color: #333333 !important;
}
......@@ -105,7 +105,7 @@ export const ManageLayout = function ({ content, location }) {
</Menu>
</Sider>
<Content className="m-3" style={{ backgroundColor: '#ECEEF3', height: '100%' }}>
<Content className="m-4" style={{ backgroundColor: '#ECEEF3', height: '100%' }}>
{content}
</Content>
......
import * as service from '../service/dataassetmanager';
import { call } from 'redux-saga/effects';
export function* importElement(payload) {
return yield call(service.importElement, payload);
}
export function* getLogs(payload) {
return yield call(service.getLogs, payload);
}
......@@ -70,8 +70,11 @@ export function* extractExcelContent(payload) {
return yield call(datamodelerService.extractExcelContent, payload);
}
export function* getAllConstraints() {
return yield call(datamodelerService.constraints);
export function* getAllConstraintsAndTemplates() {
const constraints = yield call(datamodelerService.constraints);
const templates = yield call(datamodelerService.templates);
return { constraints, templates };
}
//获取初稿
......
......@@ -8,9 +8,15 @@ import { reducers } from './reducer';
import * as user from './user';
import * as map from './map';
import * as datamodel from './datamodel';
<<<<<<< HEAD
import * as assetmanage from './assetmanage';
const funcs = Connect({ user, datamodel, map,assetmanage })
=======
import * as dataassetelement from './dataassetelement';
const funcs = Connect({ user, datamodel, map, dataassetelement })
>>>>>>> 38340f8dec7bab08e4d6f7a73db93a2d59a4c42c
function* request(args) {
const { type, payload, callback, error } = args.args;
......
......@@ -22,6 +22,9 @@ export const routes = [
{
name: 'assetmanage',
text: '资产管理',
},{
name: 'element',
text: '资产要素',
}
]
}
......
import { filePost, GetJSON } from "../util/axios"
export function importElement(payload) {
return filePost("/dataassetmanager/elementApi/import", payload);
}
export function getLogs(payload) {
return GetJSON("/dataassetmanager/elementApi/listOperationLogsByPage", payload);
}
......@@ -31,6 +31,11 @@ export function constraints() {
return GetJSON("/datamodeler/easyDataModelerConstraint/constraints");
}
//模版
export function templates() {
return GetJSON("/datamodeler/easyDataModelerConstraint/templates");
}
//创建初稿
export function draft(payload) {
return PostJSON("/datamodeler/easyDataModelerDesign/draft", payload);
......
import React, { useState } from 'react';
import { Modal, Button, Upload } from 'antd';
import { DownloadOutlined, UploadOutlined } from '@ant-design/icons';
import { dispatchLatest } from '../../../../model';
import { showMessage } from '../../../../util';
const ImportModal = (props) => {
const { onCancel, visible } = props;
const [ fileList, setFileList ] = useState([]);
const [ confirmLoading, setConfirmLoading ] = useState(false);
const downloadTemplate = () => {
window.open("/data-govern/docs/ElementModel.xlsx");
}
const uploadProps = {
onRemove: file => {
const index = fileList.indexOf(file);
const newFileList = fileList.slice();
newFileList.splice(index, 1);
setFileList(newFileList);
},
beforeUpload: file => {
setFileList([file]);
return false;
}
};
const handleOk = () => {
if ((fileList||[]).length === 0) {
showMessage('info', '请先选择模版上传');
return;
}
setConfirmLoading(true);
dispatchLatest({
type: 'dataassetelement.importElement',
payload: { fileList },
callback: () => {
setConfirmLoading(false);
onCancel && onCancel(true);
},
error: () => {
setConfirmLoading(false);
}
})
}
return (
<Modal
forceRender
confirmLoading={confirmLoading}
visible={visible}
title={"导入资产要素"}
onOk={handleOk}
onCancel={() => {
setConfirmLoading(false);
onCancel && onCancel();
}}
>
{
<>
<div>
<Button icon={<DownloadOutlined />} onClick={ downloadTemplate }>
模版下载
</Button>
</div>
<div className='mt-3'>
<Upload {...uploadProps}>
<Button icon={<UploadOutlined />}>
选择文件上传
</Button>
</Upload>
</div>
</>
}
</Modal>
)
}
export default ImportModal;
\ No newline at end of file
import React, { useState, useEffect } from 'react';
import { Button, Table, Pagination } from 'antd';
import { dispatchLatest } from '../../../model';
import ImportModal from './Component/ImportModal';
import './index.less';
const Element = (props) => {
const [ loading, setLoading ] = useState(false);
const [ tableData, setTableData ] = useState([]);
const [ total, setTotal ] = useState(0);
const [ pagination, setPagination ] = useState( { pageNum: 1, pageSize: 20 } );
const [ importModalVisible, setImportModalVisible ] = useState(false);
const { pageNum, pageSize } = pagination;
useEffect(() => {
getTableData();
//eslint-disable-next-line react-hooks/exhaustive-deps
}, [pagination])
const columns = [
{
title: '序号',
dataIndex: 'key',
render: (text, record, index) => {
return (index+1).toString();
}
},
{
title: '操作时间',
dataIndex: 'createTimeDesc',
},
{
title: '操作人',
dataIndex: 'creator',
},
{
title: '操作类型',
dataIndex: 'operateType',
},
{
title: '状态',
dataIndex: 'status',
},
{
title: '变更内容',
dataIndex: 'content'
},
];
const onImportBtnClick = () => {
setImportModalVisible(true);
}
const onExportBtnClick = () => {
window.open('/api/dataassetmanager/elementApi/export');
}
const getTableData = () => {
setLoading(true);
dispatchLatest({
type: 'dataassetelement.getLogs',
payload: {
pageNum,
pageSize
},
callback: data => {
setTableData(data.data||[]);
setTotal(data.total);
setLoading(false);
},
error: () => {
setLoading(false);
}
})
}
const onImportModalCancel = (refresh = false) => {
setImportModalVisible(false);
if (refresh) {
setPagination({ pageNum: 1, pageSize: 20 });
}
}
return (
<div className='element' style={{ backgroundColor: '#fff' }}>
<div
className='p-3'
style={{
display: 'flex',
borderBottom: "1px solid #EFEFEF",
}}
>
<Button type="primary" className='ml-3' style={{ marginLeft: 'auto' }} onClick={onImportBtnClick}>导入</Button>
<Button type="primary" className='ml-3' onClick={onExportBtnClick}>导出</Button>
</div>
<div className='p-3'>
<Table
loading={loading}
columns={columns}
rowKey={'id'}
dataSource={tableData||[]}
pagination={false}
/>
<Pagination
size="small"
className="text-center mt-3"
showSizeChanger
showQuickJumper
onChange={(_pageNum, _pageSize) => {
setPagination({ pageNum: _pageNum, pageSize: _pageSize || 10 });
}}
onShowSizeChange={(_pageNum, _pageSize) => {
setPagination({ pageNum: _pageNum || 1, pageSize: _pageSize });
}}
current={pageNum}
pageSize={pageSize}
defaultCurrent={1}
total={total}
showTotal={total => `共 ${total} 条`}
/>
</div>
<ImportModal
visible={importModalVisible}
onCancel={onImportModalCancel}
/>
</div>
);
}
export default Element;
.element {
.yy-table {
height: calc(100vh - 64px - 30px - 53px - 20px) !important;
overflow: auto !important;
}
}
\ No newline at end of file
......@@ -67,7 +67,7 @@ class Org extends React.Component {
const bbox = text.getBBox();
if (cfg.dbType==='Dir') {
if (cfg.dbType==='Root' || cfg.dbType==='Dir') {
if (!cfg.children) {
group.addShape('marker', {
attrs: {
......@@ -96,7 +96,7 @@ class Org extends React.Component {
rect.attr({
x: bbox.minX - 10,
y: bbox.minY - 10,
width: bbox.width + (cfg.dbType==='Dir'&&(!cfg.children||((cfg.children||[]).length>0)) ? 38 : 20),
width: bbox.width + ((cfg.dbType==='Root'||cfg.dbType==='Dir')&&(!cfg.children||((cfg.children||[]).length>0)) ? 38 : 20),
height: bbox.height + 20,
});
......@@ -219,18 +219,48 @@ class Org extends React.Component {
},
});
const fittingString = (str, maxWidth, fontSize) => {
const ellipsis = '...';
const ellipsisLength = G6.Util.getTextSize(ellipsis, fontSize)[0];
let currentWidth = 0;
let res = str;
const pattern = new RegExp('[\u4E00-\u9FA5]+'); // distinguish the Chinese charactors and letters
str.split('').forEach((letter, i) => {
if (currentWidth > maxWidth - ellipsisLength) return;
if (pattern.test(letter)) {
// Chinese charactors
currentWidth += fontSize;
} else {
// get the width of single letter according to the fontSize
currentWidth += G6.Util.getLetterWidth(letter, fontSize);
}
if (currentWidth > maxWidth - ellipsisLength) {
res = `${str.substr(0, i)}${ellipsis}`;
}
});
return res;
};
graph.node(function (node) {
return {
label: fittingString(node.text||'', maxTextWidth, globalFontSize),
};
});
this.layoutGraph();
graph.on('node:click', function (e) {
const node = e.item;
const nodeId = node.get('id');
const model = node.getModel();
if (model.dbType==='Dir') {
const children = model.children;
if (!children && loadMoreData) {
loadMoreData(model.dirId||'');
loadMoreData(model.dirId||'', nodeId);
}
} else {
} else if (model.dbType !== 'Root') {
//通过资产id跳转到资产详情页
// model.tableModelId
// history && history.push(`${ContextPath}/home`);
......@@ -252,49 +282,36 @@ class Org extends React.Component {
const { data } = this.props;
if(graph && data){
const fittingString = (str, maxWidth, fontSize) => {
const ellipsis = '...';
const ellipsisLength = G6.Util.getTextSize(ellipsis, fontSize)[0];
let currentWidth = 0;
let res = str;
const pattern = new RegExp('[\u4E00-\u9FA5]+'); // distinguish the Chinese charactors and letters
str.split('').forEach((letter, i) => {
if (currentWidth > maxWidth - ellipsisLength) return;
if (pattern.test(letter)) {
// Chinese charactors
currentWidth += fontSize;
} else {
// get the width of single letter according to the fontSize
currentWidth += G6.Util.getLetterWidth(letter, fontSize);
}
if (currentWidth > maxWidth - ellipsisLength) {
res = `${str.substr(0, i)}${ellipsis}`;
}
});
return res;
};
graph.node(function (node) {
return {
label: fittingString(node.text||'', maxTextWidth, globalFontSize),
};
});
graph.data(data);
graph.render();
graph.fitView();
}
}
componentDidUpdate(prevProps, prevState){
this.layoutGraph();
componentDidUpdate(prevProps, prevState) {
const { childData, parentNodeId } = this.props;
if (parentNodeId && parentNodeId!== prevProps.parentNodeId) {
const parentData = graph.findDataById(parentNodeId);
if (!parentData.children) {
parentData.children = [];
}
parentData.children = childData;
graph.changeData();
graph.updateItem(graph.findById(parentNodeId), {
collapsed: false,
});
} else {
this.layoutGraph();
}
}
render() {
const { type } = this.props;
return (
<div id={`container${type||''}`} style={{ width: '100%', height: '100%' }}></div>
<div id={`container${type||''}`} style={{ width: '100%', height: '100%' }} ></div>
);
}
}
......
......@@ -60,7 +60,7 @@ class Relation extends React.Component {
x: 0,
y: 0,
r: cfg.size/2,
fill: colors[cfg.cluster % colors.length],
fill: colors[cfg.depth % colors.length],
},
});
......@@ -78,7 +78,7 @@ class Relation extends React.Component {
const bbox = text.getBBox();
if (cfg.dbType==='Dir') {
if (cfg.dbType==='Root' || cfg.dbType==='Dir') {
if (!cfg.children) {
group.addShape('marker', {
attrs: {
......@@ -155,17 +155,11 @@ class Relation extends React.Component {
],
},
layout: {
type: 'compactBox',
type: 'dendrogram',
direction: 'RL',
getId: function getId(d) {
return d.id;
},
getHeight: () => {
return 26;
},
getWidth: () => {
return 26;
},
getVGap: () => {
return 20;
},
......@@ -185,28 +179,50 @@ class Relation extends React.Component {
type: 'cubic-horizontal',
size: 2,
color: '#e2e2e2',
// style: {
// endArrow: {
// path: 'M 0,0 L 12, 6 L 9,0 L 12, -6 Z',
// fill: '#91d5ff',
// d: -40,
// },
// }
},
});
const fittingString = (str, maxWidth, fontSize) => {
const ellipsis = '...';
const ellipsisLength = G6.Util.getTextSize(ellipsis, fontSize)[0];
let currentWidth = 0;
let res = str;
const pattern = new RegExp('[\u4E00-\u9FA5]+'); // distinguish the Chinese charactors and letters
str.split('').forEach((letter, i) => {
if (currentWidth > maxWidth - ellipsisLength) return;
if (pattern.test(letter)) {
// Chinese charactors
currentWidth += fontSize;
} else {
// get the width of single letter according to the fontSize
currentWidth += G6.Util.getLetterWidth(letter, fontSize);
}
if (currentWidth > maxWidth - ellipsisLength) {
res = `${str.substr(0, i)}${ellipsis}`;
}
});
return res;
};
graph.node(function (node) {
return {
label: fittingString(node.text||'', node.size-32, globalFontSize),
};
});
this.layoutGraph();
graph.on('node:click', function (e) {
const node = e.item;
const nodeId = node.get('id');
const model = node.getModel();
if (model.dbType==='Dir') {
const children = model.children;
if (!children && loadMoreData) {
loadMoreData(model.dirId||'');
loadMoreData(model.dirId||'', nodeId);
}
} else {
} else if (model.dbType !== 'Root') {
//通过资产id跳转到资产详情页
// model.tableModelId
// history && history.push(`${ContextPath}/home`);
......@@ -228,56 +244,22 @@ class Relation extends React.Component {
const { data } = this.props;
if(graph && data){
const fittingString = (str, maxWidth, fontSize) => {
const ellipsis = '...';
const ellipsisLength = G6.Util.getTextSize(ellipsis, fontSize)[0];
let currentWidth = 0;
let res = str;
const pattern = new RegExp('[\u4E00-\u9FA5]+'); // distinguish the Chinese charactors and letters
str.split('').forEach((letter, i) => {
if (currentWidth > maxWidth - ellipsisLength) return;
if (pattern.test(letter)) {
// Chinese charactors
currentWidth += fontSize;
} else {
// get the width of single letter according to the fontSize
currentWidth += G6.Util.getLetterWidth(letter, fontSize);
}
if (currentWidth > maxWidth - ellipsisLength) {
res = `${str.substr(0, i)}${ellipsis}`;
}
});
return res;
};
function recursionTreeData(treeData, depth, cluster = '') {
function recursionTreeData(treeData, depth) {
if ((treeData||[]).length === 0) return;
(treeData||[]).forEach((item, index) => {
let _cluster = 0;
if (depth === 1) {
_cluster = (index + 1);
} else {
_cluster = cluster;
}
item.size = (100-depth*20)>60?(100-depth*20):60;
item.cluster = _cluster;
recursionTreeData(item.children||[], depth+1, _cluster);
item.depth = depth;
recursionTreeData(item.children||[], depth+1);
})
}
data.size = 100;
data.cluster = 0;
data.depth = 0;
recursionTreeData(data.children||[], 1);
graph.node(function (node) {
return {
label: fittingString(node.text||'', node.size-32, globalFontSize),
};
});
graph.data(data);
graph.render();
graph.fitView();
......@@ -286,7 +268,31 @@ class Relation extends React.Component {
}
componentDidUpdate(prevProps, prevState){
this.layoutGraph();
const { childData, parentNodeId } = this.props;
if (parentNodeId && parentNodeId!== prevProps.parentNodeId) {
const parentData = graph.findDataById(parentNodeId);
if (!parentData.children) {
parentData.children = [];
}
let depth = childData.depth+1;
(childData||[]).forEach((item, index) => {
item.size = (100-depth*20)>60?(100-depth*20):60;
item.depth = depth;
})
parentData.children = childData;
graph.changeData();
graph.updateItem(graph.findById(parentNodeId), {
collapsed: false,
});
} else {
this.layoutGraph();
}
}
render() {
......
import React from 'react';
import { Card } from 'antd';
import { FolderAddOutlined, FileOutlined } from '@ant-design/icons';
import './SquareItem.less';
......@@ -24,15 +25,17 @@ class SquareItem extends React.Component {
{
item && (
item.dbType==='Dir' ? <Card title={
<div className='pointer' onClick={this.onItemClick}>
{item.dirName||''}
<div className='d-flex pointer' style={{ alignItems: 'center' }} onClick={this.onItemClick}>
<FolderAddOutlined className='mr-1' />
<span>{item.dirName||''}</span>
</div>
}>
<p>{`数据资产: ${item.tableModelCount}`}</p>
<p>{`资产编目: ${item.subDirCount}`}</p>
</Card> : <Card title={
<div className='pointer' onClick={this.onItemClick}>
{item.name||''}
<div className='d-flex pointer' style={{ alignItems: 'center' }} onClick={this.onItemClick}>
<FileOutlined className='mr-1' />
<span>{item.name||''}</span>
</div>
}>
<p>{`所属系统: ${item.system||''}`}</p>
......
.map-square-item {
.yy-card-head {
background-color: #ff4d4f !important;
background-color: #f4856f !important;
.yy-card-head-title {
color: #fff !important;
font-size: 20px !important;
font-weight: bold !important;
:hover {
color: #1890ff;
}
}
}
.yy-card-body {
p {
color: #1890ff !important;
font-size: 18px !important;
}
}
}
\ No newline at end of file
......@@ -68,7 +68,7 @@ class Tree extends React.Component {
const bbox = text.getBBox();
if (cfg.dbType==='Dir') {
if (cfg.dbType==='Root' || cfg.dbType==='Dir') {
if (!cfg.children) {
group.addShape('marker', {
attrs: {
......@@ -97,7 +97,7 @@ class Tree extends React.Component {
rect.attr({
x: bbox.minX - 10,
y: bbox.minY - 10,
width: bbox.width + (cfg.dbType==='Dir'&&(!cfg.children||((cfg.children||[]).length>0)) ? 38 : 20),
width: bbox.width + ((cfg.dbType==='Root'||cfg.dbType==='Dir')&&(!cfg.children||((cfg.children||[]).length>0)) ? 38 : 20),
height: bbox.height + 20,
});
......@@ -180,18 +180,48 @@ class Tree extends React.Component {
},
});
const fittingString = (str, maxWidth, fontSize) => {
const ellipsis = '...';
const ellipsisLength = G6.Util.getTextSize(ellipsis, fontSize)[0];
let currentWidth = 0;
let res = str;
const pattern = new RegExp('[\u4E00-\u9FA5]+'); // distinguish the Chinese charactors and letters
str.split('').forEach((letter, i) => {
if (currentWidth > maxWidth - ellipsisLength) return;
if (pattern.test(letter)) {
// Chinese charactors
currentWidth += fontSize;
} else {
// get the width of single letter according to the fontSize
currentWidth += G6.Util.getLetterWidth(letter, fontSize);
}
if (currentWidth > maxWidth - ellipsisLength) {
res = `${str.substr(0, i)}${ellipsis}`;
}
});
return res;
};
graph.node(function (node) {
return {
label: fittingString(node.text||'', maxTextWidth, globalFontSize),
};
});
this.layoutGraph();
graph.on('node:click', function (e) {
const node = e.item;
const nodeId = node.get('id');
const model = node.getModel();
if (model.dbType==='Dir') {
const children = model.children;
if (!children && loadMoreData) {
loadMoreData(model.dirId||'');
loadMoreData(model.dirId||'', nodeId);
}
} else {
} else if (model.dbType !== 'Root') {
//通过资产id跳转到资产详情页
// model.tableModelId
// history && history.push(`${ContextPath}/home`);
......@@ -213,35 +243,6 @@ class Tree extends React.Component {
const { data } = this.props;
if(graph && data){
const fittingString = (str, maxWidth, fontSize) => {
const ellipsis = '...';
const ellipsisLength = G6.Util.getTextSize(ellipsis, fontSize)[0];
let currentWidth = 0;
let res = str;
const pattern = new RegExp('[\u4E00-\u9FA5]+'); // distinguish the Chinese charactors and letters
str.split('').forEach((letter, i) => {
if (currentWidth > maxWidth - ellipsisLength) return;
if (pattern.test(letter)) {
// Chinese charactors
currentWidth += fontSize;
} else {
// get the width of single letter according to the fontSize
currentWidth += G6.Util.getLetterWidth(letter, fontSize);
}
if (currentWidth > maxWidth - ellipsisLength) {
res = `${str.substr(0, i)}${ellipsis}`;
}
});
return res;
};
graph.node(function (node) {
return {
label: fittingString(node.text||'', maxTextWidth, globalFontSize),
};
});
graph.data(data);
graph.render();
graph.fitView();
......@@ -249,7 +250,23 @@ class Tree extends React.Component {
}
componentDidUpdate(prevProps, prevState){
this.layoutGraph();
const { childData, parentNodeId } = this.props;
if (parentNodeId && parentNodeId!== prevProps.parentNodeId) {
const parentData = graph.findDataById(parentNodeId);
if (!parentData.children) {
parentData.children = [];
}
parentData.children = childData;
graph.changeData();
graph.updateItem(graph.findById(parentNodeId), {
collapsed: false,
});
} else {
this.layoutGraph();
}
}
render() {
......
......@@ -19,8 +19,12 @@ class MapContent extends React.Component {
tableModelData: null,
curTableModelData: null,
orgModelData: null,
orgChildData: null,
treeModelData: null,
treeChildData: null,
relationModelData: null,
relationChildData: null,
parentNodeId: null,
breadcrumbContents: null
};
}
......@@ -41,7 +45,13 @@ class MapContent extends React.Component {
curTableModelData: data||[],
breadcrumbContents: [{ data: data||[] }]
}, () => {
this.setAllTreeGraphState(data||[]);
const _treeData = this.convertTreeModelData(data||[]);
this.setState({
//深拷贝
orgModelData: JSON.parse(JSON.stringify(_treeData)),
treeModelData: JSON.parse(JSON.stringify(_treeData)),
relationModelData: JSON.parse(JSON.stringify(_treeData))
});
});
},
error: () => {
......@@ -69,6 +79,7 @@ class MapContent extends React.Component {
return {
text: topic.name||'',
id: `t${topic.id}`,
dbType: 'Root',
children: data
};
}
......@@ -83,7 +94,6 @@ class MapContent extends React.Component {
}
onSquareItemClick = (item) => {
const { tableModelData } = this.state;
if (!item.children) {
dispatchLatest({
type: 'map.getTableModelByDirIid',
......@@ -92,14 +102,12 @@ class MapContent extends React.Component {
this.convertRemoteData(data||[]);
item.children = (data||[]);
this.setSquareGraphState(item);
this.setAllTreeGraphState(tableModelData);
}
})
return;
}
this.setSquareGraphState(item);
this.setAllTreeGraphState(tableModelData);
}
onBreadcrumbItemClick = (content, index) => {
......@@ -111,48 +119,27 @@ class MapContent extends React.Component {
})
}
loadMoreData = (id) => {
const { tableModelData } = this.state;
loadMoreData = (dirId, nodeId) => {
dispatchLatest({
type: 'map.getTableModelByDirIid',
payload: { dirId: id },
payload: { dirId },
callback: data => {
this.convertRemoteData(data||[]);
function recursionData(_data) {
if ((_data||[]).length === 0)
return;
_data.forEach((item, index) => {
if (item.dirId === id) {
item.children = data;
} else {
recursionData(item.children||[]);
}
})
}
recursionData(tableModelData);
this.setAllTreeGraphState(tableModelData);
this.setState({
parentNodeId: nodeId,
orgChildData: JSON.parse(JSON.stringify(data||[])),
treeChildData: JSON.parse(JSON.stringify(data||[])),
relationChildData: JSON.parse(JSON.stringify(data)),
});
}
})
}
setAllTreeGraphState = (tableModelData) => {
const _treeData = this.convertTreeModelData(tableModelData);
this.setState({
orgModelData: JSON.parse(JSON.stringify(_treeData)),
treeModelData: JSON.parse(JSON.stringify(_treeData)),
relationModelData: JSON.parse(JSON.stringify(_treeData))
});
}
render() {
const { diagram, topic } = this.props;
const { curTableModelData, breadcrumbContents, orgModelData , treeModelData, relationModelData, loading } = this.state;
const { type, topic } = this.props;
const { curTableModelData, breadcrumbContents, orgModelData , treeModelData, relationModelData, loading, orgChildData, treeChildData, relationChildData, parentNodeId } = this.state;
let groups = [];
if (curTableModelData) {
......@@ -166,7 +153,7 @@ class MapContent extends React.Component {
{
loading ? <Spin /> : <>
{
diagram==='square' && <>
type==='square' && <>
{
breadcrumbContents && breadcrumbContents.length>1 && <Breadcrumb className='mb-3'>
{
......@@ -204,13 +191,32 @@ class MapContent extends React.Component {
</>
}
{
diagram==='org' && <Org data={orgModelData} type={`${topic.id||''}${diagram}`} {...this.props} loadMoreData={this.loadMoreData} />
type==='org' && <Org
data={orgModelData}
parentNodeId={parentNodeId}
childData={orgChildData}
type={`${topic.id||''}${type}`}
{...this.props}
loadMoreData={this.loadMoreData}
/>
}
{
diagram==='tree' && <Tree data={treeModelData} type={`${topic.id||''}${diagram}`} {...this.props} loadMoreData={this.loadMoreData} />
type==='tree' && <Tree
data={treeModelData}
parentNodeId={parentNodeId}
childData={treeChildData}
type={`${topic.id||''}${type}`}
{...this.props}
loadMoreData={this.loadMoreData} />
}
{
diagram==='relation' && <Relation data={relationModelData} type={`${topic.id||''}${diagram}`} {...this.props} loadMoreData={this.loadMoreData} />
type==='relation' && <Relation
data={relationModelData}
parentNodeId={parentNodeId}
childData={relationChildData}
type={`${topic.id||''}${type}`}
{...this.props}
loadMoreData={this.loadMoreData} />
}
</>
}
......
import React from 'react';
import { Tabs, Radio, Spin } from 'antd';
import { Tabs, Spin, Select } from 'antd';
import MapContent from './MapContent';
import { dispatchLatest } from '../../../model';
const { TabPane } = Tabs;
const { Option } = Select;
const graphModes = [
{
title: '方块图',
key: 'square'
},
{
title: '组织图',
key: 'org',
},
{
title: '树形图',
key: 'tree',
},
{
title: '关系图',
key: 'relation'
}
];
class Map extends React.Component {
constructor(props) {
super(props);
this.state = {
diagram: 'square',
type: 'square',
tabKey: '0',
loadingTopics: false,
topics: null
......@@ -36,8 +56,8 @@ class Map extends React.Component {
})
}
onRadioChange = e => {
this.setState({ diagram: e.target.value });
onTypeChange = value => {
this.setState({ type: value||'' });
};
onTabChange = activeKey => {
......@@ -45,7 +65,7 @@ class Map extends React.Component {
}
render() {
const { diagram, tabKey, topics, loadingTopics } = this.state;
const { type, tabKey, topics, loadingTopics } = this.state;
return (
<div style={{ backgroundColor: '#fff', height: '100%' }}>
{
......@@ -57,13 +77,21 @@ class Map extends React.Component {
centered
style={{ height: '100%' }}
tabBarExtraContent={{
left: <Radio.Group className='m-3' size='small' value={diagram} onChange={this.onRadioChange} >
<Radio.Button value='square'>方块图</Radio.Button>
<Radio.Button value='org'>组织图</Radio.Button>
<Radio.Button value='tree'>树形图</Radio.Button>
<Radio.Button value='relation'>关系图</Radio.Button>
</Radio.Group>,
right: <div style={{ width: 172 }}></div>
left: <div className='ml-3' style={{ width: 120, marginRight: 50 }} ></div>,
right: (
<Select
value={type}
className='mr-3'
style={{ width: 120, marginLeft: 50 }}
onChange={this.onTypeChange}
>
{
graphModes && graphModes.map((mode, index) => {
return <Option key={index} value={mode.key||''}>{mode.title||''}</Option>
})
}
</Select>
)
}}
onChange={this.onTabChange}
>
......@@ -71,7 +99,7 @@ class Map extends React.Component {
topics && topics.map((topic, index) => {
return (
<TabPane tab={topic.name||''} key={index.toString()} className='p-3' style={{ height: '100%' }}>
{ tabKey===index.toString() && <MapContent diagram={diagram} topic={topic} {...this.props} /> }
{ tabKey===index.toString() && <MapContent type={type} topic={topic} {...this.props} /> }
</TabPane>
);
})
......
......@@ -3,16 +3,12 @@ import { Modal, Radio, Button } from 'antd';
const modes = [
{
title: '导出DDL',
},
{
title: '导出Erwin',
},
{
title: '导出Excel',
key: 'excel',
},
{
title: '导出模型文档'
title: '导出Word',
key: 'word',
},
]
......@@ -21,36 +17,47 @@ class ExportModal extends React.Component {
constructor() {
super();
this.state = {
confirmLoading: false
selectedKey: '',
}
}
onModeClick = (index) => {
onModeClick = (e) => {
this.setState({ selectedKey: e.target.value });
}
handleOk = () => {
const { onCancel } = this.props;
const { onCancel, ids } = this.props;
const { selectedKey } = this.state;
if (onCancel) {
onCancel();
if (selectedKey === 'excel') {
window.open(`/api/datamodeler/easyDataModelerExport/excel?ids=${ids.join(',')}`);
} else if (selectedKey === 'word') {
window.open(`/api/datamodeler/easyDataModelerExport/word?ids=${ids.join(',')}`);
}
this.reset();
onCancel && onCancel();
}
reset = () => {
this.setState({ selectedKey: '' });
}
render() {
const { visible, onCancel } = this.props;
const { confirmLoading } = this.state;
const { selectedKey } = this.state;
return (
<Modal
visible={visible}
title={"导出方式"}
destroyOnClose
onOk={this.handleOk}
onCancel={onCancel}
onCancel={() => {
this.reset();
onCancel && onCancel();
}}
footer={[
<Button
key="0"
loading={confirmLoading}
type="primary"
onClick={this.handleOk}
>
......@@ -58,11 +65,11 @@ class ExportModal extends React.Component {
</Button>
]}
>
<Radio.Group>
<Radio.Group value={selectedKey} onChange={this.onModeClick}>
{
modes && modes.map((mode, index) => {
return (
<Radio key={index} value={index}>
<Radio key={mode.key||''} value={mode.key||''}>
{ mode.title||'' }
</Radio>
);
......
......@@ -14,6 +14,8 @@ const ImportAction = (props) => {
const [ constraints, setConstraints ] = useState([]);
const [ constraint, setConstraint ] = useState({});
const [ templates, setTemplates ] = useState([]);
const [ template, setTemplate ] = useState({});
const [ modelerData, setModelerData ] = useState(null);
const [ supportedDatatypes, setSupportedDatatypes ] = useState([]);
......@@ -22,36 +24,46 @@ const ImportAction = (props) => {
useEffect(() =>{
if (mountRef.current) {
mountRef.current = false;
if (action === 'detail') {
getCurrentDataModel();
} else {
dispatchLatest({
type: 'datamodel.getAllConstraints',
callback: data => {
setConstraints(data||[]);
if (action === 'add') {
setConstraint((data||[]).length>0?data[0]:{});
getDraft((data||[]).length>0?data[0]:{}, hints);
} else if(action === 'edit') {
getCurrentDataModel();
}
if (action==='add' && (hints||[]).length === 0) return;
//初始化form状态
if (action==='add'||action==='edit') {
form.setFieldsValue({
cnName: '',
name: '',
remark: '',
});
}
if (action === 'detail') {
getCurrentDataModel();
} else {
dispatchLatest({
type: 'datamodel.getAllConstraintsAndTemplates',
callback: data => {
setConstraints(data.constraints||[]);
setTemplates(data.templates||[]);
if (action === 'add') {
setConstraint((data.constraints||[]).length>0?data.constraints[0]:{});
setTemplate((data.templates||[]).length>0?data.templates[0]:{});
getDraft((data.constraints||[]).length>0?data.constraints[0]:{}, (data.templates||[]).length>0?data.templates[0]:{} ,hints);
} else if(action === 'edit') {
getCurrentDataModel();
}
})
}
}
})
}
//eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
}, [action, hints, modelerId ]);
const getDraft = (_constraint, _hints) => {
const getDraft = (_constraint, _template, _hints) => {
dispatchLatest({
type: 'datamodel.getDraft',
payload: {
data: {
hints,
modelerModelingConstraint: _constraint
hints: _hints,
modelerModelingConstraint: _constraint,
easyDataModelerModelingTemplate: _template
}
},
callback: data => {
......@@ -99,11 +111,32 @@ const ImportAction = (props) => {
});
(modelerData.easyDataModelerDataModelAttributes||[]).forEach((_attribute, index) => {
_hints.push(_attribute.cnName||'');
if (_attribute.name && _attribute.name!=='') {
_hints.push(_attribute.cnName);
}
});
setConstraint(currentConstraint);
getDraft(currentConstraint, _hints);
getDraft(currentConstraint, template, _hints);
}
const onTemplateChange = (value) => {
let currentTemplate = null, _hints = [];
(templates||[]).forEach((_template, index) => {
if (_template.id === value) {
currentTemplate = _template;
}
});
(modelerData.easyDataModelerDataModelAttributes||[]).forEach((_attribute, index) => {
if (_attribute.name && _attribute.name!=='') {
_hints.push(_attribute.cnName);
}
});
setTemplate(currentTemplate);
getDraft(constraint, currentTemplate, _hints);
}
const getSupportedDatatypes = () => {
......@@ -136,6 +169,15 @@ const ImportAction = (props) => {
})
}
</Select>
<Select className='ml-3' value={template.id?template.id:null} placeholder='请选择模版' style={{ minWidth: 100 }} onChange={onTemplateChange}>
{
(templates||[]).map((template, index) => {
return (
<Option key={index} value={template.id}>{ template.cnName||'' }</Option>
)
})
}
</Select>
</div>
}
<ImportActionHeader
......@@ -154,6 +196,7 @@ const ImportAction = (props) => {
<ImportActionTable
modelerData={modelerData||{}}
constraint={constraint}
template={template}
supportedDatatypes={supportedDatatypes}
onChange={onTableChange}
editable={action!=='detail'} />
......
......@@ -89,6 +89,7 @@ const DatatypeInput = ({ value = {}, datatypes, onChange }) => {
onChange={(value) => {
onParameterValuesChange(value, index);
}}
min={0}
value={parameterValues[index]}
style={{ width: 60 }}
/>
......@@ -214,7 +215,7 @@ const DragableBodyRow = ({ index, moveRow, className, style, ...restProps }) =>
};
const ImportActionTable = (props) => {
const { modelerData, onChange, editable, supportedDatatypes, constraint } = props;
const { modelerData, onChange, editable, supportedDatatypes, constraint, template } = props;
const data = modelerData.easyDataModelerDataModelAttributes||[];
const [form] = Form.useForm();
const [editingKey, setEditingKey] = useState('');
......@@ -223,7 +224,7 @@ const ImportActionTable = (props) => {
//规则改变的时候 数据表为可编辑状态
useEffect(() => {
setEditingKey('');
}, [constraint])
}, [constraint, template])
const isEditing = (record) => record.iid === editingKey;
......@@ -424,24 +425,6 @@ const ImportActionTable = (props) => {
return columns;
}
// const mergedColumns = columns.map((col) => {
// if (!col.editable) {
// return col;
// }
// return {
// ...col,
// onCell: (record) => ({
// record,
// inputType: 'text',
// dataIndex: col.dataIndex,
// title: col.title,
// editing: isEditing(record),
// datatypes: supportedDatatypes,
// }),
// };
// });
const moveRow = useCallback(
(dragIndex, hoverIndex) => {
......@@ -505,7 +488,7 @@ const ImportActionTable = (props) => {
suggests && suggests.map((suggest, index) => {
return (
<Radio key={index} value={index} className='mt-3' style={{ display: 'block' }}>
{`${suggest.name||''}`}
{`中文名称: ${suggest.cnName||''} 英文名称: ${suggest.name||''} 描述: ${suggest.remark||''}`}
</Radio>
)
})
......
......@@ -13,18 +13,18 @@ const modes = [
{
title: 'Excel导入',
},
{
title: 'Excel复制粘贴',
},
{
title: 'Erwin',
},
{
title: '元数据输入',
},
{
title: '数据模型输入',
}
// {
// title: 'Excel复制粘贴',
// },
// {
// title: 'Erwin',
// },
// {
// title: '元数据输入',
// },
// {
// title: '数据模型输入',
// }
]
const ImportModal = (props) => {
......@@ -150,9 +150,6 @@ const ImportModal = (props) => {
setHints([]);
setModelerData({});
setConfirmLoading(false);
if (form && form.resetFields) {
form.resetFields();
}
}
const onActionChange = (data) => {
......@@ -243,7 +240,7 @@ const ImportModal = (props) => {
forceRender
visible={visible}
title={title}
width={1000}
width={step===2?1000:520}
maskClosable={false}
destroyOnClose
onCancel={() => {
......@@ -256,7 +253,7 @@ const ImportModal = (props) => {
<>
{
step===0 && <>
<Radio.Group onChange={onRadioChange}>
<Radio.Group value={radioValue} onChange={onRadioChange}>
{
modes && modes.map((mode, index) => {
return (
......
import React, { useState } from "react";
import React, { useState, useEffect } from "react";
import { Table, Space, Button, Tooltip, Modal } from 'antd';
import { EditOutlined, CheckOutlined, ReconciliationOutlined, DeleteOutlined } from '@ant-design/icons';
import { EditOutlined, ReconciliationOutlined, DeleteOutlined } from '@ant-design/icons';
import { dispatchLatest } from '../../../../model';
import { showMessage } from '../../../../util';
......@@ -8,13 +8,29 @@ import './ModelTable.less';
const ModelTable = (props) => {
const { data, onChange, loading, onItemAction } = props;
const { data, onChange, loading, onItemAction, onSelect, catalogId } = props;
const [ selectedRowKeys, setSelectedRowKeys ] = useState([]);
const [modal, contextHolder] = Modal.useModal();
useEffect(() => {
setSelectedRowKeys([]);
onSelect && onSelect([]);
//eslint-disable-next-line react-hooks/exhaustive-deps
}, [ catalogId ]);
const columns = [
{
title: '序号',
dataIndex: 'key',
editable: false,
render: (text, record, index) => {
return (index+1).toString();
}
},
{
title: '模型名称',
dataIndex: 'name',
},
......@@ -38,9 +54,9 @@ const ModelTable = (props) => {
<Tooltip placement='bottom' title={'详情'}>
<Button icon={<ReconciliationOutlined />} size='small' onClick={() => { detailItem(record); }} />
</Tooltip>
<Tooltip placement='bottom' title={'提交审核'}>
{/* <Tooltip placement='bottom' title={'提交审核'}>
<Button icon={<CheckOutlined />} size='small' />
</Tooltip>
</Tooltip> */}
<Tooltip placement='bottom' title={'删除'}>
<Button icon={<DeleteOutlined />} size='small' onClick={() => { deleteItem(record); }} />
</Tooltip>
......@@ -74,6 +90,14 @@ const ModelTable = (props) => {
callback: () => {
showMessage('success', '模型删除成功');
onChange && onChange();
const index = selectedRowKeys.findIndex((rowKey) => rowKey === record.id);
if (index !== -1) {
const newSelectedRowKeys = [...selectedRowKeys];
newSelectedRowKeys.splice(index, 1);
setSelectedRowKeys(newSelectedRowKeys);
onSelect && onSelect(newSelectedRowKeys);
}
}
})
}
......@@ -82,6 +106,7 @@ const ModelTable = (props) => {
const onSelectChange = keys => {
setSelectedRowKeys(keys);
onSelect && onSelect(keys);
};
const rowSelection = {
......
.model-table {
.yy-table {
height: calc(100vh - 64px - 20px - 53px - 20px) !important;
height: calc(100vh - 64px - 30px - 53px - 20px) !important;
overflow: auto !important;
}
}
\ No newline at end of file
.model-tree {
.yy-tree-list {
height: calc(100vh - 64px - 20px - 53px - 20px) !important;
height: calc(100vh - 64px - 30px - 53px - 20px) !important;
overflow: auto !important;
}
}
\ No newline at end of file
......@@ -89,6 +89,8 @@ const UpdateTreeItemModal = (props) => {
_action = item ? 'sub' : 'root';
}
form.setFields([{ name: 'name', errors: [] }, { name: 'remark', errors: [] }]);
if (type === 'add') {
form.setFieldsValue({ action: _action, name: '', remark: '' });
} else {
......@@ -144,7 +146,7 @@ const UpdateTreeItemModal = (props) => {
}
});
} catch (errInfo) {
console.log('Validate Failed:', errInfo);
setConfirmLoading(false);
}
}
......
......@@ -19,11 +19,12 @@ class Model extends React.Component {
importModalAction: '',
tableData: [],
loadingTableData: false,
selectModelerIds: [],
}
}
onTreeSelect = (key) => {
this.setState({ catalogId: key }, () => {
this.setState({ catalogId: key, selectModelerIds: [] }, () => {
if (!key || key==='') {
this.setState({ tableData: [] });
} else {
......@@ -51,6 +52,10 @@ class Model extends React.Component {
})
}
onTableSelect = (ids) => {
this.setState({ selectModelerIds: ids });
}
onTableItemAction = (id, action) => {
this.setState({ importModalVisible: true, importModalAction: action, modelerId: id });
}
......@@ -66,6 +71,12 @@ class Model extends React.Component {
}
onExportBtnClick = () => {
const { selectModelerIds } = this.state;
if ((selectModelerIds||[]).length === 0) {
showMessage('info', '请先选择模型');
return;
}
this.setState({ exportModalVisible: true });
}
......@@ -79,13 +90,13 @@ class Model extends React.Component {
}
render() {
const { importModalVisible, exportModalVisible, catalogId, importModalAction, tableData, loadingTableData, modelerId } = this.state;
const { importModalVisible, exportModalVisible, catalogId, importModalAction, tableData, loadingTableData, modelerId, selectModelerIds } = this.state;
return (
<div style={{ backgroundColor: '#ECEEF3', height: '100%' }}>
<Row style={{ height: '100%' }}>
<Col span={6} style={{ height: '100%' }} >
<div className='mr-3' style={{ backgroundColor: '#fff' }}>
<div style={{ backgroundColor: '#ECEEF3' }}>
<Row>
<Col span={6} >
<div className='mr-4' style={{ backgroundColor: '#fff' }}>
<ModelTree onSelect={this.onTreeSelect} />
</div>
</Col>
......@@ -98,12 +109,12 @@ class Model extends React.Component {
borderBottom: "1px solid #EFEFEF",
}}
>
<Button type="primary" style={{ marginLeft: 'auto' }}>提交审核</Button>
<Button type="primary" className='ml-3' onClick={this.onImportBtnClick}>模型创建</Button>
{/* <Button type="primary" style={{ marginLeft: 'auto' }}>提交审核</Button> */}
<Button type="primary" className='ml-3' style={{ marginLeft: 'auto' }} onClick={this.onImportBtnClick}>模型创建</Button>
<Button type="primary" className='ml-3' onClick={this.onExportBtnClick}>模型导出</Button>
</div>
<div className='p-3'>
<ModelTable loading={loadingTableData} data={tableData} onChange={this.onTableChange} onItemAction={this.onTableItemAction} />
<ModelTable loading={loadingTableData} catalogId={catalogId} data={tableData} onChange={this.onTableChange} onSelect={this.onTableSelect} onItemAction={this.onTableItemAction} />
</div>
</div>
</Col>
......@@ -118,7 +129,8 @@ class Model extends React.Component {
/>
<ExportModal
visible={exportModalVisible}
visible={exportModalVisible}
ids={selectModelerIds}
onCancel={this.onExportModalCancel}
/>
</div>
......
......@@ -8,6 +8,7 @@ import { ManageLayout } from "../../layout";
import Map from './Map';
import Model from './Model';
import AssetManage from './AssetManage';
import Element from './Element';
class Manage extends Component {
constructor(props) {
......@@ -31,7 +32,7 @@ class Manage extends Component {
<Route path={`${match.path}/map`} component={Map} />
<Route path={`${match.path}/model`} component={Model} />
<Route path={`${match.path}/assetmanage`} component={AssetManage} />
<Route path={`${match.path}/element`} component={Element} />
</Switch>
) : (
<GetSession {...this.props} />
......
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