bpmn-js+vue+springboot的简单项目

这里写自定义目录标题


本文主要参考 link中自己遇到的一些问题。

与后台服务器的交互

  1. 将getXmlUrl()中url修改为后台api接口
getXmlUrl () {
	const that = this
	return new Promise(resolve => {
		setTimeout(() => {
			//const url = ''
			//const url = 'https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/mock1.bpmn'
			const url = '/manage/'+that.deploymentId+'/init'
			resolve(url)
		}, 1000)
	})
}
  1. 后台代码
    将文件流读取到response中,这里有几点注意的:
    1. 读取的文件来源于数据库中camunda的act_ge_bytearray表
    2. mybatis不支持blob类,所以要写一个handle类
@GetMapping("/{deploymentId}/init")
public void init(@PathVariable("deploymentId") String deploymentId,
                HttpServletResponse response) throws IOException {

   ByteArrayPO instance = camundaService.getInstance(deploymentId);
   // 设置响应
   response.setContentType(MediaType.MULTIPART_FORM_DATA_VALUE);
   response.setHeader("Content-Disposition",
           "attachment;filename="+ URLEncoder.encode(instance.getName_(), "utf-8"));

   try (InputStream is = instance.getBytes_().getBinaryStream();
        OutputStream os = response.getOutputStream()) {
       // 将blob写入响应流中
       byte[] buff = new byte[1024];
       int len = -1;
       while ((len = is.read(buff)) != -1) {
           os.write(buff, 0, len);
       }
   } catch (Exception e) {
   }
}

用于获得blob类的handler,代码很简单。

public class BlobTypeHandler extends BaseTypeHandler<Blob> {
   @Override
   public void setNonNullParameter(PreparedStatement ps, int i, Blob blob, JdbcType jdbcType) throws SQLException {
       ps.setBlob(i, blob);
   }
   @Override
   public Blob getNullableResult(ResultSet resultSet, String s) throws SQLException {
       return resultSet.getBlob(s);
   }
   @Override
   public Blob getNullableResult(ResultSet resultSet, int i) throws SQLException {
       return resultSet.getBlob(i);
   }
   @Override
   public Blob getNullableResult(CallableStatement cs, int i) throws SQLException {
       return cs.getBlob(i);
   }
}
  1. 将开发完成的bpmn上传至服务器
    bpmnModeler的saveSVG和saveXML函数的回调中可以获得对应的string对象,将其上传即可。其中svg为图片,xml为流程源码。
upload() {
   const that = this
   this.bpmnModeler.saveSVG(function(err, svg) {
       if (!err) {
           that.bpmnModeler.saveXML({format: true}, function(err2, xml) {
               if (!err2) {
                   axios.post('/manage/camunda/deploy', {
                       svgStr: svg,
                       xmlStr: xml
                   }).then((res) => {
                       console.log(res)
                   })
               }
           })
       }
   })
}
  1. 服务器部署
@PostMapping("/deploy")
public String deploy(@RequestBody Map<String, String> map) {
   String str = map.get("xmlStr");

   // 正则表达式
   String id = "", name = "";
   // id和name的获得方法,我是使用了很蹩脚的正则表达式

   RepositoryService repositoryService = processEngine.getRepositoryService();

   // 部署
   Deployment deployment = repositoryService.createDeployment()
           .name(name)
           .addString(id + ".bpmn20.xml", str).deploy();

   // 保存图片到服务器
   String svg = map.get("svgStr");
   try {
      CamundaUtil.savePNG(svg, "E:\\out.png");
   } catch (Exception e ) {
   }

   // 部署成功
   System.out.println("部署成功");
   System.out.println("部署ID:"+deployment.getId());
   System.out.println("部署名称:"+deployment.getName());
   return "success";
}

保存图片到指定的路径

