Commit e1ac798f by zhaochengxiang

数据地图

parent 3287dfa9
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
"crypto-js": "^4.0.0", "crypto-js": "^4.0.0",
"less": "^4.1.1", "less": "^4.1.1",
"less-loader": "^8.0.0", "less-loader": "^8.0.0",
"lodash": "^4.17.21",
"react": "^17.0.1", "react": "^17.0.1",
"react-dom": "^17.0.1", "react-dom": "^17.0.1",
"react-redux": "^7.1.0", "react-redux": "^7.1.0",
......
...@@ -5,9 +5,6 @@ export function* getAllTopics() { ...@@ -5,9 +5,6 @@ export function* getAllTopics() {
return yield call(service.getAllTopics); return yield call(service.getAllTopics);
} }
export function* getTableAndTreeModelByTid(payload) { export function* getTableModelByDirIid(payload) {
const tables = yield call(service.getTableModelByTid, payload); return yield call(service.getTableModelByDirId, payload);
const trees = yield call(service.getTreeModel);
return {tables: tables||[], trees: ((trees||[]).length>0)?trees[0]:{}};
} }
...@@ -4,10 +4,6 @@ export function getAllTopics() { ...@@ -4,10 +4,6 @@ export function getAllTopics() {
return GetJSON("/datacatalog/countCtrl/queryAllTopics"); return GetJSON("/datacatalog/countCtrl/queryAllTopics");
} }
export function getTableModelByTid(payload) { export function getTableModelByDirId(payload) {
return GetJSON("/datacatalog/countCtrl/getTableModelInfoByDirId", payload); return GetJSON("/datacatalog/countCtrl/getTableModelInfoByDirId", payload);
} }
export function getTreeModel() {
return GetJSON("/datacatalog/countCtrl/queryAllDirAsTree");
}
\ No newline at end of file
...@@ -18,38 +18,18 @@ const colors = [ ...@@ -18,38 +18,18 @@ const colors = [
const globalFontSize = 12; const globalFontSize = 12;
let graph = null;
class Relation extends React.Component { class Relation extends React.Component {
componentDidMount() { componentDidMount() {
const { data, type, history } = this.props; const { type, loadMoreData , history } = this.props;
setTimeout(() => { setTimeout(() => {
const container = document.getElementById(`container${type||''}`); const container = document.getElementById(`container${type||''}`);
const width = container.scrollWidth; const width = container.scrollWidth;
const height = container.scrollHeight || 500; const height = container.scrollHeight || 500;
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 refreshDragedNodePosition(e) { function refreshDragedNodePosition(e) {
const model = e.item.get('model'); const model = e.item.get('model');
model.fx = e.x; model.fx = e.x;
...@@ -75,7 +55,7 @@ class Relation extends React.Component { ...@@ -75,7 +55,7 @@ class Relation extends React.Component {
}, },
}); });
const graph = new G6.Graph({ graph = new G6.Graph({
container: `container${type||''}`, container: `container${type||''}`,
width, width,
height, height,
...@@ -104,6 +84,73 @@ class Relation extends React.Component { ...@@ -104,6 +84,73 @@ class Relation extends React.Component {
}, },
}); });
this.layoutGraph();
graph.on('node:dragstart', function (e) {
graph.layout();
refreshDragedNodePosition(e);
});
graph.on('node:drag', function (e) {
refreshDragedNodePosition(e);
});
graph.on('node:dragend', function (e) {
e.item.get('model').fx = null;
e.item.get('model').fy = null;
});
graph.on('node:click', function (e) {
const node = e.item;
const model = node.getModel();
if (model.dbType==='Dir') {
const children = model.children;
if (!children && loadMoreData) {
loadMoreData(model.dirId||'');
}
} else {
//通过资产id跳转到资产详情页
// model.tableModelId
// history && history.push(`${ContextPath}/home`);
}
});
if (typeof window !== 'undefined') {
window.onresize = () => {
if (!graph || graph.get('destroyed')) return;
if (!container || !container.scrollWidth || !container.scrollHeight) return;
graph.changeSize(container.scrollWidth, container.scrollHeight);
};
}
}, 100);
}
layoutGraph = () => {
const { data } = this.props;
if(graph){
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;
};
const nodes = data.nodes; const nodes = data.nodes;
const clusterMap = new Map(); const clusterMap = new Map();
let clusterId = 0; let clusterId = 0;
...@@ -121,7 +168,7 @@ class Relation extends React.Component { ...@@ -121,7 +168,7 @@ class Relation extends React.Component {
}); });
data.nodes.forEach(function (node) { data.nodes.forEach(function (node) {
node.label = fittingString(node.label, node.size, globalFontSize); node.label = fittingString(node.label||'', node.size, globalFontSize);
}); });
graph.data({ graph.data({
...@@ -132,34 +179,11 @@ class Relation extends React.Component { ...@@ -132,34 +179,11 @@ class Relation extends React.Component {
}), }),
}); });
graph.render(); graph.render();
}
}
graph.on('node:dragstart', function (e) { componentDidUpdate(prevProps, prevState){
graph.layout(); this.layoutGraph();
refreshDragedNodePosition(e);
});
graph.on('node:drag', function (e) {
refreshDragedNodePosition(e);
});
graph.on('node:dragend', function (e) {
e.item.get('model').fx = null;
e.item.get('model').fy = null;
});
graph.on('node:click', function (e) {
// const node = e.item;
// const model = node.getModel();
history && history.push(`${ContextPath}/home`);
});
if (typeof window !== 'undefined') {
window.onresize = () => {
if (!graph || graph.get('destroyed')) return;
if (!container || !container.scrollWidth || !container.scrollHeight) return;
graph.changeSize(container.scrollWidth, container.scrollHeight);
};
}
}, 100);
} }
render() { render() {
......
...@@ -5,22 +5,40 @@ class SquareItem extends React.Component { ...@@ -5,22 +5,40 @@ class SquareItem extends React.Component {
onItemClick = () => { onItemClick = () => {
const { item, onClick } = this.props; const { item, onClick } = this.props;
if (onClick) { if (onClick && item) {
onClick(item); if (item.dbType==='Dir') {
onClick(item);
} else {
//通过资产id跳转到资产详情页
// item.tableModelId
}
} }
} }
render() { render() {
const { item } = this.props; const { item } = this.props;
return ( return (
<Card title={ <>
<div className='pointer' onClick={this.onItemClick}> {
{item.dirName||''} item && (
</div> item.dbType==='Dir' ? <Card title={
}> <div className='pointer' onClick={this.onItemClick}>
<p>{`数据资产: ${item.tableModelCount}`}</p> {item.dirName||''}
<p>{`资产编目: ${item.subDirCount}`}</p> </div>
</Card> }>
<p>{`数据资产: ${item.tableModelCount}`}</p>
<p>{`资产编目: ${item.subDirCount}`}</p>
</Card> : <Card title={
<div className='pointer' onClick={this.onItemClick}>
{item.name||''}
</div>
}>
<p>{`所属系统: ${item.system||''}`}</p>
<p>{`描述: ${item.remarks||''}`}</p>
</Card>
)
}
</>
); );
} }
} }
......
...@@ -3,15 +3,17 @@ import G6 from '@antv/g6'; ...@@ -3,15 +3,17 @@ import G6 from '@antv/g6';
import { ContextPath } from '../../../../util'; import { ContextPath } from '../../../../util';
let graph = null;
class Tree extends React.Component { class Tree extends React.Component {
componentDidMount() { componentDidMount() {
const { data, type, history } = this.props; const { type, loadMoreData, history } = this.props;
const container = document.getElementById(`container${type||''}`); const container = document.getElementById(`container${type||''}`);
const width = container.scrollWidth; const width = container.scrollWidth;
const height = container.scrollHeight || 500; const height = container.scrollHeight || 500;
const graph = new G6.TreeGraph({ graph = new G6.TreeGraph({
container: `container${type||''}`, container: `container${type||''}`,
width, width,
height, height,
...@@ -57,15 +59,23 @@ class Tree extends React.Component { ...@@ -57,15 +59,23 @@ class Tree extends React.Component {
}; };
}); });
graph.data(data); this.layoutGraph();
graph.render();
graph.fitView();
graph.on('node:click', function (e) { graph.on('node:click', function (e) {
// const node = e.item; const node = e.item;
// const model = node.getModel(); const model = node.getModel();
// history && history.push(`${ContextPath}/home`); if (model.dbType==='Dir') {
const children = model.children;
if (!children && loadMoreData) {
loadMoreData(model.dirId||'');
}
} else {
//通过资产id跳转到资产详情页
// model.tableModelId
// history && history.push(`${ContextPath}/home`);
}
}); });
if (typeof window !== 'undefined') { if (typeof window !== 'undefined') {
...@@ -78,6 +88,20 @@ class Tree extends React.Component { ...@@ -78,6 +88,20 @@ class Tree extends React.Component {
} }
} }
layoutGraph = () => {
const { data } = this.props;
if(graph){
graph.data(data);
graph.render();
graph.fitView();
}
}
componentDidUpdate(prevProps, prevState){
this.layoutGraph();
}
render() { render() {
const { type } = this.props; const { type } = this.props;
return ( return (
......
...@@ -6,6 +6,7 @@ import SquareItem from './Component/SquareItem'; ...@@ -6,6 +6,7 @@ import SquareItem from './Component/SquareItem';
import Tree from './Component/Tree'; import Tree from './Component/Tree';
import Relation from './Component/Relation'; import Relation from './Component/Relation';
import { dispatchLatest } from '../../../model'; import { dispatchLatest } from '../../../model';
import lodash from 'lodash';
const column = 3; const column = 3;
...@@ -24,27 +25,52 @@ class MapContent extends React.Component { ...@@ -24,27 +25,52 @@ class MapContent extends React.Component {
} }
componentDidMount() { componentDidMount() {
const { tid } = this.props; const { topic } = this.props;
this.setState({ loading: true }, () => { this.setState({ loading: true }, () => {
dispatchLatest({ dispatchLatest({
type: 'map.getTableAndTreeModelByTid', type: 'map.getTableModelByDirIid',
payload: { dirId: tid }, payload: { dirId: topic.id },
callback: data => { callback: data => {
this.convertRemoteData(data);
this.setState({ this.setState({
loading: false, loading: false,
tableModelData: data.tables||[], tableModelData: data||[],
curTableModelData: data.tables||[], curTableModelData: data||[],
treeModelData: data.trees||{}, breadcrumbContents: [{ data: data||[] }]
relationModelData: this.convertRelationModelData(data.trees||{}), }, () => {
breadcrumbContents: [{ data: data.tables||[] }] this.setTreeAndRelationState(data||[]);
}); });
} }
}) })
}) })
} }
convertRemoteData = (data) => {
data.forEach((item, index) => {
if (item.dbType==='Dir') {
item.text = item.dirName||'';
item.id = `d${item.dirId||''}`
} else {
item.text = item.name||'';
item.id = `t${item.tableModelId||''}`;
}
})
}
convertTreeModelData = (data) => {
const { topic } = this.props;
return {
text: topic.name||'',
id: `t${topic.id}`,
children: data
};
}
convertRelationModelData = (data) => { convertRelationModelData = (data) => {
const _relationData = { const _relationData = {
nodes: [], nodes: [],
edges: [] edges: []
...@@ -62,34 +88,71 @@ class MapContent extends React.Component { ...@@ -62,34 +88,71 @@ class MapContent extends React.Component {
_cluster = cluster; _cluster = cluster;
} }
_relationData.nodes.push({ id: item.nodeId, text: item.text||'', label: item.text||'', size: (80-depth*20)>20?(80-depth*20):20, cluster: _cluster, isLeaf: (item.childSize===null || item.childSize===0) }); _relationData.nodes.push({...item, ...{ label: item.text||'', size: (80-depth*20)>20?(80-depth*20):20, cluster: _cluster, isLeaf: (item.childSize===null || item.childSize===0)}});
_relationData.edges.push({ source: sid, target: item.nodeId }); _relationData.edges.push({ source: sid, target: item.id });
recursionTreeData(item.children||[], item.nodeId, depth+1, _cluster); recursionTreeData(item.children||[], item.id, depth+1, _cluster);
}) })
} }
if (data && data.nodeId) { if (data && data.id) {
_relationData.nodes.push({...data, ...{ label: data.text||'', size: 80, cluster: '0', isLeaf: (data.childSize===null || data.childSize===0)}});
_relationData.nodes.push({ id: data.nodeId, text: data.text||'', label: data.text||'', size: 80, cluster: '0', isLeaf: (data.childSize===null || data.childSize===0) }); recursionTreeData(data.children||[], data.id, 1);
recursionTreeData(data.children||[], data.nodeId, 1);
} }
return _relationData; return _relationData;
} }
onSquareItemClick = (item) => { setSquareGraphState = (item) => {
const { breadcrumbContents } = this.state; const { breadcrumbContents } = this.state;
if ((item.children||[]).length===0) return;
this.setState({ this.setState({
breadcrumbContents: [...breadcrumbContents, { name: item.dirName||'', data: item.children||[] }], breadcrumbContents: [...breadcrumbContents, { name: item.dirName||'', data: item.children||[] }],
curTableModelData: item.children||[], curTableModelData: item.children||[],
}); });
} }
onSquareItemClick = (item) => {
const { tableModelData } = this.state;
if (!item.children) {
// dispatchLatest({
// type: 'map.getTableModelByDirIid',
// payload: { dirId: item.dirId },
// callback: data => {
// this.convertRemoteData(data||[]);
// item.children = (data||[]);
// this.setSquareGraphState(item);
// this.setTreeAndRelationState(tableModelData);
// }
// })
// return;
item.children = [
{
"subDirCount": 0,
"dbType": "Dir",
"dirId": "5f2a63e89cfac536601fb2a6",
"tableModelCount": 0,
"dirName": "公司"
},
{
"tableModelId": "1",
"name": "table",
"system": "8月5",
"remarks": "对外"
}
]
this.convertRemoteData(item.children);
this.setSquareGraphState(item);
this.setTreeAndRelationState(tableModelData);
return;
}
this.setSquareGraphState(item);
this.setTreeAndRelationState(tableModelData);
}
onBreadcrumbItemClick = (content, index) => { onBreadcrumbItemClick = (content, index) => {
const { breadcrumbContents } = this.state; const { breadcrumbContents } = this.state;
this.setState({ this.setState({
breadcrumbContents: breadcrumbContents.splice(0, index===0?1:(index+1)), breadcrumbContents: breadcrumbContents.splice(0, index===0?1:(index+1)),
...@@ -97,6 +160,76 @@ class MapContent extends React.Component { ...@@ -97,6 +160,76 @@ class MapContent extends React.Component {
}) })
} }
loadMoreData = (id) => {
const { tableModelData } = this.state;
// dispatchLatest({
// type: 'map.getTableModelByDirIid',
// payload: { dirId: id },
// 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.setTreeAndRelationState(tableModelData);
// }
// })
const data = [
{
"subDirCount": 0,
"dbType": "Dir",
"dirId": "5f2a63e89cfac536601fb2a6",
"tableModelCount": 0,
"dirName": "公司"
},
{
"tableModelId": "1",
"name": "table",
"system": "8月5",
"remarks": "对外"
}
]
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.setTreeAndRelationState(tableModelData);
}
setTreeAndRelationState = (tableModelData) => {
const _treeData = this.convertTreeModelData(tableModelData);
this.setState({
treeModelData: lodash.cloneDeep(_treeData),
relationModelData: this.convertRelationModelData(lodash.cloneDeep(_treeData))
});
}
render() { render() {
const { diagram, type } = this.props; const { diagram, type } = this.props;
const { curTableModelData, breadcrumbContents, treeModelData, relationModelData, loading } = this.state; const { curTableModelData, breadcrumbContents, treeModelData, relationModelData, loading } = this.state;
...@@ -151,10 +284,10 @@ class MapContent extends React.Component { ...@@ -151,10 +284,10 @@ class MapContent extends React.Component {
</> </>
} }
{ {
diagram==='tree' && <Tree data={treeModelData} type={type} {...this.props} /> diagram==='tree' && <Tree data={treeModelData} type={type} {...this.props} loadMoreData={this.loadMoreData} />
} }
{ {
diagram==='relation' && <Relation data={relationModelData} type={type} {...this.props} /> diagram==='relation' && <Relation data={relationModelData} type={type} {...this.props} loadMoreData={this.loadMoreData} />
} }
</> </>
} }
......
...@@ -66,8 +66,8 @@ class Map extends React.Component { ...@@ -66,8 +66,8 @@ class Map extends React.Component {
{ {
topics && topics.map((topic, index) => { topics && topics.map((topic, index) => {
return ( return (
<TabPane tab='业务' key={index.toString()} className='p-3' style={{ height: '100%' }}> <TabPane tab={topic.name||''} key={index.toString()} className='p-3' style={{ height: '100%' }}>
{ tabKey===index.toString() && <MapContent diagram={diagram} tid={topic.id||''} type='business' {...this.props} /> } { tabKey===index.toString() && <MapContent diagram={diagram} topic={topic} type='business' {...this.props} /> }
</TabPane> </TabPane>
); );
}) })
......
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