在之前的文章中,我们已经搭建了一个很完备的对camunda工作流进行管理的web应用。这一篇我们将继续完善这个平台的功能,引入对规则的管理。
首先安装相应的软件包
npm install --save dmn-js dmn-js-properties-panel camunda-dmn-moddle
我们新建一个rules.html文件,这个页面用于编辑DMN规则,内容如下:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="Mark Otto, Jacob Thornton, and Bootstrap contributors">
<meta name="generator" content="Hugo 0.101.0">
<title>Workflow Management</title>
<link rel="canonical" href="https://getbootstrap.com/docs/4.6/examples/dashboard/">
<!-- Bootstrap core CSS -->
<link href="assets/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="vendor/dmn-js/dist/assets/diagram-js.css">
<link rel="stylesheet" href="vendor/dmn-js/dist/assets/dmn-js-shared.css">
<link rel="stylesheet" href="vendor/dmn-js/dist/assets/dmn-js-drd.css">
<link rel="stylesheet" href="vendor/dmn-js/dist/assets/dmn-js-decision-table.css">
<link rel="stylesheet" href="vendor/dmn-js/dist/assets/dmn-js-decision-table-controls.css">
<link rel="stylesheet" href="vendor/dmn-js/dist/assets/dmn-js-literal-expression.css">
<link rel="stylesheet" href="vendor/dmn-js/dist/assets/dmn-font/css/dmn.css">
<link rel="stylesheet" href="vendor/dmn-js-properties-panel/dist/assets/properties-panel.css" />
<style>
.bd-placeholder-img {
font-size: 1.125rem;
text-anchor: middle;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
@media (min-width: 768px) {
.bd-placeholder-img-lg {
font-size: 3.5rem;
}
}
</style>
<!-- Custom styles for this template -->
<link href="assets/workflow.css" rel="stylesheet">
</head>
<body>
<nav class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0 shadow">
<a class="navbar-brand col-md-3 col-lg-2 mr-0 px-3" href="#">工作流管理平台</a>
<button class="navbar-toggler position-absolute d-md-none collapsed" type="button" data-toggle="collapse" data-target="#sidebarMenu" aria-controls="sidebarMenu" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<ul class="navbar-nav px-3">
<li class="nav-item text-nowrap">
<a class="nav-link" href="#">退出登录</a>
</li>
</ul>
</nav>
<div class="container-fluid d-flex h-75">
<div class="row flex-fill">
<nav id="sidebarMenu" class="col-md-3 col-lg-2 d-md-block bg-light sidebar collapse">
<div class="sidebar-sticky pt-3">
<ul class="nav flex-column">
<li class="nav-item">
<a class="nav-link active" href="workflow.html">
<i data-feather="home"></i>
编辑工作流
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="definitions.html">
<i data-feather="file"></i>
查看工作流
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="rules.html">
<i data-feather="shopping-cart"></i>
编辑规则
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">
<i data-feather="users"></i>
查看规则
</a>
</li>
</ul>
</div>
</nav>
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 px-md-4">
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<h1 class="h2">编辑规则</h1>
<div class="btn-toolbar mb-2 mb-md-0">
<div class="btn-group mr-2">
<a type="button" class="btn btn-sm btn-outline-secondary" id="js-download-diagram">下载DMN文件</a>
<a type="button" class="btn btn-sm btn-outline-secondary" id="js-deployment">部署规则</a>
</div>
</div>
</div>
<div class="content" id="js-drop-zone">
<div class="message intro">
<div class="note">
把本地的DMN文件拖到浏览器或者 <a id="js-create-diagram" href>新建一个规则</a>
</div>
</div>
<div class="message error">
<div class="note">
<p>出问题了,无法展示DMN图表</p>
<div class="details">
<span>问题原因</span>
<pre></pre>
</div>
</div>
</div>
<div class="canvas" id="js-canvas"></div>
<div class="properties-panel-parent" id="js-properties-panel"></div>
</div>
</main>
</div>
</div>
<script src="assets/jquery/dist/jquery.slim.min.js"></script>
<script src="assets/bootstrap/dist/bootstrap.bundle.min.js"></script>
<script src="assets/feather-icons/dist/feather.min.js"></script>
<script src="rules.bundle.js"></script>
<script>feather.replace()</script>
</body>
</html>
配套的rules.js文件内容如下:
import $ from 'jquery';
import './workflow.less';
import axios from 'axios';
import diagramXML from './diagram.dmn';
import DmnModeler from 'dmn-js/lib/Modeler';
import CamundaDmnModdle from 'camunda-dmn-moddle/resources/camunda.json';
import Keycloak from 'keycloak-js';
import config from './config.json';
import {
DmnPropertiesPanelModule,
DmnPropertiesProviderModule,
CamundaPropertiesProviderModule
} from 'dmn-js-properties-panel';
var dmnModeler = new DmnModeler({
drd: {
propertiesPanel: {
parent: '#js-properties-panel'
},
additionalModules: [
DmnPropertiesPanelModule,
DmnPropertiesProviderModule,
CamundaPropertiesProviderModule
],
},
container: '#js-canvas',
moddleExtensions: {
camunda: CamundaDmnModdle
}
});
var container = $('#js-drop-zone');
var token;
async function initKeycloak() {
const keycloak = new Keycloak();
await keycloak.init({onLoad: 'login-required'});
return keycloak.token;
}
$(document).ready(async function () {
token = await initKeycloak();
var str = window.location.search;
if (str) {
var parameter = str.split('=');
var definitionId = parameter[1];
axios.get(
config.baseurl + '/engine-rest/decision-requirements-definition/'+definitionId+'/xml',
{headers: {'Content-Type':'application/json', 'Authorization': 'Bearer '+token}}
).then(
res=>{
if (res.status==200) {
createNewDiagram(res.data.dmnXml);
}
else {
alert("读取规则定义失败,故障码为"+res.status.toString());
}
}
);
}
});
// Deployment button
$('#js-deployment').on("click", async function(event){
const { xml } = await dmnModeler.saveXML({ format: true });
console.log(xml);
const parser = new DOMParser();
const xmldoc = parser.parseFromString(xml, "application/xml");
const decision = xmldoc.getElementsByTagName('decision');
const decision_name = decision[0].getAttribute('name');
const file = new File([xml], "diagram.dmn", {type: "text/plain"});
const data = new FormData();
data.append("deployment-name", decision_name);
data.append("data", file);
axios.create({withCredentials: true}).post(
config.baseurl + '/engine-rest/deployment/create',
data,
{headers: {'Content-Type':'multipart/form-data', 'Authorization':'Bearer '+token}}
).then(
res=>{
console.log('res=>', res);
if (res.status==200) {
alert("部署成功,点击链接查看:"+res.data.links[0].href);
}
console.log(res.status);
console.log(res.data.links[0].href);
}
);
});
function registerFileDrop(container, callback) {
function handleFileSelect(e) {
e.stopPropagation();
e.preventDefault();
var files = e.dataTransfer.files;
var file = files[0];
var reader = new FileReader();
reader.onload = function(e) {
var xml = e.target.result;
callback(xml);
};
reader.readAsText(file);
}
function handleDragOver(e) {
e.stopPropagation();
e.preventDefault();
e.dataTransfer.dropEffect = 'copy'; // Explicitly show this is a copy.
}
container.get(0).addEventListener('dragover', handleDragOver, false);
container.get(0).addEventListener('drop', handleFileSelect, false);
}
// file drag / drop ///
// check file api availability
if (!window.FileList || !window.FileReader) {
window.alert(
'Looks like you use an older browser that does not support drag and drop. ' +
'Try using Chrome, Firefox or the Internet Explorer > 10.');
} else {
registerFileDrop(container, openDiagram);
}
function createNewDiagram(xml) {
openDiagram(xml);
}
async function openDiagram(xml) {
try {
await dmnModeler.importXML(xml);
container
.removeClass('with-error')
.addClass('with-diagram');
} catch (err) {
container
.removeClass('with-diagram')
.addClass('with-error');
container.find('.error pre').text(err.message);
console.error(err);
}
}
$(function() {
// load external diagram file via AJAX and open it
//$.get(diagramUrl, openDiagram, 'text');
$('#js-create-diagram').click(function(e) {
e.stopPropagation();
e.preventDefault();
createNewDiagram(diagramXML);
});
var downloadLink = $('#js-download-diagram');
var downloadSvgLink = $('#js-download-svg');
$('.buttons a').click(function(e) {
if (!$(this).is('.active')) {
e.preventDefault();
e.stopPropagation();
}
});
function setEncoded(link, name, data) {
var encodedData = encodeURIComponent(data);
if (data) {
link.addClass('active').attr({
'href': 'data:application/xml;charset=UTF-8,' + encodedData,
'download': name
});
console.log("added");
} else {
link.removeClass('active');
}
}
var exportArtifacts = debounce(async function() {
try {
const { xml } = await dmnModeler.saveXML({ format: true });
setEncoded(downloadLink, 'diagram.dmn', xml);
} catch (err) {
console.error('Error happened saving XML: ', err);
setEncoded(downloadLink, 'diagram.dmn', null);
}
}, 500);
dmnModeler.on('views.changed', exportArtifacts);
});
function debounce(fn, timeout) {
var timer;
return function() {
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(fn, timeout);
};
}
运行npm run build编译后,打开rules.html页面即对规则进行编辑和部署。
之后我们要实现一个对已部署好的规则进行查看和编辑的功能,这里的编辑除了可以用现有的编辑方式之外,还增加一种利用excel文件导入的方式来编辑。因为对于一些批量录入规则的场景来说,使用excel的方式来编辑更加方便。
先安装以下三个JS库
npm install --save exceljs file-saver uuid
新建一个ruleslist.html页面,用于查询展示已部署的规则,内容如下:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="Mark Otto, Jacob Thornton, and Bootstrap contributors">
<meta name="generator" content="Hugo 0.101.0">
<title>Rules Definitions</title>
<!-- Bootstrap core CSS -->
<link href="assets/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="vendor/datatables.net-bs4/assets/dataTables.bootstrap4.min.css">
<style>
.bd-placeholder-img {
font-size: 1.125rem;
text-anchor: middle;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
@media (min-width: 768px) {
.bd-placeholder-img-lg {
font-size: 3.5rem;
}
}
</style>
<!-- Custom styles for this template -->
<link href="assets/workflow.css" rel="stylesheet">
</head>
<body>
<nav class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0 shadow">
<a class="navbar-brand col-md-3 col-lg-2 mr-0 px-3" href="#">工作流管理平台</a>
<button class="navbar-toggler position-absolute d-md-none collapsed" type="button" data-toggle="collapse" data-target="#sidebarMenu" aria-controls="sidebarMenu" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<ul class="navbar-nav px-3">
<li class="nav-item text-nowrap">
<a class="nav-link" href="#">退出登录</a>
</li>
</ul>
</nav>
<div class="container-fluid d-flex h-75">
<div class="row flex-fill">
<nav id="sidebarMenu" class="col-md-3 col-lg-2 d-md-block bg-light sidebar collapse">
<div class="sidebar-sticky pt-3">
<ul class="nav flex-column">
<li class="nav-item">
<a class="nav-link" href="workflow.html">
<i data-feather="home"></i>
编辑工作流
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="definitions.html">
<i data-feather="file"></i>
查看工作流
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="rules.html">
<i data-feather="shopping-cart"></i>
编辑规则
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="ruleslist.html">
<i data-feather="users"></i>
查看规则
</a>
</li>
</ul>
</div>
</nav>
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 px-md-4">
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<h1 class="h3">规则定义列表</h1>
<input type="checkbox" checked data-toggle="toggle" data-size="sm" data-on="最新版本" data-off="所有版本">
</div>
<div class="card shadow mb-4">
<div class="card-body">
<div class="table-responsive">
<table class="table table-bordered" id="dataTable" width="100%" cellspacing="0">
<thead>
<tr>
<th>id</th>
<th>名字</th>
<th>版本</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
</div>
</main>
</div>
</div>
<script src="assets/jquery/dist/jquery.slim.min.js"></script>
<script src="assets/bootstrap/dist/bootstrap.bundle.min.js"></script>
<script src="vendor/datatables.net-bs4/assets/jquery.dataTables.min.js"></script>
<script src="vendor/datatables.net-bs4/assets/dataTables.bootstrap4.min.js"></script>
<script src="ruleslist.bundle.js"></script>
<script src="assets/feather-icons/dist/feather.min.js"></script>
<script>feather.replace()</script>
</body>
</html>
配套的ruleslist.js文件内容如下:
import axios from 'axios';
import Keycloak from 'keycloak-js';
import config from './config.json';
var token;
async function initKeycloak() {
const keycloak = new Keycloak();
await keycloak.init({onLoad: 'login-required'});
return keycloak.token;
}
$(document).ready(async function () {
token = await initKeycloak();
//?latestVersion=true
axios.create({withCredentials: true}).get(
config.baseurl + '/engine-rest/decision-definition',
{headers: {'Content-Type':'application/json', 'Authorization':'Bearer '+token}}
).then(
res=>{
$('#dataTable').DataTable({
data: res.data,
columns: [
{
data: "id",
render: function(data) {
return '<a href="' + 'ruledetail.html?id=' + data + '">' + data + '</a';
}
},
{data: "name"},
{data: "version"}
]
});
}
);
});
ruleslist.html页面展示的规则列表里面,当点击某个规则的ID时,将跳转到ruledetail.html页面,这个页面将读取该规则的具体信息,并在datatable里面展示。这个页面还有两个按钮,分别是下载规则表为excel文件,以及上传excel文件更新规则表。
ruledetail.html页面的内容如下:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="Mark Otto, Jacob Thornton, and Bootstrap contributors">
<meta name="generator" content="Hugo 0.101.0">
<title>Rules Definitions</title>
<!-- Bootstrap core CSS -->
<link href="assets/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="vendor/datatables.net-bs4/assets/dataTables.bootstrap4.min.css">
<style>
.bd-placeholder-img {
font-size: 1.125rem;
text-anchor: middle;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
@media (min-width: 768px) {
.bd-placeholder-img-lg {
font-size: 3.5rem;
}
}
</style>
<!-- Custom styles for this template -->
<link href="assets/workflow.css" rel="stylesheet">
</head>
<body>
<nav class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0 shadow">
<a class="navbar-brand col-md-3 col-lg-2 mr-0 px-3" href="#">工作流管理平台</a>
<button class="navbar-toggler position-absolute d-md-none collapsed" type="button" data-toggle="collapse" data-target="#sidebarMenu" aria-controls="sidebarMenu" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<ul class="navbar-nav px-3">
<li class="nav-item text-nowrap">
<a class="nav-link" href="#">退出登录</a>
</li>
</ul>
</nav>
<div class="container-fluid d-flex h-75">
<div class="row flex-fill">
<nav id="sidebarMenu" class="col-md-3 col-lg-2 d-md-block bg-light sidebar collapse">
<div class="sidebar-sticky pt-3">
<ul class="nav flex-column">
<li class="nav-item">
<a class="nav-link active" href="workflow.html">
<i data-feather="home"></i>
编辑工作流
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="definitions.html">
<i data-feather="file"></i>
查看工作流
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="rules.html">
<i data-feather="shopping-cart"></i>
编辑规则
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="ruleslist.html">
<i data-feather="users"></i>
查看规则
</a>
</li>
</ul>
</div>
</nav>
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 px-md-4">
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<h1 class="h2">规则详情</h1>
</div>
<!-- DataTales Example -->
<div class="card shadow mb-4">
<div class="card-header py-3">
<div class="btn-toolbar mb-2 mb-md-0">
<div class="btn-group me-2">
<a type="button" class="btn btn-sm btn-outline-secondary" id="js-download-dmn-xls">下载规则表</a>
<a type="button" class="btn btn-sm btn-outline-secondary" id="js-deploy-dmn-xls">修改规则表</a>
</div>
</div>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-bordered" id="dataTable" width="100%" cellspacing="0">
</table>
</div>
</div>
</div>
</main>
</div>
</div>
<script src="assets/jquery/dist/jquery.slim.min.js"></script>
<script src="assets/bootstrap/dist/bootstrap.bundle.min.js"></script>
<script src="vendor/datatables.net-bs4/assets/jquery.dataTables.min.js"></script>
<script src="vendor/datatables.net-bs4/assets/dataTables.bootstrap4.min.js"></script>
<script src="ruledetail.bundle.js"></script>
<script src="assets/feather-icons/dist/feather.min.js"></script>
<script>feather.replace()</script>
</body>
</html>
配套的ruledetail.js的内容如下:
import axios from 'axios';
import Keycloak from 'keycloak-js';
import { saveAs } from 'file-saver';
import config from './config.json';
async function initKeycloak() {
const keycloak = new Keycloak();
await keycloak.init({onLoad: 'login-required'});
return keycloak.token;
}
const { v4: uuidv4 } = require('uuid');
const ExcelJS = require('exceljs');
function replaceSpecialChar(inputStr) {
var count = 0;
var result = inputStr;
var length = result.length;
var index = result.indexOf('"');
while (index!=-1) {
count += 1;
if (count%2==1) {
result = result.substring(0,index)+'“'+result.substring(index+1, length);
}
else {
result = result.substring(0,index)+'”'+result.substring(index+1, length);
}
index = result.indexOf('"');
}
result = result.replace(/</g, '<');
result = result.replace(/>/g, '>');
result = result.replace(/&/g, '&');
result = result.replace(/'/g, ''');
return result;
}
function restoreSpecialChar(inputStr) {
var result = inputStr.replace(/</g, '<');
result = result.replace(/>/g, '>');
result = result.replace(/&/g, '&');
result = result.replace(/'/g, '\'');
return result;
}
$(document).ready(async function () {
var str = window.location.search;
var rule_id = str.substring(4, str.length);
var decision_id = rule_id.split(':')[0];
var token = await initKeycloak();
axios.create({withCredentials: true}).get(
config.baseurl + '/engine-rest/decision-definition/'+rule_id+'/xml',
{headers: {'Content-Type':'application/json', 'Authorization':'Bearer '+token}}
).then(
res=>{
let parser = new DOMParser();
let xmlDoc = parser.parseFromString(res.data.dmnXml,"text/xml");
let decisions = xmlDoc.getElementsByTagName("decision");
var decision = null;
//一个DRD XML里面可能包括多个Decision,根据传入的ID获取所需要的Decision
for (var i=0;i<decisions.length;i++) {
for (var j=0;j<decisions[i].attributes.length;j++) {
if (decisions[i].attributes[j].name=='id' && decisions[i].attributes[j].value==decision_id) {
decision = decisions[i];
break;
}
}
if (decision!=null) {
break;
}
}
if (decision==null) {
decision = decisions[0];
}
//获取Decision的输入和输出的变量名,类型等等
let input_arr = decision.getElementsByTagName("input");
let output_arr = decision.getElementsByTagName("output");
let input_expression_arr = decision.getElementsByTagName("inputExpression");
let output_str = []; //保存输出字段的label
let input_str = []; //保存输入字段的label
let input_values = []; //保存输入字段的预定义值
let input_id = [];
let column_names = [];
let input_type = []; //保存输出字段的类型
let output_type = []; //保存输入字段的类型
let output_id = [];
let output_values = []; //保存输出字段的预定义值
console.log('input_arr');
console.log(input_arr);
for (var i=0;i<input_arr.length;i++) {
let item = input_arr.item(i);
//获取每个输入字段的id和label
for (var j=0;j<item.attributes.length;j++) {
if (item.attributes[j].name=='label') {
input_str.push(item.attributes[j].value);
}
if (item.attributes[j].name=='id') {
input_id.push(item.attributes[j].value);
}
}
let childNodes = item.childNodes;
let inputValue = [];
//获取每个输入字段的预定值
for (var j=0;j<childNodes.length;j++) {
if (childNodes[j].nodeName=="inputValues") {
inputValue = childNodes[j].childNodes[1].firstChild.nodeValue.split(',');
for (var k=0;k<inputValue.length;k++) {
if (inputValue[k][0]=='"' && inputValue[k][inputValue[k].length-1]=='"') {
inputValue[k] = inputValue[k].substring(1,inputValue[k].length-1);
}
}
break;
}
}
input_values.push(inputValue);
}
//获取每个输入字段的类型
for (var i=0;i<input_expression_arr.length;i++) {
let item = input_expression_arr.item(i);
for (var j=0;j<item.attributes.length;j++) {
if (item.attributes[j].name=='typeRef') {
input_type.push(item.attributes[j].value);
}
}
}
for (var i=0;i<output_arr.length;i++) {
let item = output_arr.item(i);
//获取每个输出字段的id,label和类型
for (var j=0;j<item.attributes.length;j++) {
if (item.attributes[j].name=='label') {
output_str.push(item.attributes[j].value);
}
if (item.attributes[j].name=='id') {
output_id.push(item.attributes[j].value);
}
if (item.attributes[j].name=='typeRef') {
output_type.push(item.attributes[j].value);
}
}
let childNodes = item.childNodes;
let outputValue = [];
//获取每个输出字段的预定值
for (var j=0;j<childNodes.length;j++) {
if (childNodes[j].nodeName=="outputValues") {
outputValue = childNodes[j].childNodes[1].firstChild.nodeValue.split(',');
for (var k=0;k<outputValue.length;k++) {
if (outputValue[k][0]=='"' && outputValue[k][outputValue[k].length-1]=='"') {
outputValue[k] = outputValue[k].substring(1,outputValue[k].length-1);
}
}
break;
}
}
output_values.push(outputValue);
}
//根据Decision XML解析后的内容,动态生成datatable的表头以及内容
let tablestr = '<thead><tr>';
for (var i=0;i<input_str.length;i++) {
tablestr += '<th>'+input_str[i]+'</th>';
column_names.push(input_str[i]);
}
for (var i=0;i<output_str.length;i++) {
tablestr += '<th>'+output_str[i]+'</th>';
column_names.push(output_str[i]);
}
tablestr += '</tr></thead><tbody></tbody>';
console.log('column_names');
console.log(column_names);
console.log(input_str);
$('#dataTable').append(tablestr);
//获取Decision的每一条规则
let rules_arr = decision.getElementsByTagName("rule");
let rules_str = [];
let rows_arr = [];
for (i=0;i<rules_arr.length;i++) {
let ruleChild = rules_arr.item(i).childNodes;
let ruleItem = [];
let rowItem = {};
let column_id = 0;
for (j=0;j<ruleChild.length;j++) {
if (ruleChild[j].nodeName=="inputEntry" || ruleChild[j].nodeName=="outputEntry") {
if (ruleChild[j].childNodes[1].firstChild) {
if (ruleChild[j].childNodes[1].firstChild.nodeValue) {
let value = ruleChild[j].childNodes[1].firstChild.nodeValue;
if (value.indexOf('"')==0 && value.indexOf('"', value.length-1)==value.length-1) {
value = value.substring(1, value.length-1);
}
ruleItem.push(value);
rowItem[column_names[column_id]] = value;
} else {
console.log(ruleChild[j].childNodes[1]);
ruleItem.push("");
rowItem[column_names[column_id]] = "";
}
} else {
ruleItem.push("");
rowItem[column_names[column_id]] = "";
}
column_id += 1;
}
}
rules_str.push(ruleItem);
rows_arr.push(rowItem);
}
$('#dataTable').DataTable({
data: rules_str,
scrollX: true,
autoWidth: false
}).columns.adjust();
//生成excel xls格式的数据
const workbook = new ExcelJS.Workbook();
var sheetname = rule_id.split(':')[0];
var sheetname = sheetname.length>31?sheetname.substring(0,30):sheetname;
const sheet = workbook.addWorksheet(sheetname);
var columns = [];
//设定excel sheet的列
for (var i=0;i<input_id.length;i++) {
columns.push({
header: input_str[i],
key: input_id[i],
width: 30
});
}
for (var i=0;i<output_id.length;i++) {
columns.push({
header: output_str[i],
key: output_id[i],
width: 30
});
}
//把之前解析的每一条rule的数据添加到sheet的行
sheet.columns = columns;
for (var i=0;i<rows_arr.length;i++) {
sheet.addRow(rules_str[i]);
}
//根据输入字段的预设值,设置对应列的输入列表
for (var i=0;i<input_values.length;i++) {
if (input_values[i].length>0) {
var a = Math.floor(i/26);
var columnName = '';
if (a==0) {
columnName = String.fromCharCode(i%26+65);
}
else if (a==1) {
columnName = String.fromCharCode(65)+String.fromCharCode(i%26+65);
}
else {
columnName = String.fromCharCode(a-1+65)+String.fromCharCode(i%26+65);
}
sheet.dataValidations.add(columnName+"2:"+columnName+"9999",{
type: 'list',
allowBlank: true,
formulae: ['"'+input_values[i].join(',')+'"']
});
}
}
//根据输出字段的预设值,设置对应列的输入列表
for (var i=0;i<output_values.length;i++) {
if (output_values[i].length>0) {
var a = Math.floor((i+input_values.length)/26);
var columnName = '';
if (a==0) {
columnName = String.fromCharCode((i+input_values.length)%26+65);
}
else if (a==1) {
columnName = String.fromCharCode(65)+String.fromCharCode((i+input_values.length)%26+65);
}
else {
columnName = String.fromCharCode(a-1+65)+String.fromCharCode((i+input_values.length)%26+65);
}
sheet.dataValidations.add(columnName+"2:"+columnName+"9999",{
type: 'list',
allowBlank: true,
formulae: ['"'+output_values[i].join(',')+'"']
});
}
}
//把excel sheet的内容写入到文件
$('#js-download-dmn-xls').click(async function(e) {
var xls64 = await workbook.xlsx.writeBuffer({ base64: true });
saveAs(
new Blob([xls64], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }),
sheetname+'.xlsx'
)
});
//用户上传excel文件,解析之后转换为DMN文件并部署,这里采用XLSX这个库来读xls文件
$('#js-deploy-dmn-xls').click(async function(e) {
let input = document.createElement('input');
input.type = 'file';
const reader = new FileReader();
const workbook = new ExcelJS.Workbook();
reader.onload = async function fileReadCompleted() {
await workbook.xlsx.load(reader.result);
let ws = workbook.worksheets[0];
let rule_nodes = [];
//这里采用uuidv4来生成对应的ID
for (var i=2;i<=ws.actualRowCount;i++) {
let row = ws.getRow(i);
let rulestr = '\n <rule id="DecisionRule_' + uuidv4().split('-')[4].substring(0,7) + '">\n';
for (var j=0;j<input_arr.length;j++) {
let value = row.getCell(j+1).value;
if (input_type[j]=='string') {
value = replaceSpecialChar(value);
value = '"' + value + '"';
}
rulestr += ' <inputEntry id="UnaryTests_' + uuidv4().split('-')[4].substring(0,7) + '">\n';
rulestr += ' <text>' + value + '</text>\n </inputEntry>\n';
}
for (var j=0;j<output_arr.length;j++) {
let value = row.getCell(j+input_arr.length+1).value;
if (output_type[j]=='string') {
value = replaceSpecialChar(value);
value = '"' + value + '"';
}
rulestr += ' <outputEntry id="LiteralExpression_' + uuidv4().split('-')[4].substring(0,7) + '">\n';
rulestr += ' <text>' + value + '</text>\n </outputEntry>\n';
}
rulestr += ' </rule>\n';
let rule_element = parser.parseFromString(rulestr,"text/xml").firstChild;
rule_nodes.push(rule_element);
}
//移除现有Decision XML里面的rule, 并插入新的rule
let rule_elements = decision.getElementsByTagName("rule");
const rules_length = rule_elements.length;
let decision_table_element = decision.getElementsByTagName("decisionTable")[0];
for (var i=0;i<rules_length;i++) {
decision_table_element.removeChild(rule_elements[0]);
}
for (var i=0;i<rule_nodes.length;i++) {
decision_table_element.appendChild(rule_nodes[i]);
}
//生成新的Decision XML
let xmlserializer = new XMLSerializer();
let newXML = xmlserializer.serializeToString(xmlDoc);
newXML = newXML.replace(/xmlns=\"\"/g, ''); //去除掉自动生成的xml namespace,不然会有问题
const file = new File([newXML], "diagram.dmn", {type: "text/plain"});
const data = new FormData();
data.append("deployment-name", rule_id);
data.append("data", file);
axios.create({withCredentials: true}).post(
config.baseurl + '/engine-rest/deployment/create',
data,
{headers: {'Content-Type':'multipart/form-data', 'Authorization':'Bearer '+token}}
).then(
res=>{
if (res.status==200) {
alert("部署成功,点击链接查看:"+res.data.links[0].href);
}
}
);
};
input.onchange = _ => {
// you can use this method to get file and perform respective operations
let files = Array.from(input.files);
if (files.length>1) {
alert('请只选择一个文件');
}
else {
let filepart = files[0].name.split('.');
let filetype = filepart[filepart.length-1];
if (filetype!='xls' && filetype!='xlsx') {
alert('请选择excel文件');
}
else {
reader.readAsArrayBuffer(input.files[0]);
}
}
};
input.click();
});
}
)
});
这个代码比较长,主要是调用camunda的接口来读取规则表的XML,然后解析之后展示在datatable。当点击下载excel按钮时,把xml的内容转换为excel的内容并写入文件。当上传excel按钮点击时,则根据XML的格式来生成新的规则数据。
最后运行效果如下:
rule