public static void savePNG(String svg, String filePath) throws IOException, TranscoderException {
    // 输出
    File file = new File(filePath);
    FileOutputStream fos = new FileOutputStream(file);
    TranscoderOutput output = new TranscoderOutput(fos);
    // 输入
    ByteArrayInputStream bis = new ByteArrayInputStream(svg.getBytes(StandardCharsets.UTF_8));
    TranscoderInput input = new TranscoderInput(bis);

    PNGTranscoder transcoder = new PNGTranscoder();

    // Do the transformation
    transcoder.transcode(input, output);

    fos.flush();
    bis.close();
    fos.close();
}

自定义Palette图标

这里我主要用的了两种方法

  1. 修改node_module中的源码
    palette和contextPad中的图标实际上就是字体文件。
    content中就是字体
    利用字体网站开发自己的一套字体文件,修改node_modules\bpmn-js\dist\assets\bpmn-font\css路径下的bpmn-embedded.css
    源码中利用base64加载了对应的字体文件,只需把自己的字体文件用base64转码后替换即可
@font-face {
  font-family: 'bpmn';
  /** 替换xxx即可 */
  src: url('data:application/octet-stream;base64,xxx') format('woff'),
       url('data:application/octet-stream;base64,xxx') format('truetype');
}

简单的替换
2. 重写Paletteprovider
vue项目中创建路径

| -- palette
    |-- CustomPaletteProvider.js
    |-- CustomPalette.js
    |-- index.js

node_modules\bpmn-js\lib\features\palette\PaletteProvider.js内代码复制到CustomPaletteProvider.js;
node_modules\diagram-js\lib\features\palette\Palette.js内代码复制到CustomPalette.js
index.js代码

import customPalette from './CustomPalette'
import PaletteProvider from './CustomPaletteProvider'
// 除了引进的模块的名字可以修改,其他的不建议修改,会报错
export default {
  __depends__: [
    {
      __init__: ['customPalette'],
      customPalette: ['type', customPalette]
    }
  ], // 依赖于 customPalette 这个模块
  __init__: ['customPaletteProvider'], // 调用 customPaletteProvider 来初始化
  customPaletteProvider: ['type', PaletteProvider]
}

接下来开始修改CustomPalette.js

