import React, {Component} from 'react'
|
import PropTypes from 'prop-types'
|
import { is, fromJS } from 'immutable'
|
import { Spin, Tooltip, message, Modal, notification, Switch, Button } from 'antd'
|
import { UndoOutlined, RedoOutlined, VerticalAlignTopOutlined, VerticalAlignBottomOutlined, ZoomInOutlined, ZoomOutOutlined, OneToOneOutlined, QuestionCircleOutlined, ClearOutlined } 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 MKEmitter from '@/utils/events.js'
|
import asyncComponent from '@/utils/asyncComponent'
|
import NormalHeader from '@/tabviews/custom/components/share/normalheader'
|
// import lanes from '@/menu/components/chart/antv-X6/lane.json'
|
// import xflows from '@/menu/components/chart/antv-X6/xflow.json'
|
import './index.scss'
|
|
const { confirm } = Modal
|
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: 100,
|
height: 32,
|
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: 100,
|
height: 32,
|
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: 100,
|
height: 32,
|
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: 40,
|
height: 40,
|
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: 100,
|
height: 32,
|
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: 40,
|
height: 40,
|
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: 'text-block',
|
width: 100,
|
height: 50,
|
text: '文本域',
|
attrs: {
|
body: {
|
fill: '#ffffff',
|
stroke: '#000000',
|
strokeWidth: 1,
|
rx: 0,
|
ry: 0
|
}
|
}
|
},
|
true
|
)
|
|
const styles = {
|
blue: '#1890ff',
|
red: '#f5222d',
|
orange_red: '#fa541c',
|
orange: '#fa8c16',
|
orange_yellow: '#faad14',
|
yellow: '#fadb14',
|
yellow_green: '#a0d911',
|
green: '#52c41a',
|
cyan: '#13c2c2',
|
blue_purple: '#3860f4',
|
purple: '#722ed1',
|
magenta: '#eb2f96',
|
grass_green: '#aeb303',
|
deep_red: '#c32539',
|
deep_blue: '#1d3661',
|
blue1: '#e6f7ff',
|
red1: '#fff1f0',
|
orange_red1: '#fff2e8',
|
orange1: '#fff7e6',
|
orange_yellow1: '#fffbe6',
|
yellow1: '#feffe6',
|
yellow_green1: '#fcffe6',
|
green1: '#f6ffed',
|
cyan1: '#e6fffb',
|
blue_purple1: '#f0f5ff',
|
purple1: '#f9f0ff',
|
magenta1: '#fff0f6',
|
grass_green1: '#f2efda',
|
deep_red1: '#fff0f0',
|
deep_blue1: '#eff1f4'
|
}
|
|
let sysColor = '#1890ff'
|
let lightColor = '#e6f7ff'
|
if (window.GLOB.style) {
|
let type = window.GLOB.style.replace(/bg_black_style_|bg_white_style_/, '')
|
sysColor = styles[type] || '#1890ff'
|
lightColor = styles[type + '1'] || '#e6f7ff'
|
}
|
|
class antvX6Chart extends Component {
|
static propTpyes = {
|
config: PropTypes.object
|
}
|
|
state = {
|
config: null,
|
editing: false,
|
node: null,
|
loading: false,
|
status: 0,
|
flowname: '',
|
flowcode: '',
|
orgs: [],
|
empty: true
|
}
|
|
selectNode = null
|
mkGraph = null
|
cells = []
|
|
UNSAFE_componentWillMount () {
|
const { config } = this.props
|
let _config = fromJS(config).toJS()
|
|
let BID = ''
|
let BData = ''
|
|
_config.setting.supModule = ''
|
|
if (_config.plot.supModule && _config.plot.supModule.length > 0) {
|
_config.setting.supModule = _config.plot.supModule.pop()
|
}
|
|
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
|
}, () => {
|
this.loadData()
|
|
if (_config.plot.function === 'edit') {
|
this.getorganizations()
|
}
|
})
|
}
|
|
componentDidMount () {
|
MKEmitter.addListener('reloadData', this.reloadData)
|
MKEmitter.addListener('resetSelectLine', this.resetParentParam)
|
}
|
|
shouldComponentUpdate (nextProps, nextState) {
|
return !is(fromJS(this.state), fromJS(nextState))
|
}
|
|
/**
|
* @description 组件销毁,清除state更新,清除快捷键设置
|
*/
|
componentWillUnmount () {
|
this.setState = () => {
|
return
|
}
|
|
MKEmitter.removeListener('reloadData', this.reloadData)
|
MKEmitter.removeListener('resetSelectLine', this.resetParentParam)
|
}
|
|
reloadData = (menuId) => {
|
const { config } = this.state
|
|
if (config.uuid !== menuId) return
|
|
this.loadData()
|
}
|
|
resetParentParam = (MenuID, id) => {
|
const { config } = this.state
|
|
if (!config.setting.supModule || config.setting.supModule !== MenuID) return
|
if (id !== this.state.BID || id !== '') {
|
this.setState({ BID: id }, () => {
|
this.loadData()
|
})
|
}
|
}
|
|
getorganizations = () => {
|
Api.genericInterface({func: 's_get_organizations_v1'}).then(res => {
|
if (!res.status) {
|
notification.error({
|
top: 92,
|
message: res.message,
|
duration: 10
|
})
|
return
|
}
|
|
let orgs = []
|
res.organizations && res.organizations.forEach(com => {
|
let _com = {
|
key: com.OrgCode,
|
title: com.OrgName,
|
shortName: com.ShortName,
|
checkable: false,
|
selectable: false,
|
children: []
|
}
|
let _works = []
|
|
com.departments.forEach(dep => {
|
let _dep = {
|
key: dep.co_pro_code,
|
title: dep.co_pro_name,
|
checkable: false,
|
selectable: false,
|
children: []
|
}
|
|
dep.jobs.forEach(job => {
|
let _job = {
|
key: job.jobcode,
|
title: job.jobname,
|
checkable: false,
|
selectable: false,
|
children: []
|
}
|
|
job.work_group.forEach(group => {
|
if (group.work_group === 'normal') {
|
group.workers.forEach(work => {
|
let _work = {
|
key: work.worker_id,
|
title: work.workername,
|
email: work.email || '',
|
images: work.images || '',
|
mob: work.mob || '',
|
sex: work.sex || '',
|
work_grade: work.work_grade || '',
|
worker_id: work.worker_id || '',
|
workercode: work.workercode || '',
|
workername: work.workername || '',
|
job_type: job.job_type || '',
|
parentIds: [com.OrgCode, dep.co_pro_code, job.jobcode],
|
parentNames: [com.OrgName, dep.co_pro_name, job.jobname]
|
}
|
|
_job.children.push(_work)
|
})
|
} else {
|
let _group = {
|
key: group.work_group,
|
title: group.work_group,
|
checkable: false,
|
selectable: false,
|
children: []
|
}
|
|
group.workers.forEach(work => {
|
let _work = {
|
key: work.worker_id,
|
title: work.workername,
|
email: work.email || '',
|
images: work.images || '',
|
mob: work.mob || '',
|
sex: work.sex || '',
|
work_grade: work.work_grade || '',
|
worker_id: work.worker_id || '',
|
workercode: work.workercode || '',
|
workername: work.workername || '',
|
job_type: job.job_type || '',
|
parentIds: [com.OrgCode, dep.co_pro_code, job.jobcode, group.work_group],
|
parentNames: [com.OrgName, dep.co_pro_name, job.jobname, group.work_group]
|
}
|
|
_group.children.push(_work)
|
})
|
|
if (_group.children.length > 0) {
|
_job.children.push(_group)
|
}
|
}
|
})
|
|
if (_job.children.length > 0) {
|
_dep.children.push(_job)
|
}
|
})
|
|
if (_dep.children.length > 0) {
|
_com.children.push(_dep)
|
}
|
})
|
|
if (_com.children.length > 0) {
|
_com.works = _works
|
orgs.push(_com)
|
}
|
})
|
|
this.setState({orgs: orgs})
|
})
|
}
|
|
async loadData () {
|
const { BID } = this.state
|
|
if (!BID) {
|
this.cells = []
|
if (!is(fromJS(this.data), fromJS([]))) {
|
this.handleData()
|
}
|
this.setState({empty: true})
|
return
|
}
|
|
this.setState({
|
loading: true
|
})
|
|
let param = {
|
func: 's_get_works_flow_local_param_v6',
|
ID: BID
|
}
|
|
let result = await Api.genericInterface(param)
|
if (result.status) {
|
let item = result.data && result.data[0] ? result.data[0] : null
|
let cells = []
|
|
if (item && item.long_param) {
|
let long_param = JSON.parse(window.decodeURIComponent(window.atob(item.long_param)))
|
cells = long_param.cells || []
|
}
|
|
this.cells = cells
|
this.handleData()
|
|
this.setState({
|
status: item.status || 0,
|
flowname: item.works_flow_name || '',
|
flowcode: item.works_flow_code || '',
|
loading: false,
|
empty: cells.length === 0
|
})
|
|
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.function === 'show') {
|
if (plot.subtype === 'xflow') {
|
this.xflowSrender()
|
} else if (plot.subtype === 'lane') {
|
this.laneSrender()
|
}
|
} else {
|
if (plot.subtype === 'xflow') {
|
this.xflowrender()
|
} else if (plot.subtype === 'lane') {
|
this.lanerender()
|
}
|
}
|
}
|
|
xflowSrender = () => {
|
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,
|
interacting: false,
|
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
|
}
|
}
|
})
|
|
if (this.cells.length > 0) {
|
let cells = []
|
this.cells.forEach((item) => {
|
if (item.shape === 'edge') {
|
cells.push(graph.createEdge(item))
|
} else {
|
cells.push(graph.createNode(item))
|
}
|
})
|
graph.resetCells(cells)
|
graph.positionContent('center')
|
}
|
|
if (config.plot.click) {
|
graph.on('cell:click', ({ node }) => {
|
this.checkNode(graph, node)
|
})
|
|
graph.on('blank:click', () => {
|
this.checkNode(graph)
|
})
|
}
|
}
|
|
laneSrender = () => {
|
const { config } = this.state
|
|
const graph = new Graph({
|
container: document.getElementById(config.uuid + 'container'),
|
scaling: {
|
min: 0.5,
|
max: 2
|
},
|
interacting: false,
|
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
|
}
|
}
|
})
|
|
if (this.cells.length > 0) {
|
let cells = []
|
this.cells.forEach((item) => {
|
if (item.shape === 'edge') {
|
cells.push(graph.createEdge(item))
|
} else {
|
cells.push(graph.createNode(item))
|
}
|
})
|
graph.resetCells(cells)
|
graph.positionContent('top')
|
}
|
|
if (config.plot.click) {
|
graph.on('cell:click', ({ node }) => {
|
this.checkNode(graph, node)
|
})
|
|
graph.on('blank:click', () => {
|
this.checkNode(graph)
|
})
|
}
|
}
|
|
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'
|
}
|
}
|
}
|
}
|
})
|
|
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())
|
|
const stencil = new Stencil({
|
title: '流程图',
|
target: graph,
|
stencilGraphWidth: 230,
|
stencilGraphHeight: 180,
|
groups: [
|
{
|
title: '通用节点',
|
name: 'group1',
|
graphHeight: 150
|
},
|
{
|
title: '自定义',
|
name: 'group2',
|
graphHeight: 300,
|
layoutOptions: {
|
rowHeight: 70
|
}
|
}
|
],
|
layoutOptions: {
|
columns: 2,
|
columnWidth: 110,
|
rowHeight: 55
|
}
|
})
|
|
document.getElementById(config.uuid + 'stencil').appendChild(stencil.container)
|
|
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
|
})
|
|
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', ({ node }) => {
|
this.selectNode = node
|
|
this.setState({node: node.store.data})
|
})
|
graph.on('edge:click', ({ edge }) => {
|
this.selectNode = edge
|
|
this.setState({node: edge.store.data})
|
|
graph.clearTransformWidgets()
|
})
|
|
graph.on('node:dblclick', () => {
|
setTimeout(() => {
|
MKEmitter.emit('mk-x6-dbclick')
|
}, 100)
|
})
|
graph.on('edge:dblclick', () => {
|
setTimeout(() => {
|
MKEmitter.emit('mk-x6-dbclick')
|
}, 100)
|
})
|
|
graph.on('blank:click', () => {
|
this.selectNode = null
|
|
this.setState({node: null})
|
})
|
graph.on('edge:connected', ({ isNew, edge }) => {
|
if (!isNew) return
|
|
let target = edge.getTargetCell()
|
let source = edge.getSourceCell()
|
|
if (!target || !source) return
|
|
if (source.prop('mknode') === 'end') {
|
notification.warning({
|
top: 92,
|
message: '结束节点不可添加分支!',
|
duration: 2
|
})
|
graph.removeCells([edge])
|
return
|
}
|
|
let mkdata = target.prop('mkdata')
|
|
if (source.prop('mknode') === 'start' && target.prop('mknode') === 'end') {
|
edge.prop('mknode', 'throughEdge')
|
} else if (target.prop('mknode') === 'end') {
|
edge.prop('mknode', 'endEdge')
|
} else if (target.prop('mknode') === 'start') {
|
edge.prop('mknode', 'startEdge')
|
} else if (source.prop('mknode') === 'start') {
|
edge.prop('mknode', 'firstEdge')
|
}
|
if (mkdata) {
|
edge.prop('mkdata', {status: mkdata.status, statusName: mkdata.statusName})
|
}
|
})
|
|
const r1 = graph.createNode({
|
shape: 'mk-rect',
|
mknode: 'start',
|
mkdata: {status: 0, statusName: '未提交'},
|
attrs: {
|
body: {
|
rx: 0,
|
ry: 0,
|
fill: '#000000',
|
stroke: '#000000'
|
},
|
text: {
|
fill: '#ffffff',
|
text: '开始'
|
}
|
}
|
})
|
|
const r4 = graph.createNode({
|
shape: 'mk-rect',
|
attrs: {
|
body: {
|
rx: 0,
|
ry: 0,
|
fill: '#ffffff',
|
stroke: '#757575'
|
},
|
text: {
|
fill: '#000000',
|
text: '节点'
|
}
|
}
|
})
|
|
const r5 = graph.createNode({
|
shape: 'mk-polygon',
|
attrs: {
|
body: {
|
refPoints: '0,10 10,0 20,10 10,20',
|
fill: '#ffffff',
|
stroke: '#757575'
|
},
|
text: {
|
fill: '#000000',
|
text: '决策'
|
}
|
}
|
})
|
|
const r8 = graph.createNode({
|
shape: 'mk-rect',
|
mknode: 'end',
|
mkdata: {status: 888, statusName: '已完成'},
|
attrs: {
|
body: {
|
rx: 0,
|
ry: 0,
|
fill: '#000000',
|
stroke: '#000000'
|
},
|
text: {
|
fill: '#ffffff',
|
text: '结束'
|
}
|
}
|
})
|
|
stencil.load([r1, r4, r5, r8], 'group1')
|
|
const r2 = graph.createNode({
|
shape: 'mk-rect',
|
attrs: {
|
body: {
|
rx: 20,
|
ry: 26,
|
fill: '#ffffff',
|
stroke: '#757575'
|
},
|
text: {
|
fill: '#000000',
|
text: '过程'
|
}
|
}
|
})
|
const r3 = graph.createNode({
|
shape: 'mk-rect',
|
attrs: {
|
body: {
|
rx: 6,
|
ry: 6,
|
fill: '#ffffff',
|
stroke: '#757575'
|
},
|
text: {
|
fill: '#000000',
|
text: '可选过程'
|
}
|
}
|
})
|
const r6 = graph.createNode({
|
shape: 'mk-paral',
|
attrs: {
|
body: {
|
refPoints: '10,0 40,0 30,20 0,20',
|
fill: '#ffffff',
|
stroke: '#757575'
|
},
|
text: {
|
fill: '#000000',
|
text: '数据'
|
}
|
}
|
})
|
const r7 = graph.createNode({
|
shape: 'mk-circle',
|
attrs: {
|
body: {
|
fill: '#ffffff',
|
stroke: '#757575'
|
},
|
text: {
|
fill: '#000000',
|
text: '连接'
|
}
|
}
|
})
|
|
const p1 = graph.createNode({
|
shape: 'mk-ellipse',
|
label: 'ellipse'
|
})
|
const p2 = graph.createNode({
|
shape: 'mk-star',
|
label: ''
|
})
|
|
const p3 = graph.createNode({
|
shape: 'mk-text'
|
})
|
|
stencil.load([r2, r3, r6, r7, p1, p2, p3], 'group2')
|
|
if (this.cells.length > 0) {
|
let cells = []
|
this.cells.forEach((item) => {
|
if (item.shape === 'edge') {
|
cells.push(graph.createEdge(item))
|
} else {
|
cells.push(graph.createNode(item))
|
}
|
})
|
graph.resetCells(cells)
|
graph.positionContent('center')
|
}
|
|
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 Transform({
|
resizing: true,
|
rotating: true
|
}))
|
.use(new Selection())
|
.use(new Snapline({
|
enabled: true,
|
filter: (Graph) => Graph.prop('shape') !== 'lane'
|
}))
|
.use(new Keyboard())
|
.use(new Clipboard())
|
.use(new History())
|
.use(new Export())
|
|
const stencil = new Stencil({
|
title: '流程图',
|
target: graph,
|
stencilGraphWidth: 230,
|
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: 110,
|
rowHeight: 55
|
}
|
})
|
|
document.getElementById(config.uuid + 'stencil').appendChild(stencil.container)
|
|
graph.bindKey(['meta+c', 'ctrl+c'], () => {
|
const cells = graph.getSelectedCells()
|
if (cells.length) {
|
if (cells[0].prop('shape') !== 'lane' && cells[0].prop('shape') !== 'edge') {
|
graph.copy(cells)
|
}
|
}
|
return false
|
})
|
graph.bindKey(['meta+x', 'ctrl+x'], () => {
|
const cells = graph.getSelectedCells()
|
if (cells.length) {
|
if (cells[0].prop('shape') !== 'lane' && cells[0].prop('shape') !== 'edge') {
|
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) {
|
this.selectNode = null
|
this.setState({node: null})
|
|
if (cells[0].prop('shape') === 'lane') {
|
graph.startBatch('delete-lane')
|
|
graph.removeCells(cells)
|
this.deleteLane(cells[0], graph)
|
|
graph.stopBatch('delete-lane')
|
} else {
|
graph.removeCells(cells)
|
}
|
}
|
})
|
|
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 }) => {
|
if (cell.prop('shape') === 'lane') {
|
if (cell.prop('zIndex') === 0) return
|
|
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 if (!cell.prop('parent')) {
|
this.addNode(cell, graph)
|
}
|
}
|
})
|
graph.on('node:click', ({ node }) => {
|
this.selectNode = node
|
this.setState({node: node.store.data})
|
|
if (node.prop('shape') === 'lane') {
|
graph.clearTransformWidgets()
|
}
|
})
|
graph.on('edge:click', ({ edge }) => {
|
this.selectNode = edge
|
this.setState({node: edge.store.data})
|
|
graph.clearTransformWidgets()
|
})
|
graph.on('blank:click', () => {
|
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'
|
})
|
|
stencil.load([p1, p2, p3], 'group2')
|
|
const g1 = graph.createNode({
|
shape: 'lane',
|
label: '阶段n'
|
})
|
|
stencil.load([g1], 'group3')
|
|
if (this.cells.length > 0) {
|
let cells = []
|
this.cells.forEach((item) => {
|
if (item.shape === 'edge') {
|
cells.push(graph.createEdge(item))
|
} else {
|
cells.push(graph.createNode(item))
|
}
|
})
|
graph.resetCells(cells)
|
graph.positionContent('top')
|
}
|
|
this.mkGraph = graph
|
}
|
|
setTop = () => {
|
if (!this.selectNode) {
|
message.warning('请选择节点!')
|
return
|
}
|
this.selectNode.toFront()
|
}
|
|
setBottom = () => {
|
if (!this.selectNode) {
|
message.warning('请选择节点!')
|
return
|
}
|
|
this.selectNode.toBack()
|
}
|
|
setback = () => {
|
if (this.mkGraph.canUndo()) {
|
this.mkGraph.undo()
|
} else {
|
message.warning('无可撤销记录!')
|
}
|
}
|
|
setprev = () => {
|
if (this.mkGraph.canRedo()) {
|
this.mkGraph.redo()
|
} else {
|
message.warning('无可前进记录!')
|
}
|
}
|
|
setZoomIn = () => {
|
this.mkGraph.zoom(0.1)
|
}
|
|
setZoomOut = () => {
|
this.mkGraph.zoom(-0.1)
|
}
|
|
setZoomInt = () => {
|
this.mkGraph.zoomTo(1)
|
}
|
|
close = () => {
|
const { config } = this.state
|
|
let nodes = this.mkGraph.toJSON()
|
|
if (!is(fromJS(nodes.cells), fromJS(this.cells))) {
|
confirm({
|
title: '流程图已修改,确定关闭吗?',
|
okText: '确定',
|
cancelText: '取消',
|
onOk() {
|
MKEmitter.emit('closeTabView', config.$pageId)
|
},
|
onCancel() {}
|
})
|
} else {
|
MKEmitter.emit('closeTabView', config.$pageId)
|
}
|
}
|
|
save = () => {
|
const { BID, plot, status, flowname, flowcode } = this.state
|
|
if (!BID) {
|
Modal.error({
|
title: '未获取到流程ID,不可保存!',
|
})
|
return
|
}
|
|
let nodes = this.mkGraph.toJSON()
|
let _status = status
|
|
if (plot.subtype === 'xflow' && status === 10) {
|
let start_num = 0
|
let end_num = 0
|
let unvalid = false
|
let map = new Map()
|
let appMap = new Map()
|
|
nodes.cells.forEach(item => {
|
if (item.shape === 'mk-text' || item.shape === 'lane') return
|
|
if (!item.mkdata) {
|
unvalid = true
|
} else if (item.mknode === 'start') {
|
start_num++
|
} else if (item.mknode === 'end') {
|
end_num++
|
} else if (item.shape === 'edge') {
|
if (item.mknode === 'throughEdge') {
|
if (!item.mkdata.seniorbers || item.mkdata.seniorbers.length === 0) {
|
unvalid = true
|
}
|
} else if (item.mkdata.seniorCondition === 'open') {
|
|
} else if (item.mknode !== 'endEdge' && item.mknode !== 'startEdge' && (!item.mkdata.members || item.mkdata.members.length === 0)) {
|
unvalid = true
|
} else if (item.mknode === 'startEdge' || item.mkdata.flowType === 'reject') {
|
if (map.has(item.source.cell)) {
|
unvalid = true
|
} else {
|
map.set(item.source.cell, true)
|
}
|
} else if (item.mkdata.flowType === 'approval' || item.mknode === 'endEdge') {
|
let val = ''
|
if (item.mkdata.execCondition === 'open') {
|
val = item.mkdata.match + item.mkdata.matchVal
|
}
|
if (appMap.has(item.source.cell + val)) {
|
unvalid = true
|
} else {
|
appMap.set(item.source.cell + val, true)
|
}
|
}
|
}
|
})
|
|
if (start_num !== 1 || end_num === 0 || unvalid) {
|
_status = 0
|
}
|
}
|
|
this.setState({
|
loading: true
|
})
|
|
this.mkGraph.toPNG((dataUri) => {
|
let param = {
|
func: 's_works_flow_param_upt_v6',
|
long_param: window.btoa(window.encodeURIComponent(JSON.stringify(nodes))),
|
flow_image: dataUri,
|
status: status,
|
ID: BID,
|
BID: ''
|
}
|
|
let ssoParam = {
|
func: 's_works_flow_param_sso_upt_v6',
|
status: status,
|
ID: BID,
|
works_flow_code: flowcode,
|
works_flow_name: flowname,
|
long_param: window.btoa(window.encodeURIComponent(JSON.stringify(nodes)))
|
}
|
|
Api.genericInterface(param).then(res => {
|
if (res.status) {
|
if (plot.subtype === 'xflow') {
|
Api.getSystemConfig(ssoParam).then(result => {
|
if (result.status) {
|
notification.success({
|
top: 92,
|
message: '保存成功!',
|
duration: 2
|
})
|
this.setState({
|
loading: false,
|
status: _status
|
})
|
this.cells = nodes.cells
|
} else {
|
notification.error({
|
top: 92,
|
message: result.message,
|
duration: 10
|
})
|
this.setState({
|
loading: false
|
})
|
}
|
})
|
} else {
|
notification.success({
|
top: 92,
|
message: '保存成功!',
|
duration: 2
|
})
|
this.setState({
|
loading: false,
|
status: _status
|
})
|
this.cells = nodes.cells
|
}
|
} else {
|
notification.error({
|
top: 92,
|
message: res.message,
|
duration: 10
|
})
|
this.setState({
|
loading: false
|
})
|
}
|
})
|
}, {padding: 20})
|
}
|
|
clearNode = () => {
|
let cells = this.mkGraph.getCells()
|
const that = this
|
|
if (cells.length === 0) {
|
message.warning('尚未添加元素!')
|
return
|
}
|
|
confirm({
|
title: '确定清空元素吗?',
|
content: '清空后不可恢复。',
|
okText: '确定',
|
cancelText: '取消',
|
onOk() {
|
that.mkGraph.removeCells(cells)
|
that.mkGraph.cleanHistory()
|
},
|
onCancel() {}
|
})
|
}
|
|
deleteLane = (cell, graph) => {
|
let id = cell.id
|
|
let nodes = graph.getNodes()
|
let cells = []
|
|
nodes.forEach(item => {
|
if (item.prop('shape') === 'lane') return
|
|
if (item.prop('parent') === id) {
|
cells.push(item)
|
}
|
})
|
|
graph.removeCells(cells)
|
|
this.resetlane(cell.id, cell.prop('position/x'), -cell.prop('size/width'))
|
}
|
|
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)
|
}
|
}
|
}
|
|
delCell = () => {
|
const { node } = this.state
|
|
let cell = this.mkGraph.getCellById(node.id)
|
|
this.mkGraph.removeCells([cell])
|
|
this.setState({node: null})
|
}
|
|
changeProps = (value, key) => {
|
const { node } = this.state
|
|
if (node.shape === 'edge') {
|
let labels = this.selectNode.prop('labels')
|
if (!labels || labels.length === 0) {
|
labels = [{
|
attrs: {label: { text: '' }}
|
}]
|
}
|
|
if (key === 'title') {
|
labels = JSON.parse(JSON.stringify(labels))
|
labels[0].attrs.label.text = value
|
this.selectNode.setLabels(labels)
|
} else if (key === 'stroke') {
|
this.selectNode.attr('line/stroke', value)
|
} else if (key === 'strokeWidth') {
|
this.selectNode.attr('line/strokeWidth', value)
|
} else if (key === 'fontSize') {
|
labels = JSON.parse(JSON.stringify(labels))
|
labels[0].attrs.label.fontSize = value
|
this.selectNode.setLabels(labels)
|
} else if (key === 'fontFill') {
|
labels = JSON.parse(JSON.stringify(labels))
|
labels[0].attrs.label.fill = value
|
this.selectNode.setLabels(labels)
|
} else if (key === 'lineType') {
|
if (value === 'dash') {
|
this.selectNode.attr('line/strokeDasharray', 5)
|
} else {
|
this.selectNode.attr('line/strokeDasharray', 0)
|
}
|
} else if (key === 'mkdata') {
|
if (!node.mkdata) {
|
if (node.target && node.target.cell) {
|
let cell = this.mkGraph.getCellById(node.target.cell)
|
|
if (cell) {
|
cell.prop('mkdata', {status: value.status, statusName: value.statusName || ''})
|
}
|
}
|
}
|
|
this.selectNode.prop('mkdata', value)
|
}
|
} 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 (node.shape === 'mk-text') {
|
if (key === 'title') {
|
this.selectNode.attr('label/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('label/style/fontSize', value)
|
} else if (key === 'fontFill') {
|
this.selectNode.attr('label/style/color', value)
|
}
|
} 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 === 'mkdata') {
|
this.selectNode.prop('mkdata', value)
|
}
|
}
|
}
|
|
checkNode = (graph, node) => {
|
const { plot, config } = this.state
|
|
let nodes = graph.getNodes()
|
|
nodes.forEach(item => {
|
if (item.prop('shape') === 'lane') return
|
|
if (item.prop('oristyle')) {
|
let style = item.prop('oristyle')
|
|
item.prop('attrs/body/fill', style.fill)
|
item.prop('attrs/body/stroke', style.stroke)
|
item.prop('attrs/text/fill', style.text)
|
}
|
})
|
|
if (!node || ['lane', 'mk-text', 'edge'].includes(node.prop('shape'))) {
|
MKEmitter.emit('resetSelectLine', config.uuid, '', '')
|
return
|
}
|
|
if (!node.prop('oristyle')) {
|
node.prop('oristyle', {
|
fill: node.prop('attrs/body/fill'),
|
stroke: node.prop('attrs/body/stroke'),
|
text: node.prop('attrs/text/fill')
|
})
|
}
|
|
node.prop('attrs/body/fill', lightColor)
|
node.prop('attrs/body/stroke', sysColor)
|
node.prop('attrs/text/fill', sysColor)
|
|
let sign = node.prop('mksign') || ''
|
|
if (plot.click === 'menus') {
|
let menu = null
|
|
if (plot.menus && plot.menus.length > 0) {
|
plot.menus.forEach(m => {
|
if (sign !== m.sign) return
|
menu = m
|
})
|
}
|
if (!menu || !menu.MenuID) return
|
|
let newtab = {
|
MenuID: menu.MenuID,
|
MenuName: menu.MenuName,
|
MenuNo: menu.MenuNo || '',
|
type: menu.tabType,
|
param: {$BID: sign}
|
}
|
|
MKEmitter.emit('modifyTabs', newtab, true)
|
} else if (plot.click === 'menu') {
|
let menuId = plot.menu.slice(-1)[0]
|
let menu = null
|
|
if (window.GLOB.mkThdMenus.has(menuId)) {
|
menu = {...window.GLOB.mkThdMenus.get(menuId)}
|
} else if (plot.MenuID) {
|
menu = {
|
MenuID: plot.MenuID,
|
MenuName: plot.MenuName,
|
type: plot.tabType
|
}
|
}
|
|
if (!menu) return
|
|
menu.param = {$BID: sign}
|
|
MKEmitter.emit('modifyTabs', menu, true)
|
} else {
|
MKEmitter.emit('resetSelectLine', config.uuid, sign, {})
|
}
|
}
|
|
changeStatus = () => {
|
const { plot, status } = this.state
|
|
let _status = status === 10 ? 0 : 10
|
|
if (plot.subtype === 'xflow' && _status === 10) {
|
let nodes = this.mkGraph.toJSON()
|
|
let start_num = 0
|
let end_num = 0
|
let unvalidId = ''
|
let emptyNode = null
|
let emptyEdge = null
|
let map = new Map()
|
let appMap = new Map()
|
let rejectId = ''
|
let approvalId = ''
|
|
nodes.cells.forEach(item => {
|
if (item.shape === 'mk-text' || item.shape === 'lane') return
|
|
if (!item.mkdata) {
|
if (item.shape !== 'edge') {
|
if (!emptyNode) {
|
emptyNode = item
|
}
|
} else {
|
if (!emptyEdge) {
|
emptyEdge = item
|
}
|
}
|
} else if (item.mknode === 'start') {
|
start_num++
|
} else if (item.mknode === 'end') {
|
end_num++
|
} else if (item.shape === 'edge' && !unvalidId && !rejectId && !approvalId) {
|
if (item.mknode === 'throughEdge') {
|
if (!item.mkdata.seniorbers || item.mkdata.seniorbers.length === 0) {
|
unvalidId = item.id
|
}
|
} else if (item.mkdata.seniorCondition === 'open') {
|
|
} else if (item.mknode !== 'endEdge' && item.mknode !== 'startEdge' && (!item.mkdata.members || item.mkdata.members.length === 0)) {
|
unvalidId = item.id
|
} else if (item.mknode === 'startEdge' || item.mkdata.flowType === 'reject') {
|
if (map.has(item.source.cell)) {
|
rejectId = item.source.cell
|
} else {
|
map.set(item.source.cell, true)
|
}
|
} else if (item.mkdata.flowType === 'approval' || item.mknode === 'endEdge') {
|
let val = ''
|
if (item.mkdata.execCondition === 'open') {
|
val = item.mkdata.match + item.mkdata.matchVal
|
}
|
if (appMap.has(item.source.cell + val)) {
|
approvalId = item.source.cell
|
} else {
|
appMap.set(item.source.cell + val, item)
|
}
|
}
|
}
|
})
|
|
if (start_num === 0) {
|
notification.warning({
|
top: 92,
|
message: '请添加开始节点!',
|
duration: 2
|
})
|
} else if (start_num > 1) {
|
notification.warning({
|
top: 92,
|
message: '开始节点不可添加多个!',
|
duration: 2
|
})
|
} else if (end_num === 0) {
|
notification.warning({
|
top: 92,
|
message: '请添加结束节点!',
|
duration: 2
|
})
|
// } else if (end_num > 1) {
|
// notification.warning({
|
// top: 92,
|
// message: '结束节点不可添加多个!',
|
// duration: 2
|
// })
|
} else if (emptyNode) {
|
let errmsg = '部分节点未设置基本信息。'
|
if (emptyNode.attrs && emptyNode.attrs.text && emptyNode.attrs.text.text) {
|
errmsg = '节点《' + emptyNode.attrs.text.text + '》未设置基本信息。'
|
}
|
notification.warning({
|
top: 92,
|
message: errmsg,
|
duration: 2
|
})
|
} else if (emptyEdge) {
|
let errmsg = '部分连线未设置基本信息。'
|
|
let edge = this.mkGraph.getCellById(emptyEdge.id)
|
let target = edge.getTargetCell()
|
|
let t_label = ''
|
if (target.attrs && target.attrs.text && target.attrs.text.text) {
|
t_label = target.attrs.text.text
|
}
|
|
let source = edge.getSourceCell()
|
|
let s_label = ''
|
if (source.attrs && source.attrs.text && source.attrs.text.text) {
|
s_label = source.attrs.text.text
|
}
|
|
if (t_label && s_label) {
|
errmsg = '节点《' + s_label + '》与节点《' + t_label + '》间连线未设置基本信息。'
|
}
|
|
notification.warning({
|
top: 92,
|
message: errmsg,
|
duration: 2
|
})
|
} else if (rejectId) {
|
let node = this.mkGraph.getCellById(rejectId)
|
|
let title = ''
|
if (node.attrs && node.attrs.text && node.attrs.text.text) {
|
title = '节点《' + node.attrs.text.text + '》'
|
} else if (node.mkdata) {
|
title = '状态:' + node.mkdata.status + ' - ' + node.mkdata.statusName
|
}
|
|
notification.warning({
|
top: 92,
|
message: title + ' 不可设置两条驳回连线!',
|
duration: 2
|
})
|
} else if (approvalId) {
|
let node = this.mkGraph.getCellById(approvalId)
|
|
let title = ''
|
if (node.attrs && node.attrs.text && node.attrs.text.text) {
|
title = '节点《' + node.attrs.text.text + '》'
|
} else if (node.mkdata) {
|
title = '状态:' + node.mkdata.status + ' - ' + node.mkdata.statusName
|
}
|
|
notification.warning({
|
top: 92,
|
message: title + ' 后的审批分支执行条件重复!',
|
duration: 2
|
})
|
} else if (unvalidId) {
|
let edge = this.mkGraph.getCellById(unvalidId)
|
|
let node = edge.getTargetCell()
|
|
let title = ''
|
if (node.attrs && node.attrs.text && node.attrs.text.text) {
|
title = node.attrs.text.text
|
} else if (node.mkdata) {
|
title = '状态:' + node.mkdata.status + ' - ' + node.mkdata.statusName
|
}
|
|
let source = edge.getSourceCell()
|
|
let s_label = ''
|
if (source.attrs && source.attrs.text && source.attrs.text.text) {
|
s_label = source.attrs.text.text
|
} else if (source.mkdata) {
|
s_label = '状态:' + source.mkdata.status + ' - ' + source.mkdata.statusName
|
}
|
|
notification.warning({
|
top: 92,
|
message: '节点《' + s_label + '》与《' + title + '》间连线未设置审批人!',
|
duration: 2
|
})
|
} else {
|
this.setState({status: _status})
|
}
|
} else {
|
this.setState({status: _status})
|
}
|
}
|
|
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, node, orgs, status, flowname, empty } = this.state
|
|
let style = {...config.style}
|
|
if (config.plot.function === 'show') {
|
if (config.plot.empty === 'hidden' && empty) {
|
style.position = 'absolute'
|
style.width = '100%'
|
style.zIndex = -1
|
}
|
|
return (
|
<div className="custom-x6-plot-box" id={'anchor' + config.uuid} style={style}>
|
{loading ?
|
<div className="loading-mask">
|
<div className="ant-spin-blur"></div>
|
<Spin />
|
</div> : null
|
}
|
<NormalHeader config={config} />
|
<div className={'canvas only-show' + (config.plot.click ? ' switchable' : '')} style={{width: '100%', minHeight: config.plot.height, height: config.plot.height}} id={config.uuid + 'canvas'}>
|
<div id={config.uuid + 'container'} className="mk-container"></div>
|
</div>
|
</div>
|
)
|
}
|
|
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} />
|
<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="放大">
|
<ZoomInOutlined onClick={this.setZoomIn}/>
|
</Tooltip>
|
<Tooltip title="缩小">
|
<ZoomOutOutlined onClick={this.setZoomOut}/>
|
</Tooltip>
|
<Tooltip title="1:1">
|
<OneToOneOutlined onClick={this.setZoomInt}/>
|
</Tooltip>
|
<Tooltip title="后退">
|
<UndoOutlined onClick={this.setback}/>
|
</Tooltip>
|
<Tooltip title="前进">
|
<RedoOutlined onClick={this.setprev}/>
|
</Tooltip>
|
<Tooltip title="清空">
|
<ClearOutlined onClick={this.clearNode}/>
|
</Tooltip>
|
<Tooltip overlayStyle={{maxWidth: 310}} title="快捷键:复制(ctrl+c)、剪切(ctrl+x)、粘贴(ctrl+v)、后退(ctrl+z)、前进(ctrl+shift+z)、删除(backspace 或 delete);双击节点或连线可编辑自定义信息。">
|
<QuestionCircleOutlined />
|
</Tooltip>
|
</div>
|
<div className="flow-name">{flowname}</div>
|
<div className="right-tool">
|
<Switch size="large" checked={status === 10} checkedChildren="启" unCheckedChildren="停" onChange={this.changeStatus} />
|
<Button className="save" onClick={this.save}>保存</Button>
|
<Button className="close" onClick={this.close}>关闭</Button>
|
</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} orgs={orgs} onChange={this.changeProps} onDel={this.delCell}/>}
|
</div>
|
</div>
|
</div>
|
)
|
}
|
}
|
|
export default antvX6Chart
|