king
2025-05-13 7b8cfa542fb5f8fa699b114b2a71a2f6a005483a
src/tabviews/custom/components/chart/antv-X6/index.jsx
@@ -1,8 +1,8 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Spin, Tooltip, message, Modal, notification, Switch } from 'antd'
import { UndoOutlined, RedoOutlined, VerticalAlignTopOutlined, VerticalAlignBottomOutlined, SaveOutlined, ZoomInOutlined, ZoomOutOutlined, OneToOneOutlined, QuestionCircleOutlined, ClearOutlined } from '@ant-design/icons'
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'
@@ -138,8 +138,8 @@
  'mk-rect',
  {
    inherit: 'rect',
    width: 66,
    height: 40,
    width: 100,
    height: 32,
    attrs: {
      body: {
        strokeWidth: 1,
@@ -168,8 +168,8 @@
  'mk-polygon',
  {
    inherit: 'polygon',
    width: 66,
    height: 40,
    width: 100,
    height: 32,
    attrs: {
      body: {
        strokeWidth: 1,
@@ -198,8 +198,8 @@
  'mk-paral',
  {
    inherit: 'polygon',
    width: 66,
    height: 40,
    width: 100,
    height: 32,
    attrs: {
      body: {
        strokeWidth: 1,
@@ -256,8 +256,8 @@
  'mk-ellipse',
  {
    inherit: 'ellipse',
    width: 66,
    height: 40,
    width: 100,
    height: 32,
    attrs: {
      body: {
        strokeWidth: 1,
@@ -318,8 +318,8 @@
  'mk-text',
  {
    inherit: 'text-block',
    width: 66,
    height: 40,
    width: 100,
    height: 50,
    text: '文本域',
    attrs: {
      body: {
@@ -344,7 +344,7 @@
  yellow_green: '#a0d911',
  green: '#52c41a',
  cyan: '#13c2c2',
  blue_purple: '#2f54eb',
  blue_purple: '#3860f4',
  purple: '#722ed1',
  magenta: '#eb2f96',
  grass_green: '#aeb303',
@@ -388,7 +388,8 @@
    status: 0,
    flowname: '',
    flowcode: '',
    orgs: []
    orgs: [],
    empty: true
  }
  selectNode = null
@@ -526,6 +527,7 @@
                    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]
                  }
@@ -553,6 +555,7 @@
                    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]
                  }
@@ -590,8 +593,8 @@
    const { BID } = this.state
    if (!BID) {
      this.cells = []
      if (!is(fromJS(this.data), fromJS([]))) {
        this.cells = []
        this.handleData()
      }
      this.setState({empty: true})
@@ -624,7 +627,8 @@
        status: item.status || 0,
        flowname: item.works_flow_name || '',
        flowcode: item.works_flow_code || '',
        loading: false
        loading: false,
        empty: cells.length === 0
      })
      if (result.message) {
@@ -896,18 +900,18 @@
    const stencil = new Stencil({
      title: '流程图',
      target: graph,
      stencilGraphWidth: 180,
      stencilGraphWidth: 230,
      stencilGraphHeight: 180,
      groups: [
        {
          title: '通用节点',
          name: 'group1',
          graphHeight: 240
          graphHeight: 150
        },
        {
          title: '自定义',
          name: 'group2',
          graphHeight: 150,
          graphHeight: 300,
          layoutOptions: {
            rowHeight: 70
          }
@@ -915,7 +919,7 @@
      ],
      layoutOptions: {
        columns: 2,
        columnWidth: 80,
        columnWidth: 110,
        rowHeight: 55
      }
    })
@@ -989,11 +993,23 @@
    })
    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
      
@@ -1003,8 +1019,31 @@
      if (!isNew) return
      let target = edge.getTargetCell()
      let mkdata = target ? target.prop('mkdata') : null
      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})
      }
@@ -1016,10 +1055,10 @@
      mkdata: {status: 0, statusName: '未提交'},
      attrs: {
        body: {
          rx: 20,
          ry: 26,
          fill: '#52c41a',
          stroke: '#52c41a'
          rx: 0,
          ry: 0,
          fill: '#000000',
          stroke: '#000000'
        },
        text: {
          fill: '#ffffff',
@@ -1027,83 +1066,34 @@
        }
      }
    })
    const r2 = graph.createNode({
      shape: 'mk-rect',
      attrs: {
        body: {
          rx: 20,
          ry: 26,
          fill: '#e6f4ff',
          stroke: '#1890ff'
        },
        text: {
          text: '过程'
        }
      }
    })
    const r3 = graph.createNode({
      shape: 'mk-rect',
      attrs: {
        body: {
          rx: 6,
          ry: 6,
          fill: '#fff7e6',
          stroke: '#fa8c16'
        },
        text: {
          text: '可选过程'
        }
      }
    })
    const r4 = graph.createNode({
      shape: 'mk-rect',
      attrs: {
        body: {
          rx: 0,
          ry: 0,
          fill: '#fff7e6',
          stroke: '#fa8c16'
          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: '#f9f0ff',
          stroke: '#722ed1'
          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: '#e6fffb',
          stroke: '#13c2c2'
        },
        text: {
          text: '数据'
        }
      }
    })
    const r7 = graph.createNode({
      shape: 'mk-circle',
      attrs: {
        body: {
          fill: '#e6f4ff',
          stroke: '#1890ff'
        },
        text: {
          text: '连接'
        }
      }
    })
@@ -1114,10 +1104,10 @@
      mkdata: {status: 888, statusName: '已完成'},
      attrs: {
        body: {
          rx: 20,
          ry: 26,
          fill: '#fa8c16',
          stroke: '#fa8c16'
          rx: 0,
          ry: 0,
          fill: '#000000',
          stroke: '#000000'
        },
        text: {
          fill: '#ffffff',
@@ -1126,7 +1116,65 @@
      }
    })
    
    stencil.load([r1, r2, r3, r4, r5, r6, r7, r8], 'group1')
    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',
@@ -1141,7 +1189,7 @@
      shape: 'mk-text'
    })
    
    stencil.load([p1, p2, p3], 'group2')
    stencil.load([r2, r3, r6, r7, p1, p2, p3], 'group2')
    if (this.cells.length > 0) {
      let cells = []
@@ -1260,7 +1308,7 @@
    const stencil = new Stencil({
      title: '流程图',
      target: graph,
      stencilGraphWidth: 180,
      stencilGraphWidth: 230,
      stencilGraphHeight: 180,
      groups: [
        {
@@ -1286,7 +1334,7 @@
      ],
      layoutOptions: {
        columns: 2,
        columnWidth: 80,
        columnWidth: 110,
        rowHeight: 55
      }
    })
@@ -1536,6 +1584,26 @@
    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
@@ -1550,10 +1618,51 @@
    let _status = status
    if (plot.subtype === 'xflow' && status === 10) {
      let start_num = nodes.cells.filter(cell => cell.mknode === 'start').length
      let end_num = nodes.cells.filter(cell => cell.mknode === 'end').length
      let start_num = 0
      let end_num = 0
      let unvalid = false
      let map = new Map()
      let appMap = new Map()
      
      if (start_num !== 1 || end_num !== 1) {
      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
      }
    }
@@ -1595,6 +1704,7 @@
                  loading: false,
                  status: _status
                })
                this.cells = nodes.cells
              } else {
                notification.error({
                  top: 92,
@@ -1616,6 +1726,7 @@
              loading: false,
              status: _status
            })
            this.cells = nodes.cells
          }
        } else {
          notification.error({
@@ -1792,6 +1903,16 @@
    }
  }
  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
@@ -1826,6 +1947,16 @@
          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') {
@@ -1968,8 +2099,62 @@
    
    if (plot.subtype === 'xflow' && _status === 10) {
      let nodes = this.mkGraph.toJSON()
      let start_num = nodes.cells.filter(cell => cell.mknode === 'start').length
      let end_num = nodes.cells.filter(cell => cell.mknode === 'end').length
      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({
@@ -1989,10 +2174,103 @@
          message: '请添加结束节点!',
          duration: 2
        })
      } else if (end_num > 1) {
      // } 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: '结束节点不可添加多个!',
          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 {
@@ -2018,12 +2296,12 @@
  }
  render() {
    const { loading, config, node, orgs, status, flowname } = this.state
    const { loading, config, node, orgs, status, flowname, empty } = this.state
    let style = {...config.style}
    if (config.plot.function === 'show') {
      if (config.plot.empty === 'hidden' && this.cells.length === 0) {
      if (config.plot.empty === 'hidden' && empty) {
        style.position = 'absolute'
        style.width = '100%'
        style.zIndex = -1
@@ -2080,18 +2358,15 @@
            <Tooltip title="清空">
              <ClearOutlined onClick={this.clearNode}/>
            </Tooltip>
            <Tooltip overlayStyle={{maxWidth: 260}} title="快捷键:复制(ctrl+c)、剪切(ctrl+x)、粘贴(ctrl+v)、后退(ctrl+z)、前进(ctrl+shift+z)、删除(backspace 或 delete)">
            <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">
            <Tooltip title="启用/停用">
              <Switch size="small" style={{marginRight: '10px'}} checked={status === 10} onChange={this.changeStatus} />
            </Tooltip>
            <Tooltip title="保存">
              <SaveOutlined style={{marginRight: '10px'}} onClick={this.save}/>
            </Tooltip>
            <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'}>
@@ -2099,7 +2374,7 @@
          <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}/>}
            {!node ? <div className="empty">未选中</div> : <NodeUpdate node={node} orgs={orgs} onChange={this.changeProps} onDel={this.delCell}/>}
          </div>
        </div>
      </div>