// 修改注入需要的数据
Palette.$inject = [
  'eventBus',
  'canvas',
  // 自定义部分
  'elementFactory',
  'create',
  'config.paletteContainer',
  'config.paletteEntries'
]
export default function Palette (
  eventBus,
  canvas,
  // 数据赋值
  elementFactory,
  create,
  paletteContainer,
  paletteEntries
) {
  this._eventBus = eventBus
  this._canvas = canvas
  // 新增赋值
  this._entries = paletteEntries // 传入的工具栏数据
  this._paletteContainer = paletteContainer // 传入的工具栏容器
  this._elementFactory = elementFactory
  this._create = create
// 实现拖拽功能
Palette.prototype.trigger = function (action, event, autoActivate) {
  var entries = this._entries
  var entry
  var handler
  var originalEvent
  var button = event.delegateTarget || event.target
  var elementFactory = this._elementFactory,
    create = this._create

  if (!button) {
    return event.preventDefault()
  }

  entry = entries[domAttr(button, 'data-action')]

  // when user clicks on the palette and not on an action
  if (!entry) {
    return
  }

  handler = entry.action

  originalEvent = event.originalEvent || event
  // simple action (via callback function)
  if (isFunction(handler)) {
    if (action === 'click') {
      handler(originalEvent, autoActivate, elementFactory, create)
    }
  } else {
    if (handler[action]) {
      handler[action](originalEvent, autoActivate, elementFactory, create)
    }
  }

  // silence other actions
  event.preventDefault()
}

接下来修改paletteProvider.js

import { assign } from 'min-dash'

PaletteProvider.$inject = ['config.paletteEntries', 'customPalette', 'lassoTool', 'translate']

export default function PaletteProvider (paletteEntries, customPalette, lassoTool, translate) {
  this._entries = paletteEntries
  // 根据需求增加需要注入的工具类
  this._lassoTool = lassoTool
  this._translate = translate

  customPalette.registerProvider(this)
}

PaletteProvider.prototype.getPaletteEntries = function (element) {
  var lassoTool = this._lassoTool
  var translate = this._translate

  // tool图标修改请在此处进行
  // 修改参数请参考
  // node_modules\bpmn-js\lib\features\palette\PaletteProvider.js的getPaletteEntries()
  return assign(this._entries, {
    'lasso-tool': {
      group: 'tools',
      className: 'bpmn-icon-lasso-tool-custom',
      title: translate('Activate the lasso tool'),
      imageUrl: require('../img/lasso.png'),
      action: {
        click: function(event) {
          lassoTool.activateSelection(event);
        }
      }
    }
  })
}

再增加一个paletteEntries.js即可

import { is } from 'bpmn-js/lib/util/ModelUtil'
import { assign } from 'min-dash'
import {
  append as svgAppend,
  attr as svgAttr,
  create as svgCreate,
  remove as svgRemove
} from 'tiny-svg'

const TASK_BORDER_RADIUS = 2

export default {
  'create.start-event': createAction(
    'bpmn:StartEvent',
    'event',
    'bpmn-icon-start-event-none',
    'Create StartEvent',
    '',
    drawShape
  ),
  'create.intermediate-event': createAction(
    'bpmn:IntermediateThrowEvent',
    'event',
    'bpmn-icon-intermediate-event-none-custom',
    'Create Intermediate/Boundary Event',
    require('../img/IntermediateBoundaryEvent.png'),
    drawShape
  ),
  'create.task': createAction(
    'bpmn:Task',
    'activity',
    'bpmn-icon-task-custom',// 类名应该在原类名后加上后缀保证其不使用原生的icon,但保留其他属性
    '创建任务',
    require('../img/task.png'),// 在此处可以实现图片
    drawShape
  ),
  'create.data-object': createAction(
    'bpmn:DataObjectReference',
    'data-object',
    'bpmn-icon-data-object-custom',
    'Create DataObjectReference',
    require('../img/file.png'),
    drawShape
  )
}

function createAction (type, group, className, title, imageUrl = '', drawShape) {

  function createListener (event, autoActivate, elementFactory, create) {
    var shape = elementFactory.createShape(assign({ type: type }));

    create.start(event, shape);
  }

  const config = {
    type,
    group: group,
    className: className,
    title: title,
    drawShape: drawShape,
    action: {
      dragstart: createListener,
      click: createListener
    }
  }
  if (imageUrl) {
    assign(config, {
      imageUrl
    }
    )
  }
  if (drawShape) {
    assign(config, {
      drawShape
    }
    )
  }

  return config
}

function drawShape (parentNode, element, bpmnRenderer) {
  const shape = bpmnRenderer.drawShape(parentNode, element)
  const suitable = element.businessObject.suitable
  let color = '#52B415'
  if (suitable) {
    if (suitable > 50) {
      color = 'green'
    }
    if (suitable === 50) {
      color = 'yellow'
    }
    if (suitable < 50) {
      color = 'red'
    }
  }
  if (is(element, 'bpmn:Task')) {
    const height = 80
    const width = 100
    element.width = width
    element.height = height
    const rect = drawRect(parentNode, width, height, TASK_BORDER_RADIUS, color)
    prependTo(rect, parentNode)
    svgRemove(shape)
    return shape
  }

  const rect = drawRect(parentNode, 30, 20, TASK_BORDER_RADIUS, color)

  svgAttr(rect, {
    transform: 'translate(-20, -10)'
  })

  return shape
}

// helpers //

// copied from https://github.com/bpmn-io/bpmn-js/blob/master/lib/draw/BpmnRenderer.js
function drawRect (parentNode, width, height, borderRadius, strokeColor) {
  const rect = svgCreate('rect')

  svgAttr(rect, {
    width: width,
    height: height,
    rx: borderRadius,
    ry: borderRadius,
    stroke: strokeColor || '#000',
    strokeWidth: 2,
    fill: '#fff'
  })

  svgAppend(parentNode, rect)

  return rect
}

// copied from https://github.com/bpmn-io/diagram-js/blob/master/lib/core/GraphicsFactory.js
function prependTo (newNode, parentNode, siblingNode) {
  parentNode.insertBefore(newNode, siblingNode || parentNode.firstChild)
}

效果演示

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值