UNPKG

snakerflow-designer-vue-plus

Version:

首先感谢snakerflow-designer-vue的原作者,提供了一个如此好用的工具。本人在此基础上,添加了子流程相关的节点,并将折线改为横线,横线文字说明可跟着横线的位置自动跟随移动。

371 lines (366 loc) 12 kB
/** * 节点样式处理方法 * @param {*}} _this * @param {*} style * @returns */ export const nodeStyleHandle = (_this, style) => { if (_this.properties.state === 'active') { style.stroke = '#ff0000' } else if (_this.properties.state === 'history') { style.stroke = '#00ff00' } return style } /** * 边样式处理方法 * @param {*}} _this * @param {*} style * @returns */ export const edgeStyleHandle = (_this, style) => { if (_this.properties.state === 'active') { style.stroke = '#ff0000' } else if (_this.properties.state === 'history') { style.stroke = '#00ff00' } return style } /** * 解析xml成Dom对象 * @param {} xml * @returns */ export const parseXml2Dom = (xml) => { let xmlDoc = null if (window.DOMParser) { const parser = new DOMParser() xmlDoc = parser.parseFromString(xml, 'text/xml') } else { // Internet Explorer // eslint-disable-next-line no-undef xmlDoc = new ActiveXObject('Microsoft.XMLDOM') xmlDoc.async = false xmlDoc.loadXML(xml) } return xmlDoc } // 节点标签 const NODE_NAMES = ['start', 'task', 'decision', 'end', 'custom', 'join', 'fork', 'subprocess'] // 流程节点属性 const PROCESS_ATTR_KEYS = ['name', 'displayName', 'instanceUrl', 'expireTime', 'instanceNoClass'] // 节点属性 const NODE_ATTR_KEYS = ['name', 'displayName', 'form', 'assignee', 'assignmentHandler', 'taskType', 'performType', 'preInterceptors', 'postInterceptors', 'reminderTime', 'reminderRepeat', 'expireTime', 'autoExecute', 'callback', 'expr', 'handleClass', 'clazz', 'methodName', 'args', 'layout', 'g', 'processName'] // 变迁节点属性 const TRANSITION_ATTR_KEYS = ['name', 'displayName', 'to', 'expr', 'g', 'offset', 'endPoint', 'startPoint'] /** * 将snaker的定义文件转成LogicFlow支持的数据格式 * @param {*} xml * @returns */ export const snakerXml2LogicFlowJson = (xml) => { const graphData = { nodes: [], edges: [] } const xmlDoc = parseXml2Dom(xml) const processDom = xmlDoc.getElementsByTagName('process') if (!processDom.length) { return graphData } let value = null // 解析process属性 PROCESS_ATTR_KEYS.forEach(key => { value = processDom[0].getAttribute(key) if (value) { graphData[key] = value } }) let nodeEles = null let node = null let lfNode = {} // 解析节点 NODE_NAMES.forEach(key => { nodeEles = processDom[0].getElementsByTagName(key) if (nodeEles.length) { for (var i = 0, len = nodeEles.length; i < len; i++) { node = nodeEles[i] lfNode = { type: 'snaker:' + key, properties: {} } // 处理节点 NODE_ATTR_KEYS.forEach(attrKey => { value = node.getAttribute(attrKey) if (value) { if (attrKey === 'name') { lfNode.id = value } else if (attrKey === 'layout') { const attr = value.split(',') if (attr.length === 4) { lfNode.x = attr[0] lfNode.y = attr[1] lfNode.properties.width = attr[2] <= 0 ? 100 : attr[2] lfNode.properties.height = attr[3] <= 0 ? 50 : attr[3] } } else if (attrKey === 'displayName') { lfNode.text = value } else { lfNode.properties[attrKey] = value } } }) // 处理扩展属性 if (key === 'task') { const fieldEles = node.getElementsByTagName('field') if (fieldEles.length) { const fieldEle = fieldEles[0] const field = { } const attr = fieldEle.getElementsByTagName('attr') for (var iii = 0; iii < attr.length; iii++) { field[attr[iii].getAttribute('name')] = attr[iii].getAttribute('value') } lfNode.properties.field = field } } graphData.nodes.push(lfNode) // 处理边 let transitionEles = null let transitionEle = null let edge = {} if (key !== 'end') { transitionEles = node.getElementsByTagName('transition') for (var j = 0, lenn = transitionEles.length; j < lenn; j++) { transitionEle = transitionEles[j] edge = {} edge.id = transitionEle.getAttribute('name') edge.type = 'snaker:transition' edge.sourceNodeId = lfNode.id edge.targetNodeId = transitionEle.getAttribute('to') edge.text = { value: transitionEle.getAttribute('displayName') ? transitionEle.getAttribute('displayName') : '' } edge.properties = {} const expr = transitionEle.getAttribute('expr') if (expr) { edge.properties.expr = expr } // 线的起点 const startPoint = transitionEle.getAttribute('startPoint') if (startPoint) { const pointArr = startPoint.split(',') if (pointArr.length === 2) { edge.startPoint = { x: Number(pointArr[0]), y: Number(pointArr[1]) } } } // 线的终点 const endPoint = transitionEle.getAttribute('endPoint') if (endPoint) { const pointArr = endPoint.split(',') if (pointArr.length === 2) { edge.endPoint = { x: Number(pointArr[0]), y: Number(pointArr[1]) } } } // 线的转折点 const g = transitionEle.getAttribute('g') if (g) { const points = g.split(';') if (points.length >= 1) { edge.pointsList = [] points.forEach((item) => { const pointArr = item.split(',') edge.pointsList.push({ x: Number(pointArr[0]), y: Number(pointArr[1]) }) }) } } graphData.edges.push(edge) } } } } }) return graphData } /** * 将LogicFlow的数据转成snaker的定义文件 * @param {*} data(...processInfo,nodes,edges) * @returns */ export const logicFlowJsonToSnakerXml = (data) => { let xml = '' // data的数据由流程定义文件信息+logicFlow数据构成 // 先构建成流程对象 const processObj = { name: data.name, // 流程定义名称 displayName: data.displayName, // 流程定义显示名称 instanceUrl: data.instanceUrl, // 实例启动Url expireTime: data.expireTime, // 期望完成时间 instanceNoClass: data.instanceNoClass // 实例编号生成类 } /** * 获取开始节点 * @returns */ const getStartNode = () => { return data.nodes.find((node) => { return node.type === 'snaker:start' }) } /** * 获取当前节点的所有下一个节点集合 * @param {*} id 当前节点名称 * @returns */ const getNextNodes = (id) => { return data.edges.filter(edge => { return edge.sourceNodeId === id }).map(edge => { return data.nodes.find((node) => { return node.id === edge.targetNodeId }) }) } /** * 获取节点所有输出边 * @param {*} id * @returns */ const getTransitions = (id) => { return data.edges.filter((edge) => { return edge.sourceNodeId === id }).map(edge => { return { name: edge.id, displayName: (edge.text instanceof String || edge.text === undefined) ? edge.text : edge.text.value, to: edge.targetNodeId, // 目地节点id expr: edge.properties.expr, // 表达式 g: typeof edge.pointsList === 'undefined' || edge.pointsList == null ? '' : edge.pointsList.map(point => { // 转换点集合 return point.x + ',' + point.y }).join(';'), offset: '0,0', startPoint: edge.startPoint.x + ',' + edge.startPoint.y, endPoint: edge.endPoint.x + ',' + edge.endPoint.y } }) } /** * 构建节点属性 * @param {}} node * @returns */ const buildNode = (node) => { let field if (node.properties.field && Object.keys(node.properties.field).length) { field = { name: 'ext', displayName: '扩展属性', attr: Object.keys(node.properties.field).map(key => { return { name: key, value: node.properties.field[key] } }) } } return { name: node.id, displayName: (node.text instanceof String || node.text === undefined) ? node.text : node.text.value, layout: node.x + ',' + node.y + ',' + (node.properties.width ? node.properties.width : '130') + ',' + (node.properties.height ? node.properties.height : '50'), ...node.properties, transition: getTransitions(node.id), field } } /** * 特殊字符转义 * @param {*} text * @returns */ const textEncode = (text) => { text = text.replace(/&/g, '&amp;') .replace(/"/g, '&quot;') .replace(/</g, '&lt;') .replace(/>/g, '&gt;') return text } /** * 递归构建节点属性 * @param {} node */ const recursionBuildNode = (node) => { const nodeName = node.type.replace('snaker:', '') if (!processObj[nodeName + '_' + node.id]) { processObj[nodeName + '_' + node.id] = buildNode(node) const nextNodes = getNextNodes(node.id) nextNodes.forEach(nextNode => { recursionBuildNode(nextNode) }) } } const startNode = getStartNode() if (!startNode) { // 开始节点不存在,xml不合法 return '' } recursionBuildNode(startNode) xml = '<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n' xml += '<process ' Object.keys(processObj).forEach(key => { const value = processObj[key] if (PROCESS_ATTR_KEYS.includes(key) && value) { xml += ' ' + key + '=' + '"' + textEncode(value) + '"' } }) xml += '>\n' // 生成节点xml Object.keys(processObj).forEach(key => { const value = processObj[key] const nodeName = key.split('_')[0] if (NODE_NAMES.includes(nodeName)) { xml += '\t<' + nodeName // 构造属性 Object.keys(value).forEach(nodeAttrKey => { if (NODE_ATTR_KEYS.includes(nodeAttrKey) && value[nodeAttrKey]) { xml += ' ' + nodeAttrKey + '=' + '"' + textEncode(value[nodeAttrKey]) + '"' } }) xml += '>\n\t' // 构建transition if (value.transition) { value.transition.forEach(tran => { xml += '\t<transition' // transition属性 Object.keys(tran).forEach(tranAttrKey => { if (TRANSITION_ATTR_KEYS.includes(tranAttrKey) && tran[tranAttrKey]) { xml += ' ' + tranAttrKey + '=' + '"' + textEncode(tran[tranAttrKey]) + '"' } }) xml += '>' xml += '</transition>\n' }) } if (value.field && (nodeName === 'task')) { xml += '\t<field name="' + value.field.name + '" displayName="' + value.field.displayName + '">\n' value.field.attr.forEach(attrItem => { xml += '\t\t<attr name="' + attrItem.name + '" value="' + textEncode(attrItem.value) + '"></attr>\n' }) xml += '\t</field>\n' } xml += '\t</' + nodeName + '>\n' } }) xml += '</process>' return xml }