snakerflow-designer-vue-plus
Version:
首先感谢snakerflow-designer-vue的原作者,提供了一个如此好用的工具。本人在此基础上,添加了子流程相关的节点,并将折线改为横线,横线文字说明可跟着横线的位置自动跟随移动。
371 lines (366 loc) • 12 kB
JavaScript
/**
* 节点样式处理方法
* @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, '&')
.replace(/"/g, '"')
.replace(/</g, '<')
.replace(/>/g, '>')
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
}