import React, {Component} from 'react'
|
import PropTypes from 'prop-types'
|
import { is, fromJS } from 'immutable'
|
import { Modal, Button, notification, Spin, Input, message } from 'antd'
|
import { ForkOutlined } from '@ant-design/icons'
|
|
import Api from '@/api'
|
import G6 from "@antv/g6"
|
import Utils from '@/utils/utils.js'
|
import './index.scss'
|
|
const { Search } = Input
|
|
class TableNodes extends Component {
|
static propTpyes = {
|
config: PropTypes.object
|
}
|
|
state = {
|
visible: false,
|
loading: false,
|
nodes: null,
|
empty: false
|
}
|
|
shouldComponentUpdate (nextProps, nextState) {
|
return !is(fromJS(this.state), fromJS(nextState))
|
}
|
|
getTbs = (config) => {
|
let tbs = []
|
let ptbs = []
|
|
let traversal = (components) => {
|
components.forEach(item => {
|
if (item.$tables) {
|
ptbs.push(...item.$tables)
|
item.$tables.forEach(tb => {
|
tbs.push({
|
label: item.name,
|
table: tb,
|
color: '#5AD8A6',
|
id: Utils.getuuid(),
|
direction: 'left'
|
})
|
})
|
}
|
if (item.type === 'tabs') {
|
item.subtabs.forEach(tab => {
|
traversal(tab.components)
|
})
|
} else if (item.type === 'group') {
|
traversal(item.components)
|
}
|
})
|
}
|
|
if (config.Template === 'BaseTable') {
|
config.components.forEach(item => {
|
if (item.type === 'tabs') {
|
item.subtabs.forEach(tab => {
|
if (tab.components[0].$tables) {
|
ptbs.push(...tab.components[0].$tables)
|
tab.components[0].$tables.forEach(tb => {
|
tbs.push({
|
label: tab.label,
|
table: tb,
|
color: '#5AD8A6',
|
id: Utils.getuuid(),
|
direction: 'left'
|
})
|
})
|
}
|
})
|
} else if (item.$tables) {
|
ptbs.push(...item.$tables)
|
item.$tables.forEach(tb => {
|
tbs.push({
|
label: '主表',
|
table: tb,
|
color: '#5AD8A6',
|
id: Utils.getuuid(),
|
direction: 'left'
|
})
|
})
|
}
|
})
|
} else {
|
traversal(config.components)
|
|
if (config.interfaces) {
|
config.interfaces.forEach(item => {
|
if (item.$tables) {
|
ptbs.push(...item.$tables)
|
item.$tables.forEach(tb => {
|
tbs.push({
|
label: item.name,
|
table: tb,
|
color: '#5AD8A6',
|
id: Utils.getuuid(),
|
direction: 'left'
|
})
|
})
|
}
|
})
|
}
|
}
|
|
return {tbs, ptbs}
|
}
|
|
getFuncAndInterface = (config) => {
|
let ptbs = []
|
|
let filterBtn = (cell, tbs) => {
|
if (cell.intertype === 'inner') {
|
tbs.push({
|
label: cell.innerFunc + ' (' + cell.label + ')',
|
color: 'orange',
|
id: Utils.getuuid(),
|
direction: 'left'
|
})
|
} else if (cell.intertype === 'outer' || cell.intertype === 'custom') {
|
if (cell.innerFunc) {
|
tbs.push({
|
label: cell.innerFunc + ' (' + cell.label + ')',
|
color: 'orange',
|
id: Utils.getuuid(),
|
direction: 'left'
|
})
|
}
|
if (cell.outerFunc) {
|
tbs.push({
|
label: cell.outerFunc + ' (' + cell.label + ')',
|
color: 'orange',
|
id: Utils.getuuid(),
|
direction: 'left'
|
})
|
}
|
if (cell.interface && cell.sysInterface !== 'true') {
|
tbs.push({
|
label: cell.interface + ' (' + cell.label + ')',
|
color: 'orange',
|
id: Utils.getuuid(),
|
direction: 'left'
|
})
|
}
|
if (cell.proInterface) {
|
tbs.push({
|
label: cell.proInterface + ' (' + cell.label + ')',
|
color: 'orange',
|
id: Utils.getuuid(),
|
direction: 'left'
|
})
|
}
|
if (cell.exInterface) {
|
let url = ''
|
|
try {
|
url = JSON.parse(cell.exInterface).url
|
} catch(e) {
|
url = ''
|
}
|
|
if (url) {
|
tbs.push({
|
label: url + ' (' + cell.label + ')',
|
color: 'orange',
|
id: Utils.getuuid(),
|
direction: 'left'
|
})
|
}
|
}
|
if (cell.exProInterface) {
|
let url = ''
|
|
try {
|
url = JSON.parse(cell.exProInterface).url
|
} catch(e) {
|
url = ''
|
}
|
|
if (url) {
|
tbs.push({
|
label: url + ' (' + cell.label + ')',
|
color: 'orange',
|
id: Utils.getuuid(),
|
direction: 'left'
|
})
|
}
|
}
|
if (cell.callbackFunc) {
|
tbs.push({
|
label: cell.callbackFunc + ' (' + cell.label + ')',
|
color: 'orange',
|
id: Utils.getuuid(),
|
direction: 'left'
|
})
|
}
|
}
|
}
|
|
let filterSetting = (item, tbs) => {
|
if (!item.setting) return
|
if (item.setting.interType === 'inner') {
|
tbs.push({
|
label: item.setting.innerFunc + ' (数据源)',
|
color: 'orange',
|
id: Utils.getuuid(),
|
direction: 'left'
|
})
|
} else if (item.setting.interType === 'outer') {
|
if (item.setting.outerFunc) {
|
tbs.push({
|
label: item.setting.outerFunc + ' (数据源)',
|
color: 'orange',
|
id: Utils.getuuid(),
|
direction: 'left'
|
})
|
}
|
if (item.setting.interface && item.setting.sysInterface !== 'true') {
|
tbs.push({
|
label: item.setting.interface + ' (数据源)',
|
color: 'orange',
|
id: Utils.getuuid(),
|
direction: 'left'
|
})
|
}
|
if (item.setting.proInterface) {
|
tbs.push({
|
label: item.setting.proInterface + ' (数据源)',
|
color: 'orange',
|
id: Utils.getuuid(),
|
direction: 'left'
|
})
|
}
|
if (item.setting.exInterface) {
|
let url = ''
|
|
try {
|
url = JSON.parse(item.setting.exInterface).url
|
} catch(e) {
|
url = ''
|
}
|
|
if (url) {
|
tbs.push({
|
label: url + ' (数据源)',
|
color: 'orange',
|
id: Utils.getuuid(),
|
direction: 'left'
|
})
|
}
|
}
|
if (item.setting.exProInterface) {
|
let url = ''
|
|
try {
|
url = JSON.parse(item.setting.exProInterface).url
|
} catch(e) {
|
url = ''
|
}
|
|
if (url) {
|
tbs.push({
|
label: url + ' (数据源)',
|
color: 'orange',
|
id: Utils.getuuid(),
|
direction: 'left'
|
})
|
}
|
}
|
}
|
}
|
|
let traversal = (components, extra) => {
|
components.forEach(item => {
|
if (item.type === 'tabs') {
|
item.subtabs.forEach(tab => {
|
traversal(tab.components, extra)
|
})
|
} else if (item.type === 'group') {
|
traversal(item.components, extra)
|
} else {
|
let tbs = []
|
|
filterSetting(item, tbs)
|
|
if (item.action) {
|
item.action.forEach(cell => {
|
if (cell.OpenType === 'popview') {
|
if (cell.config.components) {
|
traversal(cell.config.components, `-${cell.label}(弹窗)`)
|
}
|
} else {
|
filterBtn(cell, tbs)
|
}
|
})
|
}
|
|
if (item.type === 'card' || item.type === 'carousel' || item.type === 'timeline') {
|
item.subcards.forEach(card => {
|
card.elements && card.elements.forEach(cell => {
|
if (cell.eleType !== 'button') return
|
|
if (cell.OpenType === 'popview') {
|
if (cell.config.components) {
|
traversal(cell.config.components, `-${cell.label}(弹窗)`)
|
}
|
} else {
|
filterBtn(cell, tbs)
|
}
|
})
|
card.backElements && card.backElements.forEach(cell => {
|
if (cell.eleType !== 'button') return
|
|
if (cell.OpenType === 'popview') {
|
if (cell.config.components) {
|
traversal(cell.config.components, `-${cell.label}(弹窗)`)
|
}
|
} else {
|
filterBtn(cell, tbs)
|
}
|
})
|
})
|
} else if (item.type === 'balcony') {
|
item.elements && item.elements.forEach(cell => {
|
if (cell.eleType !== 'button') return
|
|
if (cell.OpenType === 'popview') {
|
if (cell.config.components) {
|
traversal(cell.config.components, `-${cell.label}(弹窗)`)
|
}
|
} else {
|
filterBtn(cell, tbs)
|
}
|
})
|
} else if (item.type === 'table') {
|
let loopCol = (cols) => {
|
cols.forEach(col => {
|
if (col.type === 'colspan') {
|
loopCol(col.subcols)
|
} else if (col.type === 'custom') {
|
col.elements.forEach(cell => {
|
if (cell.eleType !== 'button') return
|
|
if (cell.OpenType === 'popview') {
|
if (cell.config.components) {
|
traversal(cell.config.components, `-${cell.label}(弹窗)`)
|
}
|
} else {
|
filterBtn(cell, tbs)
|
}
|
})
|
}
|
})
|
}
|
loopCol(item.cols)
|
} else if (item.type === 'form') {
|
item.subcards.forEach(group => {
|
filterBtn(group.subButton, tbs)
|
})
|
}
|
|
if (tbs.length) {
|
ptbs.push({
|
label: item.name + extra,
|
id: Utils.getuuid(),
|
direction: 'left',
|
color: 'orange',
|
children: tbs
|
})
|
}
|
}
|
})
|
}
|
|
if (config.interfaces) {
|
config.interfaces.forEach(item => {
|
let tbs = []
|
filterSetting(item, tbs)
|
if (tbs.length) {
|
ptbs.push({
|
label: item.name,
|
id: Utils.getuuid(),
|
direction: 'left',
|
color: 'orange',
|
children: tbs
|
})
|
}
|
})
|
}
|
|
traversal(config.components, '')
|
|
return ptbs
|
}
|
|
trigger = () => {
|
const { config } = this.props
|
|
if (!config) return
|
|
this.setState({visible: true, loading: true, empty: false}, () => {
|
let param = {
|
func: 's_get_menus_tb_list',
|
TypeCharOne: sessionStorage.getItem('kei_no') || '',
|
typename: sessionStorage.getItem('typename') || '',
|
MenuID: config.uuid
|
}
|
|
Api.getCloudConfig(param).then(result => {
|
if (!result.status) {
|
notification.warning({
|
top: 92,
|
message: result.message,
|
duration: 5
|
})
|
|
this.setState({empty: true, loading: false})
|
|
return
|
}
|
|
let data = {
|
label: config.MenuName || '空',
|
id: config.uuid,
|
MenuID: config.MenuID,
|
children: []
|
}
|
|
let { tbs, ptbs } = this.getTbs(config)
|
|
ptbs = Array.from(new Set(ptbs))
|
ptbs.sort()
|
if (ptbs.length && sessionStorage.getItem('mk_tb_names')) {
|
let names = sessionStorage.getItem('mk_tb_names')
|
ptbs = ptbs.filter(tb => names.indexOf(',' + tb.toLowerCase() + ',') > -1)
|
}
|
|
if (ptbs.length) {
|
ptbs.forEach((item, i) => {
|
let cell = {
|
label: item,
|
id: 'par' + i,
|
direction: 'left',
|
color: '#5AD8A6',
|
node: 'table',
|
children: []
|
}
|
|
tbs.forEach(t => {
|
if (t.table === item) {
|
cell.children.push(t)
|
}
|
})
|
|
data.children.push(cell)
|
})
|
}
|
|
let funcs = this.getFuncAndInterface(config)
|
|
if (funcs.length) {
|
data.children.push(...funcs)
|
}
|
|
if (result.tb_list) {
|
result.tb_list.sort((a, b) => a.tbname > b.tbname ? 1 : -1)
|
let length = result.tb_list.length
|
result.tb_list.forEach((item, i) => {
|
let cell = {
|
label: item.tbname,
|
name: item.tbname.toLowerCase(),
|
id: 'table' + i,
|
direction: 'right',
|
color: '#1890ff',
|
collapsed: false,
|
collable: true,
|
children: []
|
}
|
|
if (item[item.tbname]) {
|
item[item.tbname].forEach((m, i) => {
|
if (m.debug_url) {
|
let _param = JSON.parse(window.decodeURIComponent(window.atob(m.debug_url)))
|
let label = _param.MenuName
|
_param.lang = _param.lang || 'zh-CN'
|
|
if (_param && _param.type === 'app') {
|
label += ` (${_param.kei_no} | ${_param.typename}${_param.lang !== 'zh-CN' ? ' | ' + _param.lang : ''})`
|
} else if (_param && _param.lang && _param.lang !== 'zh-CN') {
|
label += ` (${_param.lang})`
|
}
|
|
cell.children.push({
|
label: label,
|
id: item.tbname + 'menu' + i,
|
direction: 'right',
|
color: '#1890ff',
|
type: 'dice-mind-map-leaf',
|
param: _param
|
})
|
}
|
})
|
}
|
|
if (cell.children.length > 5 && length > 1) {
|
cell.collapsed = true
|
} else if (cell.children.length === 0) {
|
cell.collable = false
|
}
|
|
data.children.push(cell)
|
})
|
}
|
|
if (data.children.length === 0) {
|
this.setState({empty: true, loading: false})
|
} else {
|
this.setState({loading: false, nodes: data})
|
this.getForks(data)
|
}
|
})
|
})
|
}
|
|
changeMenu = (menu) => {
|
if (menu.direction !== 'right') return
|
|
if (menu.depth === 1) {
|
sessionStorage.setItem('mk-table-node', menu.label)
|
window.open('#/hs')
|
|
setTimeout(() => {
|
sessionStorage.removeItem('mk-table-node')
|
}, 50)
|
} else if (menu.param) {
|
if (menu.param.type === 'admin') {
|
if (['custom', 'home', 'billPrint'].includes(menu.param.MenuType)) {
|
let _param = {...menu.param}
|
delete _param.type
|
_param = window.btoa(window.encodeURIComponent(JSON.stringify(_param)))
|
window.open(`#/menudesign/${_param}`)
|
} else if (menu.param.MenuType === 'BaseTable') {
|
let _param = {...menu.param}
|
delete _param.type
|
delete _param.MenuType
|
_param = window.btoa(window.encodeURIComponent(JSON.stringify(_param)))
|
window.open(`#/tabledesign/${_param}`)
|
}
|
} else if (menu.param.type === 'app') {
|
if (menu.param.typename !== 'pc') {
|
let _param = {...menu.param}
|
_param = window.btoa(window.encodeURIComponent(JSON.stringify(_param)))
|
window.open(`#/mobdesign/${_param}`)
|
} else {
|
let _param = {...menu.param}
|
_param = window.btoa(window.encodeURIComponent(JSON.stringify(_param)))
|
window.open(`#/pcdesign/${_param}`)
|
}
|
}
|
}
|
}
|
|
resetNodes = (key) => {
|
let data = fromJS(this.state.nodes).toJS()
|
key = key ? key.toLowerCase() : ''
|
|
data.children.forEach(cell => {
|
if (cell.direction === 'right') {
|
cell.fontcolor = ''
|
if (key && cell.name.indexOf(key) > -1) {
|
cell.fontcolor = 'orange'
|
}
|
}
|
})
|
let _element = document.getElementById('mkTableNode')
|
if (_element) {
|
_element.innerHTML = ''
|
}
|
|
this.setState({}, () => {
|
this.getForks(data)
|
})
|
}
|
|
getForks = (data) => {
|
const { Util } = G6
|
const that = this
|
|
G6.registerNode(
|
'dice-mind-map-root', {
|
jsx: (cfg) => {
|
const width = Util.getTextSize(cfg.label, 14)[0] + 12;
|
const stroke = cfg.style.stroke || '#096dd9';
|
|
return `
|
<group>
|
<rect draggable="true" style={{width: ${width}, height: 30, stroke: ${stroke}, radius: 4}} keyshape>
|
<text style={{ fontSize: 14, marginLeft: 6, marginTop: 6 }}>${cfg.label}</text>
|
</rect>
|
</group>
|
`;
|
},
|
getAnchorPoints() {
|
return [
|
[0, 0.5],
|
[1, 0.5],
|
];
|
},
|
},
|
'single-node',
|
);
|
|
G6.registerNode(
|
'dice-mind-map-leaf', {
|
jsx: (cfg) => {
|
const width = Util.getTextSize(cfg.label, 12)[0] + 24;
|
const color = cfg.color;
|
|
return `
|
<group>
|
<rect draggable="true" style={{width: ${width}, height: 26, cursor: ${cfg.direction !== 'left' ? 'pointer' : 'default'}, fill: 'transparent' }}>
|
<text style={{ fontSize: 12, fill: ${cfg.fontcolor ? cfg.fontcolor : 'black'}, cursor: ${cfg.direction !== 'left' ? 'pointer' : 'default'}, marginLeft: 12, marginTop: 6 }}>${cfg.label} ${cfg.collable ? '+' : ''}</text>
|
</rect>
|
<rect style={{ fill: ${color}, width: ${width}, cursor: ${cfg.direction !== 'left' ? 'pointer' : 'default'}, height: 2, x: 0, y: 32 }} />
|
</group>
|
`;
|
},
|
getAnchorPoints() {
|
return [
|
[0, 0.965],
|
[1, 0.965],
|
];
|
},
|
},
|
'single-node',
|
);
|
G6.registerBehavior('dice-mindmap', {
|
getEvents() {
|
return {
|
'node:dblclick': 'editNode',
|
};
|
},
|
editNode(evt) {
|
const item = evt.item;
|
const model = item.get('model');
|
|
// 选中节点
|
this.graph.getNodes().forEach(node => {
|
let _model = node.get('model')
|
if (_model.fontcolor === '#1890ff') {
|
_model.fontcolor = ''
|
this.graph.updateItem(node, _model, false)
|
}
|
})
|
|
if (model.direction === 'left') {
|
if (model.node === 'table') {
|
model.fontcolor = '#1890ff'
|
this.graph.updateItem(item, model, false)
|
|
let oInput = document.createElement('input')
|
oInput.value = model.label
|
document.body.appendChild(oInput)
|
oInput.select()
|
document.execCommand('Copy')
|
document.body.removeChild(oInput)
|
|
message.success('表名复制成功。')
|
}
|
return
|
}
|
|
model.fontcolor = '#1890ff'
|
this.graph.updateItem(item, model, false)
|
|
that.changeMenu(model)
|
}
|
});
|
G6.registerBehavior('scroll-canvas', {
|
getEvents: function getEvents() {
|
return {
|
wheel: 'onWheel',
|
};
|
},
|
|
onWheel: function onWheel(ev) {
|
const {
|
graph
|
} = this;
|
if (!graph) {
|
return;
|
}
|
if (ev.ctrlKey) {
|
const canvas = graph.get('canvas');
|
const point = canvas.getPointByClient(ev.clientX, ev.clientY);
|
let ratio = graph.getZoom();
|
if (ev.wheelDelta > 0) {
|
ratio += ratio * 0.05;
|
} else {
|
ratio *= ratio * 0.05;
|
}
|
graph.zoomTo(ratio, {
|
x: point.x,
|
y: point.y,
|
});
|
} else {
|
const x = ev.deltaX || ev.movementX;
|
const y = ev.deltaY || ev.movementY || (-ev.wheelDelta * 125) / 3;
|
graph.translate(-x, -y);
|
}
|
ev.preventDefault();
|
},
|
});
|
|
const dataTransform = (data) => {
|
const changeData = (d, level = 0, color) => {
|
const data = {
|
...d,
|
};
|
switch (level) {
|
case 0:
|
data.type = 'dice-mind-map-root';
|
break;
|
default:
|
data.type = 'dice-mind-map-leaf';
|
break;
|
}
|
|
data.hover = false;
|
|
if (color) {
|
data.color = color;
|
}
|
|
if (d.children && !d.collapsed) {
|
data.children = d.children.map((child) => changeData(child, level + 1, data.color));
|
}
|
return data;
|
};
|
return changeData(data);
|
};
|
|
const tree = new G6.TreeGraph({
|
container: 'mkTableNode',
|
width: this.wrap.offsetWidth,
|
height: this.wrap.offsetHeight,
|
fitView: true,
|
fitViewPadding: [10, 20],
|
layout: {
|
type: 'mindmap',
|
direction: 'H',
|
getHeight: () => {
|
return 16;
|
},
|
getWidth: (node) => {
|
return node.level === 0 ?
|
Util.getTextSize(node.label, 16)[0] + 12 :
|
Util.getTextSize(node.label, 12)[0];
|
},
|
getVGap: () => {
|
return 10;
|
},
|
getHGap: () => {
|
return 60;
|
},
|
getSide: (node) => {
|
return node.data.direction;
|
},
|
},
|
defaultEdge: {
|
type: 'cubic-horizontal',
|
style: {
|
lineWidth: 2,
|
},
|
},
|
minZoom: 0.3,
|
modes: {
|
default: [
|
{
|
type: 'collapse-expand',
|
trigger: 'click',
|
shouldBegin: (e, self) => {
|
if (e.item && e.item.getModel().collable) return true;
|
return false;
|
},
|
},
|
'drag-canvas',
|
'zoom-canvas',
|
'dice-mindmap'
|
],
|
},
|
});
|
|
tree.data(dataTransform(data));
|
|
tree.render();
|
}
|
|
render() {
|
const { visible, loading, empty } = this.state
|
|
return (
|
<>
|
<Button style={{borderColor: '#8E44AD', color: '#8E44AD'}} onClick={this.trigger}><ForkOutlined /> 表关系图</Button>
|
<Modal
|
title=""
|
wrapClassName="view-table-modal"
|
visible={visible}
|
width={'90vw'}
|
closable={false}
|
maskClosable={false}
|
footer={[]}
|
destroyOnClose
|
>
|
<div className="header">表关系图</div>
|
<Search className="tb-search" placeholder="请输入表名" onSearch={value => this.resetNodes(value)} enterButton />
|
<div className="wrap">
|
{loading ? <Spin size="large" /> : null}
|
{empty ? <div className="empty">未查询到表名信息。</div> : null}
|
<div className="mountNode" id="mkTableNode" ref={ref => this.wrap = ref}></div>
|
</div>
|
<div className="footer">
|
<Button key="cancel" onClick={() => { this.setState({ visible: false })}}>关闭</Button>
|
<span className="tip">注:点击表名(右侧)可展开/收起菜单,左侧橙色标注为接口或函数名。</span>
|
</div>
|
</Modal>
|
</>
|
)
|
}
|
}
|
|
export default TableNodes
|