bpmn-js 流程图查看设置节点颜色
项目使用的react ant-design-pro版本 5.2.0
bpmn-js版本 6.3.4
注意: 不同版本的bpmn写法不一样,我这个写法是适用6.3.4的版本,较新的版本可能不适用
如果使用vue或其他框架,写法按照你框架的写法, bpmn-js调用的方法还是一样的
效果图:
查看流程图时,展示已审批过的,与当前正在审批的节点, 以不同颜色区分
实现步骤
1. 引入BpmnViewer bpmn-js有提供流程图查看的方法,引入这个就好
import BpmnViewer from 'bpmn-js/lib/Viewer'
2. 获取流程定义文件,渲染流程
interface viewProps { info: any // 获取流程文件的参数 highLightData?: any // 高亮节点 height?: string // 流程图高度 } const ViewBpmn: React.FC<viewProps> = ({ info, highLightData, height = '60vh' }) => { const [bpmnModler, setBpmnModler] = useState<any>(null) const bpmnRef = useRef<any>() const createDiagram = async () => { const xmlstr = await processDetail(info) bpmnModler && bpmnModler.destroy && bpmnModler.destroy() const newBpmn = new BpmnViewer({ container: bpmnRef.current, height, }) const canvas = newBpmn.get('canvas') newBpmn.importXML(xmlstr, (err: string) => { if (err) { message.error(err) } else { canvas.zoom('fit-viewport', 'auto') } }) setBpmnModler(newBpmn) } return ( <Spin spinning={spinLoading} tip="正在加载..."> {info.instanceId ? ( <div className={styles.tip}> <div className={styles.item}> <div className={styles.susitem} /> <span>已审核</span> </div> <div className={styles.item}> <div className={styles.proitem} /> <span>当前审核</span> </div> <div className={styles.item}> <div className={styles.unitem} /> <span>待审核</span> </div> </div> ) : null} <div id="canvas" ref={bpmnRef} className={styles.canvas} /> </Spin> ) } export default ViewBpmn
3. 获取已审批和正在审批的节点与线段id,设置样式 我们这里拿到的数据是这样的,主要是节点与线的id
interface viewProps { info: any highLightData?: any // 高亮节点 height?: string // 流程图高度 } const ViewBpmn: React.FC<viewProps> = ({ info, highLightData, height = '60vh' }) => { const [bpmnModler, setBpmnModler] = useState<any>(null) const bpmnRef = useRef<any>() const createDiagram = async () => { const xmlstr = await processDetail(info) bpmnModler && bpmnModler.destroy && bpmnModler.destroy() const newBpmn = new BpmnViewer({ container: bpmnRef.current, height, }) const canvas = newBpmn.get('canvas') newBpmn.importXML(xmlstr, (err: string) => { if (err) { message.error(err) } else { canvas.zoom('fit-viewport', 'auto') if (highLightData) { const successIds = highLightData.highLine.concat(highLightData.highPoint) const procesingIds = highLightData.waitingToDo setNodeColor(successIds, newBpmn, 'nodeSuccess') setNodeColor(procesingIds, newBpmn, 'nodeProcing') } } }) setBpmnModler(newBpmn) } return ( <Spin spinning={spinLoading} tip="正在加载..."> {info.instanceId ? ( <div className={styles.tip}> <div className={styles.item}> <div className={styles.susitem} /> <span>已审核</span> </div> <div className={styles.item}> <div className={styles.proitem} /> <span>当前审核</span> </div> <div className={styles.item}> <div className={styles.unitem} /> <span>待审核</span> </div> </div> ) : null} <div id="canvas" ref={bpmnRef} className={styles.canvas} /> </Spin> ) } export default ViewBpmn
4. 设置不同颜色的样式,通过增加class类名来控制
// 设置节点颜色 const setNodeColor = (ids: any, newBpmn: any, colorClass: string) => { const elementRegistry = newBpmn.get('elementRegistry') ids.forEach((item: any) => { if (elementRegistry._elements[item]) { const element = elementRegistry._elements[item].gfx element.classList.add(colorClass) } // console.log(elementRegistry, element) }) }
5. less样式文件
.canvas { width: 100%; height: 100%; :global(.nodeSuccess .djs-visual > :nth-child(1)) { stroke: #52c41a !important; stroke-width: 3px; } :global(.nodeProcing .djs-visual > :nth-child(1)) { stroke: #1890ff !important; stroke-width: 3px; } }
package.json 版本
"dependencies": { "@types/codemirror": "^5.60.5", "@umijs/route-utils": "^2.0.0", "antd": "^4.19.0", "bpmn-js": "^6.3.4", "bpmn-js-bpmnlint": "^0.15.0", "bpmn-js-properties-panel": "^0.33.2", "bpmn-moddle": "^6.0.0", "bpmnlint": "^6.4.0", "braft-editor": "^2.3.9", "classnames": "^2.3.0", "codemirror": "^5.59.2", "echarts": "^5.3.2", "file-saver": "^2.0.5", "js-cookie": "^3.0.1", "lodash": "^4.17.0", "moment": "^2.29.0", "omit.js": "^2.0.2", "rc-menu": "^9.1.0", "rc-util": "^5.16.0", "react": "^17.0.0", "react-codemirror2": "^7.2.1", "react-dev-inspector": "^1.7.0", "react-dom": "^17.0.0", "react-helmet-async": "^1.2.0", "umi": "^3.5.0", "umi-plugin-keep-alive": "^0.0.1-beta.30" }, "devDependencies": { "@ant-design/pro-cli": "^2.1.0", "@playwright/test": "^1.17.0", "@types/express": "^4.17.0", "@types/history": "^4.7.0", "@types/jest": "^26.0.0", "@types/lodash": "^4.14.0", "@types/react": "^17.0.0", "@types/react-dom": "^17.0.0", "@types/react-helmet": "^6.1.0", "@umijs/fabric": "^2.8.0", "@umijs/openapi": "^1.3.0", "@umijs/plugin-blocks": "^2.2.0", "@umijs/plugin-esbuild": "^1.4.0", "@umijs/plugin-openapi": "^1.3.0", "@umijs/preset-ant-design-pro": "^1.3.0", "@umijs/preset-dumi": "^1.1.0", "@umijs/preset-react": "^2.1.0", "cross-env": "^7.0.0", "cross-port-killer": "^1.3.0", "detect-installer": "^1.0.0", "eslint": "^7.32.0", "gh-pages": "^3.2.0", "husky": "^7.0.4", "jsdom-global": "^3.0.0", "lint-staged": "^10.0.0", "mockjs": "^1.1.0", "prettier": "^2.5.0", "stylelint": "^13.0.0", "swagger-ui-react": "^3.52.0", "typescript": "^4.5.0", "umi-serve": "^1.9.10" },
完整代码
import React, { useState, useEffect, useRef } from 'react' import { Spin, message } from 'antd' import { processDetail, processApprovalDetail } from '@/services' import BpmnViewer from 'bpmn-js/lib/Viewer' import styles from './index.less' interface viewProps { info: any highLightData?: any height?: string } const ViewBpmn: React.FC<viewProps> = ({ info, highLightData, height = '60vh' }) => { const [spinLoading, setSpinLoading] = useState<boolean>(true) const [bpmnModler, setBpmnModler] = useState<any>(null) const bpmnRef = useRef<any>() // 设置节点颜色 const setNodeColor = (ids: any, newBpmn: any, colorClass: string) => { const elementRegistry = newBpmn.get('elementRegistry') ids.forEach((item: any) => { if (elementRegistry._elements[item]) { const element = elementRegistry._elements[item].gfx element.classList.add(colorClass) } // console.log(elementRegistry, element) }) } const createDiagram = (xmlstr: string) => { bpmnModler && bpmnModler.destroy && bpmnModler.destroy() const newBpmn = new BpmnViewer({ container: bpmnRef.current, height, }) const canvas = newBpmn.get('canvas') newBpmn.importXML(xmlstr, (err: string) => { if (err) { message.error(err) } else { canvas.zoom('fit-viewport', 'auto') if (highLightData) { const successIds = highLightData.highLine.concat(highLightData.highPoint) const procesingIds = highLightData.waitingToDo setNodeColor(successIds, newBpmn, 'nodeSuccess') setNodeColor(procesingIds, newBpmn, 'nodeProcing') } } }) setBpmnModler(newBpmn) } const getProcessDetail = async () => { let diagramXML if (info.instanceId) { // 如果是审批详情 diagramXML = await processApprovalDetail(info.instanceId) } else { diagramXML = await processDetail(info) } createDiagram(diagramXML) setSpinLoading(false) } useEffect(() => { getProcessDetail() }, []) return ( <Spin spinning={spinLoading} tip="正在加载..."> {info.instanceId ? ( <div className={styles.tip}> <div className={styles.item}> <div className={styles.susitem} /> <span>已审核</span> </div> <div className={styles.item}> <div className={styles.proitem} /> <span>当前审核</span> </div> <div className={styles.item}> <div className={styles.unitem} /> <span>待审核</span> </div> </div> ) : null} <div id="canvas" ref={bpmnRef} className={styles.canvas} /> </Spin> ) } export default ViewBpmn