import React, {Component} from 'react'
|
import PropTypes from 'prop-types'
|
import { is, fromJS } from 'immutable'
|
import { Spin, Tooltip, Empty, message, Modal, notification } from 'antd'
|
import { VerticalAlignTopOutlined, VerticalAlignBottomOutlined, SaveOutlined, ZoomInOutlined, ZoomOutOutlined, OneToOneOutlined, DownloadOutlined } from '@ant-design/icons'
|
import { Graph, Shape } from '@antv/x6'
|
import { Stencil } from '@antv/x6-plugin-stencil'
|
import { Transform } from '@antv/x6-plugin-transform'
|
import { Selection } from '@antv/x6-plugin-selection'
|
import { Snapline } from '@antv/x6-plugin-snapline'
|
import { Keyboard } from '@antv/x6-plugin-keyboard'
|
import { Clipboard } from '@antv/x6-plugin-clipboard'
|
import { History } from '@antv/x6-plugin-history'
|
import { Export } from '@antv/x6-plugin-export'
|
|
import Api from '@/api'
|
import UtilsDM from '@/utils/utils-datamanage.js'
|
import MKEmitter from '@/utils/events.js'
|
import asyncComponent from '@/utils/asyncComponent'
|
import NormalHeader from '@/tabviews/custom/components/share/normalheader'
|
import './index.scss'
|
|
const NodeUpdate = asyncComponent(() => import('./nodeupdate'))
|
|
const groups = {
|
top: {
|
position: 'top',
|
attrs: {
|
circle: {
|
r: 4,
|
magnet: true,
|
stroke: 'var(--mk-sys-color)',
|
strokeWidth: 1,
|
fill: '#fff',
|
style: {
|
visibility: 'hidden'
|
}
|
}
|
}
|
},
|
right: {
|
position: 'right',
|
attrs: {
|
circle: {
|
r: 4,
|
magnet: true,
|
stroke: 'var(--mk-sys-color)',
|
strokeWidth: 1,
|
fill: '#fff',
|
style: {
|
visibility: 'hidden'
|
}
|
}
|
}
|
},
|
bottom: {
|
position: 'bottom',
|
attrs: {
|
circle: {
|
r: 4,
|
magnet: true,
|
stroke: 'var(--mk-sys-color)',
|
strokeWidth: 1,
|
fill: '#fff',
|
style: {
|
visibility: 'hidden'
|
}
|
}
|
}
|
},
|
left: {
|
position: 'left',
|
attrs: {
|
circle: {
|
r: 4,
|
magnet: true,
|
stroke: 'var(--mk-sys-color)',
|
strokeWidth: 1,
|
fill: '#fff',
|
style: {
|
visibility: 'hidden'
|
}
|
}
|
}
|
}
|
}
|
|
Graph.registerNode(
|
'lane',
|
{
|
inherit: 'rect',
|
markup: [
|
{
|
tagName: 'rect',
|
selector: 'body',
|
},
|
{
|
tagName: 'rect',
|
selector: 'name-rect',
|
},
|
{
|
tagName: 'text',
|
selector: 'name-text',
|
},
|
],
|
attrs: {
|
body: {
|
fill: '#FFF',
|
stroke: '#5F95FF',
|
strokeWidth: 1,
|
},
|
'name-rect': {
|
width: 200,
|
height: 36,
|
fill: '#5F95FF',
|
stroke: '#fff',
|
strokeWidth: 1,
|
x: -1,
|
},
|
'name-text': {
|
ref: 'name-rect',
|
refY: 0.5,
|
refX: 0.5,
|
textAnchor: 'middle',
|
fontWeight: 'bold',
|
fill: '#fff',
|
fontSize: 14,
|
},
|
},
|
zIndex: 0
|
},
|
true,
|
)
|
|
Graph.registerNode(
|
'mk-rect',
|
{
|
inherit: 'rect',
|
width: 66,
|
height: 36,
|
attrs: {
|
body: {
|
strokeWidth: 1,
|
stroke: '#000000',
|
fill: '#FFFFFF'
|
},
|
text: {
|
fontSize: 12,
|
fill: '#262626'
|
}
|
},
|
ports: {
|
groups,
|
items: [
|
{ group: 'top' },
|
{ group: 'right' },
|
{ group: 'bottom' },
|
{ group: 'left' }
|
]
|
}
|
},
|
true
|
)
|
|
Graph.registerNode(
|
'mk-polygon',
|
{
|
inherit: 'polygon',
|
width: 66,
|
height: 36,
|
attrs: {
|
body: {
|
strokeWidth: 1,
|
stroke: '#000000',
|
fill: '#FFFFFF'
|
},
|
text: {
|
fontSize: 12,
|
fill: '#262626'
|
}
|
},
|
ports: {
|
groups,
|
items: [
|
{ group: 'top' },
|
{ group: 'right' },
|
{ group: 'bottom' },
|
{ group: 'left' }
|
]
|
}
|
},
|
true
|
)
|
|
Graph.registerNode(
|
'mk-paral',
|
{
|
inherit: 'polygon',
|
width: 66,
|
height: 36,
|
attrs: {
|
body: {
|
strokeWidth: 1,
|
stroke: '#000000',
|
fill: '#FFFFFF'
|
},
|
text: {
|
fontSize: 12,
|
fill: '#262626'
|
}
|
},
|
ports: {
|
groups,
|
items: [
|
{ group: 'top' },
|
{ group: 'bottom' }
|
]
|
}
|
},
|
true
|
)
|
|
Graph.registerNode(
|
'mk-circle',
|
{
|
inherit: 'circle',
|
width: 36,
|
height: 36,
|
attrs: {
|
body: {
|
strokeWidth: 1,
|
stroke: '#000000',
|
fill: '#FFFFFF'
|
},
|
text: {
|
fontSize: 12,
|
fill: '#262626'
|
}
|
},
|
ports: {
|
groups,
|
items: [
|
{ group: 'top' },
|
{ group: 'right' },
|
{ group: 'bottom' },
|
{ group: 'left' }
|
]
|
}
|
},
|
true
|
)
|
|
Graph.registerNode(
|
'mk-ellipse',
|
{
|
inherit: 'ellipse',
|
width: 66,
|
height: 36,
|
attrs: {
|
body: {
|
strokeWidth: 1,
|
stroke: '#000000',
|
fill: '#FFFFFF'
|
},
|
text: {
|
fontSize: 12,
|
fill: '#262626'
|
}
|
},
|
ports: {
|
groups,
|
items: [
|
{ group: 'top' },
|
{ group: 'right' },
|
{ group: 'bottom' },
|
{ group: 'left' }
|
]
|
}
|
},
|
true
|
)
|
|
Graph.registerNode(
|
'mk-star',
|
{
|
inherit: 'polygon',
|
width: 36,
|
height: 36,
|
points: '100,10 40,198 190,78 10,78 160,198',
|
attrs: {
|
body: {
|
fill: '#FFFFFF',
|
stroke: '#000000',
|
strokeWidth: 1,
|
fillRule: 'nonzero'
|
},
|
text: {
|
fontSize: 12,
|
fill: '#262626'
|
}
|
},
|
ports: {
|
groups,
|
items: [
|
{ group: 'top' },
|
{ group: 'right' },
|
{ group: 'bottom' },
|
{ group: 'left' }
|
]
|
}
|
},
|
true
|
)
|
|
Graph.registerNode(
|
'mk-text',
|
{
|
inherit: 'rect',
|
width: 66,
|
height: 36,
|
attrs: {
|
body: {
|
strokeWidth: 0,
|
fill: 'transparent'
|
},
|
text: {
|
fontSize: 12,
|
fill: '#262626'
|
}
|
}
|
},
|
true
|
)
|
|
class antvX6Chart extends Component {
|
static propTpyes = {
|
config: PropTypes.object,
|
mainSearch: PropTypes.any
|
}
|
|
state = {
|
config: null,
|
editing: false,
|
node: null,
|
arr_field: 'id,cells',
|
loading: false
|
}
|
|
selectNode = null
|
mkGraph = null
|
|
UNSAFE_componentWillMount () {
|
const { config } = this.props
|
let _config = fromJS(config).toJS()
|
|
let BID = ''
|
let BData = ''
|
|
if (config.setting.supModule) {
|
BData = window.GLOB.CacheData.get(config.setting.supModule)
|
} else {
|
BData = window.GLOB.CacheData.get(config.$pageId)
|
}
|
if (BData) {
|
BID = BData.$BID || ''
|
}
|
|
this.setState({
|
config: _config,
|
BID: BID || '',
|
BData: BData || '',
|
plot: _config.plot
|
}, () => {
|
if (config.setting.onload === 'true') {
|
setTimeout(() => {
|
this.loadData()
|
}, _config.setting.delay || 0)
|
}
|
})
|
}
|
|
shouldComponentUpdate (nextProps, nextState) {
|
return !is(fromJS(this.state), fromJS(nextState))
|
}
|
|
/**
|
* @description 组件销毁,清除state更新,清除快捷键设置
|
*/
|
componentWillUnmount () {
|
this.setState = () => {
|
return
|
}
|
}
|
|
async loadData () {
|
const { mainSearch } = this.props
|
const { config, arr_field, BID } = this.state
|
|
if (config.setting.supModule && !BID) { // BID 不存在时,不做查询
|
if (!is(fromJS(this.data), fromJS([]))) {
|
this.data = []
|
this.handleData()
|
}
|
return
|
}
|
|
let searches = config.setting.useMSearch && mainSearch ? mainSearch : []
|
|
let requireFields = searches.filter(item => item.required && item.value === '')
|
if (requireFields.length > 0) {
|
return
|
}
|
|
this.setState({
|
loading: true
|
})
|
|
let _orderBy = config.setting.order || ''
|
let param = UtilsDM.getQueryDataParams(config.setting, arr_field, searches, _orderBy, '', '', BID)
|
|
let result = await Api.genericInterface(param)
|
if (result.status) {
|
if (config.$cache && config.setting.onload !== 'false') {
|
Api.writeCacheConfig(config.uuid, result.data || '')
|
}
|
|
this.setState({
|
loading: false
|
})
|
|
if (!is(fromJS(this.data), fromJS(result.data || []))) {
|
this.data = result.data || []
|
this.handleData()
|
}
|
|
if (result.message) {
|
if (result.ErrCode === 'Y') {
|
Modal.success({
|
title: result.message
|
})
|
} else if (result.ErrCode === 'S') {
|
notification.success({
|
top: 92,
|
message: result.message,
|
duration: 2
|
})
|
}
|
}
|
} else {
|
this.setState({
|
loading: false
|
})
|
|
if (!result.message) return
|
if (result.ErrCode === 'N') {
|
Modal.error({
|
title: result.message,
|
})
|
} else if (result.ErrCode !== '-2') {
|
notification.error({
|
top: 92,
|
message: result.message,
|
duration: 10
|
})
|
}
|
}
|
}
|
|
handleData = () => {
|
const { config } = this.state
|
|
MKEmitter.emit('resetSelectLine', config.uuid, '', '')
|
|
let _element = document.getElementById(config.uuid + 'container')
|
if (_element) {
|
_element.innerHTML = ''
|
}
|
|
setTimeout(() => {
|
this.viewrender()
|
}, 50)
|
}
|
|
viewrender = () => {
|
const { plot } = this.state
|
|
if (plot.subtype === 'xflow') {
|
this.xflowrender()
|
} else if (plot.subtype === 'lane') {
|
this.lanerender()
|
}
|
}
|
|
xflowrender = () => {
|
const { config } = this.state
|
|
const graph = new Graph({
|
container: document.getElementById(config.uuid + 'container'),
|
grid: config.plot.grid,
|
scaling: {
|
min: 0.5,
|
max: 2
|
},
|
autoResize: true,
|
panning: true,
|
background: {
|
color: config.plot.backgroundColor || 'transparent'
|
},
|
mousewheel: {
|
enabled: true,
|
zoomAtMousePosition: true,
|
modifiers: 'ctrl'
|
},
|
connecting: {
|
router: 'manhattan',
|
connector: {
|
name: 'rounded',
|
args: {
|
radius: 8
|
}
|
},
|
anchor: 'center',
|
connectionPoint: 'anchor',
|
allowBlank: false,
|
snap: {
|
radius: 20
|
},
|
createEdge() {
|
return new Shape.Edge({
|
attrs: {
|
line: {
|
stroke: '#000000',
|
strokeWidth: 1,
|
targetMarker: {
|
name: 'block',
|
width: 12,
|
height: 8
|
}
|
}
|
},
|
zIndex: 0
|
})
|
},
|
validateConnection({ targetMagnet }) {
|
return !!targetMagnet
|
}
|
},
|
highlighting: {
|
magnetAdsorbed: {
|
name: 'stroke',
|
args: {
|
attrs: {
|
fill: '#5F95FF',
|
stroke: '#5F95FF'
|
}
|
}
|
}
|
}
|
})
|
|
// #region 使用插件
|
graph
|
.use(new Transform({
|
resizing: true,
|
rotating: true
|
}))
|
.use(new Selection())
|
.use(new Snapline())
|
.use(new Keyboard())
|
.use(new Clipboard())
|
.use(new History())
|
.use(new Export())
|
|
// #region 初始化 stencil
|
const stencil = new Stencil({
|
title: '流程图',
|
target: graph,
|
stencilGraphWidth: 180,
|
stencilGraphHeight: 180,
|
groups: [
|
{
|
title: '通用节点',
|
name: 'group1'
|
},
|
{
|
title: '自定义',
|
name: 'group2',
|
graphHeight: 150,
|
layoutOptions: {
|
rowHeight: 70
|
}
|
}
|
],
|
layoutOptions: {
|
columns: 2,
|
columnWidth: 80,
|
rowHeight: 55
|
}
|
})
|
|
document.getElementById(config.uuid + 'stencil').appendChild(stencil.container)
|
|
// #region 快捷键与事件
|
graph.bindKey(['meta+c', 'ctrl+c'], () => {
|
const cells = graph.getSelectedCells()
|
if (cells.length) {
|
graph.copy(cells)
|
}
|
return false
|
})
|
graph.bindKey(['meta+x', 'ctrl+x'], () => {
|
const cells = graph.getSelectedCells()
|
if (cells.length) {
|
graph.cut(cells)
|
}
|
return false
|
})
|
graph.bindKey(['meta+v', 'ctrl+v'], () => {
|
if (!graph.isClipboardEmpty()) {
|
graph.paste({ offset: 32 })
|
}
|
return false
|
})
|
|
// undo redo
|
graph.bindKey(['meta+z', 'ctrl+z'], () => {
|
if (graph.canUndo()) {
|
graph.undo()
|
}
|
return false
|
})
|
graph.bindKey(['meta+shift+z', 'ctrl+shift+z'], () => {
|
if (graph.canRedo()) {
|
graph.redo()
|
}
|
return false
|
})
|
|
// 删除元素
|
graph.bindKey(['backspace', 'delete'], () => {
|
const cells = graph.getSelectedCells()
|
if (cells.length) {
|
graph.removeCells(cells)
|
this.selectNode = null
|
this.setState({node: null})
|
}
|
})
|
|
// 控制连接桩显示/隐藏
|
const showPorts = (ports, show) => {
|
for (let i = 0, len = ports.length; i < len; i += 1) {
|
ports[i].style.visibility = show ? 'visible' : 'hidden'
|
}
|
}
|
graph.on('node:mouseenter', () => {
|
const container = document.getElementById(config.uuid + 'container')
|
const ports = container.querySelectorAll('.x6-port-body')
|
showPorts(ports, true)
|
})
|
graph.on('node:mouseleave', () => {
|
const container = document.getElementById(config.uuid + 'container')
|
const ports = container.querySelectorAll('.x6-port-body')
|
showPorts(ports, false)
|
})
|
|
graph.on('node:click', ({ e, x, y, node, view }) => {
|
this.selectNode = node
|
|
this.setState({node: node.store.data})
|
})
|
graph.on('edge:click', ({ e, x, y, edge, view }) => {
|
this.selectNode = edge
|
|
this.setState({node: edge.store.data})
|
|
graph.clearTransformWidgets()
|
})
|
graph.on('blank:click', ({ e, x, y }) => {
|
this.selectNode = null
|
|
this.setState({node: null})
|
})
|
|
const r1 = graph.createNode({
|
shape: 'mk-rect',
|
label: '开始',
|
attrs: {
|
body: {
|
rx: 20,
|
ry: 26
|
}
|
}
|
})
|
const r2 = graph.createNode({
|
shape: 'mk-rect',
|
label: '过程'
|
})
|
const r3 = graph.createNode({
|
shape: 'mk-rect',
|
attrs: {
|
body: {
|
rx: 6,
|
ry: 6
|
}
|
},
|
label: '可选过程'
|
})
|
const r4 = graph.createNode({
|
shape: 'mk-polygon',
|
attrs: {
|
body: {
|
refPoints: '0,10 10,0 20,10 10,20'
|
}
|
},
|
label: '决策'
|
})
|
const r5 = graph.createNode({
|
shape: 'mk-paral',
|
attrs: {
|
body: {
|
refPoints: '10,0 40,0 30,20 0,20'
|
}
|
},
|
label: '数据'
|
})
|
const r6 = graph.createNode({
|
shape: 'mk-circle',
|
label: '连接'
|
})
|
|
stencil.load([r1, r2, r3, r4, r5, r6], 'group1')
|
|
const p1 = graph.createNode({
|
shape: 'mk-ellipse',
|
label: 'ellipse'
|
})
|
const p2 = graph.createNode({
|
shape: 'mk-star',
|
label: ''
|
})
|
|
const p3 = graph.createNode({
|
shape: 'mk-text',
|
label: '文本'
|
})
|
|
stencil.load([p1, p2, p3], 'group2')
|
|
this.mkGraph = graph
|
}
|
|
lanerender = () => {
|
const { config } = this.state
|
|
const graph = new Graph({
|
container: document.getElementById(config.uuid + 'container'),
|
scaling: {
|
min: 0.5,
|
max: 2
|
},
|
autoResize: true,
|
panning: true,
|
background: { color: '#ffffff' },
|
mousewheel: {
|
enabled: true,
|
zoomAtMousePosition: true,
|
modifiers: 'ctrl'
|
},
|
connecting: {
|
router: 'manhattan',
|
connector: {
|
name: 'rounded',
|
args: {
|
radius: 8
|
}
|
},
|
anchor: 'center',
|
connectionPoint: 'anchor',
|
allowBlank: false,
|
snap: {
|
radius: 20
|
},
|
createEdge() {
|
return new Shape.Edge({
|
attrs: {
|
line: {
|
stroke: '#000000',
|
strokeWidth: 1,
|
targetMarker: {
|
name: 'block',
|
width: 12,
|
height: 8
|
}
|
}
|
},
|
zIndex: 2
|
})
|
},
|
validateConnection({ targetMagnet }) {
|
return !!targetMagnet
|
}
|
},
|
highlighting: {
|
magnetAdsorbed: {
|
name: 'stroke',
|
args: {
|
attrs: {
|
fill: '#5F95FF',
|
stroke: '#5F95FF'
|
}
|
}
|
}
|
},
|
translating: {
|
restrict(cellView) {
|
const cell = cellView.cell
|
const parentId = cell.prop('parent')
|
|
if (parentId) {
|
const parentNode = graph.getCellById(parentId)
|
if (parentNode) {
|
return parentNode.getBBox().moveAndExpand({
|
x: 0,
|
y: 36,
|
width: 0,
|
height: -36,
|
})
|
}
|
}
|
return cell.getBBox()
|
}
|
}
|
})
|
|
graph
|
.use(new Selection())
|
.use(new Snapline())
|
.use(new Keyboard())
|
.use(new Clipboard())
|
.use(new History())
|
.use(new Export())
|
|
// #region 初始化 stencil
|
const stencil = new Stencil({
|
title: '流程图',
|
target: graph,
|
stencilGraphWidth: 180,
|
stencilGraphHeight: 180,
|
groups: [
|
{
|
title: '通用节点',
|
name: 'group1'
|
},
|
{
|
title: '自定义',
|
name: 'group2',
|
graphHeight: 150,
|
layoutOptions: {
|
rowHeight: 70
|
}
|
},
|
{
|
title: '泳道',
|
name: 'group3',
|
graphHeight: 120,
|
layoutOptions: {
|
rowHeight: 70
|
}
|
}
|
],
|
layoutOptions: {
|
columns: 2,
|
columnWidth: 80,
|
rowHeight: 55
|
}
|
})
|
|
document.getElementById(config.uuid + 'stencil').appendChild(stencil.container)
|
|
// #region 快捷键与事件
|
graph.bindKey(['meta+c', 'ctrl+c'], () => {
|
const cells = graph.getSelectedCells()
|
if (cells.length) {
|
graph.copy(cells)
|
}
|
return false
|
})
|
graph.bindKey(['meta+x', 'ctrl+x'], () => {
|
const cells = graph.getSelectedCells()
|
if (cells.length) {
|
graph.cut(cells)
|
}
|
return false
|
})
|
graph.bindKey(['meta+v', 'ctrl+v'], () => {
|
if (!graph.isClipboardEmpty()) {
|
graph.paste({ offset: { dx: 0, dy: 20 } })
|
}
|
return false
|
})
|
|
graph.bindKey(['meta+z', 'ctrl+z'], () => {
|
if (graph.canUndo()) {
|
graph.undo()
|
}
|
return false
|
})
|
graph.bindKey(['meta+shift+z', 'ctrl+shift+z'], () => {
|
if (graph.canRedo()) {
|
graph.redo()
|
}
|
return false
|
})
|
|
// 删除元素
|
graph.bindKey(['backspace', 'delete'], () => {
|
const cells = graph.getSelectedCells()
|
if (cells.length) {
|
graph.removeCells(cells)
|
this.selectNode = null
|
this.setState({node: null})
|
}
|
})
|
|
// 控制连接桩显示/隐藏
|
const showPorts = (ports, show) => {
|
for (let i = 0, len = ports.length; i < len; i += 1) {
|
ports[i].style.visibility = show ? 'visible' : 'hidden'
|
}
|
}
|
graph.on('node:mouseenter', ({ cell }) => {
|
if (cell.prop('shape') === 'lane') return
|
|
const container = document.getElementById(config.uuid + 'container')
|
const ports = container.querySelectorAll('.x6-port-body')
|
showPorts(ports, true)
|
})
|
graph.on('node:mouseleave', () => {
|
const container = document.getElementById(config.uuid + 'container')
|
const ports = container.querySelectorAll('.x6-port-body')
|
showPorts(ports, false)
|
})
|
|
graph.on('node:added', ({ cell, index, options }) => {
|
if (cell.prop('shape') === 'lane') {
|
graph.startBatch('add-lane')
|
|
this.addLane(cell, graph)
|
|
graph.stopBatch('add-lane')
|
} else {
|
let num = graph.getCellCount()
|
|
if (num <= 1) {
|
message.warning('请添加泳道!')
|
graph.removeCells([cell])
|
} else {
|
this.addNode(cell, graph)
|
}
|
}
|
})
|
graph.on('node:click', ({ e, x, y, node, view }) => {
|
this.selectNode = node
|
this.setState({node: node.store.data})
|
})
|
graph.on('edge:click', ({ e, x, y, edge, view }) => {
|
this.selectNode = edge
|
this.setState({node: edge.store.data})
|
|
graph.clearTransformWidgets()
|
})
|
graph.on('blank:click', ({ e, x, y }) => {
|
this.selectNode = null
|
|
this.setState({node: null})
|
})
|
|
const r1 = graph.createNode({
|
shape: 'mk-rect',
|
label: '开始',
|
attrs: {
|
body: {
|
rx: 20,
|
ry: 26
|
}
|
}
|
})
|
const r2 = graph.createNode({
|
shape: 'mk-rect',
|
label: '过程'
|
})
|
const r3 = graph.createNode({
|
shape: 'mk-rect',
|
attrs: {
|
body: {
|
rx: 6,
|
ry: 6
|
}
|
},
|
label: '可选过程'
|
})
|
const r4 = graph.createNode({
|
shape: 'mk-polygon',
|
attrs: {
|
body: {
|
refPoints: '0,10 10,0 20,10 10,20'
|
}
|
},
|
label: '决策'
|
})
|
const r5 = graph.createNode({
|
shape: 'mk-paral',
|
attrs: {
|
body: {
|
refPoints: '10,0 40,0 30,20 0,20'
|
}
|
},
|
label: '数据'
|
})
|
const r6 = graph.createNode({
|
shape: 'mk-circle',
|
label: '连接'
|
})
|
|
stencil.load([r1, r2, r3, r4, r5, r6], 'group1')
|
|
const p1 = graph.createNode({
|
shape: 'mk-ellipse',
|
label: 'ellipse'
|
})
|
const p2 = graph.createNode({
|
shape: 'mk-star',
|
label: ''
|
})
|
|
const p3 = graph.createNode({
|
shape: 'mk-text',
|
label: '文本'
|
})
|
|
stencil.load([p1, p2, p3], 'group2')
|
|
const g1 = graph.createNode({
|
shape: 'lane',
|
label: '阶段n'
|
})
|
|
stencil.load([g1], 'group3')
|
|
// let data = [{"id":"1","shape":"lane","width":260,"height":500,"position":{"x":0,"y":0},"label":"阶段1", preId: '', nextId: '2'},{"id":"2","shape":"lane","width":200,"height":500,"position":{"x":260,"y":0},"label":"<Function>", preId: '1', nextId: '3'},{"id":"3","shape":"lane","width":200,"height":500,"position":{"x":460,"y":0},"label":"<Function>", preId: '2', nextId: '4'},{"id":"4","shape":"lane","width":200,"height":500,"position":{"x":660,"y":0},"label":"<Function>", preId: '3', nextId: ''}]
|
// let cells = []
|
// data.forEach((item) => {
|
// if (item.shape === 'edge') {
|
// cells.push(graph.createEdge(item))
|
// } else {
|
// cells.push(graph.createNode(item))
|
// }
|
// })
|
// graph.resetCells(cells)
|
// graph.zoomToFit({ padding: 10, maxScale: 1 })
|
|
this.mkGraph = graph
|
}
|
|
setTop = () => {
|
if (!this.selectNode) {
|
message.warning('请选择节点!')
|
return
|
}
|
this.selectNode.toFront()
|
}
|
|
setBottom = () => {
|
if (!this.selectNode) {
|
message.warning('请选择节点!')
|
return
|
}
|
|
// let cells = this.mkGraph.getCells()
|
this.selectNode.toBack()
|
}
|
|
// zoom() 可获取或者设置缩放比例
|
setZoomIn = () => {
|
this.mkGraph.zoom(0.1)
|
}
|
|
setZoomOut = () => {
|
this.mkGraph.zoom(-0.1)
|
}
|
|
setZoomInt = () => {
|
this.mkGraph.zoomTo(1)
|
}
|
|
save = () => {
|
// let nodes = this.mkGraph.toJSON()
|
}
|
|
savePicture = () => {
|
const { config } = this.state
|
this.mkGraph.exportPNG(config.name, {padding: 20})
|
}
|
|
addLane = (cell, graph) => {
|
const { config } = this.state
|
|
let nodes = graph.getNodes()
|
|
cell.prop('zIndex', 0)
|
let basePoint = cell.prop('position/x')
|
|
if (basePoint <= 0 || nodes.length <= 1) {
|
basePoint = 0
|
} else {
|
let _h = false
|
let _l = 0
|
nodes.forEach(item => {
|
if (item.id === cell.id) return
|
if (item.prop('shape') !== 'lane') return
|
|
let x1 = item.prop('position/x')
|
let x2 = item.prop('position/x') + item.prop('size/width')
|
if (basePoint > x1 && basePoint <= x2) {
|
basePoint = x2
|
_h = true
|
}
|
if (_l < x2) {
|
_l = x2
|
}
|
})
|
|
if (!_h && _l) {
|
basePoint = _l
|
}
|
}
|
|
let height = 400
|
if (typeof(config.plot.height) === 'number' || /px/.test(config.plot.height)) {
|
height = parseInt(config.plot.height)
|
}
|
|
cell.prop('size', {width: 200, height: height})
|
cell.prop('position', {x: basePoint, y: 0})
|
|
this.resetlane(cell.id, basePoint, 200)
|
}
|
|
addNode = (cell, graph) => {
|
let nodes = graph.getNodes()
|
|
let cx1 = cell.prop('position/x')
|
let cx2 = cx1 + cell.prop('size/width')
|
let cx3 = parseInt((cx1 + cx2) / 2)
|
|
nodes.forEach(item => {
|
if (item.prop('shape') !== 'lane') return
|
|
let x1 = item.prop('position/x')
|
let x2 = item.prop('position/x') + item.prop('size/width')
|
|
if (cx3 > x1 && cx3 <= x2) {
|
cell.prop('parent', item.id)
|
|
if (cx1 < x1) {
|
cell.prop('position/x', x1)
|
} else if (cx2 > x2) {
|
cell.prop('position/x', x2 - cell.prop('size/width'))
|
}
|
|
let y1 = item.prop('size/height') - cell.prop('size/height')
|
|
if (cell.prop('position/y') < 36) {
|
cell.prop('position/y', 36)
|
} else if (cell.prop('position/y') > y1) {
|
cell.prop('position/y', y1)
|
}
|
}
|
})
|
|
if (!cell.prop('parent')) {
|
let flane = null
|
let llane = null
|
let lane = null
|
nodes.forEach(item => {
|
if (item.prop('shape') !== 'lane') return
|
if (!flane) {
|
flane = item
|
llane = item
|
} else {
|
if (item.prop('position/x') < flane.prop('position/x')) {
|
flane = item
|
}
|
if (item.prop('position/x') > llane.prop('position/x')) {
|
llane = item
|
}
|
}
|
})
|
|
if (cx1 <= flane.prop('position/x')) {
|
lane = flane
|
} else {
|
lane = llane
|
}
|
|
let x1 = lane.prop('position/x')
|
let x2 = lane.prop('position/x') + lane.prop('size/width')
|
|
cell.prop('parent', lane.id)
|
|
if (cx1 < x1) {
|
cell.prop('position/x', x1)
|
} else if (cx2 > x2) {
|
cell.prop('position/x', x2 - cell.prop('size/width'))
|
}
|
|
let y1 = lane.prop('size/height') - cell.prop('size/height')
|
|
if (cell.prop('position/y') < 36) {
|
cell.prop('position/y', 36)
|
} else if (cell.prop('position/y') > y1) {
|
cell.prop('position/y', y1)
|
}
|
}
|
}
|
|
changeProps = (value, key) => {
|
const { node } = this.state
|
|
if (node.shape === 'edge') {
|
if (key === 'title') {
|
this.selectNode.setLabels(value)
|
} else if (key === 'stroke') {
|
this.selectNode.attr('line/stroke', value)
|
} else if (key === 'strokeWidth') {
|
this.selectNode.attr('line/strokeWidth', value)
|
} else if (key === 'lineType') {
|
if (value === 'dash') {
|
this.selectNode.attr('line/strokeDasharray', 5)
|
} else {
|
this.selectNode.attr('line/strokeDasharray', 0)
|
}
|
}
|
} else if (node.shape === 'lane') {
|
if (key === 'title') {
|
this.selectNode.attr('text/text', value)
|
} else if (key === 'fill') {
|
this.selectNode.attr('name-rect/fill', value)
|
} else if (key === 'stroke') {
|
this.selectNode.attr('body/stroke', value)
|
} else if (key === 'fontSize') {
|
this.selectNode.attr('name-text/fontSize', value)
|
} else if (key === 'fontFill') {
|
this.selectNode.attr('name-text/fill', value)
|
} else if (key === 'height') {
|
this.selectNode.prop('size/height', value)
|
} else if (key === 'width') {
|
this.mkGraph.startBatch('width-change')
|
let offset = value - this.selectNode.prop('size/width')
|
|
this.selectNode.prop('size/width', value)
|
this.selectNode.attr('name-rect/width', value)
|
|
this.resetlane(this.selectNode.id, this.selectNode.prop('position/x'), offset)
|
|
this.mkGraph.stopBatch('width-change')
|
}
|
} else {
|
if (key === 'title') {
|
this.selectNode.attr('text/text', value)
|
} else if (key === 'fill') {
|
this.selectNode.attr('body/fill', value)
|
} else if (key === 'stroke') {
|
this.selectNode.attr('body/stroke', value)
|
} else if (key === 'fontSize') {
|
this.selectNode.attr('text/fontSize', value)
|
} else if (key === 'fontFill') {
|
this.selectNode.attr('text/fill', value)
|
} else if (key === 'mksign') {
|
this.selectNode.prop('mksign', value)
|
}
|
}
|
}
|
|
resetlane = (id, x, offset) => {
|
let nodes = this.mkGraph.getNodes()
|
|
nodes.forEach(item => {
|
if (item.id === id || item.prop('parent') === id) return
|
if (item.prop('shape') === 'edge') return
|
|
let x1 = item.prop('position/x')
|
if (x1 < x) return
|
|
item.prop('position/x', x1 + offset)
|
})
|
}
|
|
render() {
|
const { loading, config, empty, node } = this.state
|
|
return (
|
<div className="custom-x6-plot-box" id={'anchor' + config.uuid} style={config.style}>
|
{loading ?
|
<div className="loading-mask">
|
<div className="ant-spin-blur"></div>
|
<Spin />
|
</div> : null
|
}
|
<NormalHeader config={config} />
|
{empty ? <Empty description={false}/> : null}
|
<div className="mk-toolbar">
|
<div className="left-tool">
|
{config.plot.subtype === 'xflow' ? <Tooltip title="置前">
|
<VerticalAlignTopOutlined onClick={this.setTop}/>
|
</Tooltip> : null}
|
{config.plot.subtype === 'xflow' ? <Tooltip title="置后">
|
<VerticalAlignBottomOutlined onClick={this.setBottom}/>
|
</Tooltip> : null}
|
<Tooltip title="保存">
|
<SaveOutlined onClick={this.save}/>
|
</Tooltip>
|
{config.plot.export === 'png' ? <Tooltip title="导出图片">
|
<DownloadOutlined onClick={this.savePicture}/>
|
</Tooltip> : null}
|
</div>
|
<div className="right-tool">
|
<Tooltip title="放大">
|
<ZoomInOutlined onClick={this.setZoomIn}/>
|
</Tooltip>
|
<Tooltip title="缩小">
|
<ZoomOutOutlined onClick={this.setZoomOut}/>
|
</Tooltip>
|
<Tooltip title="1:1">
|
<OneToOneOutlined onClick={this.setZoomInt}/>
|
</Tooltip>
|
</div>
|
</div>
|
<div className="canvas" style={{width: '100%', minHeight: config.plot.height, height: config.plot.height}} id={config.uuid + 'canvas'}>
|
<div id={config.uuid + 'stencil'} className="mk-stencil"></div>
|
<div id={config.uuid + 'container'} className="mk-container"></div>
|
<div className="mk-node-edit">
|
<div className="header">设置</div>
|
{!node ? <div className="empty">未选中</div> : <NodeUpdate node={node} onChange={this.changeProps}/>}
|
</div>
|
</div>
|
</div>
|
)
|
}
|
}
|
|
export default antvX6Chart
|