王涛 3 år sedan
incheckning
02f10df586
100 ändrade filer med 102447 tillägg och 0 borttagningar
  1. 22 0
      .editorconfig
  2. 8 0
      .env.development
  3. 5 0
      .env.production
  4. 7 0
      .env.staging
  5. 10 0
      .eslintignore
  6. 199 0
      .eslintrc.js
  7. 23 0
      .gitignore
  8. 30 0
      README.md
  9. 13 0
      babel.config.js
  10. 12 0
      bin/build.bat
  11. 12 0
      bin/package.bat
  12. 12 0
      bin/run-web.bat
  13. 35 0
      build/index.js
  14. 107 0
      package.json
  15. 2 0
      public/bpmnjs/css/app.css
  16. 717 0
      public/bpmnjs/css/diagram-js.css
  17. BIN
      public/bpmnjs/favicon.ico
  18. 110 0
      public/bpmnjs/index.html
  19. 90051 0
      public/bpmnjs/index.js
  20. 108 0
      public/bpmnjs/vendor/bpmn-font/css/bpmn-codes.css
  21. 10 0
      public/bpmnjs/vendor/bpmn-font/css/bpmn-embedded.css
  22. 164 0
      public/bpmnjs/vendor/bpmn-font/css/bpmn.css
  23. BIN
      public/bpmnjs/vendor/bpmn-font/font/bpmn.eot
  24. 20 0
      public/bpmnjs/vendor/bpmn-font/font/bpmn.svg
  25. BIN
      public/bpmnjs/vendor/bpmn-font/font/bpmn.ttf
  26. BIN
      public/bpmnjs/vendor/bpmn-font/font/bpmn.woff
  27. BIN
      public/bpmnjs/vendor/bpmn-font/font/bpmn.woff2
  28. 820 0
      public/bpmnjs/vendor/diagram-js.css
  29. BIN
      public/favicon.ico
  30. 207 0
      public/index.html
  31. 2 0
      public/robots.txt
  32. 3 0
      ruoyi-bpmnjs/.gitignore
  33. 1 0
      ruoyi-bpmnjs/.npmrc
  34. 140 0
      ruoyi-bpmnjs/Gruntfile.js
  35. 76 0
      ruoyi-bpmnjs/README.md
  36. BIN
      ruoyi-bpmnjs/app/favicon.ico
  37. 110 0
      ruoyi-bpmnjs/app/index.html
  38. 146 0
      ruoyi-bpmnjs/app/index.js
  39. 48 0
      ruoyi-bpmnjs/package.json
  40. 1087 0
      ruoyi-bpmnjs/resources/activiti.json
  41. 76 0
      ruoyi-bpmnjs/resources/customControls/CustomContextPad.js
  42. 54 0
      ruoyi-bpmnjs/resources/customControls/CustomPalette.js
  43. 8 0
      ruoyi-bpmnjs/resources/customControls/index.js
  44. 12 0
      ruoyi-bpmnjs/resources/customTranslate/customTranslate.js
  45. 239 0
      ruoyi-bpmnjs/resources/customTranslate/translationsGerman.js
  46. 13 0
      ruoyi-bpmnjs/resources/newDiagram.bpmn
  47. 67 0
      ruoyi-bpmnjs/resources/properties-panel/PropertiesActivator.js
  48. 1269 0
      ruoyi-bpmnjs/resources/properties-panel/PropertiesPanel.js
  49. 252 0
      ruoyi-bpmnjs/resources/properties-panel/Utils.js
  50. 101 0
      ruoyi-bpmnjs/resources/properties-panel/cmd/CreateAndReferenceHandler.js
  51. 110 0
      ruoyi-bpmnjs/resources/properties-panel/cmd/CreateBusinessObjectListHandler.js
  52. 32 0
      ruoyi-bpmnjs/resources/properties-panel/cmd/MultiCommandHandler.js
  53. 131 0
      ruoyi-bpmnjs/resources/properties-panel/cmd/UpdateBusinessObjectHandler.js
  54. 115 0
      ruoyi-bpmnjs/resources/properties-panel/cmd/UpdateBusinessObjectListHandler.js
  55. 27 0
      ruoyi-bpmnjs/resources/properties-panel/cmd/index.js
  56. 77 0
      ruoyi-bpmnjs/resources/properties-panel/factory/CheckboxEntryFactory.js
  57. 118 0
      ruoyi-bpmnjs/resources/properties-panel/factory/ComboEntryFactory.js
  58. 163 0
      ruoyi-bpmnjs/resources/properties-panel/factory/EntryFactory.js
  59. 49 0
      ruoyi-bpmnjs/resources/properties-panel/factory/EntryFieldDescription.js
  60. 38 0
      ruoyi-bpmnjs/resources/properties-panel/factory/LabelFactory.js
  61. 78 0
      ruoyi-bpmnjs/resources/properties-panel/factory/LinkEntryFactory.js
  62. 137 0
      ruoyi-bpmnjs/resources/properties-panel/factory/SelectEntryFactory.js
  63. 338 0
      ruoyi-bpmnjs/resources/properties-panel/factory/TableEntryFactory.js
  64. 42 0
      ruoyi-bpmnjs/resources/properties-panel/factory/TextBoxEntryFactory.js
  65. 85 0
      ruoyi-bpmnjs/resources/properties-panel/factory/TextInputEntryFactory.js
  66. 56 0
      ruoyi-bpmnjs/resources/properties-panel/factory/ValidationAwareTextInput.js
  67. 76 0
      ruoyi-bpmnjs/resources/properties-panel/helper/AsyncCapableHelper.js
  68. 31 0
      ruoyi-bpmnjs/resources/properties-panel/helper/CategoryHelper.js
  69. 77 0
      ruoyi-bpmnjs/resources/properties-panel/helper/CmdHelper.js
  70. 23 0
      ruoyi-bpmnjs/resources/properties-panel/helper/ElementHelper.js
  71. 57 0
      ruoyi-bpmnjs/resources/properties-panel/helper/EventDefinitionHelper.js
  72. 55 0
      ruoyi-bpmnjs/resources/properties-panel/helper/ExtensionElementsHelper.js
  73. 97 0
      ruoyi-bpmnjs/resources/properties-panel/helper/FormHelper.js
  74. 192 0
      ruoyi-bpmnjs/resources/properties-panel/helper/ImplementationTypeHelper.js
  75. 144 0
      ruoyi-bpmnjs/resources/properties-panel/helper/InputOutputHelper.js
  76. 36 0
      ruoyi-bpmnjs/resources/properties-panel/helper/ParticipantHelper.js
  77. 8 0
      ruoyi-bpmnjs/resources/properties-panel/index.js
  78. 103 0
      ruoyi-bpmnjs/resources/properties-panel/popup.js
  79. 542 0
      ruoyi-bpmnjs/resources/properties-panel/provider/activiti/ActivitiPropertiesProvider.js
  80. 251 0
      ruoyi-bpmnjs/resources/properties-panel/provider/activiti/element-templates/CreateHelper.js
  81. 132 0
      ruoyi-bpmnjs/resources/properties-panel/provider/activiti/element-templates/CustomElementsPropertiesActivator.js
  82. 56 0
      ruoyi-bpmnjs/resources/properties-panel/provider/activiti/element-templates/ElementTemplates.js
  83. 96 0
      ruoyi-bpmnjs/resources/properties-panel/provider/activiti/element-templates/ElementTemplatesLoader.js
  84. 239 0
      ruoyi-bpmnjs/resources/properties-panel/provider/activiti/element-templates/Helper.js
  85. 303 0
      ruoyi-bpmnjs/resources/properties-panel/provider/activiti/element-templates/Validator.js
  86. 471 0
      ruoyi-bpmnjs/resources/properties-panel/provider/activiti/element-templates/cmd/ChangeElementTemplateHandler.js
  87. 46 0
      ruoyi-bpmnjs/resources/properties-panel/provider/activiti/element-templates/cmd/index.js
  88. 13 0
      ruoyi-bpmnjs/resources/properties-panel/provider/activiti/element-templates/index.js
  89. 151 0
      ruoyi-bpmnjs/resources/properties-panel/provider/activiti/element-templates/parts/ChooserProps.js
  90. 770 0
      ruoyi-bpmnjs/resources/properties-panel/provider/activiti/element-templates/parts/CustomProps.js
  91. 16 0
      ruoyi-bpmnjs/resources/properties-panel/provider/activiti/element-templates/util/validate.js
  92. 9 0
      ruoyi-bpmnjs/resources/properties-panel/provider/activiti/index.js
  93. 16 0
      ruoyi-bpmnjs/resources/properties-panel/provider/activiti/parts/AsynchronousContinuationProps.js
  94. 90 0
      ruoyi-bpmnjs/resources/properties-panel/provider/activiti/parts/CallActivityProps.js
  95. 27 0
      ruoyi-bpmnjs/resources/properties-panel/provider/activiti/parts/CandidateStarterProps.js
  96. 185 0
      ruoyi-bpmnjs/resources/properties-panel/provider/activiti/parts/ConditionalProps.js
  97. 57 0
      ruoyi-bpmnjs/resources/properties-panel/provider/activiti/parts/ConnectorDetailProps.js
  98. 16 0
      ruoyi-bpmnjs/resources/properties-panel/provider/activiti/parts/ConnectorInputOutputParameterProps.js
  99. 18 0
      ruoyi-bpmnjs/resources/properties-panel/provider/activiti/parts/ConnectorInputOutputProps.js
  100. 40 0
      ruoyi-bpmnjs/resources/properties-panel/provider/activiti/parts/ErrorEventProps.js

+ 22 - 0
.editorconfig

@@ -0,0 +1,22 @@
+# 告诉EditorConfig插件,这是根文件,不用继续往上查找
+root = true
+
+# 匹配全部文件
+[*]
+# 设置字符集
+charset = utf-8
+# 缩进风格,可选space、tab
+indent_style = space
+# 缩进的空格数
+indent_size = 2
+# 结尾换行符,可选lf、cr、crlf
+end_of_line = lf
+# 在文件结尾插入新行
+insert_final_newline = true
+# 删除一行中的前后空格
+trim_trailing_whitespace = true
+
+# 匹配md结尾的文件
+[*.md]
+insert_final_newline = false
+trim_trailing_whitespace = false

+ 8 - 0
.env.development

@@ -0,0 +1,8 @@
+# 开发环境配置
+ENV = 'development'
+
+# 中控管理系统/开发环境
+VUE_APP_BASE_API = '/dev-api'
+
+# 路由懒加载
+VUE_CLI_BABEL_TRANSPILE_MODULES = true

+ 5 - 0
.env.production

@@ -0,0 +1,5 @@
+# 生产环境配置
+ENV = 'production'
+
+# 中控管理系统/生产环境
+VUE_APP_BASE_API = '/prod-api'

+ 7 - 0
.env.staging

@@ -0,0 +1,7 @@
+NODE_ENV = production
+
+# 测试环境配置
+ENV = 'staging'
+
+# 中控管理系统/测试环境
+VUE_APP_BASE_API = '/stage-api'

+ 10 - 0
.eslintignore

@@ -0,0 +1,10 @@
+# 忽略build目录下类型为js的文件的语法检查
+build/*.js
+# 忽略src/assets目录下文件的语法检查
+src/assets
+# 忽略public目录下文件的语法检查
+public
+# 忽略当前目录下为js的文件的语法检查
+*.js
+# 忽略当前目录下为vue的文件的语法检查
+*.vue

+ 199 - 0
.eslintrc.js

@@ -0,0 +1,199 @@
+// ESlint 检查配置
+module.exports = {
+  root: true,
+  parserOptions: {
+    parser: 'babel-eslint',
+    sourceType: 'module'
+  },
+  env: {
+    browser: true,
+    node: true,
+    es6: true,
+  },
+  extends: ['plugin:vue/recommended', 'eslint:recommended'],
+
+  // add your custom rules here
+  //it is base on https://github.com/vuejs/eslint-config-vue
+  rules: {
+    "vue/max-attributes-per-line": [2, {
+      "singleline": 10,
+      "multiline": {
+        "max": 1,
+        "allowFirstLine": false
+      }
+    }],
+    "vue/singleline-html-element-content-newline": "off",
+    "vue/multiline-html-element-content-newline":"off",
+    "vue/name-property-casing": ["error", "PascalCase"],
+    "vue/no-v-html": "off",
+    'accessor-pairs': 2,
+    'arrow-spacing': [2, {
+      'before': true,
+      'after': true
+    }],
+    'block-spacing': [2, 'always'],
+    'brace-style': [2, '1tbs', {
+      'allowSingleLine': true
+    }],
+    'camelcase': [0, {
+      'properties': 'always'
+    }],
+    'comma-dangle': [2, 'never'],
+    'comma-spacing': [2, {
+      'before': false,
+      'after': true
+    }],
+    'comma-style': [2, 'last'],
+    'constructor-super': 2,
+    'curly': [2, 'multi-line'],
+    'dot-location': [2, 'property'],
+    'eol-last': 2,
+    'eqeqeq': ["error", "always", {"null": "ignore"}],
+    'generator-star-spacing': [2, {
+      'before': true,
+      'after': true
+    }],
+    'handle-callback-err': [2, '^(err|error)$'],
+    'indent': [2, 2, {
+      'SwitchCase': 1
+    }],
+    'jsx-quotes': [2, 'prefer-single'],
+    'key-spacing': [2, {
+      'beforeColon': false,
+      'afterColon': true
+    }],
+    'keyword-spacing': [2, {
+      'before': true,
+      'after': true
+    }],
+    'new-cap': [2, {
+      'newIsCap': true,
+      'capIsNew': false
+    }],
+    'new-parens': 2,
+    'no-array-constructor': 2,
+    'no-caller': 2,
+    'no-console': 'off',
+    'no-class-assign': 2,
+    'no-cond-assign': 2,
+    'no-const-assign': 2,
+    'no-control-regex': 0,
+    'no-delete-var': 2,
+    'no-dupe-args': 2,
+    'no-dupe-class-members': 2,
+    'no-dupe-keys': 2,
+    'no-duplicate-case': 2,
+    'no-empty-character-class': 2,
+    'no-empty-pattern': 2,
+    'no-eval': 2,
+    'no-ex-assign': 2,
+    'no-extend-native': 2,
+    'no-extra-bind': 2,
+    'no-extra-boolean-cast': 2,
+    'no-extra-parens': [2, 'functions'],
+    'no-fallthrough': 2,
+    'no-floating-decimal': 2,
+    'no-func-assign': 2,
+    'no-implied-eval': 2,
+    'no-inner-declarations': [2, 'functions'],
+    'no-invalid-regexp': 2,
+    'no-irregular-whitespace': 2,
+    'no-iterator': 2,
+    'no-label-var': 2,
+    'no-labels': [2, {
+      'allowLoop': false,
+      'allowSwitch': false
+    }],
+    'no-lone-blocks': 2,
+    'no-mixed-spaces-and-tabs': 2,
+    'no-multi-spaces': 2,
+    'no-multi-str': 2,
+    'no-multiple-empty-lines': [2, {
+      'max': 1
+    }],
+    'no-native-reassign': 2,
+    'no-negated-in-lhs': 2,
+    'no-new-object': 2,
+    'no-new-require': 2,
+    'no-new-symbol': 2,
+    'no-new-wrappers': 2,
+    'no-obj-calls': 2,
+    'no-octal': 2,
+    'no-octal-escape': 2,
+    'no-path-concat': 2,
+    'no-proto': 2,
+    'no-redeclare': 2,
+    'no-regex-spaces': 2,
+    'no-return-assign': [2, 'except-parens'],
+    'no-self-assign': 2,
+    'no-self-compare': 2,
+    'no-sequences': 2,
+    'no-shadow-restricted-names': 2,
+    'no-spaced-func': 2,
+    'no-sparse-arrays': 2,
+    'no-this-before-super': 2,
+    'no-throw-literal': 2,
+    'no-trailing-spaces': 2,
+    'no-undef': 2,
+    'no-undef-init': 2,
+    'no-unexpected-multiline': 2,
+    'no-unmodified-loop-condition': 2,
+    'no-unneeded-ternary': [2, {
+      'defaultAssignment': false
+    }],
+    'no-unreachable': 2,
+    'no-unsafe-finally': 2,
+    'no-unused-vars': [2, {
+      'vars': 'all',
+      'args': 'none'
+    }],
+    'no-useless-call': 2,
+    'no-useless-computed-key': 2,
+    'no-useless-constructor': 2,
+    'no-useless-escape': 0,
+    'no-whitespace-before-property': 2,
+    'no-with': 2,
+    'one-var': [2, {
+      'initialized': 'never'
+    }],
+    'operator-linebreak': [2, 'after', {
+      'overrides': {
+        '?': 'before',
+        ':': 'before'
+      }
+    }],
+    'padded-blocks': [2, 'never'],
+    'quotes': [2, 'single', {
+      'avoidEscape': true,
+      'allowTemplateLiterals': true
+    }],
+    'semi': [2, 'never'],
+    'semi-spacing': [2, {
+      'before': false,
+      'after': true
+    }],
+    'space-before-blocks': [2, 'always'],
+    'space-before-function-paren': [2, 'never'],
+    'space-in-parens': [2, 'never'],
+    'space-infix-ops': 2,
+    'space-unary-ops': [2, {
+      'words': true,
+      'nonwords': false
+    }],
+    'spaced-comment': [2, 'always', {
+      'markers': ['global', 'globals', 'eslint', 'eslint-disable', '*package', '!', ',']
+    }],
+    'template-curly-spacing': [2, 'never'],
+    'use-isnan': 2,
+    'valid-typeof': 2,
+    'wrap-iife': [2, 'any'],
+    'yield-star-spacing': [2, 'both'],
+    'yoda': [2, 'never'],
+    'prefer-const': 2,
+    'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0,
+    'object-curly-spacing': [2, 'always', {
+      objectsInObjects: false
+    }],
+    'array-bracket-spacing': [2, 'never']
+  }
+}

+ 23 - 0
.gitignore

@@ -0,0 +1,23 @@
+.DS_Store
+node_modules/
+dist/
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+**/*.log
+
+tests/**/coverage/
+tests/e2e/reports
+selenium-debug.log
+
+# Editor directories and files
+.idea
+.vscode
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.local
+
+package-lock.json
+yarn.lock

+ 30 - 0
README.md

@@ -0,0 +1,30 @@
+## 开发
+
+```bash
+# 克隆项目
+git clone https://gitee.com/y_project/RuoYi-Vue
+
+# 进入项目目录
+cd ruoyi-ui
+
+# 安装依赖
+npm install
+
+# 建议不要直接使用 cnpm 安装依赖,会有各种诡异的 bug。可以通过如下操作解决 npm 下载速度慢的问题
+npm install --registry=https://registry.npm.taobao.org
+
+# 启动服务
+npm run dev
+```
+
+浏览器访问 http://localhost:80
+
+## 发布
+
+```bash
+# 构建测试环境
+npm run build:stage
+
+# 构建生产环境
+npm run build:prod
+```

+ 13 - 0
babel.config.js

@@ -0,0 +1,13 @@
+module.exports = {
+  presets: [
+    // https://github.com/vuejs/vue-cli/tree/master/packages/@vue/babel-preset-app
+    '@vue/cli-plugin-babel/preset'
+  ],
+  'env': {
+    'development': {
+      // babel-plugin-dynamic-import-node plugin only does one thing by converting all import() to require().
+      // This plugin can significantly increase the speed of hot updates, when you have a large number of pages.
+      'plugins': ['dynamic-import-node']
+    }
+  }
+}

+ 12 - 0
bin/build.bat

@@ -0,0 +1,12 @@
+@echo off
+echo.
+echo [信息] 打包Web工程,生成dist文件。
+echo.
+
+%~d0
+cd %~dp0
+
+cd ..
+npm run build:prod
+
+pause

+ 12 - 0
bin/package.bat

@@ -0,0 +1,12 @@
+@echo off
+echo.
+echo [信息] 安装Web工程,生成node_modules文件。
+echo.
+
+%~d0
+cd %~dp0
+
+cd ..
+npm install --registry=https://registry.npm.taobao.org
+
+pause

+ 12 - 0
bin/run-web.bat

@@ -0,0 +1,12 @@
+@echo off
+echo.
+echo [ÐÅÏ¢] ʹÓà Vue ÔËÐÐ Web ¹¤³Ì¡£
+echo.
+
+%~d0
+cd %~dp0
+
+cd ..
+npm run dev
+
+pause

+ 35 - 0
build/index.js

@@ -0,0 +1,35 @@
+const { run } = require('runjs')
+const chalk = require('chalk')
+const config = require('../vue.config.js')
+const rawArgv = process.argv.slice(2)
+const args = rawArgv.join(' ')
+
+if (process.env.npm_config_preview || rawArgv.includes('--preview')) {
+  const report = rawArgv.includes('--report')
+
+  run(`vue-cli-service build ${args}`)
+
+  const port = 9526
+  const publicPath = config.publicPath
+
+  var connect = require('connect')
+  var serveStatic = require('serve-static')
+  const app = connect()
+
+  app.use(
+    publicPath,
+    serveStatic('./dist', {
+      index: ['index.html', '/']
+    })
+  )
+
+  app.listen(port, function () {
+    console.log(chalk.green(`> Preview at  http://localhost:${port}${publicPath}`))
+    if (report) {
+      console.log(chalk.green(`> Report at  http://localhost:${port}${publicPath}report.html`))
+    }
+
+  })
+} else {
+  run(`vue-cli-service build ${args}`)
+}

+ 107 - 0
package.json

@@ -0,0 +1,107 @@
+{
+  "name": "ruoyi",
+  "version": "3.2.0",
+  "description": "中控管理系统",
+  "author": "中控",
+  "license": "MIT",
+  "scripts": {
+    "dev": "vue-cli-service serve",
+    "build:prod": "vue-cli-service build",
+    "build:stage": "vue-cli-service build --mode staging",
+    "preview": "node build/index.js --preview",
+    "lint": "eslint --ext .js,.vue src",
+    "test:unit": "jest --clearCache && vue-cli-service test:unit",
+    "test:ci": "npm run lint && npm run test:unit",
+    "svgo": "svgo -f src/icons/svg --config=src/icons/svgo.yml",
+    "new": "plop"
+  },
+  "husky": {
+    "hooks": {
+      "pre-commit": "lint-staged"
+    }
+  },
+  "lint-staged": {
+    "src/**/*.{js,vue}": [
+      "eslint --fix",
+      "git add"
+    ]
+  },
+  "keywords": [
+    "vue",
+    "admin",
+    "dashboard",
+    "element-ui",
+    "boilerplate",
+    "admin-template",
+    "management-system"
+  ],
+  "repository": {
+    "type": "git",
+    "url": "https://gitee.com/y_project/RuoYi-Vue.git"
+  },
+  "dependencies": {
+    "@riophae/vue-treeselect": "0.4.0",
+    "axios": "0.18.1",
+    "bpmn-js": "^9.1.0",
+    "clipboard": "2.0.4",
+    "core-js": "3.6.5",
+    "echarts": "4.2.1",
+    "element-ui": "2.13.2",
+    "file-saver": "2.0.1",
+    "fuse.js": "3.4.4",
+    "js-beautify": "1.10.2",
+    "js-cookie": "2.2.0",
+    "jsencrypt": "3.0.0-rc.1",
+    "normalize.css": "7.0.0",
+    "nprogress": "0.2.0",
+    "path-to-regexp": "2.4.0",
+    "quill": "1.3.7",
+    "screenfull": "4.2.0",
+    "sortablejs": "1.8.4",
+    "vue": "2.6.10",
+    "vue-count-to": "1.0.13",
+    "vue-cropper": "0.4.9",
+    "vue-router": "3.0.2",
+    "vue-splitpane": "1.0.4",
+    "vuedraggable": "2.20.0",
+    "vuex": "3.1.0"
+  },
+  "devDependencies": {
+    "@vue/cli-plugin-babel": "4.4.4",
+    "@vue/cli-plugin-eslint": "4.4.4",
+    "@vue/cli-plugin-unit-jest": "4.4.4",
+    "@vue/cli-service": "4.4.4",
+    "@vue/test-utils": "1.0.0-beta.29",
+    "autoprefixer": "9.5.1",
+    "babel-eslint": "10.1.0",
+    "babel-jest": "23.6.0",
+    "babel-plugin-dynamic-import-node": "2.3.3",
+    "chalk": "2.4.2",
+    "chokidar": "2.1.5",
+    "connect": "3.6.6",
+    "eslint": "6.7.2",
+    "eslint-plugin-vue": "6.2.2",
+    "html-webpack-plugin": "3.2.0",
+    "husky": "1.3.1",
+    "lint-staged": "8.1.5",
+    "mockjs": "1.0.1-beta3",
+    "plop": "2.3.0",
+    "runjs": "4.3.2",
+    "node-sass": "4.14.1",
+    "sass-loader": "8.0.2",
+    "script-ext-html-webpack-plugin": "2.1.3",
+    "script-loader": "0.7.2",
+    "serve-static": "1.13.2",
+    "svg-sprite-loader": "4.1.3",
+    "svgo": "1.2.0",
+    "vue-template-compiler": "2.6.10"
+  },
+  "engines": {
+    "node": ">=8.9",
+    "npm": ">= 3.0.0"
+  },
+  "browserslist": [
+    "> 1%",
+    "last 2 versions"
+  ]
+}

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 2 - 0
public/bpmnjs/css/app.css


+ 717 - 0
public/bpmnjs/css/diagram-js.css

@@ -0,0 +1,717 @@
+/**
+ * outline styles
+ */
+
+.djs-outline {
+  fill: none;
+  visibility: hidden;
+}
+
+.djs-element.hover .djs-outline,
+.djs-element.selected .djs-outline {
+  visibility: visible;
+  shape-rendering: crispEdges;
+  stroke-dasharray: 3,3;
+}
+
+.djs-element.selected .djs-outline {
+  stroke: #8888FF;
+  stroke-width: 1px;
+}
+
+.djs-element.hover .djs-outline {
+  stroke: #FF8888;
+  stroke-width: 1px;
+}
+
+.djs-shape.connect-ok .djs-visual > :nth-child(1) {
+  fill: #DCFECC /* light-green */ !important;
+}
+
+.djs-shape.connect-not-ok .djs-visual > :nth-child(1),
+.djs-shape.drop-not-ok .djs-visual > :nth-child(1) {
+  fill: #f9dee5 /* light-red */ !important;
+}
+
+.djs-shape.new-parent .djs-visual > :nth-child(1) {
+  fill: #F7F9FF !important;
+}
+
+svg.drop-not-ok {
+  background: #f9dee5 /* light-red */ !important;
+}
+
+svg.new-parent {
+  background: #F7F9FF /* light-blue */ !important;
+}
+
+.djs-connection.connect-ok .djs-visual > :nth-child(1),
+.djs-connection.drop-ok .djs-visual > :nth-child(1) {
+  stroke: #90DD5F /* light-green */ !important;
+}
+
+.djs-connection.connect-not-ok .djs-visual > :nth-child(1),
+.djs-connection.drop-not-ok .djs-visual > :nth-child(1) {
+  stroke: #E56283 /* light-red */ !important;
+}
+
+.drop-not-ok,
+.connect-not-ok {
+  cursor: not-allowed;
+}
+
+.djs-element.attach-ok .djs-visual > :nth-child(1) {
+  stroke-width: 5px !important;
+  stroke: rgba(255, 116, 0, 0.7) !important;
+}
+
+.djs-frame.connect-not-ok .djs-visual > :nth-child(1),
+.djs-frame.drop-not-ok .djs-visual > :nth-child(1) {
+  stroke-width: 3px !important;
+  stroke: #E56283 /* light-red */ !important;
+  fill: none !important;
+}
+
+/**
+* Selection box style
+*
+*/
+.djs-lasso-overlay {
+  fill: rgb(255, 116, 0);
+  fill-opacity: 0.1;
+
+  stroke-dasharray: 5 1 3 1;
+  stroke: rgb(255, 116, 0);
+
+  shape-rendering: crispEdges;
+  pointer-events: none;
+}
+
+/**
+ * Resize styles
+ */
+.djs-resize-overlay {
+  fill: none;
+
+  stroke-dasharray: 5 1 3 1;
+  stroke: rgb(255, 116, 0);
+
+  pointer-events: none;
+}
+
+.djs-resizer-hit {
+  fill: none;
+  pointer-events: all;
+}
+
+.djs-resizer-visual {
+  fill: white;
+  stroke-width: 1px;
+  stroke: black;
+  shape-rendering: crispEdges;
+  stroke-opacity: 0.2;
+}
+
+.djs-cursor-resize-nwse,
+.djs-resizer-nw,
+.djs-resizer-se {
+  cursor: nwse-resize;
+}
+
+.djs-cursor-resize-nesw,
+.djs-resizer-ne,
+.djs-resizer-sw {
+  cursor: nesw-resize;
+}
+
+.djs-shape.djs-resizing > .djs-outline {
+  visibility: hidden !important;
+}
+
+.djs-shape.djs-resizing > .djs-resizer {
+  visibility: hidden;
+}
+
+.djs-dragger > .djs-resizer {
+  visibility: hidden;
+}
+
+/**
+ * drag styles
+ */
+.djs-dragger * {
+  fill: none !important;
+  stroke: rgb(255, 116, 0) !important;
+}
+
+.djs-dragger tspan,
+.djs-dragger text {
+  fill: rgb(255, 116, 0) !important;
+  stroke: none !important;
+}
+
+marker.djs-dragger circle,
+marker.djs-dragger path,
+marker.djs-dragger polygon,
+marker.djs-dragger polyline,
+marker.djs-dragger rect {
+  fill: rgb(255, 116, 0) !important;
+  stroke: none !important;
+}
+
+marker.djs-dragger text,
+marker.djs-dragger tspan {
+  fill: none !important;
+  stroke: rgb(255, 116, 0) !important;
+}
+
+.djs-dragging {
+  opacity: 0.3;
+}
+
+.djs-dragging,
+.djs-dragging > * {
+  pointer-events: none !important;
+}
+
+.djs-dragging .djs-context-pad,
+.djs-dragging .djs-outline {
+  display: none !important;
+}
+
+/**
+ * no pointer events for visual
+ */
+.djs-visual,
+.djs-outline {
+  pointer-events: none;
+}
+
+.djs-element.attach-ok .djs-hit {
+  stroke-width: 60px !important;
+}
+
+/**
+ * all pointer events for hit shape
+ */
+.djs-element > .djs-hit-all {
+  pointer-events: all;
+}
+
+.djs-element > .djs-hit-stroke,
+.djs-element > .djs-hit-click-stroke {
+  pointer-events: stroke;
+}
+
+/**
+ * all pointer events for hit shape
+ */
+.djs-drag-active .djs-element > .djs-hit-click-stroke {
+  pointer-events: all;
+}
+
+/**
+ * shape / connection basic styles
+ */
+.djs-connection .djs-visual {
+  stroke-width: 2px;
+  fill: none;
+}
+
+.djs-cursor-grab {
+  cursor: -webkit-grab;
+  cursor: -moz-grab;
+  cursor: grab;
+}
+
+.djs-cursor-grabbing {
+  cursor: -webkit-grabbing;
+  cursor: -moz-grabbing;
+  cursor: grabbing;
+}
+
+.djs-cursor-crosshair {
+  cursor: crosshair;
+}
+
+.djs-cursor-move {
+  cursor: move;
+}
+
+.djs-cursor-resize-ns {
+  cursor: ns-resize;
+}
+
+.djs-cursor-resize-ew {
+  cursor: ew-resize;
+}
+
+
+/**
+ * snapping
+ */
+.djs-snap-line {
+  stroke: rgb(255, 195, 66);
+  stroke: rgba(255, 195, 66, 0.50);
+  stroke-linecap: round;
+  stroke-width: 2px;
+  pointer-events: none;
+}
+
+/**
+ * snapping
+ */
+.djs-crosshair {
+  stroke: #555;
+  stroke-linecap: round;
+  stroke-width: 1px;
+  pointer-events: none;
+  shape-rendering: crispEdges;
+  stroke-dasharray: 5, 5;
+}
+
+/**
+ * palette
+ */
+
+.djs-palette {
+  position: absolute;
+  left: 20px;
+  top: 20px;
+
+  box-sizing: border-box;
+  width: 48px;
+}
+
+.djs-palette .separator {
+  margin: 0 5px;
+  padding-top: 5px;
+
+  border: none;
+  border-bottom: solid 1px #DDD;
+
+  clear: both;
+}
+
+.djs-palette .entry:before {
+  vertical-align: text-bottom;
+}
+
+.djs-palette .djs-palette-toggle {
+  cursor: pointer;
+}
+
+.djs-palette .entry,
+.djs-palette .djs-palette-toggle {
+  color: #333;
+  font-size: 30px;
+
+  text-align: center;
+}
+
+.djs-palette .entry {
+  float: left;
+}
+
+.djs-palette .entry img {
+  max-width: 100%;
+}
+
+.djs-palette .djs-palette-entries:after {
+  content: '';
+  display: table;
+  clear: both;
+}
+
+.djs-palette .djs-palette-toggle:hover {
+  background: #666;
+}
+
+.djs-palette .entry:hover {
+  color: rgb(255, 116, 0);
+}
+
+.djs-palette .highlighted-entry {
+  color: rgb(255, 116, 0) !important;
+}
+
+.djs-palette .entry,
+.djs-palette .djs-palette-toggle {
+  width: 46px;
+  height: 46px;
+  line-height: 46px;
+  cursor: default;
+}
+
+/**
+ * Palette open / two-column layout is controlled via
+ * classes on the palette. Events to hook into palette
+ * changed life-cycle are available in addition.
+ */
+.djs-palette.two-column.open {
+  width: 94px;
+}
+
+.djs-palette:not(.open) .djs-palette-entries {
+  display: none;
+}
+
+.djs-palette:not(.open) {
+  overflow: hidden;
+}
+
+.djs-palette.open .djs-palette-toggle {
+  display: none;
+}
+
+/**
+ * context-pad
+ */
+.djs-overlay-context-pad {
+  width: 72px;
+}
+
+.djs-context-pad {
+  position: absolute;
+  display: none;
+  pointer-events: none;
+}
+
+.djs-context-pad .entry {
+  width: 22px;
+  height: 22px;
+  text-align: center;
+  display: inline-block;
+  font-size: 22px;
+  margin: 0 2px 2px 0;
+
+  border-radius: 3px;
+
+  cursor: default;
+
+  background-color: #FEFEFE;
+  box-shadow: 0 0 2px 1px #FEFEFE;
+  pointer-events: all;
+}
+
+.djs-context-pad .entry:before {
+  vertical-align: top;
+}
+
+.djs-context-pad .entry:hover {
+  background: rgb(255, 252, 176);
+}
+
+.djs-context-pad.open {
+  display: block;
+}
+
+/**
+ * popup styles
+ */
+.djs-popup .entry {
+  line-height: 20px;
+  white-space: nowrap;
+  cursor: default;
+}
+
+/* larger font for prefixed icons */
+.djs-popup .entry:before {
+  vertical-align: middle;
+  font-size: 20px;
+}
+
+.djs-popup .entry > span {
+  vertical-align: middle;
+  font-size: 14px;
+}
+
+.djs-popup .entry:hover,
+.djs-popup .entry.active:hover {
+  background: rgb(255, 252, 176);
+}
+
+.djs-popup .entry.disabled {
+  background: inherit;
+}
+
+.djs-popup .djs-popup-header .entry {
+  display: inline-block;
+  padding: 2px 3px 2px 3px;
+
+  border: solid 1px transparent;
+  border-radius: 3px;
+}
+
+.djs-popup .djs-popup-header .entry.active {
+  color: rgb(255, 116, 0);
+  border: solid 1px rgb(255, 116, 0);
+  background-color: #F6F6F6;
+}
+
+.djs-popup-body .entry {
+  padding: 4px 10px 4px 5px;
+}
+
+.djs-popup-body .entry > span {
+  margin-left: 5px;
+}
+
+.djs-popup-body {
+  background-color: #FEFEFE;
+}
+
+.djs-popup-header {
+  border-bottom: 1px solid #DDD;
+}
+
+.djs-popup-header .entry {
+  margin: 1px;
+  margin-left: 3px;
+}
+
+.djs-popup-header .entry:last-child {
+  margin-right: 3px;
+}
+
+/**
+ * popup / palette styles
+ */
+.djs-popup, .djs-palette {
+  background: #FAFAFA;
+  border: solid 1px #CCC;
+  border-radius: 2px;
+}
+
+/**
+ * touch
+ */
+
+.djs-shape,
+.djs-connection {
+  touch-action: none;
+}
+
+.djs-segment-dragger,
+.djs-bendpoint {
+  display: none;
+}
+
+/**
+ * bendpoints
+ */
+.djs-segment-dragger .djs-visual {
+  fill: rgba(255, 255, 121, 0.2);
+  stroke-width: 1px;
+  stroke-opacity: 1;
+  stroke: rgba(255, 255, 121, 0.3);
+}
+
+.djs-bendpoint .djs-visual {
+  fill: rgba(255, 255, 121, 0.8);
+  stroke-width: 1px;
+  stroke-opacity: 0.5;
+  stroke: black;
+}
+
+.djs-segment-dragger:hover,
+.djs-bendpoints.hover .djs-segment-dragger,
+.djs-bendpoints.selected .djs-segment-dragger,
+.djs-bendpoint:hover,
+.djs-bendpoints.hover .djs-bendpoint,
+.djs-bendpoints.selected .djs-bendpoint {
+  display: block;
+}
+
+.djs-drag-active .djs-bendpoints * {
+  display: none;
+}
+
+.djs-bendpoints:not(.hover) .floating {
+  display: none;
+}
+
+.djs-segment-dragger:hover .djs-visual,
+.djs-segment-dragger.djs-dragging .djs-visual,
+.djs-bendpoint:hover .djs-visual,
+.djs-bendpoint.floating .djs-visual {
+  fill: yellow;
+  stroke-opacity: 0.5;
+  stroke: black;
+}
+
+.djs-bendpoint.floating .djs-hit {
+  pointer-events: none;
+}
+
+.djs-segment-dragger .djs-hit,
+.djs-bendpoint .djs-hit {
+  pointer-events: all;
+  fill: none;
+}
+
+.djs-segment-dragger.horizontal .djs-hit {
+  cursor: ns-resize;
+}
+
+.djs-segment-dragger.vertical .djs-hit {
+  cursor: ew-resize;
+}
+
+.djs-segment-dragger.djs-dragging .djs-hit {
+  pointer-events: none;
+}
+
+.djs-updating,
+.djs-updating > * {
+  pointer-events: none !important;
+}
+
+.djs-updating .djs-context-pad,
+.djs-updating .djs-outline,
+.djs-updating .djs-bendpoint,
+.connect-ok .djs-bendpoint,
+.connect-not-ok .djs-bendpoint,
+.drop-ok .djs-bendpoint,
+.drop-not-ok .djs-bendpoint {
+  display: none !important;
+}
+
+.djs-segment-dragger.djs-dragging,
+.djs-bendpoint.djs-dragging {
+  display: block;
+  opacity: 1.0;
+}
+
+.djs-segment-dragger.djs-dragging .djs-visual,
+.djs-bendpoint.djs-dragging .djs-visual {
+  fill: yellow;
+  stroke-opacity: 0.5;
+}
+
+
+/**
+ * tooltips
+ */
+.djs-tooltip-error {
+  font-size: 11px;
+  line-height: 18px;
+  text-align: left;
+
+  padding: 5px;
+
+  opacity: 0.7;
+}
+
+.djs-tooltip-error > * {
+  width: 160px;
+
+  background: rgb(252, 236, 240);
+  color: rgb(158, 76, 76);
+  padding: 3px 7px;
+  border-radius: 5px;
+  border-left: solid 5px rgb(174, 73, 73);
+}
+
+.djs-tooltip-error:hover {
+  opacity: 1;
+}
+
+
+/**
+ * search pad
+ */
+.djs-search-container {
+  position: absolute;
+  top: 20px;
+  left: 0;
+  right: 0;
+  margin-left: auto;
+  margin-right: auto;
+
+  width: 25%;
+  min-width: 300px;
+  max-width: 400px;
+  z-index: 10;
+
+  font-size: 1.05em;
+  opacity: 0.9;
+  background: #FAFAFA;
+  border: solid 1px #CCC;
+  border-radius: 2px;
+}
+
+.djs-search-container:not(.open) {
+  display: none;
+}
+
+.djs-search-input input {
+  font-size: 1.05em;
+  width: 100%;
+  padding: 6px 10px;
+  border: 1px solid #ccc;
+}
+
+.djs-search-input input:focus {
+  outline: none;
+  border-color: #52B415;
+}
+
+.djs-search-results {
+  position: relative;
+  overflow-y: auto;
+  max-height: 200px;
+}
+
+.djs-search-results:hover {
+  /*background: #fffdd7;*/
+  cursor: pointer;
+}
+
+.djs-search-result {
+  width: 100%;
+  padding: 6px 10px;
+  background: white;
+  border-bottom: solid 1px #AAA;
+  border-radius: 1px;
+}
+
+.djs-search-highlight {
+  color: black;
+}
+
+.djs-search-result-primary {
+  margin: 0 0 10px;
+}
+
+.djs-search-result-secondary {
+  font-family: monospace;
+  margin: 0;
+}
+
+.djs-search-result:hover {
+  background: #fdffd6;
+}
+
+.djs-search-result-selected {
+  background: #fffcb0;
+}
+
+.djs-search-result-selected:hover {
+  background: #f7f388;
+}
+
+.djs-search-overlay {
+  background: yellow;
+  opacity: 0.3;
+}
+
+/**
+ * hidden styles
+ */
+.djs-element-hidden,
+.djs-element-hidden .djs-hit,
+.djs-element-hidden .djs-outline,
+.djs-label-hidden .djs-label {
+  display: none !important;
+}

BIN
public/bpmnjs/favicon.ico


+ 110 - 0
public/bpmnjs/index.html

@@ -0,0 +1,110 @@
+<!DOCTYPE html>
+<html >
+
+<head>
+  <meta charset="utf-8" />
+  <title>在线绘制流程</title>
+  <link rel="icon" href="favicon.ico">
+  <link rel="stylesheet" href="css/diagram-js.css" />
+  <link rel="stylesheet" href="vendor/bpmn-font/css/bpmn-embedded.css" />
+  <link rel="stylesheet" href="css/app.css" />
+
+  <style>
+    .item{
+      display: none;
+      cursor: pointer;
+    }
+    .bjs-powered-by {
+      display: none;
+    }
+    .buttons > li {
+      /* display: inline-block; */
+      margin-right: 10px;
+      height: 26px;
+      line-height: 26px;
+      float: left;
+    }
+    .buttons > li > a.btn  {
+      background: #00BCD4;
+      border: none;
+      outline: none;
+      padding: 0px 10px;
+      color: #fff;
+      display: inline-block;
+      opacity: 1;
+      height: 26px;
+      font-size: 14px;
+      line-height: 26px;
+    }
+    .label  {
+      background: #00BCD4;
+      border: none;
+      outline: none;
+      padding: 0px 10px;
+      color: #fff;
+      display: inline-block;
+      cursor: pointer;
+      opacity: 1;
+      height: 26px;
+      font-size: 14px;
+      line-height: 26px;
+    }
+    .sy-mask{width:100%;
+      height:100%;
+      position:fixed;
+      background:rgba(0,0,0,0.8);
+      left:0;top:0;z-index:1000;
+      display: none;
+    }
+    .sy-alert{
+      position:fixed;display:none;background:#fff;border-radius:5px;overflow:hidden;width:300px;max-width:90%;max-height:80%;left:0;right:0;margin:0 auto;z-index:9999}.sy-alert.animated{-webkit-animation-duration:.3s;animation-duration:.3s}.sy-alert .sy-title{height:45px;color:#333;line-height:45px;font-size:15px;border-bottom:1px solid #eee;padding:0 12px}.sy-alert .sy-content{padding:20px;text-align:center;font-size:14px;line-height:24px;color:#666;overflow-y:auto}.sy-alert .sy-btn{height:50%;border-top:1px solid #eee;overflow:hidden}.sy-alert .sy-btn button{float:left;border:0;color:#333;cursor:pointer;background:#fff;width:50%;line-height:45px;font-size:15px;text-align:center}.sy-alert .sy-btn button:nth-child(1){color:#888;border-right:1px solid #eee}.sy-alert.sy-alert-alert .sy-btn button{float:none;width:100%}.sy-alert.sy-alert-tips{text-align:center;width:150px;background:rgba(0,0,0,0.7)}.sy-alert.sy-alert-tips .sy-content{padding:8px;color:#fff;font-size:14px}.sy-alert.sy-alert-model .sy-content{text-align:left}.sy-alert.sy-alert-model .sy-content .form .input-item{margin-bottom:12px;position:relative}.sy-alert.sy-alert-model .sy-content .form .input-item input{display:block;position:relative;width:100%;border:1px solid #eee;padding:10px}.sy-alert.sy-alert-model .sy-content .form .input-item .getcode{border:0;top:0;right:0;position:absolute;background:0;line-height:37px;color:#f60;width:100px;text-align:center}
+  </style>
+</head>
+
+<body>
+<div class="content with-diagram" id="js-drop-zone">
+  <div class="message error">
+    <div class="note">
+      <p>无法显示bpms2.0</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>
+<ul class="buttons">
+  <li class="item upload">
+    <form id="form1" name="myForm" onsubmit="return false" method="post" enctype="multipart/form-data" title="上传文件">
+      <input type="file" name="uploadFile" id="uploadFile" accept=".bpmn" style="display: none">
+      <label class="label" for="uploadFile" >导入</label>
+    </form>
+  </li>
+  <li class="item download">
+    <a class="btn" href id="downloadBpmn">导出</a>
+  </li>
+  <li class="item submit">
+    <a class="btn" id="js-download-diagram">
+      部署
+    </a>
+  </li>
+</ul>
+<div class="sy-alert sy-alert-model animated" sy-enter="zoomIn" sy-leave="zoomOut" sy-type="confirm" sy-mask="true" id="alert" >
+  <div class="sy-title">部署流程</div>
+  <div class="sy-content">
+        确认是否部署该流程
+<!--    <div class="form">-->
+<!--      <p class="input-item"><input id="deploymentName" type="text" placeholder="请输入流程名称"></p>-->
+<!--    </div>-->
+  </div>
+  <div class="sy-btn">
+    <button id="sure">确定</button>
+    <button class="cancel" >取消</button>
+  </div>
+</div>
+<div class="sy-mask cancel"></div>
+<script src="index.js"></script>
+</body>
+</html>

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 90051 - 0
public/bpmnjs/index.js


+ 108 - 0
public/bpmnjs/vendor/bpmn-font/css/bpmn-codes.css

@@ -0,0 +1,108 @@
+
+.bpmn-icon-screw-wrench:before { content: '\e800'; } /* '' */
+.bpmn-icon-trash:before { content: '\e801'; } /* '' */
+.bpmn-icon-conditional-flow:before { content: '\e802'; } /* '' */
+.bpmn-icon-default-flow:before { content: '\e803'; } /* '' */
+.bpmn-icon-gateway-parallel:before { content: '\e804'; } /* '' */
+.bpmn-icon-intermediate-event-catch-cancel:before { content: '\e805'; } /* '' */
+.bpmn-icon-intermediate-event-catch-non-interrupting-message:before { content: '\e806'; } /* '' */
+.bpmn-icon-start-event-compensation:before { content: '\e807'; } /* '' */
+.bpmn-icon-start-event-non-interrupting-parallel-multiple:before { content: '\e808'; } /* '' */
+.bpmn-icon-loop-marker:before { content: '\e809'; } /* '' */
+.bpmn-icon-parallel-mi-marker:before { content: '\e80a'; } /* '' */
+.bpmn-icon-start-event-non-interrupting-signal:before { content: '\e80b'; } /* '' */
+.bpmn-icon-intermediate-event-catch-non-interrupting-timer:before { content: '\e80c'; } /* '' */
+.bpmn-icon-intermediate-event-catch-parallel-multiple:before { content: '\e80d'; } /* '' */
+.bpmn-icon-intermediate-event-catch-compensation:before { content: '\e80e'; } /* '' */
+.bpmn-icon-gateway-xor:before { content: '\e80f'; } /* '' */
+.bpmn-icon-connection:before { content: '\e810'; } /* '' */
+.bpmn-icon-end-event-cancel:before { content: '\e811'; } /* '' */
+.bpmn-icon-intermediate-event-catch-condition:before { content: '\e812'; } /* '' */
+.bpmn-icon-intermediate-event-catch-non-interrupting-parallel-multiple:before { content: '\e813'; } /* '' */
+.bpmn-icon-start-event-condition:before { content: '\e814'; } /* '' */
+.bpmn-icon-start-event-non-interrupting-timer:before { content: '\e815'; } /* '' */
+.bpmn-icon-sequential-mi-marker:before { content: '\e816'; } /* '' */
+.bpmn-icon-user-task:before { content: '\e817'; } /* '' */
+.bpmn-icon-business-rule:before { content: '\e818'; } /* '' */
+.bpmn-icon-sub-process-marker:before { content: '\e819'; } /* '' */
+.bpmn-icon-start-event-parallel-multiple:before { content: '\e81a'; } /* '' */
+.bpmn-icon-start-event-error:before { content: '\e81b'; } /* '' */
+.bpmn-icon-intermediate-event-catch-signal:before { content: '\e81c'; } /* '' */
+.bpmn-icon-intermediate-event-catch-error:before { content: '\e81d'; } /* '' */
+.bpmn-icon-end-event-compensation:before { content: '\e81e'; } /* '' */
+.bpmn-icon-subprocess-collapsed:before { content: '\e81f'; } /* '' */
+.bpmn-icon-subprocess-expanded:before { content: '\e820'; } /* '' */
+.bpmn-icon-task:before { content: '\e821'; } /* '' */
+.bpmn-icon-end-event-error:before { content: '\e822'; } /* '' */
+.bpmn-icon-intermediate-event-catch-escalation:before { content: '\e823'; } /* '' */
+.bpmn-icon-intermediate-event-catch-timer:before { content: '\e824'; } /* '' */
+.bpmn-icon-start-event-escalation:before { content: '\e825'; } /* '' */
+.bpmn-icon-start-event-signal:before { content: '\e826'; } /* '' */
+.bpmn-icon-business-rule-task:before { content: '\e827'; } /* '' */
+.bpmn-icon-manual:before { content: '\e828'; } /* '' */
+.bpmn-icon-receive:before { content: '\e829'; } /* '' */
+.bpmn-icon-call-activity:before { content: '\e82a'; } /* '' */
+.bpmn-icon-start-event-timer:before { content: '\e82b'; } /* '' */
+.bpmn-icon-start-event-message:before { content: '\e82c'; } /* '' */
+.bpmn-icon-intermediate-event-none:before { content: '\e82d'; } /* '' */
+.bpmn-icon-intermediate-event-catch-link:before { content: '\e82e'; } /* '' */
+.bpmn-icon-end-event-escalation:before { content: '\e82f'; } /* '' */
+.bpmn-icon-text-annotation:before { content: '\e830'; } /* '' */
+.bpmn-icon-bpmn-io:before { content: '\e831'; } /* '' */
+.bpmn-icon-gateway-complex:before { content: '\e832'; } /* '' */
+.bpmn-icon-gateway-eventbased:before { content: '\e833'; } /* '' */
+.bpmn-icon-gateway-none:before { content: '\e834'; } /* '' */
+.bpmn-icon-gateway-or:before { content: '\e835'; } /* '' */
+.bpmn-icon-end-event-terminate:before { content: '\e836'; } /* '' */
+.bpmn-icon-end-event-signal:before { content: '\e837'; } /* '' */
+.bpmn-icon-end-event-none:before { content: '\e838'; } /* '' */
+.bpmn-icon-end-event-multiple:before { content: '\e839'; } /* '' */
+.bpmn-icon-end-event-message:before { content: '\e83a'; } /* '' */
+.bpmn-icon-end-event-link:before { content: '\e83b'; } /* '' */
+.bpmn-icon-intermediate-event-catch-message:before { content: '\e83c'; } /* '' */
+.bpmn-icon-intermediate-event-throw-compensation:before { content: '\e83d'; } /* '' */
+.bpmn-icon-start-event-multiple:before { content: '\e83e'; } /* '' */
+.bpmn-icon-script:before { content: '\e83f'; } /* '' */
+.bpmn-icon-manual-task:before { content: '\e840'; } /* '' */
+.bpmn-icon-send:before { content: '\e841'; } /* '' */
+.bpmn-icon-service:before { content: '\e842'; } /* '' */
+.bpmn-icon-receive-task:before { content: '\e843'; } /* '' */
+.bpmn-icon-user:before { content: '\e844'; } /* '' */
+.bpmn-icon-start-event-none:before { content: '\e845'; } /* '' */
+.bpmn-icon-intermediate-event-throw-escalation:before { content: '\e846'; } /* '' */
+.bpmn-icon-intermediate-event-catch-multiple:before { content: '\e847'; } /* '' */
+.bpmn-icon-intermediate-event-catch-non-interrupting-escalation:before { content: '\e848'; } /* '' */
+.bpmn-icon-intermediate-event-throw-link:before { content: '\e849'; } /* '' */
+.bpmn-icon-start-event-non-interrupting-condition:before { content: '\e84a'; } /* '' */
+.bpmn-icon-data-object:before { content: '\e84b'; } /* '' */
+.bpmn-icon-script-task:before { content: '\e84c'; } /* '' */
+.bpmn-icon-send-task:before { content: '\e84d'; } /* '' */
+.bpmn-icon-data-store:before { content: '\e84e'; } /* '' */
+.bpmn-icon-start-event-non-interrupting-escalation:before { content: '\e84f'; } /* '' */
+.bpmn-icon-intermediate-event-throw-message:before { content: '\e850'; } /* '' */
+.bpmn-icon-intermediate-event-catch-non-interrupting-multiple:before { content: '\e851'; } /* '' */
+.bpmn-icon-intermediate-event-catch-non-interrupting-signal:before { content: '\e852'; } /* '' */
+.bpmn-icon-intermediate-event-throw-multiple:before { content: '\e853'; } /* '' */
+.bpmn-icon-start-event-non-interrupting-message:before { content: '\e854'; } /* '' */
+.bpmn-icon-ad-hoc-marker:before { content: '\e855'; } /* '' */
+.bpmn-icon-service-task:before { content: '\e856'; } /* '' */
+.bpmn-icon-task-none:before { content: '\e857'; } /* '' */
+.bpmn-icon-compensation-marker:before { content: '\e858'; } /* '' */
+.bpmn-icon-start-event-non-interrupting-multiple:before { content: '\e859'; } /* '' */
+.bpmn-icon-intermediate-event-throw-signal:before { content: '\e85a'; } /* '' */
+.bpmn-icon-intermediate-event-catch-non-interrupting-condition:before { content: '\e85b'; } /* '' */
+.bpmn-icon-participant:before { content: '\e85c'; } /* '' */
+.bpmn-icon-event-subprocess-expanded:before { content: '\e85d'; } /* '' */
+.bpmn-icon-lane-insert-below:before { content: '\e85e'; } /* '' */
+.bpmn-icon-space-tool:before { content: '\e85f'; } /* '' */
+.bpmn-icon-connection-multi:before { content: '\e860'; } /* '' */
+.bpmn-icon-lane:before { content: '\e861'; } /* '' */
+.bpmn-icon-lasso-tool:before { content: '\e862'; } /* '' */
+.bpmn-icon-lane-insert-above:before { content: '\e863'; } /* '' */
+.bpmn-icon-lane-divide-three:before { content: '\e864'; } /* '' */
+.bpmn-icon-lane-divide-two:before { content: '\e865'; } /* '' */
+.bpmn-icon-data-input:before { content: '\e866'; } /* '' */
+.bpmn-icon-data-output:before { content: '\e867'; } /* '' */
+.bpmn-icon-hand-tool:before { content: '\e868'; } /* '' */
+.bpmn-icon-group:before { content: '\e869'; } /* '' */
+.bpmn-icon-transaction:before { content: '\e8c4'; } /* '' */

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 10 - 0
public/bpmnjs/vendor/bpmn-font/css/bpmn-embedded.css


+ 164 - 0
public/bpmnjs/vendor/bpmn-font/css/bpmn.css

@@ -0,0 +1,164 @@
+@font-face {
+  font-family: 'bpmn';
+  src: url('../font/bpmn.eot?26374340');
+  src: url('../font/bpmn.eot?26374340#iefix') format('embedded-opentype'),
+       url('../font/bpmn.woff2?26374340') format('woff2'),
+       url('../font/bpmn.woff?26374340') format('woff'),
+       url('../font/bpmn.ttf?26374340') format('truetype'),
+       url('../font/bpmn.svg?26374340#bpmn') format('svg');
+  font-weight: normal;
+  font-style: normal;
+}
+/* Chrome hack: SVG is rendered more smooth in Windozze. 100% magic, uncomment if you need it. */
+/* Note, that will break hinting! In other OS-es font will be not as sharp as it could be */
+/*
+@media screen and (-webkit-min-device-pixel-ratio:0) {
+  @font-face {
+    font-family: 'bpmn';
+    src: url('../font/bpmn.svg?26374340#bpmn') format('svg');
+  }
+}
+*/
+
+ [class^="bpmn-icon-"]:before, [class*=" bpmn-icon-"]:before {
+  font-family: "bpmn";
+  font-style: normal;
+  font-weight: normal;
+  speak: never;
+
+  display: inline-block;
+  text-decoration: inherit;
+  width: 1em;
+  /* margin-right: .2em; */
+  text-align: center;
+  /* opacity: .8; */
+
+  /* For safety - reset parent styles, that can break glyph codes*/
+  font-variant: normal;
+  text-transform: none;
+
+  /* fix buttons height, for twitter bootstrap */
+  line-height: 1em;
+
+  /* Animation center compensation - margins should be symmetric */
+  /* remove if not needed */
+  /* margin-left: .2em; */
+
+  /* you can be more comfortable with increased icons size */
+  /* font-size: 120%; */
+
+  /* Font smoothing. That was taken from TWBS */
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+
+  /* Uncomment for 3D effect */
+  /* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */
+}
+
+.bpmn-icon-screw-wrench:before { content: '\e800'; } /* '' */
+.bpmn-icon-trash:before { content: '\e801'; } /* '' */
+.bpmn-icon-conditional-flow:before { content: '\e802'; } /* '' */
+.bpmn-icon-default-flow:before { content: '\e803'; } /* '' */
+.bpmn-icon-gateway-parallel:before { content: '\e804'; } /* '' */
+.bpmn-icon-intermediate-event-catch-cancel:before { content: '\e805'; } /* '' */
+.bpmn-icon-intermediate-event-catch-non-interrupting-message:before { content: '\e806'; } /* '' */
+.bpmn-icon-start-event-compensation:before { content: '\e807'; } /* '' */
+.bpmn-icon-start-event-non-interrupting-parallel-multiple:before { content: '\e808'; } /* '' */
+.bpmn-icon-loop-marker:before { content: '\e809'; } /* '' */
+.bpmn-icon-parallel-mi-marker:before { content: '\e80a'; } /* '' */
+.bpmn-icon-start-event-non-interrupting-signal:before { content: '\e80b'; } /* '' */
+.bpmn-icon-intermediate-event-catch-non-interrupting-timer:before { content: '\e80c'; } /* '' */
+.bpmn-icon-intermediate-event-catch-parallel-multiple:before { content: '\e80d'; } /* '' */
+.bpmn-icon-intermediate-event-catch-compensation:before { content: '\e80e'; } /* '' */
+.bpmn-icon-gateway-xor:before { content: '\e80f'; } /* '' */
+.bpmn-icon-connection:before { content: '\e810'; } /* '' */
+.bpmn-icon-end-event-cancel:before { content: '\e811'; } /* '' */
+.bpmn-icon-intermediate-event-catch-condition:before { content: '\e812'; } /* '' */
+.bpmn-icon-intermediate-event-catch-non-interrupting-parallel-multiple:before { content: '\e813'; } /* '' */
+.bpmn-icon-start-event-condition:before { content: '\e814'; } /* '' */
+.bpmn-icon-start-event-non-interrupting-timer:before { content: '\e815'; } /* '' */
+.bpmn-icon-sequential-mi-marker:before { content: '\e816'; } /* '' */
+.bpmn-icon-user-task:before { content: '\e817'; } /* '' */
+.bpmn-icon-business-rule:before { content: '\e818'; } /* '' */
+.bpmn-icon-sub-process-marker:before { content: '\e819'; } /* '' */
+.bpmn-icon-start-event-parallel-multiple:before { content: '\e81a'; } /* '' */
+.bpmn-icon-start-event-error:before { content: '\e81b'; } /* '' */
+.bpmn-icon-intermediate-event-catch-signal:before { content: '\e81c'; } /* '' */
+.bpmn-icon-intermediate-event-catch-error:before { content: '\e81d'; } /* '' */
+.bpmn-icon-end-event-compensation:before { content: '\e81e'; } /* '' */
+.bpmn-icon-subprocess-collapsed:before { content: '\e81f'; } /* '' */
+.bpmn-icon-subprocess-expanded:before { content: '\e820'; } /* '' */
+.bpmn-icon-task:before { content: '\e821'; } /* '' */
+.bpmn-icon-end-event-error:before { content: '\e822'; } /* '' */
+.bpmn-icon-intermediate-event-catch-escalation:before { content: '\e823'; } /* '' */
+.bpmn-icon-intermediate-event-catch-timer:before { content: '\e824'; } /* '' */
+.bpmn-icon-start-event-escalation:before { content: '\e825'; } /* '' */
+.bpmn-icon-start-event-signal:before { content: '\e826'; } /* '' */
+.bpmn-icon-business-rule-task:before { content: '\e827'; } /* '' */
+.bpmn-icon-manual:before { content: '\e828'; } /* '' */
+.bpmn-icon-receive:before { content: '\e829'; } /* '' */
+.bpmn-icon-call-activity:before { content: '\e82a'; } /* '' */
+.bpmn-icon-start-event-timer:before { content: '\e82b'; } /* '' */
+.bpmn-icon-start-event-message:before { content: '\e82c'; } /* '' */
+.bpmn-icon-intermediate-event-none:before { content: '\e82d'; } /* '' */
+.bpmn-icon-intermediate-event-catch-link:before { content: '\e82e'; } /* '' */
+.bpmn-icon-end-event-escalation:before { content: '\e82f'; } /* '' */
+.bpmn-icon-text-annotation:before { content: '\e830'; } /* '' */
+.bpmn-icon-bpmn-io:before { content: '\e831'; } /* '' */
+.bpmn-icon-gateway-complex:before { content: '\e832'; } /* '' */
+.bpmn-icon-gateway-eventbased:before { content: '\e833'; } /* '' */
+.bpmn-icon-gateway-none:before { content: '\e834'; } /* '' */
+.bpmn-icon-gateway-or:before { content: '\e835'; } /* '' */
+.bpmn-icon-end-event-terminate:before { content: '\e836'; } /* '' */
+.bpmn-icon-end-event-signal:before { content: '\e837'; } /* '' */
+.bpmn-icon-end-event-none:before { content: '\e838'; } /* '' */
+.bpmn-icon-end-event-multiple:before { content: '\e839'; } /* '' */
+.bpmn-icon-end-event-message:before { content: '\e83a'; } /* '' */
+.bpmn-icon-end-event-link:before { content: '\e83b'; } /* '' */
+.bpmn-icon-intermediate-event-catch-message:before { content: '\e83c'; } /* '' */
+.bpmn-icon-intermediate-event-throw-compensation:before { content: '\e83d'; } /* '' */
+.bpmn-icon-start-event-multiple:before { content: '\e83e'; } /* '' */
+.bpmn-icon-script:before { content: '\e83f'; } /* '' */
+.bpmn-icon-manual-task:before { content: '\e840'; } /* '' */
+.bpmn-icon-send:before { content: '\e841'; } /* '' */
+.bpmn-icon-service:before { content: '\e842'; } /* '' */
+.bpmn-icon-receive-task:before { content: '\e843'; } /* '' */
+.bpmn-icon-user:before { content: '\e844'; } /* '' */
+.bpmn-icon-start-event-none:before { content: '\e845'; } /* '' */
+.bpmn-icon-intermediate-event-throw-escalation:before { content: '\e846'; } /* '' */
+.bpmn-icon-intermediate-event-catch-multiple:before { content: '\e847'; } /* '' */
+.bpmn-icon-intermediate-event-catch-non-interrupting-escalation:before { content: '\e848'; } /* '' */
+.bpmn-icon-intermediate-event-throw-link:before { content: '\e849'; } /* '' */
+.bpmn-icon-start-event-non-interrupting-condition:before { content: '\e84a'; } /* '' */
+.bpmn-icon-data-object:before { content: '\e84b'; } /* '' */
+.bpmn-icon-script-task:before { content: '\e84c'; } /* '' */
+.bpmn-icon-send-task:before { content: '\e84d'; } /* '' */
+.bpmn-icon-data-store:before { content: '\e84e'; } /* '' */
+.bpmn-icon-start-event-non-interrupting-escalation:before { content: '\e84f'; } /* '' */
+.bpmn-icon-intermediate-event-throw-message:before { content: '\e850'; } /* '' */
+.bpmn-icon-intermediate-event-catch-non-interrupting-multiple:before { content: '\e851'; } /* '' */
+.bpmn-icon-intermediate-event-catch-non-interrupting-signal:before { content: '\e852'; } /* '' */
+.bpmn-icon-intermediate-event-throw-multiple:before { content: '\e853'; } /* '' */
+.bpmn-icon-start-event-non-interrupting-message:before { content: '\e854'; } /* '' */
+.bpmn-icon-ad-hoc-marker:before { content: '\e855'; } /* '' */
+.bpmn-icon-service-task:before { content: '\e856'; } /* '' */
+.bpmn-icon-task-none:before { content: '\e857'; } /* '' */
+.bpmn-icon-compensation-marker:before { content: '\e858'; } /* '' */
+.bpmn-icon-start-event-non-interrupting-multiple:before { content: '\e859'; } /* '' */
+.bpmn-icon-intermediate-event-throw-signal:before { content: '\e85a'; } /* '' */
+.bpmn-icon-intermediate-event-catch-non-interrupting-condition:before { content: '\e85b'; } /* '' */
+.bpmn-icon-participant:before { content: '\e85c'; } /* '' */
+.bpmn-icon-event-subprocess-expanded:before { content: '\e85d'; } /* '' */
+.bpmn-icon-lane-insert-below:before { content: '\e85e'; } /* '' */
+.bpmn-icon-space-tool:before { content: '\e85f'; } /* '' */
+.bpmn-icon-connection-multi:before { content: '\e860'; } /* '' */
+.bpmn-icon-lane:before { content: '\e861'; } /* '' */
+.bpmn-icon-lasso-tool:before { content: '\e862'; } /* '' */
+.bpmn-icon-lane-insert-above:before { content: '\e863'; } /* '' */
+.bpmn-icon-lane-divide-three:before { content: '\e864'; } /* '' */
+.bpmn-icon-lane-divide-two:before { content: '\e865'; } /* '' */
+.bpmn-icon-data-input:before { content: '\e866'; } /* '' */
+.bpmn-icon-data-output:before { content: '\e867'; } /* '' */
+.bpmn-icon-hand-tool:before { content: '\e868'; } /* '' */
+.bpmn-icon-group:before { content: '\e869'; } /* '' */
+.bpmn-icon-transaction:before { content: '\e8c4'; } /* '' */

BIN
public/bpmnjs/vendor/bpmn-font/font/bpmn.eot


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 20 - 0
public/bpmnjs/vendor/bpmn-font/font/bpmn.svg


BIN
public/bpmnjs/vendor/bpmn-font/font/bpmn.ttf


BIN
public/bpmnjs/vendor/bpmn-font/font/bpmn.woff


BIN
public/bpmnjs/vendor/bpmn-font/font/bpmn.woff2


+ 820 - 0
public/bpmnjs/vendor/diagram-js.css

@@ -0,0 +1,820 @@
+/**
+ * color definitions
+ */
+.djs-container {
+  --blue-base-65: #4d90ff;
+  --blue-base-65-opacity-30: rgba(77, 144, 255, 0.3);
+  --blue-darken-48: #005df7;
+  --blue-darken-55: #1a70ff;
+  --blue-lighten-82: #a2c5ff;
+
+  --orange-base-60: #ffa533;
+  --orange-base-60-opacity-30: rgba(255, 165, 51, 0.3);
+  --orange-base-60-opacity-50: rgba(255, 165, 51, 0.5);
+  --orange-lighten-85: #ffddb3;
+
+  --red-base-62: #ff3d3d;
+  --red-base-62-opacity-10: rgba(255, 61, 61, 0.1);
+
+  --silver-darken-94: #efefef;
+
+  --color-000000: #000000;
+  --color-000000-opacity-05: rgba(0, 0, 0, 0.05);
+  --color-000000-opacity-10: rgba(0, 0, 0, 0.1);
+  --color-333333: #333333;
+  --color-666666: #666666;
+  --color-aaaaaa: #aaaaaa;
+  --color-cccccc: #cccccc;
+  --color-cdcdcd: #cdcdcd;
+  --color-dddddd: #dddddd;
+  --color-f6f6f6: #f6f6f6;
+  --color-fafafa: #fafafa;
+  --color-fefefe: #fefefe;
+  --color-ffffff: #ffffff;
+
+  --bendpoint-fill-color: var(--blue-base-65-opacity-30);
+  --bendpoint-stroke-color: var(--blue-base-65);
+
+  --context-pad-entry-background-color: var(--color-fefefe);
+  --context-pad-entry-hover-background-color: var(--silver-darken-94);
+ 
+  --element-dragger-color: var(--blue-base-65);
+  --element-hover-outline-fill-color: var(--blue-darken-48);
+  --element-selected-outline-stroke-color: var(--blue-base-65);
+
+  --lasso-fill-color: var(--color-000000-opacity-05);
+  --lasso-stroke-color: var(--color-000000);
+
+  --palette-entry-color: var(--color-333333);
+  --palette-entry-hover-color: var(--blue-darken-48);
+  --palette-entry-selected-color: var(--blue-base-65);
+  --palette-separator-color: var(--color-aaaaaa);
+  --palette-toggle-hover-background-color: var(--color-666666);
+  --palette-background-color: var(--color-fafafa);
+  --palette-border-color: var(--color-cccccc);
+
+  --popup-body-background-color: var(--color-fefefe);
+  --popup-header-entry-selected-color: var(--blue-base-65);
+  --popup-header-entry-selected-background-color: var(--color-000000-opacity-10);
+  --popup-header-separator-color: var(--color-dddddd);
+  --popup-background-color: var(--color-fafafa);
+  --popup-border-color: var(--color-cccccc);
+ 
+  --resizer-fill-color: var(--blue-base-65-opacity-30);
+  --resizer-stroke-color: var(--blue-base-65);
+
+  --search-container-background-color: var(--color-fafafa);
+  --search-container-border-color: var(--blue-darken-55);
+  --search-container-box-shadow-color: var(--blue-lighten-82);
+  --search-container-box-shadow-inset-color: var(--color-cdcdcd);
+  --search-input-border-color: var(--color-cccccc);
+  --search-result-border-color: var(--color-aaaaaa);
+  --search-result-highlight-color: var(--color-000000);
+  --search-result-selected-color: var(--blue-base-65-opacity-30);
+
+  --shape-attach-allowed-stroke-color: var(--blue-base-65);
+  --shape-connect-allowed-fill-color: var(--color-000000-opacity-05);
+  --shape-drop-allowed-fill-color: var(--color-000000-opacity-05);
+  --shape-drop-not-allowed-fill-color: var(--red-base-62-opacity-10);
+  --shape-resize-preview-stroke-color: var(--blue-base-65);
+
+  --snap-line-stroke-color: var(--blue-base-65-opacity-30);
+
+  --space-tool-crosshair-stroke-color: var(--color-000000);
+ 
+  --tooltip-error-background-color: var(--red-base-62-opacity-10);
+  --tooltip-error-border-color: var(--red-base-62);
+  --tooltip-error-color: var(--red-base-62);
+}
+
+/**
+ * outline styles
+ */
+
+.djs-outline {
+  fill: none;
+  visibility: hidden;
+}
+
+.djs-element.hover .djs-outline,
+.djs-element.selected .djs-outline {
+  visibility: visible;
+  shape-rendering: geometricPrecision;
+  stroke-dasharray: 3,3;
+}
+
+.djs-element.selected .djs-outline {
+  stroke: var(--element-selected-outline-stroke-color);
+  stroke-width: 1px;
+}
+
+.djs-element.hover .djs-outline {
+  stroke: var(--element-hover-outline-fill-color);
+  stroke-width: 1px;
+}
+
+.djs-shape.connect-ok .djs-visual > :nth-child(1) {
+  fill: var(--shape-connect-allowed-fill-color) !important;
+}
+
+.djs-shape.connect-not-ok .djs-visual > :nth-child(1),
+.djs-shape.drop-not-ok .djs-visual > :nth-child(1) {
+  fill: var(--shape-drop-not-allowed-fill-color) !important;
+}
+
+.djs-shape.new-parent .djs-visual > :nth-child(1) {
+  fill: var(--shape-drop-allowed-fill-color) !important;
+}
+
+svg.drop-not-ok {
+  background: var(--shape-drop-not-allowed-fill-color) !important;
+}
+
+svg.new-parent {
+  background: var(--shape-drop-allowed-fill-color) !important;
+}
+
+.djs-connection.connect-ok .djs-visual > :nth-child(1),
+.djs-connection.drop-ok .djs-visual > :nth-child(1) {
+  stroke: var(--shape-drop-allowed-fill-color) !important;
+}
+
+.djs-connection.connect-not-ok .djs-visual > :nth-child(1),
+.djs-connection.drop-not-ok .djs-visual > :nth-child(1) {
+  stroke: var(--shape-drop-not-allowed-fill-color) !important;
+}
+
+.drop-not-ok,
+.connect-not-ok {
+  stroke: var(--shape-attach-allowed-stroke-color) !important;
+  cursor: not-allowed;
+}
+
+.djs-element.attach-ok .djs-visual > :nth-child(1) {
+  stroke-width: 5px !important;
+}
+
+.djs-frame.connect-not-ok .djs-visual > :nth-child(1),
+.djs-frame.drop-not-ok .djs-visual > :nth-child(1) {
+  stroke-width: 3px !important;
+  stroke: var(--shape-drop-not-allowed-fill-color) !important;
+  fill: none !important;
+}
+
+/**
+* Selection box style
+*
+*/
+.djs-lasso-overlay {
+  fill: var(--lasso-fill-color);
+
+  stroke-dasharray: 5 1 3 1;
+  stroke: var(--lasso-stroke-color);
+
+  shape-rendering: geometricPrecision;
+  pointer-events: none;
+}
+
+/**
+ * Resize styles
+ */
+.djs-resize-overlay {
+  fill: none;
+
+  stroke-dasharray: 5 1 3 1;
+  stroke: var(--shape-resize-preview-stroke-color);
+
+  pointer-events: none;
+}
+
+.djs-resizer-hit {
+  fill: none;
+  pointer-events: all;
+}
+
+.djs-resizer-visual {
+  fill: var(--resizer-fill-color);;
+  stroke-width: 1px;
+  stroke-opacity: 0.5;
+  stroke: var(--resizer-stroke-color);
+  shape-rendering: geometricprecision;
+}
+
+.djs-resizer:hover .djs-resizer-visual {
+  stroke: var(--resizer-stroke-color);
+  stroke-opacity: 1;
+}
+
+.djs-cursor-resize-ns,
+.djs-resizer-n,
+.djs-resizer-s {
+  cursor: ns-resize;
+}
+
+.djs-cursor-resize-ew,
+.djs-resizer-e,
+.djs-resizer-w {
+  cursor: ew-resize;
+}
+
+.djs-cursor-resize-nwse,
+.djs-resizer-nw,
+.djs-resizer-se {
+  cursor: nwse-resize;
+}
+
+.djs-cursor-resize-nesw,
+.djs-resizer-ne,
+.djs-resizer-sw {
+  cursor: nesw-resize;
+}
+
+.djs-shape.djs-resizing > .djs-outline {
+  visibility: hidden !important;
+}
+
+.djs-shape.djs-resizing > .djs-resizer {
+  visibility: hidden;
+}
+
+.djs-dragger > .djs-resizer {
+  visibility: hidden;
+}
+
+/**
+ * drag styles
+ */
+.djs-dragger * {
+  fill: none !important;
+  stroke: var(--element-dragger-color) !important;
+}
+
+.djs-dragger tspan,
+.djs-dragger text {
+  fill: var(--element-dragger-color) !important;
+  stroke: none !important;
+}
+
+marker.djs-dragger circle,
+marker.djs-dragger path,
+marker.djs-dragger polygon,
+marker.djs-dragger polyline,
+marker.djs-dragger rect {
+  fill: var(--element-dragger-color) !important;
+  stroke: none !important;
+}
+
+marker.djs-dragger text,
+marker.djs-dragger tspan {
+  fill: none !important;
+  stroke: var(--element-dragger-color) !important;
+}
+
+.djs-dragging {
+  opacity: 0.3;
+}
+
+.djs-dragging,
+.djs-dragging > * {
+  pointer-events: none !important;
+}
+
+.djs-dragging .djs-context-pad,
+.djs-dragging .djs-outline {
+  display: none !important;
+}
+
+/**
+ * no pointer events for visual
+ */
+.djs-visual,
+.djs-outline {
+  pointer-events: none;
+}
+
+.djs-element.attach-ok .djs-hit {
+  stroke-width: 60px !important;
+}
+
+/**
+ * all pointer events for hit shape
+ */
+.djs-element > .djs-hit-all {
+  pointer-events: all;
+}
+
+.djs-element > .djs-hit-stroke,
+.djs-element > .djs-hit-click-stroke {
+  pointer-events: stroke;
+}
+
+/**
+ * all pointer events for hit shape
+ */
+.djs-drag-active .djs-element > .djs-hit-click-stroke {
+  pointer-events: all;
+}
+
+/**
+ * shape / connection basic styles
+ */
+.djs-connection .djs-visual {
+  stroke-width: 2px;
+  fill: none;
+}
+
+.djs-cursor-grab {
+  cursor: -webkit-grab;
+  cursor: -moz-grab;
+  cursor: grab;
+}
+
+.djs-cursor-grabbing {
+  cursor: -webkit-grabbing;
+  cursor: -moz-grabbing;
+  cursor: grabbing;
+}
+
+.djs-cursor-crosshair {
+  cursor: crosshair;
+}
+
+.djs-cursor-move {
+  cursor: move;
+}
+
+.djs-cursor-resize-ns {
+  cursor: ns-resize;
+}
+
+.djs-cursor-resize-ew {
+  cursor: ew-resize;
+}
+
+
+/**
+ * snapping
+ */
+.djs-snap-line {
+  stroke: var(--snap-line-stroke-color);
+  stroke-linecap: round;
+  stroke-width: 2px;
+  pointer-events: none;
+}
+
+/**
+ * snapping
+ */
+.djs-crosshair {
+  stroke: var(--space-tool-crosshair-stroke-color);
+  stroke-linecap: round;
+  stroke-width: 1px;
+  pointer-events: none;
+  shape-rendering: crispEdges;
+  stroke-dasharray: 5, 5;
+}
+
+/**
+ * palette
+ */
+
+.djs-palette {
+  position: absolute;
+  left: 20px;
+  top: 20px;
+
+  box-sizing: border-box;
+  width: 48px;
+}
+
+.djs-palette .separator {
+  margin: 0 5px;
+  padding-top: 5px;
+
+  border: none;
+  border-bottom: solid 1px var(--palette-separator-color);
+
+  clear: both;
+}
+
+.djs-palette .entry:before {
+  vertical-align: text-bottom;
+}
+
+.djs-palette .djs-palette-toggle {
+  cursor: pointer;
+}
+
+.djs-palette .entry,
+.djs-palette .djs-palette-toggle {
+  color: var(--palette-entry-color);
+  font-size: 30px;
+
+  text-align: center;
+}
+
+.djs-palette .entry {
+  float: left;
+}
+
+.djs-palette .entry img {
+  max-width: 100%;
+}
+
+.djs-palette .djs-palette-entries:after {
+  content: '';
+  display: table;
+  clear: both;
+}
+
+.djs-palette .djs-palette-toggle:hover {
+  background: var(--palette-toggle-hover-background-color);
+}
+
+.djs-palette .entry:hover {
+  color: var(--palette-entry-hover-color);
+}
+
+.djs-palette .highlighted-entry {
+  color: var(--palette-entry-selected-color) !important;
+}
+
+.djs-palette .entry,
+.djs-palette .djs-palette-toggle {
+  width: 46px;
+  height: 46px;
+  line-height: 46px;
+  cursor: default;
+}
+
+/**
+ * Palette open / two-column layout is controlled via
+ * classes on the palette. Events to hook into palette
+ * changed life-cycle are available in addition.
+ */
+.djs-palette.two-column.open {
+  width: 94px;
+}
+
+.djs-palette:not(.open) .djs-palette-entries {
+  display: none;
+}
+
+.djs-palette:not(.open) {
+  overflow: hidden;
+}
+
+.djs-palette.open .djs-palette-toggle {
+  display: none;
+}
+
+/**
+ * context-pad
+ */
+.djs-overlay-context-pad {
+  width: 72px;
+}
+
+.djs-context-pad {
+  position: absolute;
+  display: none;
+  pointer-events: none;
+}
+
+.djs-context-pad .entry {
+  width: 22px;
+  height: 22px;
+  text-align: center;
+  display: inline-block;
+  font-size: 22px;
+  margin: 0 2px 2px 0;
+
+  border-radius: 3px;
+
+  cursor: default;
+
+  background-color: var(--context-pad-entry-background-color);
+  box-shadow: 0 0 2px 1px var(--context-pad-entry-background-color);
+  pointer-events: all;
+}
+
+.djs-context-pad .entry:before {
+  vertical-align: top;
+}
+
+.djs-context-pad .entry:hover {
+  background: var(--context-pad-entry-hover-background-color);
+}
+
+.djs-context-pad.open {
+  display: block;
+}
+
+/**
+ * popup styles
+ */
+.djs-popup .entry {
+  line-height: 20px;
+  white-space: nowrap;
+  cursor: default;
+}
+
+/* larger font for prefixed icons */
+.djs-popup .entry:before {
+  vertical-align: middle;
+  font-size: 20px;
+}
+
+.djs-popup .entry > span {
+  vertical-align: middle;
+  font-size: 14px;
+}
+
+.djs-popup .entry:hover,
+.djs-popup .entry.active:hover {
+  background: var(--popup-header-entry-selected-background-color);
+}
+
+.djs-popup .entry.disabled {
+  background: inherit;
+}
+
+.djs-popup .djs-popup-header .entry {
+  display: inline-block;
+  padding: 2px 3px 2px 3px;
+
+  border: solid 1px transparent;
+  border-radius: 3px;
+}
+
+.djs-popup .djs-popup-header .entry.active {
+  color: var(--popup-header-entry-selected-color);
+  border: solid 1px var(--popup-header-entry-selected-color);
+  background-color: var(--popup-header-entry-selected-background-color);
+}
+
+.djs-popup-body .entry {
+  padding: 4px 10px 4px 5px;
+}
+
+.djs-popup-body .entry > span {
+  margin-left: 5px;
+}
+
+.djs-popup-body {
+  background-color: var(--popup-body-background-color);
+}
+
+.djs-popup-header {
+  border-bottom: 1px solid var(--popup-header-separator-color);
+}
+
+.djs-popup-header .entry {
+  margin: 1px;
+  margin-left: 3px;
+}
+
+.djs-popup-header .entry:last-child {
+  margin-right: 3px;
+}
+
+/**
+ * popup / palette styles
+ */
+.djs-palette {
+  background: var(--palette-background-color);
+  border: solid 1px var(--palette-border-color);
+  border-radius: 2px;
+}
+
+.djs-popup {
+  background: var(--popup-background-color);
+  border: solid 1px var(--popup-border-color);
+  border-radius: 2px;
+}
+
+/**
+ * touch
+ */
+
+.djs-shape,
+.djs-connection {
+  touch-action: none;
+}
+
+.djs-segment-dragger,
+.djs-bendpoint {
+  display: none;
+}
+
+/**
+ * bendpoints
+ */
+.djs-segment-dragger .djs-visual {
+  display: none;
+
+  fill: var(--bendpoint-fill-color);
+  stroke: var(--bendpoint-stroke-color);
+  stroke-width: 1px;
+  stroke-opacity: 1;
+}
+
+.djs-segment-dragger:hover .djs-visual {
+  display: block;
+}
+
+.djs-bendpoint .djs-visual {
+  fill: var(--bendpoint-fill-color);
+  stroke: var(--bendpoint-stroke-color);
+  stroke-width: 1px;
+  stroke-opacity: 0.5;
+}
+
+.djs-segment-dragger:hover,
+.djs-bendpoints.hover .djs-segment-dragger,
+.djs-bendpoints.selected .djs-segment-dragger,
+.djs-bendpoint:hover,
+.djs-bendpoints.hover .djs-bendpoint,
+.djs-bendpoints.selected .djs-bendpoint {
+  display: block;
+}
+
+.djs-drag-active .djs-bendpoints * {
+  display: none;
+}
+
+.djs-bendpoints:not(.hover) .floating {
+  display: none;
+}
+
+.djs-segment-dragger:hover .djs-visual,
+.djs-segment-dragger.djs-dragging .djs-visual,
+.djs-bendpoint:hover .djs-visual,
+.djs-bendpoint.floating .djs-visual {
+  fill: var(--bendpoint-fill-color);
+  stroke: var(--bendpoint-stroke-color);
+  stroke-opacity: 1;
+}
+
+.djs-bendpoint.floating .djs-hit {
+  pointer-events: none;
+}
+
+.djs-segment-dragger .djs-hit,
+.djs-bendpoint .djs-hit {
+  fill: none;
+  pointer-events: all;
+}
+
+.djs-segment-dragger.horizontal .djs-hit {
+  cursor: ns-resize;
+}
+
+.djs-segment-dragger.vertical .djs-hit {
+  cursor: ew-resize;
+}
+
+.djs-segment-dragger.djs-dragging .djs-hit {
+  pointer-events: none;
+}
+
+.djs-updating,
+.djs-updating > * {
+  pointer-events: none !important;
+}
+
+.djs-updating .djs-context-pad,
+.djs-updating .djs-outline,
+.djs-updating .djs-bendpoint,
+.connect-ok .djs-bendpoint,
+.connect-not-ok .djs-bendpoint,
+.drop-ok .djs-bendpoint,
+.drop-not-ok .djs-bendpoint {
+  display: none !important;
+}
+
+.djs-segment-dragger.djs-dragging,
+.djs-bendpoint.djs-dragging {
+  display: block;
+  opacity: 1.0;
+}
+
+
+/**
+ * tooltips
+ */
+.djs-tooltip-error {
+  width: 160px;
+  padding: 6px;
+
+  background: var(--tooltip-error-background-color);
+  border: solid 1px var(--tooltip-error-border-color);
+  border-radius: 2px;
+  color: var(--tooltip-error-color);
+  font-size: 12px;
+  line-height: 16px;
+  
+  opacity: 0.75;
+}
+
+.djs-tooltip-error:hover {
+  opacity: 1;
+}
+
+
+/**
+ * search pad
+ */
+.djs-search-container {
+  position: absolute;
+  top: 20px;
+  left: 0;
+  right: 0;
+  margin-left: auto;
+  margin-right: auto;
+
+  width: 25%;
+  min-width: 300px;
+  max-width: 400px;
+  z-index: 10;
+
+  font-size: 1.05em;
+  opacity: 0.9;
+  background: var(--search-container-background-color);
+  border: solid 1px var(--search-container-border-color);
+  border-radius: 2px;
+  box-shadow: 0 0 0 2px var(--search-container-box-shadow-color), 0 0 0 1px var(--search-container-box-shadow-inset-color) inset;
+}
+
+.djs-search-container:not(.open) {
+  display: none;
+}
+
+.djs-search-input input {
+  font-size: 1.05em;
+  width: 100%;
+  padding: 6px 10px;
+  border: 1px solid var(--search-input-border-color);
+}
+
+.djs-search-input input:focus {
+  outline: none;
+  border-color: var(--search-input-border-color);
+}
+
+.djs-search-results {
+  position: relative;
+  overflow-y: auto;
+  max-height: 200px;
+}
+
+.djs-search-results:hover {
+  cursor: pointer;
+}
+
+.djs-search-result {
+  width: 100%;
+  padding: 6px 10px;
+  background: white;
+  border-bottom: solid 1px var(--search-result-border-color);
+  border-radius: 1px;
+}
+
+.djs-search-highlight {
+  color: var(--search-result-highlight-color);
+}
+
+.djs-search-result-primary {
+  margin: 0 0 10px;
+}
+
+.djs-search-result-secondary {
+  font-family: monospace;
+  margin: 0;
+}
+
+.djs-search-result:hover {
+  background: var(--search-result-selected-color);
+}
+
+.djs-search-result-selected {
+  background: var(--search-result-selected-color);
+}
+
+.djs-search-result-selected:hover {
+  background: var(--search-result-selected-color);
+}
+
+.djs-search-overlay {
+  background: var(--search-result-selected-color);
+}
+
+/**
+ * hidden styles
+ */
+.djs-element-hidden,
+.djs-element-hidden .djs-hit,
+.djs-element-hidden .djs-outline,
+.djs-label-hidden .djs-label {
+  display: none !important;
+}

BIN
public/favicon.ico


+ 207 - 0
public/index.html

@@ -0,0 +1,207 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+    <meta name="renderer" content="webkit">
+    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
+    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
+    <title><%= webpackConfig.name %></title>
+	  <style>
+    html,
+    body,
+    #app {
+      height: 100%;
+      margin: 0px;
+      padding: 0px;
+    }
+    .chromeframe {
+      margin: 0.2em 0;
+      background: #ccc;
+      color: #000;
+      padding: 0.2em 0;
+    }
+
+    #loader-wrapper {
+      position: fixed;
+      top: 0;
+      left: 0;
+      width: 100%;
+      height: 100%;
+      z-index: 999999;
+    }
+
+    #loader {
+      display: block;
+      position: relative;
+      left: 50%;
+      top: 50%;
+      width: 150px;
+      height: 150px;
+      margin: -75px 0 0 -75px;
+      border-radius: 50%;
+      border: 3px solid transparent;
+      border-top-color: #FFF;
+      -webkit-animation: spin 2s linear infinite;
+      -ms-animation: spin 2s linear infinite;
+      -moz-animation: spin 2s linear infinite;
+      -o-animation: spin 2s linear infinite;
+      animation: spin 2s linear infinite;
+      z-index: 1001;
+    }
+
+    #loader:before {
+      content: "";
+      position: absolute;
+      top: 5px;
+      left: 5px;
+      right: 5px;
+      bottom: 5px;
+      border-radius: 50%;
+      border: 3px solid transparent;
+      border-top-color: #FFF;
+      -webkit-animation: spin 3s linear infinite;
+      -moz-animation: spin 3s linear infinite;
+      -o-animation: spin 3s linear infinite;
+      -ms-animation: spin 3s linear infinite;
+      animation: spin 3s linear infinite;
+    }
+
+    #loader:after {
+      content: "";
+      position: absolute;
+      top: 15px;
+      left: 15px;
+      right: 15px;
+      bottom: 15px;
+      border-radius: 50%;
+      border: 3px solid transparent;
+      border-top-color: #FFF;
+      -moz-animation: spin 1.5s linear infinite;
+      -o-animation: spin 1.5s linear infinite;
+      -ms-animation: spin 1.5s linear infinite;
+      -webkit-animation: spin 1.5s linear infinite;
+      animation: spin 1.5s linear infinite;
+    }
+
+
+    @-webkit-keyframes spin {
+      0% {
+        -webkit-transform: rotate(0deg);
+        -ms-transform: rotate(0deg);
+        transform: rotate(0deg);
+      }
+      100% {
+        -webkit-transform: rotate(360deg);
+        -ms-transform: rotate(360deg);
+        transform: rotate(360deg);
+      }
+    }
+
+    @keyframes spin {
+      0% {
+        -webkit-transform: rotate(0deg);
+        -ms-transform: rotate(0deg);
+        transform: rotate(0deg);
+      }
+      100% {
+        -webkit-transform: rotate(360deg);
+        -ms-transform: rotate(360deg);
+        transform: rotate(360deg);
+      }
+    }
+
+
+    #loader-wrapper .loader-section {
+      position: fixed;
+      top: 0;
+      width: 51%;
+      height: 100%;
+      background: #7171C6;
+      z-index: 1000;
+      -webkit-transform: translateX(0);
+      -ms-transform: translateX(0);
+      transform: translateX(0);
+    }
+
+    #loader-wrapper .loader-section.section-left {
+      left: 0;
+    }
+
+    #loader-wrapper .loader-section.section-right {
+      right: 0;
+    }
+
+
+    .loaded #loader-wrapper .loader-section.section-left {
+      -webkit-transform: translateX(-100%);
+      -ms-transform: translateX(-100%);
+      transform: translateX(-100%);
+      -webkit-transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
+      transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
+    }
+
+    .loaded #loader-wrapper .loader-section.section-right {
+      -webkit-transform: translateX(100%);
+      -ms-transform: translateX(100%);
+      transform: translateX(100%);
+      -webkit-transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
+      transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
+    }
+
+    .loaded #loader {
+      opacity: 0;
+      -webkit-transition: all 0.3s ease-out;
+      transition: all 0.3s ease-out;
+    }
+
+    .loaded #loader-wrapper {
+      visibility: hidden;
+      -webkit-transform: translateY(-100%);
+      -ms-transform: translateY(-100%);
+      transform: translateY(-100%);
+      -webkit-transition: all 0.3s 1s ease-out;
+      transition: all 0.3s 1s ease-out;
+    }
+
+    .no-js #loader-wrapper {
+      display: none;
+    }
+
+    .no-js h1 {
+      color: #222222;
+    }
+
+    #loader-wrapper .load_title {
+      font-family: 'Open Sans';
+      color: #FFF;
+      font-size: 19px;
+      width: 100%;
+      text-align: center;
+      z-index: 9999999999999;
+      position: absolute;
+      top: 60%;
+      opacity: 1;
+      line-height: 30px;
+    }
+
+    #loader-wrapper .load_title span {
+      font-weight: normal;
+      font-style: italic;
+      font-size: 13px;
+      color: #FFF;
+      opacity: 0.5;
+    }
+  </style>
+  </head>
+  <body>
+    <div id="app">
+	    <div id="loader-wrapper">
+		    <div id="loader"></div>
+		    <div class="loader-section section-left"></div>
+		    <div class="loader-section section-right"></div>
+		    <div class="load_title">正在加载系统资源,请耐心等待</div>
+        </div>
+	</div>
+  </body>
+</html>

+ 2 - 0
public/robots.txt

@@ -0,0 +1,2 @@
+User-agent: *
+Disallow: /

+ 3 - 0
ruoyi-bpmnjs/.gitignore

@@ -0,0 +1,3 @@
+node_modules/
+tmp/
+dist/

+ 1 - 0
ruoyi-bpmnjs/.npmrc

@@ -0,0 +1 @@
+package-lock=false

+ 140 - 0
ruoyi-bpmnjs/Gruntfile.js

@@ -0,0 +1,140 @@
+  var path = require('path');
+
+module.exports = function(grunt) {
+
+  require('load-grunt-tasks')(grunt);
+
+  /**
+   * Resolve external project resource as file path
+   */
+  function resolvePath(project, file) {
+    return path.join(path.dirname(require.resolve(project)), file);
+  }
+
+
+  grunt.initConfig({
+    browserify: {
+      options: {
+        browserifyOptions: {
+          debug: true
+        },
+        transform: [
+          [ 'stringify', {
+            extensions: [ '.bpmn' ]
+          } ]
+        ],
+        plugin: [
+          'esmify'
+        ]
+      },
+      watch: {
+        options: {
+          watch: true
+        },
+        files: {
+          'dist/index.js': [ 'app/**/*.js' ]
+        }
+      },
+      app: {
+        files: {
+          'dist/index.js': [ 'app/**/*.js' ]
+        }
+      }
+    },
+
+    copy: {
+      diagram_js: {
+        files: [
+          {
+            src: resolvePath('diagram-js', 'assets/diagram-js.css'),
+            dest: 'dist/css/diagram-js.css'
+          }
+        ]
+      },
+      bpmn_js: {
+        files: [
+          {
+            expand: true,
+            cwd: resolvePath('bpmn-js', 'dist/assets'),
+            src: ['**/*.*', '!**/*.js'],
+            dest: 'dist/vendor'
+          }
+        ]
+      },
+      app: {
+        files: [
+          {
+            expand: true,
+            cwd: 'app/',
+            src: ['**/*.*', '!**/*.js'],
+            dest: 'dist'
+          }
+        ]
+      }
+    },
+
+    less: {
+      options: {
+        dumpLineNumbers: 'comments',
+        paths: [
+          'node_modules'
+        ]
+      },
+
+      styles: {
+        files: {
+          'dist/css/app.css': 'styles/app.less'
+        }
+      }
+    },
+
+    watch: {
+      options: {
+        livereload: true
+      },
+
+      samples: {
+        files: [ 'app/**/*.*' ],
+        tasks: [ 'copy:app' ]
+      },
+
+      less: {
+        files: [
+          'styles/**/*.less',
+          'node_modules/bpmn-js-properties-panel/styles/**/*.less'
+        ],
+        tasks: [
+          'less'
+        ]
+      },
+    },
+
+    connect: {
+      livereload: {
+        options: {
+          port: 9013,
+          livereload: true,
+          hostname: 'localhost',
+          open: true,
+          base: [
+            'dist'
+          ]
+        }
+      }
+    }
+  });
+
+  // tasks
+
+  grunt.registerTask('build', [ 'copy', 'less', 'browserify:app' ]);
+
+  grunt.registerTask('auto-build', [
+    'copy',
+    'less',
+    'browserify:watch',
+    'connect:livereload',
+    'watch'
+  ]);
+
+  grunt.registerTask('default', [ 'build' ]);
+};

+ 76 - 0
ruoyi-bpmnjs/README.md

@@ -0,0 +1,76 @@
+# bpmn-js Modeler + Properties Panel Example
+
+This example uses [bpmn-js](https://github.com/bpmn-io/bpmn-js) and [bpmn-js-properties-panel](https://github.com/bpmn-io/bpmn-js-properties-panel). It implements a BPMN 2.0 modeler that allows you to edit execution related properties via a properties panel.
+
+
+## About
+
+This example is a node-style web application that builds a user interface around the bpmn-js BPMN 2.0 modeler.
+
+![demo application screenshot](https://raw.githubusercontent.com/bpmn-io/bpmn-js-examples/master/properties-panel/docs/screenshot.png "Screenshot of the modeler + properties panel example")
+
+
+## Usage
+
+Add the [properties panel](https://github.com/bpmn-io/bpmn-js-properties-panel) to your project:
+
+```
+npm install --save bpmn-js-properties-panel
+```
+
+Additionally, if you'd like to use [Camunda BPM](https://camunda.org) execution related properties, include the [camunda-bpmn-moddle](https://github.com/camunda/camunda-bpmn-moddle) dependency which tells the modeler about `camunda:XXX` extension properties:
+
+```
+npm install --save camunda-bpmn-moddle
+```
+
+Now extend the [bpmn-js](https://github.com/bpmn-io/bpmn-js) modeler with two properties panel related modules, the panel itself and a provider module that controls which properties are visible for each element. Additionally you must pass an element via `propertiesPanel.parent` into which the properties panel will be rendered (cf. [`app/index.js`](https://github.com/bpmn-io/bpmn-js-examples/blob/master/properties-panel/app/index.js#L16) for details).
+
+```javascript
+var propertiesPanelModule = require('bpmn-js-properties-panel'),
+    // providing camunda executable properties, too
+    propertiesProviderModule = require('bpmn-js-properties-panel/lib/provider/camunda'),
+    camundaModdleDescriptor = require('camunda-bpmn-moddle/resources/camunda');
+
+var bpmnModeler = new BpmnModeler({
+  container: '#js-canvas',
+  propertiesPanel: {
+    parent: '#js-properties-panel'
+  },
+  additionalModules: [
+    propertiesPanelModule,
+    propertiesProviderModule
+  ],
+  // needed if you'd like to maintain camunda:XXX properties in the properties panel
+  moddleExtensions: {
+    camunda: camundaModdleDescriptor
+  }
+});
+```
+
+
+## Building the Example
+
+You need a [NodeJS](http://nodejs.org) development stack with [npm](https://npmjs.org) and installed to build the project.
+
+To install all project dependencies execute
+
+```
+npm install
+```
+
+Build the example using [browserify](http://browserify.org) via
+
+```
+npm run all
+```
+
+You may also spawn a development setup by executing
+
+```
+npm run dev
+```
+
+Both tasks generate the distribution ready client-side modeler application into the `dist` folder.
+
+Serve the application locally or via a web server (nginx, apache, embedded).

BIN
ruoyi-bpmnjs/app/favicon.ico


+ 110 - 0
ruoyi-bpmnjs/app/index.html

@@ -0,0 +1,110 @@
+<!DOCTYPE html>
+<html >
+
+<head>
+  <meta charset="utf-8" />
+  <title>在线绘制流程</title>
+  <link rel="icon" href="favicon.ico">
+  <link rel="stylesheet" href="css/diagram-js.css" />
+  <link rel="stylesheet" href="vendor/bpmn-font/css/bpmn-embedded.css" />
+  <link rel="stylesheet" href="css/app.css" />
+
+  <style>
+    .item{
+      display: none;
+      cursor: pointer;
+    }
+    .bjs-powered-by {
+      display: none;
+    }
+    .buttons > li {
+      /* display: inline-block; */
+      margin-right: 10px;
+      height: 26px;
+      line-height: 26px;
+      float: left;
+    }
+    .buttons > li > a.btn  {
+      background: #00BCD4;
+      border: none;
+      outline: none;
+      padding: 0px 10px;
+      color: #fff;
+      display: inline-block;
+      opacity: 1;
+      height: 26px;
+      font-size: 14px;
+      line-height: 26px;
+    }
+    .label  {
+      background: #00BCD4;
+      border: none;
+      outline: none;
+      padding: 0px 10px;
+      color: #fff;
+      display: inline-block;
+      cursor: pointer;
+      opacity: 1;
+      height: 26px;
+      font-size: 14px;
+      line-height: 26px;
+    }
+    .sy-mask{width:100%;
+      height:100%;
+      position:fixed;
+      background:rgba(0,0,0,0.8);
+      left:0;top:0;z-index:1000;
+      display: none;
+    }
+    .sy-alert{
+      position:fixed;display:none;background:#fff;border-radius:5px;overflow:hidden;width:300px;max-width:90%;max-height:80%;left:0;right:0;margin:0 auto;z-index:9999}.sy-alert.animated{-webkit-animation-duration:.3s;animation-duration:.3s}.sy-alert .sy-title{height:45px;color:#333;line-height:45px;font-size:15px;border-bottom:1px solid #eee;padding:0 12px}.sy-alert .sy-content{padding:20px;text-align:center;font-size:14px;line-height:24px;color:#666;overflow-y:auto}.sy-alert .sy-btn{height:50%;border-top:1px solid #eee;overflow:hidden}.sy-alert .sy-btn button{float:left;border:0;color:#333;cursor:pointer;background:#fff;width:50%;line-height:45px;font-size:15px;text-align:center}.sy-alert .sy-btn button:nth-child(1){color:#888;border-right:1px solid #eee}.sy-alert.sy-alert-alert .sy-btn button{float:none;width:100%}.sy-alert.sy-alert-tips{text-align:center;width:150px;background:rgba(0,0,0,0.7)}.sy-alert.sy-alert-tips .sy-content{padding:8px;color:#fff;font-size:14px}.sy-alert.sy-alert-model .sy-content{text-align:left}.sy-alert.sy-alert-model .sy-content .form .input-item{margin-bottom:12px;position:relative}.sy-alert.sy-alert-model .sy-content .form .input-item input{display:block;position:relative;width:100%;border:1px solid #eee;padding:10px}.sy-alert.sy-alert-model .sy-content .form .input-item .getcode{border:0;top:0;right:0;position:absolute;background:0;line-height:37px;color:#f60;width:100px;text-align:center}
+  </style>
+</head>
+
+<body>
+<div class="content with-diagram" id="js-drop-zone">
+  <div class="message error">
+    <div class="note">
+      <p>无法显示bpms2.0</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>
+<ul class="buttons">
+  <li class="item upload">
+    <form id="form1" name="myForm" onsubmit="return false" method="post" enctype="multipart/form-data" title="上传文件">
+      <input type="file" name="uploadFile" id="uploadFile" accept=".bpmn" style="display: none">
+      <label class="label" for="uploadFile" >导入</label>
+    </form>
+  </li>
+  <li class="item download">
+    <a class="btn" href id="downloadBpmn">导出</a>
+  </li>
+  <li class="item submit">
+    <a class="btn" id="js-download-diagram">
+      部署
+    </a>
+  </li>
+</ul>
+<div class="sy-alert sy-alert-model animated" sy-enter="zoomIn" sy-leave="zoomOut" sy-type="confirm" sy-mask="true" id="alert" >
+  <div class="sy-title">部署流程</div>
+  <div class="sy-content">
+        确认是否部署该流程
+<!--    <div class="form">-->
+<!--      <p class="input-item"><input id="deploymentName" type="text" placeholder="请输入流程名称"></p>-->
+<!--    </div>-->
+  </div>
+  <div class="sy-btn">
+    <button id="sure">确定</button>
+    <button class="cancel" >取消</button>
+  </div>
+</div>
+<div class="sy-mask cancel"></div>
+<script src="index.js"></script>
+</body>
+</html>

+ 146 - 0
ruoyi-bpmnjs/app/index.js

@@ -0,0 +1,146 @@
+
+import $ from 'jquery';
+import BpmnModeler from 'bpmn-js/lib/Modeler';
+//import propertiesPanelModule from '../resources/properties-panel';
+import propertiesPanelModule from 'bpmn-js-properties-panel';
+import propertiesProviderModule from '../resources/properties-panel/provider/activiti';
+import activitiModdleDescriptor from '../resources/activiti.json';
+import customTranslate from '../resources/customTranslate/customTranslate';
+import customControlsModule from '../resources/customControls';
+import tools from '../resources/tools'
+import diagramXML from '../resources/newDiagram.bpmn';
+const proHost = window.location.protocol + "//" + window.location.host;
+const href = window.location.href.split("bpmnjs")[0];
+const key = href.split(window.location.host)[1];
+const publicurl = proHost + key;
+
+// 添加翻译组件
+var customTranslateModule = {
+    translate: ['value', customTranslate]
+};
+var container = $('#js-drop-zone');
+var canvas = $('#js-canvas');
+var bpmnModeler = new BpmnModeler({
+    container: canvas,
+    propertiesPanel: {
+        parent: '#js-properties-panel'
+    },
+    additionalModules: [
+        propertiesPanelModule,
+        propertiesProviderModule,
+        customControlsModule,
+        customTranslateModule
+    ],
+    moddleExtensions: {
+        activiti:activitiModdleDescriptor
+    }
+});
+container.removeClass('with-diagram');
+// 判断浏览器支持程度
+if (!window.FileList || !window.FileReader) {
+    window.alert('请使用谷歌、火狐、IE10+浏览器');
+} else {
+    tools.registerFileDrop(container, tools.createDiagram(diagramXML, bpmnModeler, container));
+}
+
+
+$(function () {
+    // 创建bpmn
+    var param = tools.getUrlParam(window.location.href)
+        $('.item').show()
+    if (param.type === 'addBpmn') {
+        tools.createDiagram(diagramXML, bpmnModeler, container);
+    } else if (param.type === 'lookBpmn') { //编辑bpmn
+        $('.item').hide()
+        $('.download').show()
+        const Id = param.deploymentFileUUID || '6d4af2dc-bab0-11ea-b584-3cf011eaafca'
+        const Name=param.deploymentName || 'String.bpmn'
+        const instanceId=param.instanceId
+        var param={
+            "deploymentId":Id,
+            "resourceName":decodeURI(Name)
+        }
+        if(instanceId){
+            var param1={
+                instanceId
+            }
+            $.ajax({
+                url: localStorage.getItem("VUE_APP_BASE_API")+'/activitiHistory/gethighLine',
+                // url: 'http://localhost:8080/activitiHistory/gethighLine',
+                type: 'GET',
+                data: param1,
+                dataType:'json',
+                success: function (result) {
+                  console.log(result)
+                  var ColorJson=tools.getByColor(result.data)
+                    $.ajax({
+                        url: localStorage.getItem("VUE_APP_BASE_API")+'/processDefinition/getDefinitionXML',
+                        // url: 'http://localhost:8080/processDefinition/getDefinitionXML',
+                        type: 'GET',
+                        data: param,
+                        dataType:'text',
+                        success: function (result) {
+                            var newXmlData = result
+                            tools.createDiagram(newXmlData, bpmnModeler, container);
+                            setTimeout(function () {
+                                for (var i in ColorJson) {
+                                    tools.setColor(ColorJson[i],bpmnModeler)
+                                }
+                            }, 200)
+                        },
+                        error: function (err) {
+                            console.log(err)
+                        }
+                    });
+                },
+                error: function (err) {
+                    console.log(err)
+                }
+            });
+        }else{
+            //加载后台方法获取xml
+            $.ajax({
+                url: localStorage.getItem("VUE_APP_BASE_API")+'/processDefinition/getDefinitionXML',
+              // url: 'http://localhost:8080/processDefinition/getDefinitionXML',
+                type: 'GET',
+                data: param,
+                dataType:'text',
+                success: function (result) {
+                    var newXmlData = result
+                    tools.createDiagram(newXmlData, bpmnModeler, container);
+                },
+                error: function (err) {
+                    console.log(err)
+                }
+            });
+        }
+    } else if(param.type === "historyBpmn") { // bpmn历史
+        $('.item').hide()
+        $('.download').show()
+    }
+    // 点击新增
+    $('#js-download-diagram').on("click", function () {
+       tools.syopen('alert')
+    })
+
+    // 点击取消
+    $('.cancel').on("click",function () {
+        tools.syhide('alert')
+    })
+    // 点击确定
+    $('#sure').on('click',function(){
+       // const text=$("#deploymentName").val()
+        tools.saveBpmn(bpmnModeler)
+    })
+
+
+
+    // 点击下载
+    $("#downloadBpmn").on("click", function () {
+        tools.downLoad(bpmnModeler)
+    })
+    // 点击上传
+    $("#uploadFile").on("change", function () {
+        tools.upload(bpmnModeler,container)
+    })
+});

+ 48 - 0
ruoyi-bpmnjs/package.json

@@ -0,0 +1,48 @@
+{
+  "name": "bpmn-js-example-properties-panel",
+  "version": "0.0.0",
+  "description": "A bpmn-js modeler + properties panel example",
+  "main": "app/index.js",
+  "scripts": {
+    "all": "grunt",
+    "dev": "grunt auto-build"
+  },
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/bpmn-io/bpmn-js-examples"
+  },
+  "keywords": [
+    "bpmnjs-example"
+  ],
+  "author": {
+    "name": "Nico Rehwaldt",
+    "url": "https://github.com/nikku"
+  },
+  "contributors": [
+    {
+      "name": "bpmn.io contributors",
+      "url": "https://github.com/bpmn-io"
+    }
+  ],
+  "license": "MIT",
+  "devDependencies": {
+    "esmify": "^2.1.1",
+    "grunt": "^1.0.4",
+    "grunt-browserify": "^5.3.0",
+    "grunt-contrib-connect": "^2.0.0",
+    "grunt-contrib-copy": "^1.0.0",
+    "grunt-contrib-less": "^2.0.0",
+    "grunt-contrib-watch": "^1.1.0",
+    "load-grunt-tasks": "^5.0.0",
+    "stringify": "^5.2.0"
+  },
+  "dependencies": {
+    "bpmn-js": "^7.0.0",
+    "bpmn-js-properties-panel": "^0.32.0",
+    "camunda-bpmn-moddle": "^4.0.1",
+    "diagram-js": "^5.0.0",
+    "jquery": "^3.4.1",
+    "min-dash": "^3.5.0",
+    "x2js": "^3.4.0"
+  }
+}

+ 1087 - 0
ruoyi-bpmnjs/resources/activiti.json

@@ -0,0 +1,1087 @@
+{
+  "name": "Activiti",
+  "uri": "http://activiti.org/bpmn",
+  "prefix": "activiti",
+  "xml": {
+    "tagAlias": "lowerCase"
+  },
+  "associations": [],
+  "types": [
+    {
+      "name": "Definitions",
+      "isAbstract": true,
+      "extends": [
+        "bpmn:Definitions"
+      ],
+      "properties": [
+        {
+          "name": "diagramRelationId",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "InOutBinding",
+      "superClass": [
+        "Element"
+      ],
+      "isAbstract": true,
+      "properties": [
+        {
+          "name": "source",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "sourceExpression",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "target",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "businessKey",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "local",
+          "isAttr": true,
+          "type": "Boolean",
+          "default": false
+        },
+        {
+          "name": "variables",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "In",
+      "superClass": [
+        "InOutBinding"
+      ],
+      "meta": {
+        "allowedIn": [
+          "bpmn:CallActivity"
+        ]
+      }
+    },
+    {
+      "name": "Out",
+      "superClass": [
+        "InOutBinding"
+      ],
+      "meta": {
+        "allowedIn": [
+          "bpmn:CallActivity"
+        ]
+      }
+    },
+    {
+      "name": "AsyncCapable",
+      "isAbstract": true,
+      "extends": [
+        "bpmn:Activity",
+        "bpmn:Gateway",
+        "bpmn:Event"
+      ],
+      "properties": [
+        {
+          "name": "async",
+          "isAttr": true,
+          "type": "Boolean",
+          "default": false
+        },
+        {
+          "name": "asyncBefore",
+          "isAttr": true,
+          "type": "Boolean",
+          "default": false
+        },
+        {
+          "name": "asyncAfter",
+          "isAttr": true,
+          "type": "Boolean",
+          "default": false
+        },
+        {
+          "name": "exclusive",
+          "isAttr": true,
+          "type": "Boolean",
+          "default": true
+        }
+      ]
+    },
+    {
+      "name": "JobPriorized",
+      "isAbstract": true,
+      "extends": [
+        "bpmn:Process",
+        "activiti:AsyncCapable"
+      ],
+      "properties": [
+        {
+          "name": "jobPriority",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "SignalEventDefinition",
+      "isAbstract": true,
+      "extends": [
+        "bpmn:SignalEventDefinition"
+      ],
+      "properties": [
+        {
+          "name": "async",
+          "isAttr": true,
+          "type": "Boolean",
+          "default": false
+        }
+      ]
+    },
+    {
+      "name": "ErrorEventDefinition",
+      "isAbstract": true,
+      "extends": [
+        "bpmn:ErrorEventDefinition"
+      ],
+      "properties": [
+        {
+          "name": "errorCodeVariable",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "errorMessageVariable",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "Error",
+      "isAbstract": true,
+      "extends": [
+        "bpmn:Error"
+      ],
+      "properties": [
+        {
+          "name": "activiti:errorMessage",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "PotentialStarter",
+      "superClass": [
+        "Element"
+      ],
+      "properties": [
+        {
+          "name": "resourceAssignmentExpression",
+          "type": "bpmn:ResourceAssignmentExpression"
+        }
+      ]
+    },
+    {
+      "name": "FormSupported",
+      "isAbstract": true,
+      "extends": [
+        "bpmn:StartEvent",
+        "bpmn:UserTask"
+      ],
+      "properties": [
+        {
+          "name": "formHandlerClass",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "formKey",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "TemplateSupported",
+      "isAbstract": true,
+      "extends": [
+        "bpmn:Process",
+        "bpmn:FlowElement"
+      ],
+      "properties": [
+        {
+          "name": "modelerTemplate",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "Initiator",
+      "isAbstract": true,
+      "extends": [ "bpmn:StartEvent" ],
+      "properties": [
+        {
+          "name": "initiator",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "ScriptTask",
+      "isAbstract": true,
+      "extends": [
+        "bpmn:ScriptTask"
+      ],
+      "properties": [
+        {
+          "name": "resultVariable",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "resource",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "Process",
+      "isAbstract": true,
+      "extends": [
+        "bpmn:Process"
+      ],
+      "properties": [
+        {
+          "name": "candidateStarterConfiguration",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "candidateStarterUsers",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "versionTag",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "historyTimeToLive",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "isStartableInTasklist",
+          "isAttr": true,
+          "type": "Boolean",
+          "default": true
+        },
+        {
+          "name": "process-is-executable",
+          "isAttr": true,
+          "type": "Boolean",
+          "default": true
+        }
+      ]
+    },
+    {
+      "name": "EscalationEventDefinition",
+      "isAbstract": true,
+      "extends": [
+        "bpmn:EscalationEventDefinition"
+      ],
+      "properties": [
+        {
+          "name": "escalationCodeVariable",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "FormalExpression",
+      "isAbstract": true,
+      "extends": [
+        "bpmn:FormalExpression"
+      ],
+      "properties": [
+        {
+          "name": "resource",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "Assignable",
+      "extends": [ "bpmn:UserTask" ],
+      "properties": [
+        {
+          "name": "assignee",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "candidateUsers",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "candidateGroups",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "dueDate",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "followUpDate",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "priority",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "CallActivity",
+      "extends": [ "bpmn:CallActivity" ],
+      "properties": [
+        {
+          "name": "calledElementBinding",
+          "isAttr": true,
+          "type": "String",
+          "default": "latest"
+        },
+        {
+          "name": "calledElementVersion",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "calledElementVersionTag",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "calledElementTenantId",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "caseRef",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "caseBinding",
+          "isAttr": true,
+          "type": "String",
+          "default": "latest"
+        },
+        {
+          "name": "caseVersion",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "caseTenantId",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "variableMappingClass",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "variableMappingDelegateExpression",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "ServiceTaskLike",
+      "extends": [
+        "bpmn:ServiceTask",
+        "bpmn:BusinessRuleTask",
+        "bpmn:SendTask",
+        "bpmn:MessageEventDefinition"
+      ],
+      "properties": [
+        {
+          "name": "expression",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "class",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "delegateExpression",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "resultVariable",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "DmnCapable",
+      "extends": [
+        "bpmn:BusinessRuleTask"
+      ],
+      "properties": [
+        {
+          "name": "decisionRef",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "decisionRefBinding",
+          "isAttr": true,
+          "type": "String",
+          "default": "latest"
+        },
+        {
+          "name": "decisionRefVersion",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "mapDecisionResult",
+          "isAttr": true,
+          "type": "String",
+          "default": "resultList"
+        },
+        {
+          "name": "decisionRefTenantId",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "ExternalCapable",
+      "extends": [
+        "activiti:ServiceTaskLike"
+      ],
+      "properties": [
+        {
+          "name": "type",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "topic",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "TaskPriorized",
+      "extends": [
+        "bpmn:Process",
+        "activiti:ExternalCapable"
+      ],
+      "properties": [
+        {
+          "name": "taskPriority",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "Properties",
+      "superClass": [
+        "Element"
+      ],
+      "meta": {
+        "allowedIn": [ "*" ]
+      },
+      "properties": [
+        {
+          "name": "values",
+          "type": "Property",
+          "isMany": true
+        }
+      ]
+    },
+    {
+      "name": "Property",
+      "superClass": [
+        "Element"
+      ],
+      "properties": [
+        {
+          "name": "id",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "name",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "value",
+          "type": "String",
+          "isAttr": true
+        }
+      ]
+    },
+    {
+      "name": "Connector",
+      "superClass": [
+        "Element"
+      ],
+      "meta": {
+        "allowedIn": [
+          "activiti:ServiceTaskLike"
+        ]
+      },
+      "properties": [
+        {
+          "name": "inputOutput",
+          "type": "InputOutput"
+        },
+        {
+          "name": "connectorId",
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "InputOutput",
+      "superClass": [
+        "Element"
+      ],
+      "meta": {
+        "allowedIn": [
+          "bpmn:FlowNode",
+          "activiti:Connector"
+        ]
+      },
+      "properties": [
+        {
+          "name": "inputOutput",
+          "type": "InputOutput"
+        },
+        {
+          "name": "connectorId",
+          "type": "String"
+        },
+        {
+          "name": "inputParameters",
+          "isMany": true,
+          "type": "InputParameter"
+        },
+        {
+          "name": "outputParameters",
+          "isMany": true,
+          "type": "OutputParameter"
+        }
+      ]
+    },
+    {
+      "name": "InputOutputParameter",
+      "properties": [
+        {
+          "name": "name",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "value",
+          "isBody": true,
+          "type": "String"
+        },
+        {
+          "name": "definition",
+          "type": "InputOutputParameterDefinition"
+        }
+      ]
+    },
+    {
+      "name": "InputOutputParameterDefinition",
+      "isAbstract": true
+    },
+    {
+      "name": "List",
+      "superClass": [ "InputOutputParameterDefinition" ],
+      "properties": [
+        {
+          "name": "items",
+          "isMany": true,
+          "type": "InputOutputParameterDefinition"
+        }
+      ]
+    },
+    {
+      "name": "Map",
+      "superClass": [ "InputOutputParameterDefinition" ],
+      "properties": [
+        {
+          "name": "entries",
+          "isMany": true,
+          "type": "Entry"
+        }
+      ]
+    },
+    {
+      "name": "Entry",
+      "properties": [
+        {
+          "name": "key",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "value",
+          "isBody": true,
+          "type": "String"
+        },
+        {
+          "name": "definition",
+          "type": "InputOutputParameterDefinition"
+        }
+      ]
+    },
+    {
+      "name": "Value",
+      "superClass": [
+        "InputOutputParameterDefinition"
+      ],
+      "properties": [
+        {
+          "name": "id",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "name",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "value",
+          "isBody": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "Script",
+      "superClass": [ "InputOutputParameterDefinition" ],
+      "properties": [
+        {
+          "name": "scriptFormat",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "resource",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "value",
+          "isBody": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "Field",
+      "superClass": [ "Element" ],
+      "meta": {
+        "allowedIn": [
+          "activiti:ServiceTaskLike",
+          "activiti:ExecutionListener",
+          "activiti:TaskListener"
+        ]
+      },
+      "properties": [
+        {
+          "name": "name",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "expression",
+          "type": "String"
+        },
+        {
+          "name": "stringValue",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "string",
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "InputParameter",
+      "superClass": [ "InputOutputParameter" ]
+    },
+    {
+      "name": "OutputParameter",
+      "superClass": [ "InputOutputParameter" ]
+    },
+    {
+      "name": "Collectable",
+      "isAbstract": true,
+      "extends": [ "bpmn:MultiInstanceLoopCharacteristics" ],
+      "superClass": [ "activiti:AsyncCapable" ],
+      "properties": [
+        {
+          "name": "collection",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "elementVariable",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "FailedJobRetryTimeCycle",
+      "superClass": [ "Element" ],
+      "meta": {
+        "allowedIn": [
+          "activiti:AsyncCapable",
+          "bpmn:MultiInstanceLoopCharacteristics"
+        ]
+      },
+      "properties": [
+        {
+          "name": "body",
+          "isBody": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "ExecutionListener",
+      "superClass": [ "Element" ],
+      "meta": {
+        "allowedIn": [
+          "bpmn:Task",
+          "bpmn:ServiceTask",
+          "bpmn:UserTask",
+          "bpmn:BusinessRuleTask",
+          "bpmn:ScriptTask",
+          "bpmn:ReceiveTask",
+          "bpmn:ManualTask",
+          "bpmn:ExclusiveGateway",
+          "bpmn:SequenceFlow",
+          "bpmn:ParallelGateway",
+          "bpmn:InclusiveGateway",
+          "bpmn:EventBasedGateway",
+          "bpmn:StartEvent",
+          "bpmn:IntermediateCatchEvent",
+          "bpmn:IntermediateThrowEvent",
+          "bpmn:EndEvent",
+          "bpmn:BoundaryEvent",
+          "bpmn:CallActivity",
+          "bpmn:SubProcess",
+          "bpmn:Process"
+        ]
+      },
+      "properties": [
+        {
+          "name": "expression",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "class",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "delegateExpression",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "event",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "script",
+          "type": "Script"
+        },
+        {
+          "name": "fields",
+          "type": "Field",
+          "isMany": true
+        }
+      ]
+    },
+    {
+      "name": "TaskListener",
+      "superClass": [ "Element" ],
+      "meta": {
+        "allowedIn": [
+          "bpmn:UserTask"
+        ]
+      },
+      "properties": [
+        {
+          "name": "expression",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "class",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "delegateExpression",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "event",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "script",
+          "type": "Script"
+        },
+        {
+          "name": "fields",
+          "type": "Field",
+          "isMany": true
+        },
+        {
+          "name": "id",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "eventDefinitions",
+          "type": "bpmn:TimerEventDefinition",
+          "isMany": true
+        }
+      ]
+    },
+    {
+      "name": "FormProperty",
+      "superClass": [ "Element" ],
+      "meta": {
+        "allowedIn": [
+          "bpmn:StartEvent",
+          "bpmn:UserTask"
+        ]
+      },
+      "properties": [
+        {
+          "name": "id",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "name",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "type",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "required",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "readable",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "writable",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "variable",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "expression",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "datePattern",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "default",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "values",
+          "type": "Value",
+          "isMany": true
+        }
+      ]
+    },
+    {
+      "name": "FormData",
+      "superClass": [ "Element" ],
+      "meta": {
+        "allowedIn": [
+          "bpmn:StartEvent",
+          "bpmn:UserTask"
+        ]
+      },
+      "properties": [
+        {
+          "name": "fields",
+          "type": "FormField",
+          "isMany": true
+        },
+        {
+          "name": "businessKey",
+          "type": "String",
+          "isAttr": true
+        }
+      ]
+    },
+    {
+      "name": "FormField",
+      "superClass": [ "Element" ],
+      "properties": [
+        {
+          "name": "id",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "label",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "type",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "datePattern",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "defaultValue",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "properties",
+          "type": "Properties"
+        },
+        {
+          "name": "validation",
+          "type": "Validation"
+        },
+        {
+          "name": "values",
+          "type": "Value",
+          "isMany": true
+        }
+      ]
+    },
+    {
+      "name": "Validation",
+      "superClass": [ "Element" ],
+      "properties": [
+        {
+          "name": "constraints",
+          "type": "Constraint",
+          "isMany": true
+        }
+      ]
+    },
+    {
+      "name": "Constraint",
+      "superClass": [ "Element" ],
+      "properties": [
+        {
+          "name": "name",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "config",
+          "type": "String",
+          "isAttr": true
+        }
+      ]
+    },
+    {
+      "name": "ConditionalEventDefinition",
+      "isAbstract": true,
+      "extends": [
+        "bpmn:ConditionalEventDefinition"
+      ],
+      "properties": [
+        {
+          "name": "variableName",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "variableEvent",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    }
+  ],
+  "emumerations": [ ]
+}

+ 76 - 0
ruoyi-bpmnjs/resources/customControls/CustomContextPad.js

@@ -0,0 +1,76 @@
+export default class CustomContextPad {
+  constructor(config, contextPad, create, elementFactory, injector, translate) {
+    this.create = create;
+    this.elementFactory = elementFactory;
+    this.translate = translate;
+    //自动摆放位置
+    if (config.autoPlace !== false) {
+      this.autoPlace = injector.get('autoPlace', false);
+    }
+    //注册工具
+    contextPad.registerProvider(this);
+  }
+  getContextPadEntries(element) {
+    const {
+      autoPlace,
+      create,
+      elementFactory,
+      translate
+    } = this;
+
+    function appendUserTask(event, element) {
+      if (autoPlace) {
+        const shape = elementFactory.createShape({ type: 'bpmn:UserTask' });
+        autoPlace.append(element, shape);
+      } else {
+        appendUserTaskStart(event, element);
+      }
+    }
+
+    function appendUserTaskStart(event) {
+          const shape = elementFactory.createShape({ type: 'bpmn:UserTask' });
+          create.start(event, shape, element);
+    }
+    function appendCallActivityStart(event) {
+      const shape = elementFactory.createShape({ type: 'bpmn:CallActivity' });
+      create.start(event, shape, element);
+    }
+
+    function appendCallActivity(event, element) {
+        if (autoPlace) {
+            const shape = elementFactory.createShape({ type: 'bpmn:CallActivity' });
+            autoPlace.append(element, shape);
+        } else {
+            appendCallActivityStart(event, element);
+        }
+    }
+    return {
+      'append.user-task': {
+        group: 'model',
+        className: 'bpmn-icon-user-task',
+        title: translate('Append ServiceTask'),
+        action: {
+          click: appendUserTask,
+          dragstart: appendUserTaskStart
+        }
+      },
+      'append.call-activity':{
+          group: 'model',
+          className: 'bpmn-icon-call-activity',
+          title: translate('Append CallActivity'),
+          action: {
+              click: appendCallActivity,
+              dragstart: appendCallActivityStart
+          }
+      }
+    };
+  }
+}
+CustomContextPad.$inject = [
+  'config',
+  'contextPad',
+  'create',
+  'elementFactory',
+  'injector',
+  'translate'
+];

+ 54 - 0
ruoyi-bpmnjs/resources/customControls/CustomPalette.js

@@ -0,0 +1,54 @@
+export default class CustomPalette {
+  constructor(create, elementFactory, palette, translate) {
+    this.create = create;
+    this.elementFactory = elementFactory;
+    this.translate = translate;
+    palette.registerProvider(this);
+  }
+
+  getPaletteEntries(element) {
+    const {
+      create,
+      elementFactory,
+      translate
+    } = this;
+
+    function createServiceTask(event) {
+      const shape = elementFactory.createShape({ type: 'bpmn:UserTask' });
+      create.start(event, shape);
+    }
+      function createCallActivity(event) {
+          const shape = elementFactory.createShape({ type: 'bpmn:CallActivity' });
+          create.start(event, shape);
+      }
+
+
+    return {
+      'create.user-task': {
+        group: 'activity',
+        className: 'bpmn-icon-user-task',
+        title: translate('Create UserTask'),
+        action: {
+          dragstart: createServiceTask,
+          click: createServiceTask
+        }
+      },
+        'create.call-activity': {
+            group: 'activity',
+            className: 'bpmn-icon-call-activity',
+            title: translate('Create CallActivity'),
+            action: {
+                dragstart: createCallActivity,
+                click: createCallActivity
+            }
+        }
+    }
+  }
+}
+
+CustomPalette.$inject = [
+  'create',
+  'elementFactory',
+  'palette',
+  'translate'
+];

+ 8 - 0
ruoyi-bpmnjs/resources/customControls/index.js

@@ -0,0 +1,8 @@
+import CustomContextPad from './CustomContextPad';
+import CustomPalette from './CustomPalette';
+
+export default {
+  __init__: [ 'customContextPad', 'customPalette' ],
+  customContextPad: [ 'type', CustomContextPad ],
+  customPalette: [ 'type', CustomPalette ]
+};

+ 12 - 0
ruoyi-bpmnjs/resources/customTranslate/customTranslate.js

@@ -0,0 +1,12 @@
+import translations from './translationsGerman';
+export default function customTranslate(template, replacements) {
+  replacements = replacements || {};
+  template = translations[template] || template;
+  return template.replace(/{([^}]+)}/g, function(_, key) {
+	 var str = replacements[key];
+	  if(translations[replacements[key]] != null && translations [replacements[key]] != 'undefined'){
+		  str = translations[replacements[key]];
+	  }
+    return  str || '{' + key + '}';
+  });
+}

+ 239 - 0
ruoyi-bpmnjs/resources/customTranslate/translationsGerman.js

@@ -0,0 +1,239 @@
+export default {
+    // Labels
+    'Activate the global connect tool' : '激活全局连接工具',
+    'Append {type}': '追加 {type}',
+    'Append EndEvent': '追加 结束事件 ',
+    'Append Task':'追加 任务',
+    'Append Gateway':'追加 网关',
+    'Append Intermediate/Boundary Event':'追加 中间/边界 事件',
+    'Add Lane above': '在上面添加道',
+    'Divide into two Lanes': '分割成两个道',
+    'Divide into three Lanes': '分割成三个道',
+    'Add Lane below': '在下面添加道',
+    'Append compensation activity': '追加补偿活动',
+    'Change type': '修改类型',
+    'Connect using Association': '使用关联连接',
+    'Connect using Sequence/MessageFlow or Association': '使用顺序/消息流或者关联连接',
+    'Connect using DataInputAssociation': '使用数据输入关联连接',
+    'Remove': '移除',
+    'Activate the hand tool': '激活抓手工具',
+    'Activate the lasso tool': '激活套索工具',
+    'Activate the create/remove space tool': '激活创建/删除空间工具',
+    'Create expanded SubProcess': '创建扩展子过程',
+    'Create IntermediateThrowEvent/BoundaryEvent' : '创建中间抛出事件/边界事件',
+    'Create Pool/Participant': '创建池/参与者',
+    'Parallel Multi Instance': '并行多重事件',
+    'Sequential Multi Instance': '时序多重事件',
+    'DataObjectReference':'数据对象参考',
+    'DataStoreReference':'数据存储参考',
+    'Loop': '循环',
+    'Ad-hoc': '即席',
+    'Create {type}': '创建 {type}',
+    'Create Task':'创建任务',
+    'Create StartEvent':'创建开始事件',
+    'Create EndEvent':'创建结束事件',
+    'Create Group':'创建组',
+    'Task': '任务',
+    'Send Task': '发送任务',
+    'Receive Task': '接收任务',
+    'User Task': '用户任务',
+    'Manual Task': '手工任务',
+    'Business Rule Task': '业务规则任务',
+    'Service Task': '服务任务',
+    'Script Task': '脚本任务',
+    'Call Activity': '调用活动',
+    'Sub Process (collapsed)': '子流程(折叠的)',
+    'Sub Process (expanded)': '子流程(展开的)',
+    'Start Event': '开始事件',
+    'StartEvent': '开始事件',
+    'Intermediate Throw Event': '中间事件',
+    'End Event': '结束事件',
+    'EndEvent': '结束事件',
+    'Create Gateway': '创建网关',
+    'GateWay':'网关',
+    'Create Intermediate/Boundary Event': '创建中间/边界事件',
+    'Message Start Event': '消息开始事件',
+    'Timer Start Event': '定时开始事件',
+    'Conditional Start Event': '条件开始事件',
+    'Signal Start Event': '信号开始事件',
+    'Error Start Event': '错误开始事件',
+    'Escalation Start Event': '升级开始事件',
+    'Compensation Start Event': '补偿开始事件',
+    'Message Start Event (non-interrupting)': '消息开始事件(非中断)',
+    'Timer Start Event (non-interrupting)': '定时开始事件(非中断)',
+    'Conditional Start Event (non-interrupting)': '条件开始事件(非中断)',
+    'Signal Start Event (non-interrupting)': '信号开始事件(非中断)',
+    'Escalation Start Event (non-interrupting)': '升级开始事件(非中断)',
+    'Message Intermediate Catch Event': '消息中间捕获事件',
+    'Message Intermediate Throw Event': '消息中间抛出事件',
+    'Timer Intermediate Catch Event': '定时中间捕获事件',
+    'Escalation Intermediate Throw Event': '升级中间抛出事件',
+    'Conditional Intermediate Catch Event': '条件中间捕获事件',
+    'Link Intermediate Catch Event': '链接中间捕获事件',
+    'Link Intermediate Throw Event': '链接中间抛出事件',
+    'Compensation Intermediate Throw Event': '补偿中间抛出事件',
+    'Signal Intermediate Catch Event': '信号中间捕获事件',
+    'Signal Intermediate Throw Event': '信号中间抛出事件',
+    'Message End Event': '消息结束事件',
+    'Escalation End Event': '定时结束事件',
+    'Error End Event': '错误结束事件',
+    'Cancel End Event': '取消结束事件',
+    'Compensation End Event': '补偿结束事件',
+    'Signal End Event': '信号结束事件',
+    'Terminate End Event': '终止结束事件',
+    'Message Boundary Event': '消息边界事件',
+    'Message Boundary Event (non-interrupting)': '消息边界事件(非中断)',
+    'Timer Boundary Event': '定时边界事件',
+    'Timer Boundary Event (non-interrupting)': '定时边界事件(非中断)',
+    'Escalation Boundary Event': '升级边界事件',
+    'Escalation Boundary Event (non-interrupting)': '升级边界事件(非中断)',
+    'Conditional Boundary Event': '条件边界事件',
+    'Conditional Boundary Event (non-interrupting)': '条件边界事件(非中断)',
+    'Error Boundary Event': '错误边界事件',
+    'Cancel Boundary Event': '取消边界事件',
+    'Signal Boundary Event': '信号边界事件',
+    'Signal Boundary Event (non-interrupting)': '信号边界事件(非中断)',
+    'Compensation Boundary Event': '补偿边界事件',
+    'Exclusive Gateway': '互斥网关',
+    'Parallel Gateway': '并行网关',
+    'Inclusive Gateway': '相容网关',
+    'Complex Gateway': '复杂网关',
+    'Event based Gateway': '事件网关',
+    'Transaction': '转运',
+    'Sub Process': '子流程',
+    'Event Sub Process': '事件子流程',
+    'Collapsed Pool': '折叠池',
+    'Expanded Pool': '展开池',
+    // Errors
+    'no parent for {element} in {parent}': '在{parent}里,{element}没有父类',
+    'no shape type specified': '没有指定的形状类型',
+    'flow elements must be children of pools/participants': '流元素必须是池/参与者的子类',
+    'out of bounds release': 'out of bounds release',
+    'more than {count} child lanes': '子道大于{count} ',
+    'element required': '元素不能为空',
+    'diagram not part of bpmn:Definitions': '流程图不符合bpmn规范',
+    'no diagram to display': '没有可展示的流程图',
+    'no process or collaboration to display': '没有可展示的流程/协作',
+    'element {element} referenced by {referenced}#{property} not yet drawn': '由{referenced}#{property}引用的{element}元素仍未绘制',
+    'already rendered {element}': '{element} 已被渲染',
+    'failed to import {element}': '导入{element}失败',
+    //属性面板的参数
+    'Id':'编号',
+    'Name':'名称',
+    'General':'常规',
+    'Details':'详情',
+    'Message Name':'消息名称',
+    'Message':'消息',
+    'Initiator':'创建者',
+    'Asynchronous Continuations':'持续异步',
+    'Asynchronous Before':'异步前',
+    'Asynchronous After':'异步后',
+    'Job Configuration':'工作配置',
+    'Exclusive':'排除',
+    'Job Priority':'工作优先级',
+    'Retry Time Cycle':'重试时间周期',
+    'Documentation':'文档',
+    'Element Documentation':'元素文档',
+    'History Configuration':'历史配置',
+    'History Time To Live':'历史的生存时间',
+    'Forms':'表单',
+    'Form Key':'表单key',
+    'Form Fields':'表单字段',
+    'Business Key':'业务key',
+    'Form Field':'表单字段',
+    'ID':'编号',
+    'Type':'类型',
+    'Label':'名称',
+    'Default Value':'默认值',
+    'Validation':'校验',
+    'Add Constraint':'添加约束',
+    'Config':'配置',
+    'Properties':'属性',
+    'Add Property':'添加属性',
+    'Value':'值',
+    'Add':'添加',
+    'Values':'值',
+    'Add Value':'添加值',
+    'Listeners':'监听器',
+    'Execution Listener':'执行监听',
+    'Event Type':'事件类型',
+    'Listener Type':'监听器类型',
+    'Java Class':'Java类',
+    'Expression':'表达式',
+    'Must provide a value':'必须提供一个值',
+    'Delegate Expression':'代理表达式',
+    'Script':'脚本',
+    'Script Format':'脚本格式',
+    'Script Type':'脚本类型',
+    'Inline Script':'内联脚本',
+    'External Script':'外部脚本',
+    'Resource':'资源',
+    'Field Injection':'字段注入',
+    'Extensions':'扩展',
+    'Input/Output':'输入/输出',
+    'Input Parameters':'输入参数',
+    'Output Parameters':'输出参数',
+    'Parameters':'参数',
+    'Output Parameter':'输出参数',
+    'Timer Definition Type':'定时器定义类型',
+    'Timer Definition':'定时器定义',
+    'Date':'日期',
+    'Duration':'持续',
+    'Cycle':'循环',
+    'Signal':'信号',
+    'Signal Name':'信号名称',
+    'Escalation':'升级',
+    'Error':'错误',
+    'Link Name':'链接名称',
+    'Condition':'条件名称',
+    'Variable Name':'变量名称',
+    'Variable Event':'变量事件',
+    'Specify more than one variable change event as a comma separated list.':'多个变量事件以逗号隔开',
+    'Wait for Completion':'等待完成',
+    'Activity Ref':'活动参考',
+    'Version Tag':'版本标签',
+    'Executable':'可执行文件',
+    'External Task Configuration':'扩展任务配置',
+    'Task Priority':'任务优先级',
+    'External':'外部',
+    'Connector':'连接器',
+    'Must configure Connector':'必须配置连接器',
+    'Connector Id':'连接器编号',
+    'Implementation':'实现方式',
+    'Field Injections':'字段注入',
+    'Fields':'字段',
+    'Result Variable':'结果变量',
+    'Topic':'主题',
+    'Configure Connector':'配置连接器',
+    'Input Parameter':'输入参数',
+    'Assignee':'代理人',
+    'Candidate Users':'候选用户',
+    'Candidate Groups':'候选组',
+    'Due Date':'到期时间',
+    'Follow Up Date':'跟踪日期',
+    'Priority':'优先级',
+    'The follow up date as an EL expression (e.g. ${someDate} or an ISO date (e.g. 2015-06-26T09:54:00)':'跟踪日期必须符合EL表达式,如: ${someDate} ,或者一个ISO标准日期,如:2015-06-26T09:54:00',
+    'The due date as an EL expression (e.g. ${someDate} or an ISO date (e.g. 2015-06-26T09:54:00)':'跟踪日期必须符合EL表达式,如: ${someDate} ,或者一个ISO标准日期,如:2015-06-26T09:54:00',
+    'Variables':'变量',
+    'Candidate Starter Configuration':'候选开始配置',
+    'Task Listener':'任务监听器',
+    'Candidate Starter Groups':'候选开始组',
+    'Candidate Starter Users':'候选开始用户',
+    'Tasklist Configuration':'任务列表配置',
+    'Startable':'启动',
+    'Specify more than one group as a comma separated list.':'指定多个组,用逗号分隔',
+    'Specify more than one user as a comma separated list.':'指定多个用户,用逗号分隔',
+    'This maps to the process definition key.':'这会映射为流程定义的键',
+    'CallActivity Type':'调用活动类型',
+    'Condition Type':'条件类型',
+    'Create UserTask':'创建用户任务',
+    'Create CallActivity':'创建调用活动',
+    'Called Element':'调用元素',
+    'Create DataObjectReference':'创建数据对象引用',
+    'Create DataStoreReference':'创建数据存储引用',
+    'Multi Instance':'多实例',
+    'Loop Cardinality':'实例数量',
+    'Collection':'任务参与人列表',
+    'Element Variable':'元素变量',
+    'Completion Condition':'完成条件'
+};

+ 13 - 0
ruoyi-bpmnjs/resources/newDiagram.bpmn

@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<bpmn2:definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmn2="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd" id="sample-diagram" targetNamespace="http://activiti.org/bpmn">
+  <bpmn2:process id="Process_1" isExecutable="true">
+    <bpmn2:startEvent id="StartEvent_1"/>
+  </bpmn2:process>
+  <bpmndi:BPMNDiagram id="BPMNDiagram_1">
+    <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1">
+      <bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
+        <dc:Bounds height="36.0" width="36.0" x="412.0" y="240.0"/>
+      </bpmndi:BPMNShape>
+    </bpmndi:BPMNPlane>
+  </bpmndi:BPMNDiagram>
+</bpmn2:definitions>

+ 67 - 0
ruoyi-bpmnjs/resources/properties-panel/PropertiesActivator.js

@@ -0,0 +1,67 @@
+'use strict';
+
+var DEFAULT_PRIORITY = 1000;
+
+
+/**
+ * A component that decides upon the visibility / editable
+ * state of properties in the properties panel.
+ *
+ * Implementors must subclass this component and override
+ * {@link PropertiesActivator#isEntryVisible} and
+ * {@link PropertiesActivator#isPropertyEditable} to provide
+ * custom behavior.
+ *
+ * @class
+ * @constructor
+ *
+ * @param {EventBus} eventBus
+ * @param {Number} [priority] at which priority to hook into the activation
+ */
+function PropertiesActivator(eventBus, priority) {
+  var self = this;
+
+  priority = priority || DEFAULT_PRIORITY;
+
+  eventBus.on('propertiesPanel.isEntryVisible', priority, function(e) {
+    return self.isEntryVisible(e.entry, e.element);
+  });
+
+  eventBus.on('propertiesPanel.isPropertyEditable', priority, function(e) {
+    return self.isPropertyEditable(e.entry, e.propertyName, e.element);
+  });
+}
+
+PropertiesActivator.$inject = [ 'eventBus' ];
+
+module.exports = PropertiesActivator;
+
+
+/**
+ * Should the given entry be visible for the specified element.
+ *
+ * @method  PropertiesActivator#isEntryVisible
+ *
+ * @param {EntryDescriptor} entry
+ * @param {ModdleElement} element
+ *
+ * @returns {Boolean}
+ */
+PropertiesActivator.prototype.isEntryVisible = function(entry, element) {
+  return true;
+};
+
+/**
+ * Should the given property be editable for the specified element
+ *
+ * @method  PropertiesActivator#isPropertyEditable
+ *
+ * @param {EntryDescriptor} entry
+ * @param {String} propertyName
+ * @param {ModdleElement} element
+ *
+ * @returns {Boolean}
+ */
+PropertiesActivator.prototype.isPropertyEditable = function(entry, propertyName, element) {
+  return true;
+};

+ 1269 - 0
ruoyi-bpmnjs/resources/properties-panel/PropertiesPanel.js

@@ -0,0 +1,1269 @@
+'use strict';
+
+var escapeHTML = require('./Utils').escapeHTML;
+
+var domify = require('min-dom').domify,
+    domQuery = require('min-dom').query,
+    domQueryAll = require('min-dom').queryAll,
+    domRemove = require('min-dom').remove,
+    domClasses = require('min-dom').classes,
+    domClosest = require('min-dom').closest,
+    domAttr = require('min-dom').attr,
+    domDelegate = require('min-dom').delegate,
+    domMatches = require('min-dom').matches;
+
+var forEach = require('lodash/forEach'),
+    filter = require('lodash/filter'),
+    get = require('lodash/get'),
+    keys = require('lodash/keys'),
+    isEmpty = require('lodash/isEmpty'),
+    isArray = require('lodash/isArray'),
+    xor = require('lodash/xor'),
+    debounce = require('lodash/debounce');
+
+var updateSelection = require('selection-update');
+
+var scrollTabs = require('scroll-tabs').default;
+
+var getBusinessObject = require('bpmn-js/lib/util/ModelUtil').getBusinessObject;
+
+var HIDE_CLASS = 'bpp-hidden';
+var DEBOUNCE_DELAY = 300;
+
+
+function isToggle(node) {
+  return node.type === 'checkbox' || node.type === 'radio';
+}
+
+function isSelect(node) {
+  return node.type === 'select-one';
+}
+
+function isContentEditable(node) {
+  return domAttr(node, 'contenteditable');
+}
+
+function getPropertyPlaceholders(node) {
+  var selector = 'input[name], textarea[name], [data-value], [contenteditable]';
+  var placeholders = domQueryAll(selector, node);
+  if ((!placeholders || !placeholders.length) && domMatches(node, selector)) {
+    placeholders = [ node ];
+  }
+  return placeholders;
+}
+
+/**
+ * Return all active form controls.
+ * This excludes the invisible controls unless all is true
+ *
+ * @param {Element} node
+ * @param {Boolean} [all=false]
+ */
+function getFormControls(node, all) {
+  var controls = domQueryAll('input[name], textarea[name], select[name], [contenteditable]', node);
+
+  if (!controls || !controls.length) {
+    controls = domMatches(node, 'option') ? [ node ] : controls;
+  }
+
+  if (!all) {
+    controls = filter(controls, function(node) {
+      return !domClosest(node, '.' + HIDE_CLASS);
+    });
+  }
+
+  return controls;
+}
+
+function getFormControlValuesInScope(entryNode) {
+  var values = {};
+
+  var controlNodes = getFormControls(entryNode);
+
+  forEach(controlNodes, function(controlNode) {
+    var value = controlNode.value;
+
+    var name = domAttr(controlNode, 'name') || domAttr(controlNode, 'data-name');
+
+    // take toggle state into account for radio / checkboxes
+    if (isToggle(controlNode)) {
+      if (controlNode.checked) {
+        if (!domAttr(controlNode, 'value')) {
+          value = true;
+        } else {
+          value = controlNode.value;
+        }
+      } else {
+        value = null;
+      }
+    } else
+    if (isContentEditable(controlNode)) {
+      value = controlNode.innerText;
+    }
+
+    if (value !== null) {
+      // return the actual value
+      // handle serialization in entry provider
+      // (ie. if empty string should be serialized or not)
+      values[name] = value;
+    }
+  });
+
+  return values;
+
+}
+
+/**
+ * Extract input values from entry node
+ *
+ * @param  {DOMElement} entryNode
+ * @returns {Object}
+ */
+function getFormControlValues(entryNode) {
+
+  var values;
+
+  var listContainer = domQuery('[data-list-entry-container]', entryNode);
+  if (listContainer) {
+    values = [];
+    var listNodes = listContainer.children || [];
+    forEach(listNodes, function(listNode) {
+      values.push(getFormControlValuesInScope(listNode));
+    });
+  } else {
+    values = getFormControlValuesInScope(entryNode);
+  }
+
+  return values;
+}
+
+/**
+ * Return true if the given form extracted value equals
+ * to an old cached version.
+ *
+ * @param {Object} value
+ * @param {Object} oldValue
+ * @return {Boolean}
+ */
+function valueEqual(value, oldValue) {
+
+  if (value && !oldValue) {
+    return false;
+  }
+
+  var allKeys = keys(value).concat(keys(oldValue));
+
+  return allKeys.every(function(key) {
+    return value[key] === oldValue[key];
+  });
+}
+
+/**
+ * Return true if the given form extracted value(s)
+ * equal an old cached version.
+ *
+ * @param {Array<Object>|Object} values
+ * @param {Array<Object>|Object} oldValues
+ * @return {Boolean}
+ */
+function valuesEqual(values, oldValues) {
+
+  if (isArray(values)) {
+
+    if (values.length !== oldValues.length) {
+      return false;
+    }
+
+    return values.every(function(v, idx) {
+      return valueEqual(v, oldValues[idx]);
+    });
+  }
+
+  return valueEqual(values, oldValues);
+}
+
+/**
+ * Return a mapping of { id: entry } for all entries in the given groups in the given tabs.
+ *
+ * @param {Object} tabs
+ * @return {Object}
+ */
+function extractEntries(tabs) {
+  return keyBy(flattenDeep(map(flattenDeep(map(tabs, 'groups')), 'entries')), 'id');
+}
+
+/**
+ * Return a mapping of { id: group } for all groups in the given tabs.
+ *
+ * @param {Object} tabs
+ * @return {Object}
+ */
+function extractGroups(tabs) {
+  return keyBy(flattenDeep(map(tabs, 'groups')), 'id');
+}
+
+/**
+ * A properties panel implementation.
+ *
+ * To use it provide a `propertiesProvider` component that knows
+ * about which properties to display.
+ *
+ * Properties edit state / visibility can be intercepted
+ * via a custom {@link PropertiesActivator}.
+ *
+ * @class
+ * @constructor
+ *
+ * @param {Object} config
+ * @param {EventBus} eventBus
+ * @param {Modeling} modeling
+ * @param {PropertiesProvider} propertiesProvider
+ * @param {Canvas} canvas
+ * @param {CommandStack} commandStack
+ */
+function PropertiesPanel(config, eventBus, modeling, propertiesProvider, commandStack, canvas) {
+
+  this._eventBus = eventBus;
+  this._modeling = modeling;
+  this._commandStack = commandStack;
+  this._canvas = canvas;
+  this._propertiesProvider = propertiesProvider;
+
+  this._init(config);
+}
+
+PropertiesPanel.$inject = [
+  'config.propertiesPanel',
+  'eventBus',
+  'modeling',
+  'propertiesProvider',
+  'commandStack',
+  'canvas'
+];
+
+module.exports = PropertiesPanel;
+
+
+PropertiesPanel.prototype._init = function(config) {
+
+  var canvas = this._canvas,
+      eventBus = this._eventBus;
+
+  var self = this;
+
+  /**
+   * Select the root element once it is added to the canvas
+   */
+  eventBus.on('root.added', function(e) {
+    var element = e.element;
+
+    if (isImplicitRoot(element)) {
+      return;
+    }
+
+    self.update(element);
+  });
+
+  eventBus.on('selection.changed', function(e) {
+    var newElement = e.newSelection[0];
+
+    var rootElement = canvas.getRootElement();
+
+    if (isImplicitRoot(rootElement)) {
+      return;
+    }
+
+    self.update(newElement);
+  });
+
+  // add / update tab-bar scrolling
+  eventBus.on([
+    'propertiesPanel.changed',
+    'propertiesPanel.resized'
+  ], function(event) {
+
+    var tabBarNode = domQuery('.bpp-properties-tab-bar', self._container);
+
+    if (!tabBarNode) {
+      return;
+    }
+
+    var scroller = scrollTabs.get(tabBarNode);
+
+    if (!scroller) {
+
+      // we did not initialize yet, do that
+      // now and make sure we select the active
+      // tab on scroll update
+      scroller = scrollTabs(tabBarNode, {
+        selectors: {
+          tabsContainer: '.bpp-properties-tabs-links',
+          tab: '.bpp-properties-tabs-links li',
+          ignore: '.bpp-hidden',
+          active: '.bpp-active'
+        }
+      });
+
+
+      scroller.on('scroll', function(newActiveNode, oldActiveNode, direction) {
+
+        var linkNode = domQuery('[data-tab-target]', newActiveNode);
+
+        var tabId = domAttr(linkNode, 'data-tab-target');
+
+        self.activateTab(tabId);
+      });
+    }
+
+    // react on tab changes and or tabContainer resize
+    // and make sure the active tab is shown completely
+    scroller.update();
+  });
+
+  eventBus.on('elements.changed', function(e) {
+
+    var current = self._current;
+    var element = current && current.element;
+
+    if (element) {
+      if (e.elements.indexOf(element) !== -1) {
+        self.update(element);
+      }
+    }
+  });
+
+  eventBus.on('elementTemplates.changed', function() {
+    var current = self._current;
+    var element = current && current.element;
+
+    if (element) {
+      self.update(element);
+    }
+  });
+
+  eventBus.on('diagram.destroy', function() {
+    self.detach();
+  });
+
+  this._container = domify('<div class="bpp-properties-panel"></div>');
+
+  this._bindListeners(this._container);
+
+  if (config && config.parent) {
+    this.attachTo(config.parent);
+  }
+};
+
+
+PropertiesPanel.prototype.attachTo = function(parentNode) {
+
+  if (!parentNode) {
+    throw new Error('parentNode required');
+  }
+
+  // ensure we detach from the
+  // previous, old parent
+  this.detach();
+
+  // unwrap jQuery if provided
+  if (parentNode.get && parentNode.constructor.prototype.jquery) {
+    parentNode = parentNode.get(0);
+  }
+
+  if (typeof parentNode === 'string') {
+    parentNode = domQuery(parentNode);
+  }
+
+  var container = this._container;
+
+  parentNode.appendChild(container);
+
+  this._emit('attach');
+};
+
+PropertiesPanel.prototype.detach = function() {
+
+  var container = this._container,
+      parentNode = container.parentNode;
+
+  if (!parentNode) {
+    return;
+  }
+
+  this._emit('detach');
+
+  parentNode.removeChild(container);
+};
+
+
+/**
+ * Select the given tab within the properties panel.
+ *
+ * @param {Object|String} tab
+ */
+PropertiesPanel.prototype.activateTab = function(tab) {
+
+  var tabId = typeof tab === 'string' ? tab : tab.id;
+
+  var current = this._current;
+
+  var panelNode = current.panel;
+
+  var allTabNodes = domQueryAll('.bpp-properties-tab', panelNode),
+      allTabLinkNodes = domQueryAll('.bpp-properties-tab-link', panelNode);
+
+  forEach(allTabNodes, function(tabNode) {
+
+    var currentTabId = domAttr(tabNode, 'data-tab');
+
+    domClasses(tabNode).toggle('bpp-active', tabId === currentTabId);
+  });
+
+  forEach(allTabLinkNodes, function(tabLinkNode) {
+
+    var tabLink = domQuery('[data-tab-target]', tabLinkNode),
+        currentTabId = domAttr(tabLink, 'data-tab-target');
+
+    domClasses(tabLinkNode).toggle('bpp-active', tabId === currentTabId);
+  });
+};
+
+/**
+ * Update the DOM representation of the properties panel
+ */
+PropertiesPanel.prototype.update = function(element) {
+  var current = this._current;
+
+  // no actual selection change
+  var needsCreate = true;
+
+  if (typeof element === 'undefined') {
+
+    // use RootElement of BPMN diagram to generate properties panel if no element is selected
+    element = this._canvas.getRootElement();
+  }
+
+  var newTabs = this._propertiesProvider.getTabs(element);
+
+  if (current && current.element === element) {
+    // see if we can reuse the existing panel
+
+    needsCreate = this._entriesChanged(current, newTabs);
+  }
+
+  if (needsCreate) {
+
+    if (current) {
+
+      // get active tab from the existing panel before remove it
+      var activeTabNode = domQuery('.bpp-properties-tab.bpp-active', current.panel);
+
+      var activeTabId;
+      if (activeTabNode) {
+        activeTabId = domAttr(activeTabNode, 'data-tab');
+      }
+
+      // remove old panel
+      domRemove(current.panel);
+    }
+
+    this._current = this._create(element, newTabs);
+
+    // activate the saved active tab from the remove panel or the first tab
+    (activeTabId) ? this.activateTab(activeTabId) : this.activateTab(this._current.tabs[0]);
+
+  }
+
+  if (this._current) {
+    // make sure correct tab contents are visible
+    this._updateActivation(this._current);
+
+  }
+
+  this._emit('changed');
+};
+
+
+/**
+ * Returns true if one of two groups has different entries than the other.
+ *
+ * @param  {Object} current
+ * @param  {Object} newTabs
+ * @return {Boolean}
+ */
+PropertiesPanel.prototype._entriesChanged = function(current, newTabs) {
+
+  var oldEntryIds = keys(current.entries),
+      newEntryIds = keys(extractEntries(newTabs));
+
+  return !isEmpty(xor(oldEntryIds, newEntryIds));
+};
+
+PropertiesPanel.prototype._emit = function(event) {
+  this._eventBus.fire('propertiesPanel.' + event, { panel: this, current: this._current });
+};
+
+PropertiesPanel.prototype._bindListeners = function(container) {
+
+  var self = this;
+
+  // handles a change for a given event
+  var handleChange = function handleChange(event) {
+
+    // see if we handle a change inside a [data-entry] element.
+    // if not, drop out
+    var inputNode = event.delegateTarget,
+        entryNode = domClosest(inputNode, '[data-entry]'),
+        entryId, entry;
+
+    // change from outside a [data-entry] element, simply ignore
+    if (!entryNode) {
+      return;
+    }
+
+    entryId = domAttr(entryNode, 'data-entry');
+    entry = self.getEntry(entryId);
+
+    var values = getFormControlValues(entryNode);
+
+    if (event.type === 'change') {
+
+      // - if the "data-on-change" attribute is present and a value is changed,
+      //   then the associated action is performed.
+      // - if the associated action returns "true" then an update to the business
+      //   object is done
+      // - if it does not return "true", then only the DOM content is updated
+      var onChangeAction = domAttr(inputNode, 'data-on-change');
+
+      if (onChangeAction) {
+        var isEntryDirty = self.executeAction(entry, entryNode, onChangeAction, event);
+
+        if (!isEntryDirty) {
+          return self.update(self._current.element);
+        }
+      }
+    }
+    self.applyChanges(entry, values, entryNode);
+    self.updateState(entry, entryNode);
+  };
+
+  // debounce update only elements that are target of key events,
+  // i.e. INPUT and TEXTAREA. SELECTs will trigger an immediate update anyway.
+  domDelegate.bind(container, 'input, textarea, [contenteditable]', 'input', debounce(handleChange, DEBOUNCE_DELAY));
+  domDelegate.bind(container, 'input, textarea, select, [contenteditable]', 'change', handleChange);
+
+  // handle key events
+  domDelegate.bind(container, 'select', 'keydown', function(e) {
+
+    // DEL
+    if (e.keyCode === 46) {
+      e.stopPropagation();
+      e.preventDefault();
+    }
+  });
+
+  domDelegate.bind(container, '[data-action]', 'click', function onClick(event) {
+
+    // triggers on all inputs
+    var inputNode = event.delegateTarget,
+        entryNode = domClosest(inputNode, '[data-entry]');
+
+    var actionId = domAttr(inputNode, 'data-action'),
+        entryId = domAttr(entryNode, 'data-entry');
+
+    var entry = self.getEntry(entryId);
+
+    var isEntryDirty = self.executeAction(entry, entryNode, actionId, event);
+
+    if (isEntryDirty) {
+      var values = getFormControlValues(entryNode);
+
+      self.applyChanges(entry, values, entryNode);
+    }
+
+    self.updateState(entry, entryNode);
+  });
+
+  function handleInput(event, element) {
+    // triggers on all inputs
+    var inputNode = event.delegateTarget;
+
+    var entryNode = domClosest(inputNode, '[data-entry]');
+
+    // only work on data entries
+    if (!entryNode) {
+      return;
+    }
+
+    var eventHandlerId = domAttr(inputNode, 'data-blur'),
+        entryId = domAttr(entryNode, 'data-entry');
+
+    var entry = self.getEntry(entryId);
+
+    var isEntryDirty = self.executeAction(entry, entryNode, eventHandlerId, event);
+
+    if (isEntryDirty) {
+      var values = getFormControlValues(entryNode);
+
+      self.applyChanges(entry, values, entryNode);
+    }
+
+    self.updateState(entry, entryNode);
+  }
+
+  domDelegate.bind(container, '[data-blur]', 'blur', handleInput, true);
+
+  // make tab links interactive
+  domDelegate.bind(container, '.bpp-properties-tabs-links [data-tab-target]', 'click', function(event) {
+    event.preventDefault();
+
+    var delegateTarget = event.delegateTarget;
+
+    var tabId = domAttr(delegateTarget, 'data-tab-target');
+
+    // activate tab on link click
+    self.activateTab(tabId);
+  });
+
+};
+
+PropertiesPanel.prototype.updateState = function(entry, entryNode) {
+  this.updateShow(entry, entryNode);
+  this.updateDisable(entry, entryNode);
+};
+
+/**
+ * Update the visibility of the entry node in the DOM
+ */
+PropertiesPanel.prototype.updateShow = function(entry, node) {
+
+  var current = this._current;
+
+  if (!current) {
+    return;
+  }
+
+  var showNodes = domQueryAll('[data-show]', node) || [];
+
+  forEach(showNodes, function(showNode) {
+
+    var expr = domAttr(showNode, 'data-show');
+    var fn = get(entry, expr);
+    if (fn) {
+      var scope = domClosest(showNode, '[data-scope]') || node;
+      var shouldShow = fn(current.element, node, showNode, scope) || false;
+      if (shouldShow) {
+        domClasses(showNode).remove(HIDE_CLASS);
+      } else {
+        domClasses(showNode).add(HIDE_CLASS);
+      }
+    }
+  });
+};
+
+/**
+ * Evaluates a given function. If it returns true, then the
+ * node is marked as "disabled".
+ */
+PropertiesPanel.prototype.updateDisable = function(entry, node) {
+  var current = this._current;
+
+  if (!current) {
+    return;
+  }
+
+  var nodes = domQueryAll('[data-disable]', node) || [];
+
+  forEach(nodes, function(currentNode) {
+    var expr = domAttr(currentNode, 'data-disable');
+    var fn = get(entry, expr);
+    if (fn) {
+      var scope = domClosest(currentNode, '[data-scope]') || node;
+      var shouldDisable = fn(current.element, node, currentNode, scope) || false;
+      domAttr(currentNode, 'disabled', shouldDisable ? '' : null);
+    }
+  });
+};
+
+PropertiesPanel.prototype.executeAction = function(entry, entryNode, actionId, event) {
+  var current = this._current;
+
+  if (!current) {
+    return;
+  }
+
+  var fn = get(entry, actionId);
+  if (fn) {
+    var scopeNode = domClosest(event.target, '[data-scope]') || entryNode;
+    return fn.apply(entry, [ current.element, entryNode, event, scopeNode ]);
+  }
+};
+
+/**
+ * Apply changes to the business object by executing a command
+ */
+PropertiesPanel.prototype.applyChanges = function(entry, values, containerElement) {
+
+  var element = this._current.element;
+
+  // ensure we only update the model if we got dirty changes
+  if (valuesEqual(values, entry.oldValues)) {
+    return;
+  }
+
+  var command = entry.set(element, values, containerElement);
+
+  var commandToExecute;
+
+  if (isArray(command)) {
+    if (command.length) {
+      commandToExecute = {
+        cmd: 'properties-panel.multi-command-executor',
+        context: flattenDeep(command)
+      };
+    }
+  } else {
+    commandToExecute = command;
+  }
+
+  if (commandToExecute) {
+    this._commandStack.execute(commandToExecute.cmd, commandToExecute.context || { element : element });
+  } else {
+    this.update(element);
+  }
+};
+
+
+/**
+ * apply validation errors in the DOM and show or remove an error message near the entry node.
+ */
+PropertiesPanel.prototype.applyValidationErrors = function(validationErrors, entryNode) {
+
+  var valid = true;
+
+  var controlNodes = getFormControls(entryNode, true);
+
+  forEach(controlNodes, function(controlNode) {
+
+    var name = domAttr(controlNode, 'name') || domAttr(controlNode, 'data-name');
+
+    var error = validationErrors && validationErrors[name];
+
+    var errorMessageNode = domQuery('.bpp-error-message', controlNode.parentNode);
+
+    if (error) {
+      valid = false;
+
+      if (!errorMessageNode) {
+        errorMessageNode = domify('<div></div>');
+
+        domClasses(errorMessageNode).add('bpp-error-message');
+
+        // insert errorMessageNode after controlNode
+        controlNode.parentNode.insertBefore(errorMessageNode, controlNode.nextSibling);
+      }
+
+      errorMessageNode.textContent = error;
+
+      domClasses(controlNode).add('invalid');
+    } else {
+      domClasses(controlNode).remove('invalid');
+
+      if (errorMessageNode) {
+        controlNode.parentNode.removeChild(errorMessageNode);
+      }
+    }
+  });
+
+  return valid;
+};
+
+
+/**
+ * Check if the entry contains valid input
+ */
+PropertiesPanel.prototype.validate = function(entry, values, entryNode) {
+  var self = this;
+
+  var current = this._current;
+
+  var valid = true;
+
+  entryNode = entryNode || domQuery('[data-entry="' + entry.id + '"]', current.panel);
+
+  if (values instanceof Array) {
+    var listContainer = domQuery('[data-list-entry-container]', entryNode),
+        listEntryNodes = listContainer.children || [];
+
+    // create new elements
+    for (var i = 0; i < values.length; i++) {
+      var listValue = values[i];
+
+      if (entry.validateListItem) {
+
+        var validationErrors = entry.validateListItem(current.element, listValue, entryNode, i),
+            listEntryNode = listEntryNodes[i];
+
+        valid = self.applyValidationErrors(validationErrors, listEntryNode) && valid;
+      }
+    }
+  } else {
+    if (entry.validate) {
+      this.validationErrors = entry.validate(current.element, values, entryNode);
+
+      valid = self.applyValidationErrors(this.validationErrors, entryNode) && valid;
+    }
+  }
+
+  return valid;
+};
+
+PropertiesPanel.prototype.getEntry = function(id) {
+  return this._current && this._current.entries[id];
+};
+
+var flattenDeep = require('lodash/flattenDeep'),
+    keyBy = require('lodash/keyBy'),
+    map = require('lodash/map');
+
+PropertiesPanel.prototype._create = function(element, tabs) {
+
+  if (!element) {
+    return null;
+  }
+
+  var containerNode = this._container;
+
+  var panelNode = this._createPanel(element, tabs);
+
+  containerNode.appendChild(panelNode);
+
+  var entries = extractEntries(tabs);
+  var groups = extractGroups(tabs);
+
+  return {
+    tabs: tabs,
+    groups: groups,
+    entries: entries,
+    element: element,
+    panel: panelNode
+  };
+};
+
+/**
+ * Update variable parts of the entry node on element changes.
+ *
+ * @param {djs.model.Base} element
+ * @param {EntryDescriptor} entry
+ * @param {Object} values
+ * @param {HTMLElement} entryNode
+ * @param {Number} idx
+ */
+PropertiesPanel.prototype._bindTemplate = function(element, entry, values, entryNode, idx) {
+
+  var eventBus = this._eventBus;
+
+  function isPropertyEditable(entry, propertyName) {
+    return eventBus.fire('propertiesPanel.isPropertyEditable', {
+      entry: entry,
+      propertyName: propertyName,
+      element: element
+    });
+  }
+
+  var inputNodes = getPropertyPlaceholders(entryNode);
+
+  forEach(inputNodes, function(node) {
+
+    var name,
+        newValue,
+        editable;
+
+    // we deal with an input element
+    if ('value' in node || isContentEditable(node) === 'true') {
+      name = domAttr(node, 'name') || domAttr(node, 'data-name');
+      newValue = values[name];
+
+      editable = isPropertyEditable(entry, name);
+      if (editable && entry.editable) {
+        editable = entry.editable(element, entryNode, node, name, newValue, idx);
+      }
+
+      domAttr(node, 'readonly', editable ? null : '');
+      domAttr(node, 'disabled', editable ? null : '');
+
+      // take full control over setting the value
+      // and possibly updating the input in entry#setControlValue
+      if (entry.setControlValue) {
+        entry.setControlValue(element, entryNode, node, name, newValue, idx);
+      } else if (isToggle(node)) {
+        setToggleValue(node, newValue);
+      } else if (isSelect(node)) {
+        setSelectValue(node, newValue);
+      } else {
+        setInputValue(node, newValue);
+      }
+    }
+
+    // we deal with some non-editable html element
+    else {
+      name = domAttr(node, 'data-value');
+      newValue = values[name];
+      if (entry.setControlValue) {
+        entry.setControlValue(element, entryNode, node, name, newValue, idx);
+      } else {
+        setTextValue(node, newValue);
+      }
+    }
+  });
+};
+
+// TODO(nikku): WTF freaking name? Change / clarify.
+PropertiesPanel.prototype._updateActivation = function(current) {
+  var self = this;
+
+  var eventBus = this._eventBus;
+
+  var element = current.element;
+
+  function isEntryVisible(entry) {
+    return eventBus.fire('propertiesPanel.isEntryVisible', {
+      entry: entry,
+      element: element
+    });
+  }
+
+  function isGroupVisible(group, element, groupNode) {
+    if (typeof group.enabled === 'function') {
+      return group.enabled(element, groupNode);
+    } else {
+      return true;
+    }
+  }
+
+  function isTabVisible(tab, element) {
+    if (typeof tab.enabled === 'function') {
+      return tab.enabled(element);
+    } else {
+      return true;
+    }
+  }
+
+  function toggleVisible(node, visible) {
+    domClasses(node).toggle(HIDE_CLASS, !visible);
+  }
+
+  // check whether the active tab is visible
+  // if not: set the first tab as active tab
+  function checkActiveTabVisibility(node, visible) {
+    var isActive = domClasses(node).has('bpp-active');
+    if (!visible && isActive) {
+      self.activateTab(current.tabs[0]);
+    }
+  }
+
+  function updateLabel(element, selector, text) {
+    var labelNode = domQuery(selector, element);
+
+    if (!labelNode) {
+      return;
+    }
+
+    labelNode.textContent = text;
+  }
+
+  var panelNode = current.panel;
+
+  forEach(current.tabs, function(tab) {
+
+    var tabNode = domQuery('[data-tab=' + tab.id + ']', panelNode);
+    var tabLinkNode = domQuery('[data-tab-target=' + tab.id + ']', panelNode).parentNode;
+
+    var tabVisible = false;
+
+    forEach(tab.groups, function(group) {
+
+      var groupVisible = false;
+
+      var groupNode = domQuery('[data-group=' + group.id + ']', tabNode);
+
+      forEach(group.entries, function(entry) {
+
+        var entryNode = domQuery('[data-entry="' + entry.id + '"]', groupNode);
+
+        var entryVisible = isEntryVisible(entry);
+
+        groupVisible = groupVisible || entryVisible;
+
+        toggleVisible(entryNode, entryVisible);
+
+        var values = 'get' in entry ? entry.get(element, entryNode) : {};
+
+        if (values instanceof Array) {
+          var listEntryContainer = domQuery('[data-list-entry-container]', entryNode);
+          var existingElements = listEntryContainer.children || [];
+
+          for (var i = 0; i < values.length; i++) {
+            var listValue = values[i];
+            var listItemNode = existingElements[i];
+            if (!listItemNode) {
+              listItemNode = domify(entry.createListEntryTemplate(listValue, i, listEntryContainer));
+              listEntryContainer.appendChild(listItemNode);
+            }
+            domAttr(listItemNode, 'data-index', i);
+
+            self._bindTemplate(element, entry, listValue, listItemNode, i);
+          }
+
+          var entriesToRemove = existingElements.length - values.length;
+
+          for (var j = 0; j < entriesToRemove; j++) {
+            // remove orphaned element
+            listEntryContainer.removeChild(listEntryContainer.lastChild);
+          }
+
+        } else {
+          self._bindTemplate(element, entry, values, entryNode);
+        }
+
+        // update conditionally visible elements
+        self.updateState(entry, entryNode);
+        self.validate(entry, values, entryNode);
+
+        // remember initial state for later dirty checking
+        entry.oldValues = getFormControlValues(entryNode);
+      });
+
+      if (typeof group.label === 'function') {
+        updateLabel(groupNode, '.group-label', group.label(element, groupNode));
+      }
+
+      groupVisible = groupVisible && isGroupVisible(group, element, groupNode);
+
+      tabVisible = tabVisible || groupVisible;
+
+      toggleVisible(groupNode, groupVisible);
+    });
+
+    tabVisible = tabVisible && isTabVisible(tab, element);
+
+    toggleVisible(tabNode, tabVisible);
+    toggleVisible(tabLinkNode, tabVisible);
+
+    checkActiveTabVisibility(tabNode, tabVisible);
+  });
+
+  // inject elements id into header
+  updateLabel(panelNode, '[data-label-id]', getBusinessObject(element).id || '');
+};
+
+PropertiesPanel.prototype._createPanel = function(element, tabs) {
+  var self = this;
+
+  var panelNode = domify('<div class="bpp-properties"></div>'),
+      headerNode = domify('<div class="bpp-properties-header">' +
+        '<div class="label" data-label-id></div>' +
+        '<div class="search">' +
+          '<input type="search" placeholder="Search for property" />' +
+          '<button><span>Search</span></button>' +
+        '</div>' +
+      '</div>'),
+      tabBarNode = domify('<div class="bpp-properties-tab-bar"></div>'),
+      tabLinksNode = domify('<ul class="bpp-properties-tabs-links"></ul>'),
+      tabContainerNode = domify('<div class="bpp-properties-tabs-container"></div>');
+
+  panelNode.appendChild(headerNode);
+
+  forEach(tabs, function(tab, tabIndex) {
+
+    if (!tab.id) {
+      throw new Error('tab must have an id');
+    }
+
+    var tabNode = domify('<div class="bpp-properties-tab" data-tab="' + escapeHTML(tab.id) + '"></div>'),
+        tabLinkNode = domify('<li class="bpp-properties-tab-link">' +
+          '<a href data-tab-target="' + escapeHTML(tab.id) + '">' + escapeHTML(tab.label) + '</a>' +
+        '</li>');
+
+    var groups = tab.groups;
+
+    forEach(groups, function(group) {
+
+      if (!group.id) {
+        throw new Error('group must have an id');
+      }
+
+      var groupNode = domify('<div class="bpp-properties-group" data-group="' + escapeHTML(group.id) + '">' +
+          '<span class="group-toggle"></span>' +
+          '<span class="group-label">' + escapeHTML(group.label) + '</span>' +
+        '</div>');
+
+      // TODO(nre): use event delegation to handle that...
+      groupNode.querySelector('.group-toggle').addEventListener('click', function(evt) {
+        domClasses(groupNode).toggle('group-closed');
+        evt.preventDefault();
+        evt.stopPropagation();
+      });
+      groupNode.addEventListener('click', function(evt) {
+        if (!evt.defaultPrevented && domClasses(groupNode).has('group-closed')) {
+          domClasses(groupNode).remove('group-closed');
+        }
+      });
+
+      forEach(group.entries, function(entry) {
+
+        if (!entry.id) {
+          throw new Error('entry must have an id');
+        }
+
+        var html = entry.html;
+
+        if (typeof html === 'string') {
+          html = domify(html);
+        }
+
+        // unwrap jquery
+        if (html.get && html.constructor.prototype.jquery) {
+          html = html.get(0);
+        }
+
+        var entryNode = domify('<div class="bpp-properties-entry" data-entry="' + escapeHTML(entry.id) + '"></div>');
+
+        forEach(entry.cssClasses || [], function(cssClass) {
+          domClasses(entryNode).add(cssClass);
+        });
+
+        entryNode.appendChild(html);
+
+        groupNode.appendChild(entryNode);
+
+        // update conditionally visible elements
+        self.updateState(entry, entryNode);
+      });
+
+      tabNode.appendChild(groupNode);
+    });
+
+    tabLinksNode.appendChild(tabLinkNode);
+    tabContainerNode.appendChild(tabNode);
+  });
+
+  tabBarNode.appendChild(tabLinksNode);
+
+  panelNode.appendChild(tabBarNode);
+  panelNode.appendChild(tabContainerNode);
+
+  return panelNode;
+};
+
+
+
+function setInputValue(node, value) {
+
+  var contentEditable = isContentEditable(node);
+
+  var oldValue = contentEditable ? node.innerText : node.value;
+
+  var selection;
+
+  // prevents input fields from having the value 'undefined'
+  if (value === undefined) {
+    value = '';
+  }
+
+  if (oldValue === value) {
+    return;
+  }
+
+  // update selection on undo/redo
+  if (document.activeElement === node) {
+    selection = updateSelection(getSelection(node), oldValue, value);
+  }
+
+  if (contentEditable) {
+    node.innerText = value;
+  } else {
+    node.value = value;
+  }
+
+  if (selection) {
+    setSelection(node, selection);
+  }
+}
+
+function setSelectValue(node, value) {
+  if (value !== undefined) {
+    node.value = value;
+  }
+}
+
+function setToggleValue(node, value) {
+  var nodeValue = node.value;
+
+  node.checked = (value === nodeValue) || (!domAttr(node, 'value') && value);
+}
+
+function setTextValue(node, value) {
+  node.textContent = value;
+}
+
+function getSelection(node) {
+
+  return isContentEditable(node) ? getContentEditableSelection(node) : {
+    start: node.selectionStart,
+    end: node.selectionEnd
+  };
+}
+
+function getContentEditableSelection(node) {
+
+  var selection = window.getSelection();
+
+  var focusNode = selection.focusNode,
+      focusOffset = selection.focusOffset,
+      anchorOffset = selection.anchorOffset;
+
+  if (!focusNode) {
+    throw new Error('not selected');
+  }
+
+  // verify we have selection on the current element
+  if (!node.contains(focusNode)) {
+    throw new Error('not selected');
+  }
+
+  return {
+    start: Math.min(focusOffset, anchorOffset),
+    end: Math.max(focusOffset, anchorOffset)
+  };
+}
+
+function setSelection(node, selection) {
+
+  if (isContentEditable(node)) {
+    setContentEditableSelection(node, selection);
+  } else {
+    node.selectionStart = selection.start;
+    node.selectionEnd = selection.end;
+  }
+}
+
+function setContentEditableSelection(node, selection) {
+
+  var focusNode,
+      domRange,
+      domSelection;
+
+  focusNode = node.firstChild || node,
+  domRange = document.createRange();
+  domRange.setStart(focusNode, selection.start);
+  domRange.setEnd(focusNode, selection.end);
+
+  domSelection = window.getSelection();
+  domSelection.removeAllRanges();
+  domSelection.addRange(domRange);
+}
+
+function isImplicitRoot(element) {
+  return element.id === '__implicitroot';
+}

+ 252 - 0
ruoyi-bpmnjs/resources/properties-panel/Utils.js

@@ -0,0 +1,252 @@
+'use strict';
+
+var domQuery = require('min-dom').query,
+    domClear = require('min-dom').clear,
+    is = require('bpmn-js/lib/util/ModelUtil').is,
+    forEach = require('lodash/forEach'),
+    domify = require('min-dom').domify,
+    Ids = require('ids').default;
+
+var SPACE_REGEX = /\s/;
+
+// for QName validation as per http://www.w3.org/TR/REC-xml/#NT-NameChar
+var QNAME_REGEX = /^([a-z][\w-.]*:)?[a-z_][\w-.]*$/i;
+
+// for ID validation as per BPMN Schema (QName - Namespace)
+var ID_REGEX = /^[a-z_][\w-.]*$/i;
+
+var PLACEHOLDER_REGEX = /\$\{([^}]*)\}/g;
+
+var HTML_ESCAPE_MAP = {
+  '&': '&amp;',
+  '<': '&lt;',
+  '>': '&gt;',
+  '"': '&quot;',
+  '\'': '&#39;'
+};
+
+function selectedOption(selectBox) {
+  if (selectBox.selectedIndex >= 0) {
+    return selectBox.options[selectBox.selectedIndex].value;
+  }
+}
+
+module.exports.selectedOption = selectedOption;
+
+
+function selectedType(elementSyntax, inputNode) {
+  var typeSelect = domQuery(elementSyntax, inputNode);
+  return selectedOption(typeSelect);
+}
+
+module.exports.selectedType = selectedType;
+
+
+/**
+ * Retrieve the root element the document this
+ * business object is contained in.
+ *
+ * @return {ModdleElement}
+ */
+function getRoot(businessObject) {
+  var parent = businessObject;
+  while (parent.$parent) {
+    parent = parent.$parent;
+  }
+  return parent;
+}
+
+module.exports.getRoot = getRoot;
+
+
+/**
+ * filters all elements in the list which have a given type.
+ * removes a new list
+ */
+function filterElementsByType(objectList, type) {
+  var list = objectList || [];
+  var result = [];
+  forEach(list, function(obj) {
+    if (is(obj, type)) {
+      result.push(obj);
+    }
+  });
+  return result;
+}
+
+module.exports.filterElementsByType = filterElementsByType;
+
+
+function findRootElementsByType(businessObject, referencedType) {
+  var root = getRoot(businessObject);
+
+  return filterElementsByType(root.rootElements, referencedType);
+}
+
+module.exports.findRootElementsByType = findRootElementsByType;
+
+
+function removeAllChildren(domElement) {
+  while (domElement.firstChild) {
+    domElement.removeChild(domElement.firstChild);
+  }
+}
+
+module.exports.removeAllChildren = removeAllChildren;
+
+
+/**
+ * adds an empty option to the list
+ */
+function addEmptyParameter(list) {
+  return list.push({ 'label': '', 'value': '', 'name': '' });
+}
+
+module.exports.addEmptyParameter = addEmptyParameter;
+
+
+/**
+ * returns a list with all root elements for the given parameter 'referencedType'
+ */
+function refreshOptionsModel(businessObject, referencedType) {
+  var model = [];
+  var referableObjects = findRootElementsByType(businessObject, referencedType);
+  forEach(referableObjects, function(obj) {
+    model.push({
+      label: (obj.name || '') + ' (id='+obj.id+')',
+      value: obj.id,
+      name: obj.name
+    });
+  });
+  return model;
+}
+
+module.exports.refreshOptionsModel = refreshOptionsModel;
+
+
+/**
+ * fills the drop down with options
+ */
+function updateOptionsDropDown(domSelector, businessObject, referencedType, entryNode) {
+  var options = refreshOptionsModel(businessObject, referencedType);
+  addEmptyParameter(options);
+  var selectBox = domQuery(domSelector, entryNode);
+  domClear(selectBox);
+
+  forEach(options, function(option) {
+    var optionEntry = domify('<option value="' + escapeHTML(option.value) + '">' + escapeHTML(option.label) + '</option>');
+    selectBox.appendChild(optionEntry);
+  });
+  return options;
+}
+
+module.exports.updateOptionsDropDown = updateOptionsDropDown;
+
+
+/**
+ * checks whether the id value is valid
+ *
+ * @param {ModdleElement} bo
+ * @param {String} idValue
+ * @param {Function} translate
+ *
+ * @return {String} error message
+ */
+function isIdValid(bo, idValue, translate) {
+  var assigned = bo.$model.ids.assigned(idValue);
+
+  var idExists = assigned && assigned !== bo;
+
+  if (!idValue || idExists) {
+    return translate('Element must have an unique id.');
+  }
+
+  return validateId(idValue, translate);
+}
+
+module.exports.isIdValid = isIdValid;
+
+
+function validateId(idValue, translate) {
+
+  idValue = stripPlaceholders(idValue);
+
+  if (containsSpace(idValue)) {
+    return translate('Id must not contain spaces.');
+  }
+
+  if (!ID_REGEX.test(idValue)) {
+
+    if (QNAME_REGEX.test(idValue)) {
+      return translate('Id must not contain prefix.');
+    }
+
+    return translate('Id must be a valid QName.');
+  }
+}
+
+module.exports.validateId = validateId;
+
+
+function containsSpace(value) {
+  return SPACE_REGEX.test(value);
+}
+
+module.exports.containsSpace = containsSpace;
+
+
+function stripPlaceholders(idValue) {
+
+  // replace expression e.g. ${VERSION_TAG}
+  // use only the content between ${}
+  // for the REGEX check
+  return idValue.replace(PLACEHOLDER_REGEX, '$1');
+}
+
+/**
+ * generate a semantic id with given prefix
+ */
+function nextId(prefix) {
+  var ids = new Ids([32,32,1]);
+
+  return ids.nextPrefixed(prefix);
+}
+
+module.exports.nextId = nextId;
+
+
+function triggerClickEvent(element) {
+  var evt;
+  var eventType = 'click';
+
+  if (document.createEvent) {
+    try {
+      // Chrome, Safari, Firefox
+      evt = new MouseEvent((eventType), { view: window, bubbles: true, cancelable: true });
+    } catch (e) {
+      // IE 11, PhantomJS (wat!)
+      evt = document.createEvent('MouseEvent');
+
+      evt.initEvent((eventType), true, true);
+    }
+    return element.dispatchEvent(evt);
+  } else {
+    // Welcome IE
+    evt = document.createEventObject();
+
+    return element.fireEvent('on' + eventType, evt);
+  }
+}
+
+module.exports.triggerClickEvent = triggerClickEvent;
+
+
+function escapeHTML(str) {
+  str = '' + str;
+
+  return str && str.replace(/[&<>"']/g, function(match) {
+    return HTML_ESCAPE_MAP[match];
+  });
+}
+
+module.exports.escapeHTML = escapeHTML;

+ 101 - 0
ruoyi-bpmnjs/resources/properties-panel/cmd/CreateAndReferenceHandler.js

@@ -0,0 +1,101 @@
+'use strict';
+
+var elementHelper = require('../helper/ElementHelper');
+
+/**
+ * A handler capable of creating a new element under a provided parent
+ * and updating / creating a reference to it in one atomic action.
+ *
+ * @class
+ * @constructor
+ */
+function CreateAndReferenceElementHandler(elementRegistry, bpmnFactory) {
+  this._elementRegistry = elementRegistry;
+  this._bpmnFactory = bpmnFactory;
+}
+
+CreateAndReferenceElementHandler.$inject = [ 'elementRegistry', 'bpmnFactory' ];
+
+module.exports = CreateAndReferenceElementHandler;
+
+
+// api ////////////////////
+
+/**
+ * Creates a new element under a provided parent and updates / creates a reference to it in
+ * one atomic action.
+ *
+ * @method  CreateAndReferenceElementHandler#execute
+ *
+ * @param {Object} context
+ * @param {djs.model.Base} context.element which is the context for the reference
+ * @param {moddle.referencingObject} context.referencingObject the object which creates the reference
+ * @param {String} context.referenceProperty the property of the referencingObject which makes the reference
+ * @param {moddle.newObject} context.newObject the new object to add
+ * @param {moddle.newObjectContainer} context.newObjectContainer the container for the new object
+ *
+ * @returns {Array<djs.mode.Base>} the updated element
+ */
+CreateAndReferenceElementHandler.prototype.execute = function(context) {
+
+  var referencingObject = ensureNotNull(context.referencingObject, 'referencingObject'),
+      referenceProperty = ensureNotNull(context.referenceProperty, 'referenceProperty'),
+      newObject = ensureNotNull(context.newObject, 'newObject'),
+      newObjectContainer = ensureNotNull(context.newObjectContainer, 'newObjectContainer'),
+      newObjectParent = ensureNotNull(context.newObjectParent, 'newObjectParent'),
+      changed = [ context.element ]; // this will not change any diagram-js elements
+
+  // create new object
+  var referencedObject = elementHelper
+    .createElement(newObject.type, newObject.properties, newObjectParent, this._bpmnFactory);
+  context.referencedObject = referencedObject;
+
+  // add to containing list
+  newObjectContainer.push(referencedObject);
+
+  // adjust reference attribute
+  context.previousReference = referencingObject[referenceProperty];
+  referencingObject[referenceProperty] = referencedObject;
+
+  context.changed = changed;
+
+  // indicate changed on objects affected by the update
+  return changed;
+};
+
+/**
+ * Reverts the update
+ *
+ * @method  CreateAndReferenceElementHandler#revert
+ *
+ * @param {Object} context
+ *
+ * @returns {djs.mode.Base} the updated element
+ */
+CreateAndReferenceElementHandler.prototype.revert = function(context) {
+
+  var referencingObject = context.referencingObject,
+      referenceProperty = context.referenceProperty,
+      previousReference = context.previousReference,
+      referencedObject = context.referencedObject,
+      newObjectContainer = context.newObjectContainer;
+
+  // reset reference
+  referencingObject.set(referenceProperty, previousReference);
+
+  // remove new element
+  newObjectContainer.splice(newObjectContainer.indexOf(referencedObject), 1);
+
+  return context.changed;
+};
+
+
+
+// helpers //////////////
+
+function ensureNotNull(prop, name) {
+  if (!prop) {
+    throw new Error(name + ' required');
+  }
+  return prop;
+}

+ 110 - 0
ruoyi-bpmnjs/resources/properties-panel/cmd/CreateBusinessObjectListHandler.js

@@ -0,0 +1,110 @@
+'use strict';
+
+var forEach = require('lodash/forEach');
+
+var elementHelper = require('../helper/ElementHelper');
+
+/**
+ * A handler that implements a BPMN 2.0 property update
+ * for business objects which are not represented in the
+ * diagram.
+ *
+ * This is useful in the context of the properties panel in
+ * order to update child elements of elements visible in
+ * the diagram.
+ *
+ * Example: perform an update of a specific event definition
+ * of an intermediate event.
+ *
+ * @class
+ * @constructor
+ */
+function CreateBusinessObjectListHandler(elementRegistry, bpmnFactory) {
+  this._elementRegistry = elementRegistry;
+  this._bpmnFactory = bpmnFactory;
+}
+
+CreateBusinessObjectListHandler.$inject = [ 'elementRegistry', 'bpmnFactory' ];
+
+module.exports = CreateBusinessObjectListHandler;
+
+function ensureNotNull(prop, name) {
+  if (!prop) {
+    throw new Error(name + ' required');
+  }
+  return prop;
+
+}
+function ensureList(prop, name) {
+  if (!prop || Object.prototype.toString.call(prop) !== '[object Array]') {
+    throw new Error(name + ' needs to be a list');
+  }
+  return prop;
+}
+
+// api /////////////////////////////////////////////
+
+/**
+ * Creates a new element under a provided parent and updates / creates a reference to it in
+ * one atomic action.
+ *
+ * @method  CreateBusinessObjectListHandler#execute
+ *
+ * @param {Object} context
+ * @param {djs.model.Base} context.element which is the context for the reference
+ * @param {moddle.referencingObject} context.referencingObject the object which creates the reference
+ * @param {String} context.referenceProperty the property of the referencingObject which makes the reference
+ * @param {moddle.newObject} context.newObject the new object to add
+ * @param {moddle.newObjectContainer} context.newObjectContainer the container for the new object
+ *
+ * @return {Array<djs.mode.Base>} the updated element
+ */
+CreateBusinessObjectListHandler.prototype.execute = function(context) {
+
+  var currentObject = ensureNotNull(context.currentObject, 'currentObject'),
+      propertyName = ensureNotNull(context.propertyName, 'propertyName'),
+      newObjects = ensureList(context.newObjects, 'newObjects'),
+      changed = [ context.element ]; // this will not change any diagram-js elements
+
+
+  var childObjects = [];
+  var self = this;
+
+  // create new array of business objects
+  forEach(newObjects, function(obj) {
+    var element = elementHelper.createElement(obj.type, obj.properties, currentObject, self._bpmnFactory);
+
+    childObjects.push(element);
+  });
+  context.childObject = childObjects;
+
+  // adjust array reference in the parent business object
+  context.previousChilds = currentObject[propertyName];
+  currentObject[propertyName] = childObjects;
+
+  context.changed = changed;
+
+  // indicate changed on objects affected by the update
+  return changed;
+};
+
+/**
+ * Reverts the update
+ *
+ * @method  CreateBusinessObjectListHandler#revert
+ *
+ * @param {Object} context
+ *
+ * @return {djs.mode.Base} the updated element
+ */
+CreateBusinessObjectListHandler.prototype.revert = function(context) {
+
+  var currentObject = context.currentObject,
+      propertyName = context.propertyName,
+      previousChilds = context.previousChilds;
+
+  // remove new element
+  currentObject.set(propertyName, previousChilds);
+
+  return context.changed;
+};

+ 32 - 0
ruoyi-bpmnjs/resources/properties-panel/cmd/MultiCommandHandler.js

@@ -0,0 +1,32 @@
+'use strict';
+
+var forEach = require('lodash/forEach');
+
+/**
+ * A handler that combines and executes multiple commands.
+ *
+ * All updates are bundled on the command stack and executed in one step.
+ * This also makes it possible to revert the changes in one step.
+ *
+ * Example use case: remove the camunda:formKey attribute and in addition
+ * add all form fields needed for the camunda:formData property.
+ *
+ * @class
+ * @constructor
+ */
+function MultiCommandHandler(commandStack) {
+  this._commandStack = commandStack;
+}
+
+MultiCommandHandler.$inject = [ 'commandStack' ];
+
+module.exports = MultiCommandHandler;
+
+MultiCommandHandler.prototype.preExecute = function(context) {
+
+  var commandStack = this._commandStack;
+
+  forEach(context, function(command) {
+    commandStack.execute(command.cmd, command.context);
+  });
+};

+ 131 - 0
ruoyi-bpmnjs/resources/properties-panel/cmd/UpdateBusinessObjectHandler.js

@@ -0,0 +1,131 @@
+'use strict';
+
+var reduce = require('lodash/transform'),
+    is = require('bpmn-js/lib/util/ModelUtil').is,
+    keys = require('lodash/keys'),
+    forEach = require('lodash/forEach');
+
+/**
+ * A handler that implements a BPMN 2.0 property update
+ * for business objects which are not represented in the
+ * diagram.
+ *
+ * This is useful in the context of the properties panel in
+ * order to update child elements of elements visible in
+ * the diagram.
+ *
+ * Example: perform an update of a specific event definition
+ * of an intermediate event.
+ *
+ * @class
+ * @constructor
+ */
+function UpdateBusinessObjectHandler(elementRegistry) {
+  this._elementRegistry = elementRegistry;
+}
+
+UpdateBusinessObjectHandler.$inject = [ 'elementRegistry' ];
+
+module.exports = UpdateBusinessObjectHandler;
+
+/**
+ * returns the root element
+ */
+function getRoot(businessObject) {
+  var parent = businessObject;
+  while (parent.$parent) {
+    parent = parent.$parent;
+  }
+  return parent;
+}
+
+function getProperties(businessObject, propertyNames) {
+  return reduce(propertyNames, function(result, key) {
+    result[key] = businessObject.get(key);
+    return result;
+  }, {});
+}
+
+
+function setProperties(businessObject, properties) {
+  forEach(properties, function(value, key) {
+    businessObject.set(key, value);
+  });
+}
+
+
+// api /////////////////////////////////////////////
+
+/**
+ * Updates a business object with a list of new properties
+ *
+ * @method  UpdateBusinessObjectHandler#execute
+ *
+ * @param {Object} context
+ * @param {djs.model.Base} context.element the element which has a child business object updated
+ * @param {moddle.businessObject} context.businessObject the businessObject to update
+ * @param {Object} context.properties a list of properties to set on the businessObject
+ *
+ * @return {Array<djs.mode.Base>} the updated element
+ */
+UpdateBusinessObjectHandler.prototype.execute = function(context) {
+
+  var element = context.element,
+      businessObject = context.businessObject,
+      rootElements = getRoot(businessObject).rootElements,
+      referenceType = context.referenceType,
+      referenceProperty = context.referenceProperty,
+      changed = [ element ]; // this will not change any diagram-js elements
+
+  if (!element) {
+    throw new Error('element required');
+  }
+
+  if (!businessObject) {
+    throw new Error('businessObject required');
+  }
+
+  var properties = context.properties,
+      oldProperties = context.oldProperties || getProperties(businessObject, keys(properties));
+
+  // check if there the update needs an external element for reference
+  if (typeof referenceType !== 'undefined' && typeof referenceProperty !== 'undefined') {
+    forEach(rootElements, function(rootElement) {
+      if (is(rootElement, referenceType)) {
+        if (rootElement.id === properties[referenceProperty]) {
+          properties[referenceProperty] = rootElement;
+        }
+      }
+    });
+  }
+
+  // update properties
+  setProperties(businessObject, properties);
+
+  // store old values
+  context.oldProperties = oldProperties;
+  context.changed = changed;
+
+  // indicate changed on objects affected by the update
+  return changed;
+};
+
+/**
+ * Reverts the update
+ *
+ * @method  UpdateBusinessObjectHandler#revert
+ *
+ * @param {Object} context
+ *
+ * @return {djs.mode.Base} the updated element
+ */
+UpdateBusinessObjectHandler.prototype.revert = function(context) {
+
+  var oldProperties = context.oldProperties,
+      businessObject = context.businessObject;
+
+  // update properties
+  setProperties(businessObject, oldProperties);
+
+  return context.changed;
+};

+ 115 - 0
ruoyi-bpmnjs/resources/properties-panel/cmd/UpdateBusinessObjectListHandler.js

@@ -0,0 +1,115 @@
+'use strict';
+
+var forEach = require('lodash/forEach');
+
+/**
+ * A handler that implements a BPMN 2.0 property update
+ * for business object lists which are not represented in the
+ * diagram.
+ *
+ * This is useful in the context of the properties panel in
+ * order to update child elements of elements visible in
+ * the diagram.
+ *
+ * Example: perform an update of a specific event definition
+ * of an intermediate event.
+ *
+ * @class
+ * @constructor
+ */
+function UpdateBusinessObjectListHandler(elementRegistry, bpmnFactory) {
+  this._elementRegistry = elementRegistry;
+  this._bpmnFactory = bpmnFactory;
+}
+
+UpdateBusinessObjectListHandler.$inject = [ 'elementRegistry', 'bpmnFactory' ];
+
+module.exports = UpdateBusinessObjectListHandler;
+
+function ensureNotNull(prop, name) {
+  if (!prop) {
+    throw new Error(name + 'required');
+  }
+  return prop;
+}
+
+// api /////////////////////////////////////////////
+
+/**
+ * Updates a element under a provided parent.
+ */
+UpdateBusinessObjectListHandler.prototype.execute = function(context) {
+
+  var currentObject = ensureNotNull(context.currentObject, 'currentObject'),
+      propertyName = ensureNotNull(context.propertyName, 'propertyName'),
+      updatedObjectList = context.updatedObjectList,
+      objectsToRemove = context.objectsToRemove || [],
+      objectsToAdd = context.objectsToAdd || [],
+      changed = [ context.element], // this will not change any diagram-js elements
+      referencePropertyName;
+
+  if (context.referencePropertyName) {
+    referencePropertyName = context.referencePropertyName;
+  }
+
+  var objectList = currentObject[propertyName];
+  // adjust array reference in the parent business object
+  context.previousList = currentObject[propertyName];
+
+  if (updatedObjectList) {
+    currentObject[propertyName] = updatedObjectList;
+  } else {
+    var listCopy = [];
+    // remove all objects which should be removed
+    forEach(objectList, function(object) {
+      if (objectsToRemove.indexOf(object) == -1) {
+        listCopy.push(object);
+      }
+    });
+    // add all objects which should be added
+    listCopy = listCopy.concat(objectsToAdd);
+
+    // set property to new list
+    if (listCopy.length > 0 || !referencePropertyName) {
+
+      // as long as there are elements in the list update the list
+      currentObject[propertyName] = listCopy;
+    } else if (referencePropertyName) {
+
+      // remove the list when it is empty
+      var parentObject = currentObject.$parent;
+      parentObject.set(referencePropertyName, undefined);
+    }
+  }
+
+  context.changed = changed;
+
+  // indicate changed on objects affected by the update
+  return changed;
+};
+
+/**
+ * Reverts the update
+ *
+ * @method  CreateBusinessObjectListHandler#revert
+ *
+ * @param {Object} context
+ *
+ * @return {djs.mode.Base} the updated element
+ */
+UpdateBusinessObjectListHandler.prototype.revert = function(context) {
+
+  var currentObject = context.currentObject,
+      propertyName = context.propertyName,
+      previousList = context.previousList,
+      parentObject = currentObject.$parent;
+
+  if (context.referencePropertyName) {
+    parentObject.set(context.referencePropertyName, currentObject);
+  }
+
+  // remove new element
+  currentObject.set(propertyName, previousList);
+
+  return context.changed;
+};

+ 27 - 0
ruoyi-bpmnjs/resources/properties-panel/cmd/index.js

@@ -0,0 +1,27 @@
+'use strict';
+
+var forEach = require('lodash/forEach');
+
+var HANDLERS = {
+  'properties-panel.update-businessobject': require('./UpdateBusinessObjectHandler'),
+  'properties-panel.create-and-reference': require('./CreateAndReferenceHandler'),
+  'properties-panel.create-businessobject-list': require('./CreateBusinessObjectListHandler'),
+  'properties-panel.update-businessobject-list': require('./UpdateBusinessObjectListHandler'),
+  'properties-panel.multi-command-executor': require('./MultiCommandHandler')
+};
+
+
+function CommandInitializer(eventBus, commandStack) {
+
+  eventBus.on('diagram.init', function() {
+    forEach(HANDLERS, function(handler, id) {
+      commandStack.registerHandler(id, handler);
+    });
+  });
+}
+
+CommandInitializer.$inject = [ 'eventBus', 'commandStack' ];
+
+module.exports = {
+  __init__: [ CommandInitializer ]
+};

+ 77 - 0
ruoyi-bpmnjs/resources/properties-panel/factory/CheckboxEntryFactory.js

@@ -0,0 +1,77 @@
+'use strict';
+
+var getBusinessObject = require('bpmn-js/lib/util/ModelUtil').getBusinessObject,
+    cmdHelper = require('../helper/CmdHelper'),
+    escapeHTML = require('../Utils').escapeHTML;
+
+var entryFieldDescription = require('./EntryFieldDescription');
+
+
+var checkbox = function(options, defaultParameters) {
+  var resource = defaultParameters,
+      id = resource.id,
+      label = options.label || id,
+      canBeDisabled = !!options.disabled && typeof options.disabled === 'function',
+      canBeHidden = !!options.hidden && typeof options.hidden === 'function',
+      description = options.description;
+
+  resource.html =
+    '<input id="activiti-' + escapeHTML(id) + '" ' +
+         'type="checkbox" ' +
+         'name="' + escapeHTML(options.modelProperty) + '" ' +
+         (canBeDisabled ? 'data-disable="isDisabled"' : '') +
+         (canBeHidden ? 'data-show="isHidden"' : '') +
+         ' />' +
+    '<label for="activiti-' + escapeHTML(id) + '" ' +
+         (canBeDisabled ? 'data-disable="isDisabled"' : '') +
+         (canBeHidden ? 'data-show="isHidden"' : '') +
+         '>' + escapeHTML(label) + '</label>';
+
+  // add description below checkbox entry field
+  if (description) {
+    resource.html += entryFieldDescription(description);
+  }
+
+  resource.get = function(element) {
+    var bo = getBusinessObject(element),
+        res = {};
+
+    res[options.modelProperty] = bo.get(options.modelProperty);
+
+    return res;
+  };
+
+  resource.set = function(element, values) {
+    var res = {};
+
+    res[options.modelProperty] = !!values[options.modelProperty];
+
+    return cmdHelper.updateProperties(element, res);
+  };
+
+  if (typeof options.set === 'function') {
+    resource.set = options.set;
+  }
+
+  if (typeof options.get === 'function') {
+    resource.get = options.get;
+  }
+
+  if (canBeDisabled) {
+    resource.isDisabled = function() {
+      return options.disabled.apply(resource, arguments);
+    };
+  }
+
+  if (canBeHidden) {
+    resource.isHidden = function() {
+      return !options.hidden.apply(resource, arguments);
+    };
+  }
+
+  resource.cssClasses = ['bpp-checkbox'];
+
+  return resource;
+};
+
+module.exports = checkbox;

+ 118 - 0
ruoyi-bpmnjs/resources/properties-panel/factory/ComboEntryFactory.js

@@ -0,0 +1,118 @@
+'use strict';
+
+var assign = require('lodash/assign'),
+    find = require('lodash/find');
+
+var domQuery = require('min-dom').query;
+
+var escapeHTML = require('../Utils').escapeHTML;
+
+var selectEntryFactory = require('./SelectEntryFactory'),
+    entryFieldDescription = require('./EntryFieldDescription');
+
+
+/**
+ * The combo box is a special implementation of the select entry and adds the option 'custom' to the
+ * select box. If 'custom' is selected, an additional text input field is shown which allows to define
+ * a custom value.
+ *
+ * @param  {Object} options
+ * @param  {string} options.id
+ * @param  {string} options.label
+ * @param  {Array<Object>} options.selectOptions list of name/value pairs
+ * @param  {string} options.modelProperty
+ * @param  {function} options.get
+ * @param  {function} options.set
+ * @param  {string} [options.customValue] custom select option value (default: 'custom')
+ * @param  {string} [options.customName] custom select option name visible in the select box (default: 'custom')
+ *
+ * @return {Object}
+ */
+var comboBox = function(options) {
+
+  var selectOptions = options.selectOptions,
+      modelProperty = options.modelProperty,
+      customValue = options.customValue || 'custom',
+      customName = options.customName || 'custom ' + modelProperty,
+      description = options.description;
+
+  // check if a value is not a built in value
+  var isCustomValue = function(value) {
+    if (typeof value[modelProperty] === 'undefined') {
+      return false;
+    }
+
+    var isCustom = !find(selectOptions, function(option) {
+      return value[modelProperty] === option.value;
+    });
+
+    return isCustom;
+  };
+
+  var comboOptions = assign({}, options);
+
+  // true if the selected value in the select box is customValue
+  comboOptions.showCustomInput = function(element, node) {
+    var selectBox = domQuery('[data-entry="'+ options.id +'"] select', node.parentNode);
+
+    if (selectBox) {
+      return selectBox.value === customValue;
+    }
+
+    return false;
+  };
+
+  comboOptions.get = function(element, node) {
+    var value = options.get(element, node);
+
+    var modifiedValues = {};
+
+    if (!isCustomValue(value)) {
+      modifiedValues[modelProperty] = value[modelProperty] || '';
+
+      return modifiedValues;
+    }
+
+    modifiedValues[modelProperty] = customValue;
+    modifiedValues['custom-'+modelProperty] = value[modelProperty];
+
+    return modifiedValues;
+  };
+
+  comboOptions.set = function(element, values, node) {
+    var modifiedValues = {};
+
+    // if the custom select option has been selected
+    // take the value from the text input field
+    if (values[modelProperty] === customValue) {
+      modifiedValues[modelProperty] = values['custom-' + modelProperty] || '';
+    }
+    else if (options.emptyParameter && values[modelProperty] === '') {
+      modifiedValues[modelProperty] = undefined;
+    } else {
+      modifiedValues[modelProperty] = values[modelProperty];
+    }
+    return options.set(element, modifiedValues, node);
+  };
+
+  comboOptions.selectOptions.push({ name: customName, value: customValue });
+
+  var comboBoxEntry = assign({}, selectEntryFactory(comboOptions, comboOptions));
+
+  comboBoxEntry.html += '<div class="bpp-field-wrapper bpp-combo-input" ' +
+    'data-show="showCustomInput"' +
+    '>' +
+    '<input id="activiti-' + escapeHTML(options.id) + '-input" type="text" name="custom-' +
+      escapeHTML(modelProperty) + '" ' +
+    ' />' +
+  '</div>';
+
+  // add description below combo box entry field
+  if (description) {
+    comboBoxEntry.html += entryFieldDescription(description);
+  }
+
+  return comboBoxEntry;
+};
+
+module.exports = comboBox;

+ 163 - 0
ruoyi-bpmnjs/resources/properties-panel/factory/EntryFactory.js

@@ -0,0 +1,163 @@
+'use strict';
+
+var getBusinessObject = require('bpmn-js/lib/util/ModelUtil').getBusinessObject;
+
+// input entities
+var textInputField = require('./TextInputEntryFactory'),
+    checkboxField = require('./CheckboxEntryFactory'),
+    selectBoxField = require('./SelectEntryFactory'),
+    comboBoxField = require('./ComboEntryFactory'),
+    textBoxField = require('./TextBoxEntryFactory'),
+    validationAwareTextInputField = require('./ValidationAwareTextInput'),
+    tableField = require('./TableEntryFactory'),
+    labelEntry = require('./LabelFactory'),
+    link = require('./LinkEntryFactory');
+
+var cmdHelper = require('../helper/CmdHelper');
+
+// helpers ////////////////////////////////////////
+
+function ensureNotNull(prop) {
+  if (!prop) {
+    throw new Error(prop + ' must be set.');
+  }
+
+  return prop;
+}
+
+/**
+ * sets the default parameters which are needed to create an entry
+ *
+ * @param options
+ * @returns {{id: *, description: (*|string), get: (*|Function), set: (*|Function),
+ *            validate: (*|Function), html: string}}
+ */
+var setDefaultParameters = function(options) {
+
+  // default method to fetch the current value of the input field
+  var defaultGet = function(element) {
+    var bo = getBusinessObject(element),
+        res = {},
+        prop = ensureNotNull(options.modelProperty);
+    res[prop] = bo.get(prop);
+
+    return res;
+  };
+
+  // default method to set a new value to the input field
+  var defaultSet = function(element, values) {
+    var res = {},
+        prop = ensureNotNull(options.modelProperty);
+    if (values[prop] !== '') {
+      res[prop] = values[prop];
+    } else {
+      res[prop] = undefined;
+    }
+
+    return cmdHelper.updateProperties(element, res);
+  };
+
+  // default validation method
+  var defaultValidate = function() {
+    return {};
+  };
+
+  return {
+    id : options.id,
+    description : (options.description || ''),
+    get : (options.get || defaultGet),
+    set : (options.set || defaultSet),
+    validate : (options.validate || defaultValidate),
+    html: ''
+  };
+};
+
+function EntryFactory() {
+
+}
+
+/**
+ * Generates an text input entry object for a property panel.
+ * options are:
+ * - id: id of the entry - String
+ *
+ * - description: description of the property - String
+ *
+ * - label: label for the input field - String
+ *
+ * - set: setter method - Function
+ *
+ * - get: getter method - Function
+ *
+ * - validate: validation mehtod - Function
+ *
+ * - modelProperty: name of the model property - String
+ *
+ * - buttonAction: Object which contains the following properties: - Object
+ * ---- name: name of the [data-action] callback - String
+ * ---- method: callback function for [data-action] - Function
+ *
+ * - buttonShow: Object which contains the following properties: - Object
+ * ---- name: name of the [data-show] callback - String
+ * ---- method: callback function for [data-show] - Function
+ *
+ * @param options
+ * @returns the propertyPanel entry resource object
+ */
+EntryFactory.textField = function(options) {
+  return textInputField(options, setDefaultParameters(options));
+};
+
+EntryFactory.validationAwareTextField = function(options) {
+  return validationAwareTextInputField(options, setDefaultParameters(options));
+};
+
+/**
+ * Generates a checkbox input entry object for a property panel.
+ * options are:
+ * - id: id of the entry - String
+ *
+ * - description: description of the property - String
+ *
+ * - label: label for the input field - String
+ *
+ * - set: setter method - Function
+ *
+ * - get: getter method - Function
+ *
+ * - validate: validation method - Function
+ *
+ * - modelProperty: name of the model property - String
+ *
+ * @param options
+ * @returns the propertyPanel entry resource object
+ */
+EntryFactory.checkbox = function(options) {
+  return checkboxField(options, setDefaultParameters(options));
+};
+
+EntryFactory.textBox = function(options) {
+  return textBoxField(options, setDefaultParameters(options));
+};
+
+EntryFactory.selectBox = function(options) {
+  return selectBoxField(options, setDefaultParameters(options));
+};
+
+EntryFactory.comboBox = function(options) {
+  return comboBoxField(options);
+};
+
+EntryFactory.table = function(options) {
+  return tableField(options);
+};
+
+EntryFactory.label = function(options) {
+  return labelEntry(options);
+};
+
+EntryFactory.link = function(options) {
+  return link(options);
+};
+
+module.exports = EntryFactory;

+ 49 - 0
ruoyi-bpmnjs/resources/properties-panel/factory/EntryFieldDescription.js

@@ -0,0 +1,49 @@
+'use strict';
+
+var escapeHTML = require('../Utils').escapeHTML;
+
+/**
+ * Create a linkified and HTML escaped entry field description.
+ *
+ * As a special feature, this description may contain both markdown
+ * and plain <a href> links.
+ *
+ * @param {String} description
+ */
+module.exports = function entryFieldDescription(description) {
+
+  // we tokenize the description to extract text, HTML and markdown links
+  // text and links are handled seperately
+
+  var escaped = [];
+
+  // match markdown [{TEXT}]({URL}) and HTML links <a href="{URL}">{TEXT}</a>
+  var pattern = /(?:\[([^\]]+)\]\((https?:\/\/[^"<>\]]+)\))|(?:<a href="(https?:\/\/[^"<>]+)">([^<]*)<\/a>)/gi;
+
+  var index = 0;
+  var match;
+  var link, text;
+
+  while ((match = pattern.exec(description))) {
+
+    // escape + insert text before match
+    if (match.index > index) {
+      escaped.push(escapeHTML(description.substring(index, match.index)));
+    }
+
+    link = match[2] || match[3];
+    text = match[1] || match[4];
+
+    // insert safe link
+    escaped.push('<a href="' + link + '" target="_blank">' + escapeHTML(text) + '</a>');
+
+    index = match.index + match[0].length;
+  }
+
+  // escape and insert text after last match
+  if (index < description.length) {
+    escaped.push(escapeHTML(description.substring(index)));
+  }
+
+  return '<div class="bpp-field-description">' + escaped.join('') + '</div>';
+};

+ 38 - 0
ruoyi-bpmnjs/resources/properties-panel/factory/LabelFactory.js

@@ -0,0 +1,38 @@
+'use strict';
+
+/**
+ * The label factory provides a label entry. For the label text
+ * it expects either a string provided by the options.labelText
+ * parameter or it could be generated programmatically using a
+ * function passed as the options.get parameter.
+ *
+ * @param  {Object} options
+ * @param  {string} options.id
+ * @param  {string} [options.labelText]
+ * @param  {Function} [options.get]
+ * @param  {Function} [options.showLabel]
+ * @param  {Boolean} [options.divider] adds a divider at the top of the label if true; default: false
+ */
+var label = function(options) {
+  return {
+    id: options.id,
+    html: '<label data-value="label" ' +
+            'data-show="showLabel" ' +
+            'class="entry-label' + (options.divider ? ' divider' : '') + '">' +
+          '</label>',
+    get: function(element, node) {
+      if (typeof options.get === 'function') {
+        return options.get(element, node);
+      }
+      return { label: options.labelText };
+    },
+    showLabel: function(element, node) {
+      if (typeof options.showLabel === 'function') {
+        return options.showLabel(element, node);
+      }
+      return true;
+    }
+  };
+};
+
+module.exports = label;

+ 78 - 0
ruoyi-bpmnjs/resources/properties-panel/factory/LinkEntryFactory.js

@@ -0,0 +1,78 @@
+'use strict';
+
+var escapeHTML = require('../Utils').escapeHTML;
+
+var entryFieldDescription = require('./EntryFieldDescription');
+
+var bind = require('lodash/bind');
+
+/**
+ * An entry that renders a clickable link.
+ *
+ * A passed {@link options#handleClick} handler is responsible
+ * to process the click.
+ *
+ * The link may be conditionally shown or hidden. This can be
+ * controlled via the {@link options.showLink}.
+ *
+ * @param {Object} options
+ * @param {String} options.id
+ * @param {String} [options.label]
+ * @param {Function} options.handleClick
+ * @param {Function} [options.showLink] returning false to hide link
+ * @param {String} [options.description]
+ *
+ * @example
+ *
+ * var linkEntry = link({
+ *   id: 'foo',
+ *   description: 'Some Description',
+ *   handleClick: function(element, node, event) { ... },
+ *   showLink: function(element, node) { ... }
+ * });
+ *
+ * @return {Entry} the newly created entry
+ */
+function link(options) {
+
+  var id = options.id,
+      label = options.label || id,
+      showLink = options.showLink,
+      handleClick = options.handleClick,
+      description = options.description;
+
+  if (showLink && typeof showLink !== 'function') {
+    throw new Error('options.showLink must be a function');
+  }
+
+  if (typeof handleClick !== 'function') {
+    throw new Error('options.handleClick must be a function');
+  }
+
+  var resource = {
+    id: id
+  };
+
+  resource.html =
+    '<a data-action="handleClick" ' +
+    (showLink ? 'data-show="showLink" ' : '') +
+    'class="bpp-entry-link' + (options.cssClasses ? ' ' + escapeHTML(options.cssClasses) : '') +
+    '">' + escapeHTML(label) + '</a>';
+
+  // add description below link entry field
+  if (description) {
+    resource.html += entryFieldDescription(description);
+  }
+
+  resource.handleClick = bind(handleClick, resource);
+
+  if (typeof showLink === 'function') {
+    resource.showLink = function() {
+      return showLink.apply(resource, arguments);
+    };
+  }
+
+  return resource;
+}
+
+module.exports = link;

+ 137 - 0
ruoyi-bpmnjs/resources/properties-panel/factory/SelectEntryFactory.js

@@ -0,0 +1,137 @@
+'use strict';
+
+var escapeHTML = require('../Utils').escapeHTML;
+
+var domify = require('min-dom').domify;
+
+var forEach = require('lodash/forEach');
+
+var entryFieldDescription = require('./EntryFieldDescription');
+
+
+var isList = function(list) {
+  return !(!list || Object.prototype.toString.call(list) !== '[object Array]');
+};
+
+var addEmptyParameter = function(list) {
+  return list.concat([ { name: '', value: '' } ]);
+};
+
+var createOption = function(option) {
+  return '<option value="' + option.value + '">' + option.name + '</option>';
+};
+
+/**
+ * @param  {Object} options
+ * @param  {string} options.id
+ * @param  {string} [options.label]
+ * @param  {Array<Object>} options.selectOptions
+ * @param  {string} options.modelProperty
+ * @param  {boolean} options.emptyParameter
+ * @param  {function} options.disabled
+ * @param  {function} options.hidden
+ * @param  {Object} defaultParameters
+ *
+ * @return {Object}
+ */
+var selectbox = function(options, defaultParameters) {
+  var resource = defaultParameters,
+      label = options.label || resource.id,
+      selectOptions = options.selectOptions || [ { name: '', value: '' } ],
+      modelProperty = options.modelProperty,
+      emptyParameter = options.emptyParameter,
+      canBeDisabled = !!options.disabled && typeof options.disabled === 'function',
+      canBeHidden = !!options.hidden && typeof options.hidden === 'function',
+      description = options.description;
+
+
+  if (emptyParameter) {
+    selectOptions = addEmptyParameter(selectOptions);
+  }
+
+
+  resource.html =
+    '<label for="activiti-' + escapeHTML(resource.id) + '"' +
+    (canBeDisabled ? 'data-disable="isDisabled" ' : '') +
+    (canBeHidden ? 'data-show="isHidden" ' : '') +
+    '>' + escapeHTML(label) + '</label>' +
+    '<select id="activiti-' + escapeHTML(resource.id) + '-select" name="' +
+    escapeHTML(modelProperty) + '"' +
+    (canBeDisabled ? 'data-disable="isDisabled" ' : '') +
+    (canBeHidden ? 'data-show="isHidden" ' : '') +
+    ' data-value>';
+
+  if (isList(selectOptions)) {
+    forEach(selectOptions, function(option) {
+      resource.html += '<option value="' + escapeHTML(option.value) + '">' +
+      (option.name ? escapeHTML(option.name) : '') + '</option>';
+    });
+  }
+
+  resource.html += '</select>';
+
+  // add description below select box entry field
+  if (description && typeof options.showCustomInput !== 'function') {
+    resource.html += entryFieldDescription(description);
+  }
+
+  /**
+   * Fill the select box options dynamically.
+   *
+   * Calls the defined function #selectOptions in the entry to get the
+   * values for the options and set the value to the inputNode.
+   *
+   * @param {djs.model.Base} element
+   * @param {HTMLElement} entryNode
+   * @param {EntryDescriptor} inputNode
+   * @param {Object} inputName
+   * @param {Object} newValue
+   */
+  resource.setControlValue = function(element, entryNode, inputNode, inputName, newValue) {
+    if (typeof selectOptions === 'function') {
+
+      var options = selectOptions(element, inputNode);
+
+      if (options) {
+
+        // remove existing options
+        while (inputNode.firstChild) {
+          inputNode.removeChild(inputNode.firstChild);
+        }
+
+        // add options
+        forEach(options, function(option) {
+          var template = domify(createOption(option));
+
+          inputNode.appendChild(template);
+        });
+
+
+      }
+    }
+
+    // set select value
+    if (newValue !== undefined) {
+      inputNode.value = newValue;
+    }
+
+  };
+
+  if (canBeDisabled) {
+    resource.isDisabled = function() {
+      return options.disabled.apply(resource, arguments);
+    };
+  }
+
+  if (canBeHidden) {
+    resource.isHidden = function() {
+      return !options.hidden.apply(resource, arguments);
+    };
+  }
+
+  resource.cssClasses = ['bpp-dropdown'];
+
+  return resource;
+};
+
+module.exports = selectbox;

+ 338 - 0
ruoyi-bpmnjs/resources/properties-panel/factory/TableEntryFactory.js

@@ -0,0 +1,338 @@
+'use strict';
+
+var escapeHTML = require('../Utils').escapeHTML;
+
+var cmdHelper = require('../helper/CmdHelper');
+
+var domQuery = require('min-dom').query,
+    domAttr = require('min-dom').attr,
+    domClosest = require('min-dom').closest;
+
+var filter = require('lodash/filter'),
+    forEach = require('lodash/forEach'),
+    keys = require('lodash/keys');
+
+var domify = require('min-dom').domify;
+
+var entryFieldDescription = require('./EntryFieldDescription');
+
+var updateSelection = require('selection-update');
+
+var TABLE_ROW_DIV_SNIPPET = '<div class="bpp-field-wrapper bpp-table-row">';
+var DELETE_ROW_BUTTON_SNIPPET = '<button class="clear" data-action="deleteElement">' +
+                                  '<span>X</span>' +
+                                '</button>';
+
+function createInputRowTemplate(properties, canRemove) {
+  var template = TABLE_ROW_DIV_SNIPPET;
+  template += createInputTemplate(properties, canRemove);
+  template += canRemove ? DELETE_ROW_BUTTON_SNIPPET : '';
+  template += '</div>';
+
+  return template;
+}
+
+function createInputTemplate(properties, canRemove) {
+  var columns = properties.length;
+  var template = '';
+  forEach(properties, function(prop) {
+    template += '<input class="bpp-table-row-columns-' + columns + ' ' +
+                               (canRemove ? 'bpp-table-row-removable' : '') + '" ' +
+                       'id="activiti-table-row-cell-input-value" ' +
+                       'type="text" ' +
+                       'name="' + escapeHTML(prop) + '" />';
+  });
+  return template;
+}
+
+function createLabelRowTemplate(labels) {
+  var template = TABLE_ROW_DIV_SNIPPET;
+  template += createLabelTemplate(labels);
+  template += '</div>';
+
+  return template;
+}
+
+function createLabelTemplate(labels) {
+  var columns = labels.length;
+  var template = '';
+  forEach(labels, function(label) {
+    template += '<label class="bpp-table-row-columns-' + columns + '">' + escapeHTML(label) + '</label>';
+  });
+  return template;
+}
+
+function pick(elements, properties) {
+  return (elements || []).map(function(elem) {
+    var newElement = {};
+    forEach(properties, function(prop) {
+      newElement[prop] = elem[prop] || '';
+    });
+    return newElement;
+  });
+}
+
+function diff(element, node, values, oldValues, editable) {
+  return filter(values, function(value, idx) {
+    return !valueEqual(element, node, value, oldValues[idx], editable, idx);
+  });
+}
+
+function valueEqual(element, node, value, oldValue, editable, idx) {
+  if (value && !oldValue) {
+    return false;
+  }
+  var allKeys = keys(value).concat(keys(oldValue));
+
+  return allKeys.every(function(key) {
+    var n = value[key] || undefined;
+    var o = oldValue[key] || undefined;
+    return !editable(element, node, key, idx) || n === o;
+  });
+}
+
+function getEntryNode(node) {
+  return domClosest(node, '[data-entry]', true);
+}
+
+function getContainer(node) {
+  return domQuery('div[data-list-entry-container]', node);
+}
+
+function getSelection(node) {
+  return {
+    start: node.selectionStart,
+    end: node.selectionEnd
+  };
+}
+
+function setSelection(node, selection) {
+  node.selectionStart = selection.start;
+  node.selectionEnd = selection.end;
+}
+
+/**
+ * @param  {Object} options
+ * @param  {string} options.id
+ * @param  {string} options.description
+ * @param  {Array<string>} options.modelProperties
+ * @param  {Array<string>} options.labels
+ * @param  {Function} options.getElements - this callback function must return a list of business object items
+ * @param  {Function} options.removeElement
+ * @param  {Function} options.addElement
+ * @param  {Function} options.updateElement
+ * @param  {Function} options.editable
+ * @param  {Function} options.setControlValue
+ * @param  {Function} options.show
+ *
+ * @return {Object}
+ */
+module.exports = function(options) {
+
+  var id = options.id,
+      modelProperties = options.modelProperties,
+      labels = options.labels,
+      description = options.description;
+
+  var labelRow = createLabelRowTemplate(labels);
+
+  var getElements = options.getElements;
+
+  var removeElement = options.removeElement,
+      canRemove = typeof removeElement === 'function';
+
+  var addElement = options.addElement,
+      canAdd = typeof addElement === 'function',
+      addLabel = options.addLabel || 'Add Value';
+
+  var updateElement = options.updateElement,
+      canUpdate = typeof updateElement === 'function';
+
+  var editable = options.editable || function() { return true; },
+      setControlValue = options.setControlValue;
+
+  var show = options.show,
+      canBeShown = typeof show === 'function';
+
+  var elements = function(element, node) {
+    return pick(getElements(element, node), modelProperties);
+  };
+
+  var factory = {
+    id: id,
+    html: (canAdd ?
+      '<div class="bpp-table-add-row" ' + (canBeShown ? 'data-show="show"' : '') + '>' +
+            '<label>' + escapeHTML(addLabel) + '</label>' +
+            '<button class="add" data-action="addElement"><span>+</span></button>' +
+          '</div>' : '') +
+          '<div class="bpp-table" data-show="showTable">' +
+            '<div class="bpp-field-wrapper bpp-table-row">' +
+               labelRow +
+            '</div>' +
+            '<div data-list-entry-container>' +
+            '</div>' +
+          '</div>' +
+
+          // add description below table entry field
+          (description ? entryFieldDescription(description) : ''),
+
+    get: function(element, node) {
+      var boElements = elements(element, node, this.__invalidValues);
+
+      var invalidValues = this.__invalidValues;
+
+      delete this.__invalidValues;
+
+      forEach(invalidValues, function(value, idx) {
+        var element = boElements[idx];
+
+        forEach(modelProperties, function(prop) {
+          element[prop] = value[prop];
+        });
+      });
+
+      return boElements;
+    },
+
+    set: function(element, values, node) {
+      var action = this.__action || {};
+      delete this.__action;
+
+      if (action.id === 'delete-element') {
+        return removeElement(element, node, action.idx);
+      }
+      else if (action.id === 'add-element') {
+        return addElement(element, node);
+      }
+      else if (canUpdate) {
+        var commands = [],
+            valuesToValidate = values;
+
+        if (typeof options.validate !== 'function') {
+          valuesToValidate = diff(element, node, values, elements(element, node), editable);
+        }
+
+        var self = this;
+
+        forEach(valuesToValidate, function(value) {
+          var validationError,
+              idx = values.indexOf(value);
+
+          if (typeof options.validate === 'function') {
+            validationError = options.validate(element, value, node, idx);
+          }
+
+          if (!validationError) {
+            var cmd = updateElement(element, value, node, idx);
+
+            if (cmd) {
+              commands.push(cmd);
+            }
+          } else {
+            // cache invalid value in an object by index as key
+            self.__invalidValues = self.__invalidValues || {};
+            self.__invalidValues[idx] = value;
+
+            // execute a command, which does not do anything
+            commands.push(cmdHelper.updateProperties(element, {}));
+          }
+        });
+
+        return commands;
+      }
+    },
+    createListEntryTemplate: function(value, index, selectBox) {
+      return createInputRowTemplate(modelProperties, canRemove);
+    },
+
+    addElement: function(element, node, event, scopeNode) {
+      var template = domify(createInputRowTemplate(modelProperties, canRemove));
+
+      var container = getContainer(node);
+      container.appendChild(template);
+
+      this.__action = {
+        id: 'add-element'
+      };
+
+      return true;
+    },
+
+    deleteElement: function(element, node, event, scopeNode) {
+      var container = getContainer(node);
+      var rowToDelete = event.delegateTarget.parentNode;
+      var idx = parseInt(domAttr(rowToDelete, 'data-index'), 10);
+
+      container.removeChild(rowToDelete);
+
+      this.__action = {
+        id: 'delete-element',
+        idx: idx
+      };
+
+      return true;
+    },
+
+    editable: function(element, rowNode, input, prop, value, idx) {
+      var entryNode = domClosest(rowNode, '[data-entry]');
+      return editable(element, entryNode, prop, idx);
+    },
+
+    show: function(element, entryNode, node, scopeNode) {
+      entryNode = getEntryNode(entryNode);
+      return show(element, entryNode, node, scopeNode);
+    },
+
+    showTable: function(element, entryNode, node, scopeNode) {
+      entryNode = getEntryNode(entryNode);
+      var elems = elements(element, entryNode);
+      return elems && elems.length && (!canBeShown || show(element, entryNode, node, scopeNode));
+    },
+
+    validateListItem: function(element, value, node, idx) {
+      if (typeof options.validate === 'function') {
+        return options.validate(element, value, node, idx);
+      }
+    }
+
+  };
+
+  // Update/set the selection on the correct position.
+  // It's the same code like for an input value in the PropertiesPanel.js.
+  if (setControlValue) {
+    factory.setControlValue = function(element, rowNode, input, prop, value, idx) {
+      var entryNode = getEntryNode(rowNode);
+
+      var isReadOnly = domAttr(input, 'readonly');
+      var oldValue = input.value;
+
+      var selection;
+
+      // prevents input fields from having the value 'undefined'
+      if (value === undefined) {
+        value = '';
+      }
+
+      // when the attribute 'readonly' exists, ignore the comparison
+      // with 'oldValue' and 'value'
+      if (!!isReadOnly && oldValue === value) {
+        return;
+      }
+
+      // update selection on undo/redo
+      if (document.activeElement === input) {
+        selection = updateSelection(getSelection(input), oldValue, value);
+      }
+
+      setControlValue(element, entryNode, input, prop, value, idx);
+
+      if (selection) {
+        setSelection(input, selection);
+      }
+
+    };
+  }
+
+  return factory;
+
+};

+ 42 - 0
ruoyi-bpmnjs/resources/properties-panel/factory/TextBoxEntryFactory.js

@@ -0,0 +1,42 @@
+'use strict';
+
+var escapeHTML = require('../Utils').escapeHTML;
+
+var entryFieldDescription = require('./EntryFieldDescription');
+
+
+var textBox = function(options, defaultParameters) {
+
+  var resource = defaultParameters,
+      label = options.label || resource.id,
+      canBeShown = !!options.show && typeof options.show === 'function',
+      description = options.description;
+
+  resource.html =
+    '<label for="activiti-' + escapeHTML(resource.id) + '" ' +
+    (canBeShown ? 'data-show="isShown"' : '') +
+    '>' + label + '</label>' +
+    '<div class="bpp-field-wrapper" ' +
+    (canBeShown ? 'data-show="isShown"' : '') +
+    '>' +
+      '<div contenteditable="true" id="activiti-' + escapeHTML(resource.id) + '" ' +
+            'name="' + escapeHTML(options.modelProperty) + '" />' +
+    '</div>';
+
+  // add description below text box entry field
+  if (description) {
+    resource.html += entryFieldDescription(description);
+  }
+
+  if (canBeShown) {
+    resource.isShown = function() {
+      return options.show.apply(resource, arguments);
+    };
+  }
+
+  resource.cssClasses = ['bpp-textbox'];
+
+  return resource;
+};
+
+module.exports = textBox;

+ 85 - 0
ruoyi-bpmnjs/resources/properties-panel/factory/TextInputEntryFactory.js

@@ -0,0 +1,85 @@
+'use strict';
+
+var escapeHTML = require('../Utils').escapeHTML;
+
+var domQuery = require('min-dom').query;
+
+var entryFieldDescription = require('./EntryFieldDescription');
+
+
+var textField = function(options, defaultParameters) {
+
+  // Default action for the button next to the input-field
+  var defaultButtonAction = function(element, inputNode) {
+    var input = domQuery('input[name="' + options.modelProperty + '"]', inputNode);
+    input.value = '';
+
+    return true;
+  };
+
+  // default method to determine if the button should be visible
+  var defaultButtonShow = function(element, inputNode) {
+    var input = domQuery('input[name="' + options.modelProperty + '"]', inputNode);
+
+    return input.value !== '';
+  };
+
+
+  var resource = defaultParameters,
+      label = options.label || resource.id,
+      dataValueLabel = options.dataValueLabel,
+      buttonLabel = (options.buttonLabel || 'X'),
+      actionName = (typeof options.buttonAction != 'undefined') ? options.buttonAction.name : 'clear',
+      actionMethod = (typeof options.buttonAction != 'undefined') ? options.buttonAction.method : defaultButtonAction,
+      showName = (typeof options.buttonShow != 'undefined') ? options.buttonShow.name : 'canClear',
+      showMethod = (typeof options.buttonShow != 'undefined') ? options.buttonShow.method : defaultButtonShow,
+      canBeDisabled = !!options.disabled && typeof options.disabled === 'function',
+      canBeHidden = !!options.hidden && typeof options.hidden === 'function',
+      description = options.description;
+
+  resource.html =
+    '<label for="activiti-' + escapeHTML(resource.id) + '" ' +
+      (canBeDisabled ? 'data-disable="isDisabled" ' : '') +
+      (canBeHidden ? 'data-show="isHidden" ' : '') +
+      (dataValueLabel ? 'data-value="' + escapeHTML(dataValueLabel) + '"' : '') + '>'+ escapeHTML(label) +'</label>' +
+    '<div class="bpp-field-wrapper" ' +
+      (canBeDisabled ? 'data-disable="isDisabled"' : '') +
+      (canBeHidden ? 'data-show="isHidden"' : '') +
+      '>' +
+      '<input id="activiti-' + escapeHTML(resource.id) + '" type="text" name="' + escapeHTML(options.modelProperty) + '" ' +
+        (canBeDisabled ? 'data-disable="isDisabled"' : '') +
+        (canBeHidden ? 'data-show="isHidden"' : '') +
+        ' />' +
+      '<button class="' + escapeHTML(actionName) + '" data-action="' + escapeHTML(actionName) + '" data-show="' + escapeHTML(showName) + '" ' +
+        (canBeDisabled ? 'data-disable="isDisabled"' : '') +
+        (canBeHidden ? ' data-show="isHidden"' : '') + '>' +
+        '<span>' + escapeHTML(buttonLabel) + '</span>' +
+      '</button>' +
+    '</div>';
+
+  // add description below text input entry field
+  if (description) {
+    resource.html += entryFieldDescription(description);
+  }
+
+  resource[actionName] = actionMethod;
+  resource[showName] = showMethod;
+
+  if (canBeDisabled) {
+    resource.isDisabled = function() {
+      return options.disabled.apply(resource, arguments);
+    };
+  }
+
+  if (canBeHidden) {
+    resource.isHidden = function() {
+      return !options.hidden.apply(resource, arguments);
+    };
+  }
+
+  resource.cssClasses = ['bpp-textfield'];
+
+  return resource;
+};
+
+module.exports = textField;

+ 56 - 0
ruoyi-bpmnjs/resources/properties-panel/factory/ValidationAwareTextInput.js

@@ -0,0 +1,56 @@
+'use strict';
+
+var textField = require('./TextInputEntryFactory');
+
+/**
+ * This function is a wrapper around TextInputEntryFactory.
+ * It adds functionality to cache an invalid value entered in the
+ * text input, instead of setting it on the business object.
+ */
+var validationAwareTextField = function(options, defaultParameters) {
+
+  var modelProperty = options.modelProperty;
+
+  defaultParameters.get = function(element, node) {
+    var value = this.__lastInvalidValue;
+
+    delete this.__lastInvalidValue;
+
+    var properties = {};
+
+    properties[modelProperty] = value !== undefined ? value : options.getProperty(element, node);
+
+    return properties;
+  };
+
+  defaultParameters.set = function(element, values, node) {
+    var validationErrors = validate.apply(this, [ element, values, node ]),
+        propertyValue = values[modelProperty];
+
+    // make sure we do not update the id
+    if (validationErrors && validationErrors[modelProperty]) {
+      this.__lastInvalidValue = propertyValue;
+
+      return options.setProperty(element, {}, node);
+    } else {
+      var properties = {};
+
+      properties[modelProperty] = propertyValue;
+
+      return options.setProperty(element, properties, node);
+    }
+  };
+
+  var validate = defaultParameters.validate = function(element, values, node) {
+    var value = values[modelProperty] || this.__lastInvalidValue;
+
+    var property = {};
+    property[modelProperty] = value;
+
+    return options.validate(element, property, node);
+  };
+
+  return textField(options, defaultParameters);
+};
+
+module.exports = validationAwareTextField;

+ 76 - 0
ruoyi-bpmnjs/resources/properties-panel/helper/AsyncCapableHelper.js

@@ -0,0 +1,76 @@
+'use strict';
+
+var map = require('lodash/map');
+
+var extensionElementsHelper = require('./ExtensionElementsHelper');
+
+/**
+ * Returns true if the attribute 'activiti:asyncBefore' is set
+ * to true.
+ *
+ * @param  {ModdleElement} bo
+ *
+ * @return {boolean} a boolean value
+ */
+function isAsyncBefore(bo) {
+  return !!(bo.get('activiti:asyncBefore') || bo.get('activiti:async'));
+}
+
+module.exports.isAsyncBefore = isAsyncBefore;
+
+/**
+ * Returns true if the attribute 'activiti:asyncAfter' is set
+ * to true.
+ *
+ * @param  {ModdleElement} bo
+ *
+ * @return {boolean} a boolean value
+ */
+function isAsyncAfter(bo) {
+  return !!bo.get('activiti:asyncAfter');
+}
+
+module.exports.isAsyncAfter = isAsyncAfter;
+
+/**
+ * Returns true if the attribute 'activiti:exclusive' is set
+ * to true.
+ *
+ * @param  {ModdleElement} bo
+ *
+ * @return {boolean} a boolean value
+ */
+function isExclusive(bo) {
+  return !!bo.get('activiti:exclusive');
+}
+
+module.exports.isExclusive = isExclusive;
+
+/**
+ * Get first 'activiti:FailedJobRetryTimeCycle' from the business object.
+ *
+ * @param  {ModdleElement} bo
+ *
+ * @return {Array<ModdleElement>} a list of 'activiti:FailedJobRetryTimeCycle'
+ */
+function getFailedJobRetryTimeCycle(bo) {
+  return (extensionElementsHelper.getExtensionElements(bo, 'activiti:FailedJobRetryTimeCycle') || [])[0];
+}
+
+module.exports.getFailedJobRetryTimeCycle = getFailedJobRetryTimeCycle;
+
+/**
+ * Removes all existing 'activiti:FailedJobRetryTimeCycle' from the business object
+ *
+ * @param  {ModdleElement} bo
+ *
+ * @return {Array<ModdleElement>} a list of 'activiti:FailedJobRetryTimeCycle'
+ */
+function removeFailedJobRetryTimeCycle(bo, element) {
+  var retryTimeCycles = extensionElementsHelper.getExtensionElements(bo, 'activiti:FailedJobRetryTimeCycle');
+  return map(retryTimeCycles, function(cycle) {
+    return extensionElementsHelper.removeEntry(bo, element, cycle);
+  });
+}
+
+module.exports.removeFailedJobRetryTimeCycle = removeFailedJobRetryTimeCycle;

+ 31 - 0
ruoyi-bpmnjs/resources/properties-panel/helper/CategoryHelper.js

@@ -0,0 +1,31 @@
+'use strict';
+
+var collectionAdd = require('diagram-js/lib/util/Collections').add,
+    getBusinessObject = require('bpmn-js/lib/util/ModelUtil').getBusinessObject;
+
+var CategoryHelper = {};
+
+module.exports = CategoryHelper;
+
+/**
+ * Creates a new bpmn:CategoryValue inside a new bpmn:Category
+ *
+ * @param {ModdleElement} definitions
+ * @param {BpmnFactory} bpmnFactory
+ *
+ * @return {ModdleElement} categoryValue.
+ */
+CategoryHelper.createCategoryValue = function(definitions, bpmnFactory) {
+  var categoryValue = bpmnFactory.create('bpmn:CategoryValue'),
+      category = bpmnFactory.create('bpmn:Category', {
+        categoryValue: [ categoryValue ]
+      });
+
+  // add to correct place
+  collectionAdd(definitions.get('rootElements'), category);
+  getBusinessObject(category).$parent = definitions;
+  getBusinessObject(categoryValue).$parent = category;
+
+  return categoryValue;
+
+};

+ 77 - 0
ruoyi-bpmnjs/resources/properties-panel/helper/CmdHelper.js

@@ -0,0 +1,77 @@
+'use strict';
+
+var CmdHelper = {};
+module.exports = CmdHelper;
+
+CmdHelper.updateProperties = function(element, properties) {
+  return {
+    cmd: 'element.updateProperties',
+    context: { element: element, properties: properties }
+  };
+};
+
+CmdHelper.updateBusinessObject = function(element, businessObject, newProperties) {
+  return {
+    cmd: 'properties-panel.update-businessobject',
+    context: {
+      element: element,
+      businessObject: businessObject,
+      properties: newProperties
+    }
+  };
+};
+
+CmdHelper.addElementsTolist = function(element, businessObject, listPropertyName, objectsToAdd) {
+  return {
+    cmd: 'properties-panel.update-businessobject-list',
+    context: {
+      element: element,
+      currentObject: businessObject,
+      propertyName: listPropertyName,
+      objectsToAdd: objectsToAdd
+    }
+  };
+};
+
+CmdHelper.removeElementsFromList = function(element, businessObject, listPropertyName, referencePropertyName, objectsToRemove) {
+
+  return {
+    cmd: 'properties-panel.update-businessobject-list',
+    context: {
+      element: element,
+      currentObject: businessObject,
+      propertyName: listPropertyName,
+      referencePropertyName: referencePropertyName,
+      objectsToRemove: objectsToRemove
+    }
+  };
+};
+
+
+CmdHelper.addAndRemoveElementsFromList = function(element, businessObject, listPropertyName, referencePropertyName, objectsToAdd, objectsToRemove) {
+
+  return {
+    cmd: 'properties-panel.update-businessobject-list',
+    context: {
+      element: element,
+      currentObject: businessObject,
+      propertyName: listPropertyName,
+      referencePropertyName: referencePropertyName,
+      objectsToAdd: objectsToAdd,
+      objectsToRemove: objectsToRemove
+    }
+  };
+};
+
+
+CmdHelper.setList = function(element, businessObject, listPropertyName, updatedObjectList) {
+  return {
+    cmd: 'properties-panel.update-businessobject-list',
+    context: {
+      element: element,
+      currentObject: businessObject,
+      propertyName: listPropertyName,
+      updatedObjectList: updatedObjectList
+    }
+  };
+};

+ 23 - 0
ruoyi-bpmnjs/resources/properties-panel/helper/ElementHelper.js

@@ -0,0 +1,23 @@
+'use strict';
+
+var ElementHelper = {};
+module.exports = ElementHelper;
+
+/**
+ * Creates a new element and set the parent to it
+ *
+ * @method ElementHelper#createElement
+ *
+ * @param {String} elementType of the new element
+ * @param {Object} properties of the new element in key-value pairs
+ * @param {moddle.object} parent of the new element
+ * @param {BpmnFactory} factory which creates the new element
+ *
+ * @returns {djs.model.Base} element which is created
+ */
+ElementHelper.createElement = function(elementType, properties, parent, factory) {
+  var element = factory.create(elementType, properties);
+  element.$parent = parent;
+
+  return element;
+};

+ 57 - 0
ruoyi-bpmnjs/resources/properties-panel/helper/EventDefinitionHelper.js

@@ -0,0 +1,57 @@
+'use strict';
+
+var getBusinessObject = require('bpmn-js/lib/util/ModelUtil').getBusinessObject,
+    is = require('bpmn-js/lib/util/ModelUtil').is,
+    forEach = require('lodash/forEach');
+
+var EventDefinitionHelper = {};
+
+module.exports = EventDefinitionHelper;
+
+EventDefinitionHelper.getEventDefinition = function(element, eventType) {
+
+  var bo = getBusinessObject(element),
+      eventDefinition = null;
+
+  if (bo.eventDefinitions) {
+    forEach(bo.eventDefinitions, function(event) {
+      if (is(event, eventType)) {
+        eventDefinition = event;
+      }
+    });
+  }
+
+  return eventDefinition;
+};
+
+EventDefinitionHelper.getTimerEventDefinition = function(element) {
+  return this.getEventDefinition(element, 'bpmn:TimerEventDefinition');
+};
+
+EventDefinitionHelper.getMessageEventDefinition = function(element) {
+  return this.getEventDefinition(element, 'bpmn:MessageEventDefinition');
+};
+
+EventDefinitionHelper.getSignalEventDefinition = function(element) {
+  return this.getEventDefinition(element, 'bpmn:SignalEventDefinition');
+};
+
+EventDefinitionHelper.getErrorEventDefinition = function(element) {
+  return this.getEventDefinition(element, 'bpmn:ErrorEventDefinition');
+};
+
+EventDefinitionHelper.getEscalationEventDefinition = function(element) {
+  return this.getEventDefinition(element, 'bpmn:EscalationEventDefinition');
+};
+
+EventDefinitionHelper.getCompensateEventDefinition = function(element) {
+  return this.getEventDefinition(element, 'bpmn:CompensateEventDefinition');
+};
+
+EventDefinitionHelper.getLinkEventDefinition = function(element) {
+  return this.getEventDefinition(element, 'bpmn:LinkEventDefinition');
+};
+
+EventDefinitionHelper.getConditionalEventDefinition = function(element) {
+  return this.getEventDefinition(element, 'bpmn:ConditionalEventDefinition');
+};

+ 55 - 0
ruoyi-bpmnjs/resources/properties-panel/helper/ExtensionElementsHelper.js

@@ -0,0 +1,55 @@
+'use strict';
+
+var cmdHelper = require('./CmdHelper'),
+    elementHelper = require('./ElementHelper');
+
+var is = require('bpmn-js/lib/util/ModelUtil').is;
+
+var ExtensionElementsHelper = {};
+
+var getExtensionElements = function(bo) {
+  return bo.get('extensionElements');
+};
+
+ExtensionElementsHelper.getExtensionElements = function(bo, type) {
+  var extensionElements = getExtensionElements(bo);
+  if (typeof extensionElements !== 'undefined') {
+    var extensionValues = extensionElements.get('values');
+    if (typeof extensionValues !== 'undefined') {
+      var elements = extensionValues.filter(function(value) {
+        return is(value, type);
+      });
+      if (elements.length) {
+        return elements;
+      }
+    }
+  }
+};
+
+ExtensionElementsHelper.addEntry = function(bo, element, entry, bpmnFactory) {
+  var extensionElements = bo.get('extensionElements');
+
+  // if there is no extensionElements list, create one
+  if (!extensionElements) {
+    // TODO: Ask Daniel which operation costs more
+    extensionElements = elementHelper.createElement('bpmn:ExtensionElements', { values: [entry] }, bo, bpmnFactory);
+    return { extensionElements : extensionElements };
+  } else {
+    // add new failedJobRetryExtensionElement to existing extensionElements list
+    return cmdHelper.addElementsTolist(element, extensionElements, 'values', [entry]);
+  }
+};
+
+ExtensionElementsHelper.removeEntry = function(bo, element, entry) {
+  var extensionElements = bo.get('extensionElements');
+
+  if (!extensionElements) {
+
+    // return an empty command when there is no extensionElements list
+    return {};
+  }
+
+  return cmdHelper.removeElementsFromList(element, extensionElements, 'values', 'extensionElements', [entry]);
+};
+
+module.exports = ExtensionElementsHelper;

+ 97 - 0
ruoyi-bpmnjs/resources/properties-panel/helper/FormHelper.js

@@ -0,0 +1,97 @@
+'use strict';
+
+var getBusinessObject = require('bpmn-js/lib/util/ModelUtil').getBusinessObject,
+    getExtensionElements = require('./ExtensionElementsHelper').getExtensionElements;
+
+var FormHelper = {};
+
+module.exports = FormHelper;
+
+/**
+ * Return form data from business object or undefined if none exist
+ *
+ * @param  {djs.model.Base} element
+ *
+ * @return {ModdleElement|undefined} formData
+ *
+ * 此方法废弃
+ *
+ */
+FormHelper.getFormData = function(element) {
+ /* var bo = getBusinessObject(element);
+
+  var formFields = getExtensionElements(bo, 'activiti:FormProperty');
+
+  var formData = {}
+
+  if (typeof formData !== 'undefined') {
+    return formData[0];
+  }*/
+ return {};
+};
+
+
+/**
+ * Return all form fields existing in the business object, and
+ * an empty array if none exist.
+ *
+ * @param  {djs.model.Base} element
+ *
+ * @return {Array} a list of form field objects
+ */
+FormHelper.getFormFields = function(element) {
+
+  /**直接获取 ExtensionElements的 activiti:FormProperty元素*/
+  var bo = getBusinessObject(element);
+
+  var formFields = getExtensionElements(bo, 'activiti:FormProperty');
+
+  return formFields || [];
+};
+
+
+/**
+ * Get a form field from the business object at given index
+ *
+ * @param {djs.model.Base} element
+ * @param {number} idx
+ *
+ * @return {ModdleElement} the form field
+ */
+FormHelper.getFormField = function(element, idx) {
+
+  var formFields = this.getFormFields(element);
+
+  return formFields[idx];
+};
+
+
+/**
+ * Get all constraints for a specific form field from the business object
+ *
+ * @param  {ModdleElement} formField
+ *
+ * @return {Array<ModdleElement>} a list of constraint objects
+ */
+FormHelper.getConstraints = function(formField) {
+  if (formField && formField.validation && formField.validation.constraints) {
+    return formField.validation.constraints;
+  }
+  return [];
+};
+
+
+/**
+ * Get all activiti:value objects for a specific form field from the business object
+ *
+ * @param  {ModdleElement} formField
+ *
+ * @return {Array<ModdleElement>} a list of activiti:value objects
+ */
+FormHelper.getEnumValues = function(formField) {
+  if (formField && formField.values) {
+    return formField.values;
+  }
+  return [];
+};
+

+ 192 - 0
ruoyi-bpmnjs/resources/properties-panel/helper/ImplementationTypeHelper.js

@@ -0,0 +1,192 @@
+'use strict';
+
+var ModelUtil = require('bpmn-js/lib/util/ModelUtil'),
+    is = ModelUtil.is,
+    getBusinessObject = ModelUtil.getBusinessObject;
+
+var eventDefinitionHelper = require('./EventDefinitionHelper');
+var extensionsElementHelper = require('./ExtensionElementsHelper');
+
+var ImplementationTypeHelper = {};
+
+module.exports = ImplementationTypeHelper;
+
+/**
+ * Returns 'true' if the given element is 'activiti:ServiceTaskLike'
+ *
+ * @param {djs.model.Base} element
+ *
+ * @return {boolean} a boolean value
+ */
+ImplementationTypeHelper.isServiceTaskLike = function(element) {
+  return is(element, 'activiti:ServiceTaskLike');
+};
+
+/**
+ * Returns 'true' if the given element is 'activiti:DmnCapable'
+ *
+ * @param {djs.model.Base} element
+ *
+ * @return {boolean} a boolean value
+ */
+ImplementationTypeHelper.isDmnCapable = function(element) {
+  return is(element, 'activiti:DmnCapable');
+};
+
+/**
+ * Returns 'true' if the given element is 'activiti:ExternalCapable'
+ *
+ * @param {djs.model.Base} element
+ *
+ * @return {boolean} a boolean value
+ */
+ImplementationTypeHelper.isExternalCapable = function(element) {
+  return is(element, 'activiti:ExternalCapable');
+};
+
+/**
+ * Returns 'true' if the given element is 'activiti:TaskListener'
+ *
+ * @param {djs.model.Base} element
+ *
+ * @return {boolean} a boolean value
+ */
+ImplementationTypeHelper.isTaskListener = function(element) {
+  return is(element, 'activiti:TaskListener');
+};
+
+/**
+ * Returns 'true' if the given element is 'activiti:ExecutionListener'
+ *
+ * @param {djs.model.Base} element
+ *
+ * @return {boolean} a boolean value
+ */
+ImplementationTypeHelper.isExecutionListener = function(element) {
+  return is(element, 'activiti:ExecutionListener');
+};
+
+/**
+ * Returns 'true' if the given element is 'activiti:ExecutionListener' or
+ * 'activiti:TaskListener'
+ *
+ * @param {djs.model.Base} element
+ *
+ * @return {boolean} a boolean value
+ */
+ImplementationTypeHelper.isListener = function(element) {
+  return this.isTaskListener(element) || this.isExecutionListener(element);
+};
+
+/**
+ * Returns 'true' if the given element is 'bpmn:SequenceFlow'
+ *
+ * @param {djs.model.Base} element
+ *
+ * @return {boolean} a boolean value
+ */
+ImplementationTypeHelper.isSequenceFlow = function(element) {
+  return is(element, 'bpmn:SequenceFlow');
+};
+
+/**
+ * Get a 'activiti:ServiceTaskLike' business object.
+ *
+ * If the given element is not a 'activiti:ServiceTaskLike', then 'false'
+ * is returned.
+ *
+ * @param {djs.model.Base} element
+ *
+ * @return {ModdleElement} the 'activiti:ServiceTaskLike' business object
+ */
+ImplementationTypeHelper.getServiceTaskLikeBusinessObject = function(element) {
+
+  if (is(element, 'bpmn:IntermediateThrowEvent') || is(element, 'bpmn:EndEvent')) {
+
+    // change business object to 'messageEventDefinition' when
+    // the element is a message intermediate throw event or message end event
+    // because the activiti extensions (e.g. activiti:class) are in the message
+    // event definition tag and not in the intermediate throw event or end event tag
+    var messageEventDefinition = eventDefinitionHelper.getMessageEventDefinition(element);
+    if (messageEventDefinition) {
+      element = messageEventDefinition;
+    }
+  }
+
+  return this.isServiceTaskLike(element) && getBusinessObject(element);
+
+};
+
+/**
+ * Returns the implementation type of the given element.
+ *
+ * Possible implementation types are:
+ * - dmn
+ * - connector
+ * - external
+ * - class
+ * - expression
+ * - delegateExpression
+ * - script
+ * - or undefined, when no matching implementation type is found
+ *
+ * @param  {djs.model.Base} element
+ *
+ * @return {String} the implementation type
+ */
+ImplementationTypeHelper.getImplementationType = function(element) {
+
+  var bo = this.getServiceTaskLikeBusinessObject(element);
+
+  if (!bo) {
+    if (this.isListener(element)) {
+      bo = element;
+    } else {
+      return;
+    }
+  }
+
+  if (this.isDmnCapable(bo)) {
+    var decisionRef = bo.get('activiti:decisionRef');
+    if (typeof decisionRef !== 'undefined') {
+      return 'dmn';
+    }
+  }
+
+  if (this.isServiceTaskLike(bo)) {
+    var connectors = extensionsElementHelper.getExtensionElements(bo, 'activiti:Connector');
+    if (typeof connectors !== 'undefined') {
+      return 'connector';
+    }
+  }
+
+  if (this.isExternalCapable(bo)) {
+    var type = bo.get('activiti:type');
+    if (type === 'external') {
+      return 'external';
+    }
+  }
+
+  var cls = bo.get('activiti:class');
+  if (typeof cls !== 'undefined') {
+    return 'class';
+  }
+
+  var expression = bo.get('activiti:expression');
+  if (typeof expression !== 'undefined') {
+    return 'expression';
+  }
+
+  var delegateExpression = bo.get('activiti:delegateExpression');
+  if (typeof delegateExpression !== 'undefined') {
+    return 'delegateExpression';
+  }
+
+  if (this.isListener(bo)) {
+    var script = bo.get('script');
+    if (typeof script !== 'undefined') {
+      return 'script';
+    }
+  }
+
+};

+ 144 - 0
ruoyi-bpmnjs/resources/properties-panel/helper/InputOutputHelper.js

@@ -0,0 +1,144 @@
+'use strict';
+
+var ModelUtil = require('bpmn-js/lib/util/ModelUtil'),
+    is = ModelUtil.is,
+    getBusinessObject = ModelUtil.getBusinessObject;
+
+var extensionElementsHelper = require('./ExtensionElementsHelper'),
+    implementationTypeHelper = require('./ImplementationTypeHelper');
+
+var InputOutputHelper = {};
+
+module.exports = InputOutputHelper;
+
+function getElements(bo, type, prop) {
+  var elems = extensionElementsHelper.getExtensionElements(bo, type) || [];
+  return !prop ? elems : (elems[0] || {})[prop] || [];
+}
+
+function getParameters(element, prop, insideConnector) {
+  var inputOutput = InputOutputHelper.getInputOutput(element, insideConnector);
+  return (inputOutput && inputOutput.get(prop)) || [];
+}
+
+/**
+ * Get a inputOutput from the business object
+ *
+ * @param {djs.model.Base} element
+ * @param  {boolean} insideConnector
+ *
+ * @return {ModdleElement} the inputOutput object
+ */
+InputOutputHelper.getInputOutput = function(element, insideConnector) {
+  if (!insideConnector) {
+    var bo = getBusinessObject(element);
+    return (getElements(bo, 'activiti:InputOutput') || [])[0];
+  }
+  var connector = this.getConnector(element);
+  return connector && connector.get('inputOutput');
+};
+
+/**
+ * Get a connector from the business object
+ *
+ * @param {djs.model.Base} element
+ *
+ * @return {ModdleElement} the connector object
+ */
+InputOutputHelper.getConnector = function(element) {
+  var bo = implementationTypeHelper.getServiceTaskLikeBusinessObject(element);
+  return bo && (getElements(bo, 'activiti:Connector') || [])[0];
+};
+
+/**
+ * Return all input parameters existing in the business object, and
+ * an empty array if none exist.
+ *
+ * @param  {djs.model.Base} element
+ * @param  {boolean} insideConnector
+ *
+ * @return {Array} a list of input parameter objects
+ */
+InputOutputHelper.getInputParameters = function(element, insideConnector) {
+  return getParameters.apply(this, [ element, 'inputParameters', insideConnector ]);
+};
+
+/**
+ * Return all output parameters existing in the business object, and
+ * an empty array if none exist.
+ *
+ * @param  {djs.model.Base} element
+ * @param  {boolean} insideConnector
+ *
+ * @return {Array} a list of output parameter objects
+ */
+InputOutputHelper.getOutputParameters = function(element, insideConnector) {
+  return getParameters.apply(this, [ element, 'outputParameters', insideConnector ]);
+};
+
+/**
+ * Get a input parameter from the business object at given index
+ *
+ * @param {djs.model.Base} element
+ * @param  {boolean} insideConnector
+ * @param {number} idx
+ *
+ * @return {ModdleElement} input parameter
+ */
+InputOutputHelper.getInputParameter = function(element, insideConnector, idx) {
+  return this.getInputParameters(element, insideConnector)[idx];
+};
+
+/**
+ * Get a output parameter from the business object at given index
+ *
+ * @param {djs.model.Base} element
+ * @param  {boolean} insideConnector
+ * @param {number} idx
+ *
+ * @return {ModdleElement} output parameter
+ */
+InputOutputHelper.getOutputParameter = function(element, insideConnector, idx) {
+  return this.getOutputParameters(element, insideConnector)[idx];
+};
+
+/**
+ * Returns 'true' if the given element supports inputOutput
+ *
+ * @param {djs.model.Base} element
+ * @param  {boolean} insideConnector
+ *
+ * @return {boolean} a boolean value
+ */
+InputOutputHelper.isInputOutputSupported = function(element, insideConnector) {
+
+  if (insideConnector) {
+    return true;
+  }
+
+  var bo = getBusinessObject(element);
+
+  return (
+    is(bo, 'bpmn:FlowNode') && !(
+      is(bo, 'bpmn:StartEvent') ||
+      is(bo, 'bpmn:Gateway') ||
+      is(bo, 'bpmn:BoundaryEvent') ||
+      (
+        is(bo, 'bpmn:SubProcess') && bo.get('triggeredByEvent')
+      )
+    )
+  );
+};
+
+/**
+ * Returns 'true' if the given element supports output parameters
+ *
+ * @param {djs.model.Base} element
+ * @param  {boolean} insideConnector
+ *
+ * @return {boolean} a boolean value
+ */
+InputOutputHelper.areOutputParametersSupported = function(element, insideConnector) {
+  var bo = getBusinessObject(element);
+  return insideConnector || (!is(bo, 'bpmn:EndEvent') && !bo.loopCharacteristics);
+};

+ 36 - 0
ruoyi-bpmnjs/resources/properties-panel/helper/ParticipantHelper.js

@@ -0,0 +1,36 @@
+'use strict';
+
+var is = require('bpmn-js/lib/util/ModelUtil').is,
+    getBusinessObject = require('bpmn-js/lib/util/ModelUtil').getBusinessObject,
+    cmdHelper = require('./CmdHelper');
+
+
+var ParticipantHelper = {};
+
+module.exports = ParticipantHelper;
+
+ParticipantHelper.modifyProcessBusinessObject = function(element, property, values) {
+  if (!is(element, 'bpmn:Participant')) {
+    return {};
+  }
+
+  var bo = getBusinessObject(element).get('processRef'),
+      properties = {};
+
+  properties[property] = values[property];
+
+  return cmdHelper.updateBusinessObject(element, bo, properties);
+};
+
+ParticipantHelper.getProcessBusinessObject = function(element, propertyName) {
+  if (!is(element, 'bpmn:Participant')) {
+    return {};
+  }
+
+  var bo = getBusinessObject(element).get('processRef'),
+      properties = {};
+
+  properties[propertyName] = bo.get(propertyName);
+
+  return properties;
+};

+ 8 - 0
ruoyi-bpmnjs/resources/properties-panel/index.js

@@ -0,0 +1,8 @@
+module.exports = {
+  __depends__: [
+    require('./index'),
+    require('diagram-js/lib/i18n/translate').default
+  ],
+  __init__: [ 'propertiesPanel' ],
+  propertiesPanel: [ 'type', require('./PropertiesPanel') ]
+};

+ 103 - 0
ruoyi-bpmnjs/resources/properties-panel/popup.js

@@ -0,0 +1,103 @@
+'use strict';
+
+var domQuery = require('min-dom').query,
+    domClasses = require('min-dom').classes,
+    domify = require('min-dom').domify,
+    bind = require('lodash/bind');
+
+/**
+ * @class
+ * @constructor
+ */
+function Popup(options) {
+  options = options || {};
+  this.template = options.template || this.template;
+  var el = this.el = domify(this.template);
+
+  this.header = domQuery('.popup-header', el);
+  this.body = domQuery('.popup-body', el);
+  this.footer = domQuery('.popup-footer', el);
+
+  document.body.appendChild(el);
+
+  this._attachEvents();
+}
+
+Popup.prototype.template = '<div class="bpp-properties-panel-popup">' +
+                              '<div class="underlay"></div>' +
+                              '<div class="popup">' +
+                                '<button class="popup-close"><span>Close</span></button>' +
+                                '<div class="popup-header"></div>' +
+                                '<div class="popup-body"></div>' +
+                                '<div class="popup-footer"></div>' +
+                              '</div>' +
+                            '</div>';
+
+
+
+Popup.prototype._attachEvents = function() {
+  var self = this;
+  var events = this.events;
+  var el = this.el;
+
+  Object.keys(events).forEach(function(instruction) {
+    var cb = bind(self[events[instruction]], self);
+    var parts = instruction.split(' ');
+    var evtName = parts.shift();
+    var target = parts.length ? parts.shift() : false;
+    target = target ? domQuery(target, el) : el;
+    if (!target) { return; }
+    target.addEventListener(evtName, cb);
+  });
+};
+
+Popup.prototype._detachEvents = function() {
+  var self = this;
+  var events = this.events;
+  var el = this.el;
+
+  Object.keys(events).forEach(function(instruction) {
+    var cb = bind(self[events[instruction]], self);
+    var parts = instruction.split(' ');
+    var evtName = parts.shift();
+    var target = parts.length ? parts.shift() : false;
+    target = target ? domQuery(target, el) : el;
+    if (!target) { return; }
+    target.removeEventListener(evtName, cb);
+  });
+};
+
+Popup.prototype.events = {
+  // 'keydown:esc':        '_handleClose',
+  'click .underlay': '_handleClose',
+  'click .popup-close': '_handleClose'
+};
+
+
+Popup.prototype._handleClose = function(evt) {
+  this.close();
+};
+
+
+Popup.prototype.open = function(content) {
+  domClasses(this.el).add('open');
+};
+
+Popup.prototype.close = function() {
+  domClasses(this.el).remove('open');
+};
+
+Popup.prototype.remove = function() {
+  this._detachEvents();
+  if (document.body.contains(this.el)) {
+    document.body.removeChild(this.el);
+  }
+};
+
+var popup;
+module.exports = function() {
+  if (!popup) {
+    popup = new Popup();
+  }
+  return popup;
+};

+ 542 - 0
ruoyi-bpmnjs/resources/properties-panel/provider/activiti/ActivitiPropertiesProvider.js

@@ -0,0 +1,542 @@
+'use strict';
+
+var inherits = require('inherits');
+
+var PropertiesActivator = require('../../PropertiesActivator');
+
+var asyncCapableHelper = require('../../helper/AsyncCapableHelper'),
+    ImplementationTypeHelper = require('../../helper/ImplementationTypeHelper');
+
+var is = require('bpmn-js/lib/util/ModelUtil').is;
+
+// bpmn properties
+var processProps = require('../../provider/bpmn/parts/ProcessProps'),
+    eventProps = require('../../provider/bpmn/parts/EventProps'),
+    linkProps = require('../../provider/bpmn/parts/LinkProps'),
+    documentationProps = require('../../provider/bpmn/parts/DocumentationProps'),
+    idProps = require('../../provider/bpmn/parts/IdProps'),
+    nameProps = require('../../provider/bpmn/parts/NameProps'),
+    executableProps = require('../../provider/bpmn/parts/ExecutableProps');
+
+// activiti properties
+var serviceTaskDelegateProps = require('./parts/ServiceTaskDelegateProps'),
+    userTaskProps = require('./parts/UserTaskProps'),
+    asynchronousContinuationProps = require('./parts/AsynchronousContinuationProps'),
+    callActivityProps = require('./parts/CallActivityProps'),
+    multiInstanceProps = require('./parts/MultiInstanceLoopProps'),
+    conditionalProps = require('./parts/ConditionalProps'),
+    scriptProps = require('./parts/ScriptTaskProps'),
+    errorProps = require('./parts/ErrorEventProps'),
+    formProps = require('./parts/FormProps'),
+    startEventInitiator = require('./parts/StartEventInitiator'),
+    variableMapping = require('./parts/VariableMappingProps'),
+    versionTag = require('./parts/VersionTagProps');
+
+var listenerProps = require('./parts/ListenerProps'),
+    listenerDetails = require('./parts/ListenerDetailProps'),
+    listenerFields = require('./parts/ListenerFieldInjectionProps');
+
+var elementTemplateChooserProps = require('./element-templates/parts/ChooserProps'),
+    elementTemplateCustomProps = require('./element-templates/parts/CustomProps');
+
+// Input/Output
+var inputOutput = require('./parts/InputOutputProps'),
+    inputOutputParameter = require('./parts/InputOutputParameterProps');
+
+// Connector
+var connectorDetails = require('./parts/ConnectorDetailProps'),
+    connectorInputOutput = require('./parts/ConnectorInputOutputProps'),
+    connectorInputOutputParameter = require('./parts/ConnectorInputOutputParameterProps');
+
+// properties
+var properties = require('./parts/PropertiesProps');
+
+// job configuration
+var jobConfiguration = require('./parts/JobConfigurationProps');
+
+// history time to live
+var historyTimeToLive = require('./parts/HistoryTimeToLiveProps');
+
+// candidate starter groups/users
+var candidateStarter = require('./parts/CandidateStarterProps');
+
+// tasklist
+var tasklist = require('./parts/TasklistProps');
+
+// external task configuration
+var externalTaskConfiguration = require('./parts/ExternalTaskConfigurationProps');
+
+// field injection
+var fieldInjections = require('./parts/FieldInjectionProps');
+
+var getBusinessObject = require('bpmn-js/lib/util/ModelUtil').getBusinessObject,
+    eventDefinitionHelper = require('../../helper/EventDefinitionHelper'),
+    implementationTypeHelper = require('../../helper/ImplementationTypeHelper');
+
+// helpers ////////////////////////////////////////
+
+var isExternalTaskPriorityEnabled = function(element) {
+  var businessObject = getBusinessObject(element);
+
+  // show only if element is a process, a participant ...
+  if (is(element, 'bpmn:Process') || is(element, 'bpmn:Participant') && businessObject.get('processRef')) {
+    return true;
+  }
+
+  var externalBo = ImplementationTypeHelper.getServiceTaskLikeBusinessObject(element),
+      isExternalTask = ImplementationTypeHelper.getImplementationType(externalBo) === 'external';
+
+  // ... or an external task with selected external implementation type
+  return !!ImplementationTypeHelper.isExternalCapable(externalBo) && isExternalTask;
+};
+
+var isJobConfigEnabled = function(element) {
+  var businessObject = getBusinessObject(element);
+
+  if (is(element, 'bpmn:Process') || is(element, 'bpmn:Participant') && businessObject.get('processRef')) {
+    return true;
+  }
+
+  // async behavior
+  var bo = getBusinessObject(element);
+  if (asyncCapableHelper.isAsyncBefore(bo) || asyncCapableHelper.isAsyncAfter(bo)) {
+    return true;
+  }
+
+  // timer definition
+  if (is(element, 'bpmn:Event')) {
+    return !!eventDefinitionHelper.getTimerEventDefinition(element);
+  }
+
+  return false;
+};
+
+var getInputOutputParameterLabel = function(param, translate) {
+
+  if (is(param, 'activiti:InputParameter')) {
+    return translate('Input Parameter');
+  }
+
+  if (is(param, 'activiti:OutputParameter')) {
+    return translate('Output Parameter');
+  }
+
+  return '';
+};
+
+var getListenerLabel = function(param, translate) {
+
+  if (is(param, 'activiti:ExecutionListener')) {
+    return translate('Execution Listener');
+  }
+
+  if (is(param, 'activiti:TaskListener')) {
+    return translate('Task Listener');
+  }
+
+  return '';
+};
+
+var PROCESS_KEY_HINT = 'This maps to the process definition key.';
+var TASK_KEY_HINT = 'This maps to the task definition key.';
+
+function createGeneralTabGroups(
+    element, canvas, bpmnFactory,
+    elementRegistry, elementTemplates, translate) {
+
+  // refer to target element for external labels
+  element = element.labelTarget || element;
+
+  var generalGroup = {
+    id: 'general',
+    label: translate('General'),
+    entries: []
+  };
+
+  var idOptions;
+  var processOptions;
+
+  if (is(element, 'bpmn:Process')) {
+    idOptions = { description: PROCESS_KEY_HINT };
+  }
+
+  if (is(element, 'bpmn:UserTask')) {
+    idOptions = { description: TASK_KEY_HINT };
+  }
+
+  if (is(element, 'bpmn:Participant')) {
+    processOptions = { processIdDescription: PROCESS_KEY_HINT };
+  }
+
+  idProps(generalGroup, element, translate, idOptions);
+  nameProps(generalGroup, element, bpmnFactory, canvas, translate);
+  processProps(generalGroup, element, translate, processOptions);
+  versionTag(generalGroup, element, translate);
+  executableProps(generalGroup, element, translate);
+  elementTemplateChooserProps(generalGroup, element, elementTemplates, translate);
+
+  var customFieldsGroups = elementTemplateCustomProps(element, elementTemplates, bpmnFactory, translate);
+
+  var detailsGroup = {
+    id: 'details',
+    label: translate('Details'),
+    entries: []
+  };
+  serviceTaskDelegateProps(detailsGroup, element, bpmnFactory, translate);
+  userTaskProps(detailsGroup, element, translate);
+  scriptProps(detailsGroup, element, bpmnFactory, translate);
+  linkProps(detailsGroup, element, translate);
+  callActivityProps(detailsGroup, element, bpmnFactory, translate);
+  eventProps(detailsGroup, element, bpmnFactory, elementRegistry, translate);
+  errorProps(detailsGroup, element, bpmnFactory, translate);
+  conditionalProps(detailsGroup, element, bpmnFactory, translate);
+  startEventInitiator(detailsGroup, element, translate); // this must be the last element of the details group!
+
+  var multiInstanceGroup = {
+    id: 'multiInstance',
+    label: translate('Multi Instance'),
+    entries: []
+  };
+  multiInstanceProps(multiInstanceGroup, element, bpmnFactory, translate);
+
+  var asyncGroup = {
+    id : 'async',
+    label: translate('Asynchronous Continuations'),
+    entries : []
+  };
+  asynchronousContinuationProps(asyncGroup, element, bpmnFactory, translate);
+
+  var jobConfigurationGroup = {
+    id : 'jobConfiguration',
+    label : translate('Job Configuration'),
+    entries : [],
+    enabled: isJobConfigEnabled
+  };
+  jobConfiguration(jobConfigurationGroup, element, bpmnFactory, translate);
+
+  var externalTaskGroup = {
+    id : 'externalTaskConfiguration',
+    label : translate('External Task Configuration'),
+    entries : [],
+    enabled: isExternalTaskPriorityEnabled
+  };
+  externalTaskConfiguration(externalTaskGroup, element, bpmnFactory, translate);
+
+
+  var candidateStarterGroup = {
+    id: 'candidateStarterConfiguration',
+    label: translate('Candidate Starter Configuration'),
+    entries: []
+  };
+  candidateStarter(candidateStarterGroup, element, bpmnFactory, translate);
+
+  var historyTimeToLiveGroup = {
+    id: 'historyConfiguration',
+    label: translate('History Configuration'),
+    entries: []
+  };
+  historyTimeToLive(historyTimeToLiveGroup, element, bpmnFactory, translate);
+
+  var tasklistGroup = {
+    id: 'tasklist',
+    label: translate('Tasklist Configuration'),
+    entries: []
+  };
+  tasklist(tasklistGroup, element, bpmnFactory, translate);
+
+  var documentationGroup = {
+    id: 'documentation',
+    label: translate('Documentation'),
+    entries: []
+  };
+  documentationProps(documentationGroup, element, bpmnFactory, translate);
+
+  var groups = [];
+  groups.push(generalGroup);
+  customFieldsGroups.forEach(function(group) {
+    groups.push(group);
+  });
+  groups.push(detailsGroup);
+  groups.push(externalTaskGroup);
+  groups.push(multiInstanceGroup);
+  groups.push(asyncGroup);
+  groups.push(jobConfigurationGroup);
+  groups.push(candidateStarterGroup);
+  groups.push(historyTimeToLiveGroup);
+  groups.push(tasklistGroup);
+  groups.push(documentationGroup);
+
+  return groups;
+}
+
+function createVariablesTabGroups(element, bpmnFactory, elementRegistry, translate) {
+  var variablesGroup = {
+    id : 'variables',
+    label : translate('Variables'),
+    entries: []
+  };
+  variableMapping(variablesGroup, element, bpmnFactory, translate);
+
+  return [
+    variablesGroup
+  ];
+}
+
+function createFormsTabGroups(element, bpmnFactory, elementRegistry, translate) {
+  var formGroup = {
+    id : 'forms',
+    label : translate('Forms'),
+    entries: []
+  };
+  formProps(formGroup, element, bpmnFactory, translate);
+
+  return [
+    formGroup
+  ];
+}
+
+function createListenersTabGroups(element, bpmnFactory, elementRegistry, translate) {
+
+  var listenersGroup = {
+    id : 'listeners',
+    label: translate('Listeners'),
+    entries: []
+  };
+
+  var options = listenerProps(listenersGroup, element, bpmnFactory, translate);
+
+  var listenerDetailsGroup = {
+    id: 'listener-details',
+    entries: [],
+    enabled: function(element, node) {
+      return options.getSelectedListener(element, node);
+    },
+    label: function(element, node) {
+      var param = options.getSelectedListener(element, node);
+      return getListenerLabel(param, translate);
+    }
+  };
+
+  listenerDetails(listenerDetailsGroup, element, bpmnFactory, options, translate);
+
+  var listenerFieldsGroup = {
+    id: 'listener-fields',
+    label: translate('Field Injection'),
+    entries: [],
+    enabled: function(element, node) {
+      return options.getSelectedListener(element, node);
+    }
+  };
+
+  listenerFields(listenerFieldsGroup, element, bpmnFactory, options, translate);
+
+  return [
+    listenersGroup,
+    listenerDetailsGroup,
+    listenerFieldsGroup
+  ];
+}
+
+function createInputOutputTabGroups(element, bpmnFactory, elementRegistry, translate) {
+
+  var inputOutputGroup = {
+    id: 'input-output',
+    label: translate('Parameters'),
+    entries: []
+  };
+
+  var options = inputOutput(inputOutputGroup, element, bpmnFactory, translate);
+
+  var inputOutputParameterGroup = {
+    id: 'input-output-parameter',
+    entries: [],
+    enabled: function(element, node) {
+      return options.getSelectedParameter(element, node);
+    },
+    label: function(element, node) {
+      var param = options.getSelectedParameter(element, node);
+      return getInputOutputParameterLabel(param, translate);
+    }
+  };
+
+  inputOutputParameter(inputOutputParameterGroup, element, bpmnFactory, options, translate);
+
+  return [
+    inputOutputGroup,
+    inputOutputParameterGroup
+  ];
+}
+
+function createConnectorTabGroups(element, bpmnFactory, elementRegistry, translate) {
+  var connectorDetailsGroup = {
+    id: 'connector-details',
+    label: translate('Details'),
+    entries: []
+  };
+
+  connectorDetails(connectorDetailsGroup, element, bpmnFactory, translate);
+
+  var connectorInputOutputGroup = {
+    id: 'connector-input-output',
+    label: translate('Input/Output'),
+    entries: []
+  };
+
+  var options = connectorInputOutput(connectorInputOutputGroup, element, bpmnFactory, translate);
+
+  var connectorInputOutputParameterGroup = {
+    id: 'connector-input-output-parameter',
+    entries: [],
+    enabled: function(element, node) {
+      return options.getSelectedParameter(element, node);
+    },
+    label: function(element, node) {
+      var param = options.getSelectedParameter(element, node);
+      return getInputOutputParameterLabel(param, translate);
+    }
+  };
+
+  connectorInputOutputParameter(connectorInputOutputParameterGroup, element, bpmnFactory, options, translate);
+
+  return [
+    connectorDetailsGroup,
+    connectorInputOutputGroup,
+    connectorInputOutputParameterGroup
+  ];
+}
+
+function createFieldInjectionsTabGroups(element, bpmnFactory, elementRegistry, translate) {
+
+  var fieldGroup = {
+    id: 'field-injections-properties',
+    label: translate('Field Injections'),
+    entries: []
+  };
+
+  fieldInjections(fieldGroup, element, bpmnFactory, translate);
+
+  return [
+    fieldGroup
+  ];
+}
+
+function createExtensionElementsGroups(element, bpmnFactory, elementRegistry, translate) {
+
+  var propertiesGroup = {
+    id : 'extensionElements-properties',
+    label: translate('Properties'),
+    entries: []
+  };
+  properties(propertiesGroup, element, bpmnFactory, translate);
+
+  return [
+    propertiesGroup
+  ];
+}
+
+// activiti Properties Provider /////////////////////////////////////
+
+
+/**
+ * A properties provider for activiti related properties.
+ *
+ * @param {EventBus} eventBus
+ * @param {Canvas} canvas
+ * @param {BpmnFactory} bpmnFactory
+ * @param {ElementRegistry} elementRegistry
+ * @param {ElementTemplates} elementTemplates
+ * @param {Translate} translate
+ */
+function ActivitiPropertiesProvider(
+    eventBus, canvas, bpmnFactory,
+    elementRegistry, elementTemplates, translate) {
+
+  PropertiesActivator.call(this, eventBus);
+
+  this.getTabs = function(element) {
+
+    var generalTab = {
+      id: 'general',
+      label: translate('General'),
+      groups: createGeneralTabGroups(
+        element, canvas, bpmnFactory,
+        elementRegistry, elementTemplates, translate)
+    };
+
+    var variablesTab = {
+      id: 'variables',
+      label: translate('Variables'),
+      groups: createVariablesTabGroups(element, bpmnFactory, elementRegistry, translate)
+    };
+
+    var formsTab = {
+      id: 'forms',
+      label: translate('Forms'),
+      groups: createFormsTabGroups(element, bpmnFactory, elementRegistry, translate)
+    };
+
+    var listenersTab = {
+      id: 'listeners',
+      label: translate('Listeners'),
+      groups: createListenersTabGroups(element, bpmnFactory, elementRegistry, translate),
+      enabled: function(element) {
+        return !eventDefinitionHelper.getLinkEventDefinition(element)
+          || (!is(element, 'bpmn:IntermediateThrowEvent')
+          && eventDefinitionHelper.getLinkEventDefinition(element));
+      }
+    };
+
+    var inputOutputTab = {
+      id: 'input-output',
+      label: translate('Input/Output'),
+      groups: createInputOutputTabGroups(element, bpmnFactory, elementRegistry, translate)
+    };
+
+    var connectorTab = {
+      id: 'connector',
+      label: translate('Connector'),
+      groups: createConnectorTabGroups(element, bpmnFactory, elementRegistry, translate),
+      enabled: function(element) {
+        var bo = implementationTypeHelper.getServiceTaskLikeBusinessObject(element);
+        return bo && implementationTypeHelper.getImplementationType(bo) === 'connector';
+      }
+    };
+
+    var fieldInjectionsTab = {
+      id: 'field-injections',
+      label: translate('Field Injections'),
+      groups: createFieldInjectionsTabGroups(element, bpmnFactory, elementRegistry, translate)
+    };
+
+    var extensionsTab = {
+      id: 'extensionElements',
+      label: translate('Extensions'),
+      groups: createExtensionElementsGroups(element, bpmnFactory, elementRegistry, translate)
+    };
+
+    return [
+      generalTab,
+      variablesTab,
+      connectorTab,
+      formsTab,
+      listenersTab,
+      inputOutputTab,
+      fieldInjectionsTab,
+      extensionsTab
+    ];
+  };
+
+}
+
+ActivitiPropertiesProvider.$inject = [
+  'eventBus',
+  'canvas',
+  'bpmnFactory',
+  'elementRegistry',
+  'elementTemplates',
+  'translate'
+];
+
+inherits(ActivitiPropertiesProvider, PropertiesActivator);
+
+module.exports = ActivitiPropertiesProvider;

+ 251 - 0
ruoyi-bpmnjs/resources/properties-panel/provider/activiti/element-templates/CreateHelper.js

@@ -0,0 +1,251 @@
+'use strict';
+
+var assign = require('lodash/assign');
+
+/**
+ * Create an input parameter representing the given
+ * binding and value.
+ *
+ * @param {PropertyBinding} binding
+ * @param {String} value
+ * @param {BpmnFactory} bpmnFactory
+ *
+ * @return {ModdleElement}
+ */
+function createInputParameter(binding, value, bpmnFactory) {
+  var scriptFormat = binding.scriptFormat,
+      parameterValue,
+      parameterDefinition;
+
+  if (scriptFormat) {
+    parameterDefinition = bpmnFactory.create('activiti:Script', {
+      scriptFormat: scriptFormat,
+      value: value
+    });
+  } else {
+    parameterValue = value;
+  }
+
+  return bpmnFactory.create('activiti:InputParameter', {
+    name: binding.name,
+    value: parameterValue,
+    definition: parameterDefinition
+  });
+}
+
+module.exports.createInputParameter = createInputParameter;
+
+
+/**
+ * Create an output parameter representing the given
+ * binding and value.
+ *
+ * @param {PropertyBinding} binding
+ * @param {String} value
+ * @param {BpmnFactory} bpmnFactory
+ *
+ * @return {ModdleElement}
+ */
+function createOutputParameter(binding, value, bpmnFactory) {
+  var scriptFormat = binding.scriptFormat,
+      parameterValue,
+      parameterDefinition;
+
+  if (scriptFormat) {
+    parameterDefinition = bpmnFactory.create('activiti:Script', {
+      scriptFormat: scriptFormat,
+      value: binding.source
+    });
+  } else {
+    parameterValue = binding.source;
+  }
+
+  return bpmnFactory.create('activiti:OutputParameter', {
+    name: value,
+    value: parameterValue,
+    definition: parameterDefinition
+  });
+}
+
+module.exports.createOutputParameter = createOutputParameter;
+
+
+/**
+ * Create activiti property from the given binding.
+ *
+ * @param {PropertyBinding} binding
+ * @param {String} value
+ * @param {BpmnFactory} bpmnFactory
+ *
+ * @return {ModdleElement}
+ */
+function createActivitiProperty(binding, value, bpmnFactory) {
+  return bpmnFactory.create('activiti:Property', {
+    name: binding.name,
+    value: value || ''
+  });
+}
+
+module.exports.createActivitiProperty = createActivitiProperty;
+
+
+/**
+ * Create activiti:in element from given binding.
+ *
+ * @param {PropertyBinding} binding
+ * @param {String} value
+ * @param {BpmnFactory} bpmnFactory
+ *
+ * @return {ModdleElement}
+ */
+function createActivitiIn(binding, value, bpmnFactory) {
+
+  var properties = createActivitiInOutAttrs(binding, value);
+
+  return bpmnFactory.create('activiti:In', properties);
+}
+
+module.exports.createActivitiIn = createActivitiIn;
+
+
+/**
+ * Create activiti:in with businessKey element from given binding.
+ *
+ * @param {PropertyBinding} binding
+ * @param {String} value
+ * @param {BpmnFactory} bpmnFactory
+ *
+ * @return {ModdleElement}
+ */
+function createActivitiInWithBusinessKey(binding, value, bpmnFactory) {
+  return bpmnFactory.create('activiti:In', {
+    businessKey: value
+  });
+}
+
+module.exports.createActivitiInWithBusinessKey = createActivitiInWithBusinessKey;
+
+
+/**
+ * Create activiti:out element from given binding.
+ *
+ * @param {PropertyBinding} binding
+ * @param {String} value
+ * @param {BpmnFactory} bpmnFactory
+ *
+ * @return {ModdleElement}
+ */
+function createActivitiOut(binding, value, bpmnFactory) {
+  var properties = createActivitiInOutAttrs(binding, value);
+
+  return bpmnFactory.create('activiti:Out', properties);
+}
+
+module.exports.createActivitiOut = createActivitiOut;
+
+
+/**
+ * Create activiti:executionListener element containing an inline script from given binding.
+ *
+ * @param {PropertyBinding} binding
+ * @param {String} value
+ * @param {BpmnFactory} bpmnFactory
+ *
+ * @return {ModdleElement}
+ */
+function createActivitiExecutionListenerScript(binding, value, bpmnFactory) {
+  var scriptFormat = binding.scriptFormat,
+      parameterValue,
+      parameterDefinition;
+
+  if (scriptFormat) {
+    parameterDefinition = bpmnFactory.create('activiti:Script', {
+      scriptFormat: scriptFormat,
+      value: value
+    });
+  } else {
+    parameterValue = value;
+  }
+
+  return bpmnFactory.create('activiti:ExecutionListener', {
+    event: binding.event,
+    value: parameterValue,
+    script: parameterDefinition
+  });
+}
+
+module.exports.createActivitiExecutionListenerScript = createActivitiExecutionListenerScript;
+
+/**
+ * Create activiti:field element containing string or expression from given binding.
+ *
+ * @param {PropertyBinding} binding
+ * @param {String} value
+ * @param {BpmnFactory} bpmnFactory
+ *
+ * @return {ModdleElement}
+ */
+function createActivitiFieldInjection(binding, value, bpmnFactory) {
+  var DEFAULT_PROPS = {
+    'string': undefined,
+    'expression': undefined,
+    'name': undefined
+  };
+
+  var props = assign({}, DEFAULT_PROPS);
+
+  if (!binding.expression) {
+    props.string = value;
+  } else {
+    props.expression = value;
+  }
+  props.name = binding.name;
+
+  return bpmnFactory.create('activiti:Field', props);
+}
+module.exports.createActivitiFieldInjection = createActivitiFieldInjection;
+
+
+// helpers ////////////////////////////
+
+/**
+ * Create properties for activiti:in and activiti:out types.
+ */
+function createActivitiInOutAttrs(binding, value) {
+
+  var properties = {};
+
+  // activiti:in source(Expression) target
+  if (binding.target) {
+
+    properties.target = binding.target;
+
+    if (binding.expression) {
+      properties.sourceExpression = value;
+    } else {
+      properties.source = value;
+    }
+  } else
+
+  // activiti:(in|out) variables local
+  if (binding.variables) {
+    properties.variables = 'all';
+
+    if (binding.variables === 'local') {
+      properties.local = true;
+    }
+  }
+
+  // activiti:out source(Expression) target
+  else {
+    properties.target = value;
+
+    [ 'source', 'sourceExpression' ].forEach(function(k) {
+      if (binding[k]) {
+        properties[k] = binding[k];
+      }
+    });
+  }
+
+  return properties;
+}

+ 132 - 0
ruoyi-bpmnjs/resources/properties-panel/provider/activiti/element-templates/CustomElementsPropertiesActivator.js

@@ -0,0 +1,132 @@
+'use strict';
+
+var inherits = require('inherits');
+
+var getTemplate = require('./Helper').getTemplate;
+
+var PropertiesActivator = require('../../../PropertiesActivator');
+
+var HIGHER_PRIORITY = 1100;
+
+/**
+ * Applies template visibility settings.
+ *
+ * Controlled using `entriesVisible` on template config object:
+ *
+ * ```json
+ *     "entriesVisible": {
+ *       "_all": true|false,
+ *       "entryName": true|false,
+ *       ...
+ *     }
+ * ```
+ *
+ * @param {EventBus} eventBus
+ * @param {ElementTemplates} elementTemplates
+ */
+function CustomElementsPropertiesActivator(eventBus, elementTemplates) {
+  PropertiesActivator.call(this, eventBus, HIGHER_PRIORITY);
+
+  this.isEntryVisible = function(entry, element) {
+
+    var template = getTemplate(element, elementTemplates);
+
+    if (template && !isEntryVisible(entry, template)) {
+      return false;
+    }
+  };
+
+  this.isPropertyEditable = function(entry, propertyName, element) {
+
+    var template = getTemplate(element, elementTemplates);
+
+    if (template && !isEntryEditable(entry, template)) {
+      return false;
+    }
+  };
+}
+
+CustomElementsPropertiesActivator.$inject = [ 'eventBus', 'elementTemplates' ];
+
+inherits(CustomElementsPropertiesActivator, PropertiesActivator);
+
+module.exports = CustomElementsPropertiesActivator;
+
+
+
+// helpers ////////////////////////////////////
+
+
+var CUSTOM_PROPERTIES_PATTERN = /^custom-/;
+
+var DEFAULT_ENTRIES_VISIBLE = {
+  _all: false,
+  id: true,
+  name: true
+};
+
+function isCustomEntry(entry) {
+  return CUSTOM_PROPERTIES_PATTERN.test(entry.id);
+}
+
+function isEntryVisible(entry, template) {
+
+  var entryId = entry.id;
+
+  if (entryId === 'elementTemplate-chooser' || isCustomEntry(entry)) {
+    return true;
+  }
+
+  var entriesVisible = template.entriesVisible || DEFAULT_ENTRIES_VISIBLE;
+
+  if (typeof entriesVisible === 'boolean') {
+    return entriesVisible;
+  }
+
+  var defaultVisible = entriesVisible._all || false,
+      entryVisible = entriesVisible[entryId];
+
+  // d = true, e = false => false
+  // d = false, e = true => true
+  // d = false, e = false
+  return (
+    (defaultVisible === true && entryVisible !== false) ||
+    (defaultVisible === false && entryVisible === true)
+  );
+}
+
+function isEntryEditable(entry, template) {
+
+  var property;
+
+  if (isCustomEntry(entry)) {
+    property = getProperty(template, entry);
+
+    return property && property.editable !== false;
+  }
+
+  return true;
+}
+
+function getProperty(template, entry) {
+
+  var index;
+  var idx = entry.id.replace('custom-' + template.id + '-', '');
+  if (idx.indexOf('-') !== -1) {
+    var indexes = idx.split('-');
+    if (indexes.length == 2) {
+      var scopeName = indexes[0].replace(/_/g, ':');
+      index = parseInt(indexes[1], 10);
+      if (scopeName && !isNaN(index)) {
+        return template.scopes[scopeName].properties[index];
+      }
+    }
+  } else {
+    index = parseInt(idx, 10);
+    if (!isNaN(index)) {
+      return template.properties[index];
+    }
+  }
+
+  throw new Error('cannot extract property index for entry <' + entry.id + '>');
+}

+ 56 - 0
ruoyi-bpmnjs/resources/properties-panel/provider/activiti/element-templates/ElementTemplates.js

@@ -0,0 +1,56 @@
+'use strict';
+
+var values = require('lodash/values');
+
+/**
+ * The guy knowing all configured element templates.
+ *
+ * This registry won't validate. Use the {@link Validator}
+ * to verify a template is valid prior to adding it to
+ * this registry.
+ */
+function ElementTemplates() {
+
+  this._templates = {};
+
+  /**
+   * Sets the known element templates.
+   *
+   * @param {Array<TemplateDescriptor>} descriptors
+   *
+   * @return {ElementTemplates}
+   */
+  this.set = function(descriptors) {
+
+    var templates = this._templates = {};
+
+    descriptors.forEach(function(descriptor) {
+      templates[descriptor.id] = descriptor;
+    });
+
+    return this;
+  };
+
+  /**
+   * Get template descriptor with given id.
+   *
+   * @param {String} id
+   *
+   * @return {TemplateDescriptor}
+   */
+  this.get = function(id) {
+    return this._templates[id];
+  };
+
+  /**
+   * Return all known template descriptors.
+   *
+   * @return {Array<TemplateDescriptor>}
+   */
+  this.getAll = function() {
+    return values(this._templates);
+  };
+
+}
+
+module.exports = ElementTemplates;

+ 96 - 0
ruoyi-bpmnjs/resources/properties-panel/provider/activiti/element-templates/ElementTemplatesLoader.js

@@ -0,0 +1,96 @@
+'use strict';
+
+var Validator = require('./Validator');
+
+/**
+ * The guy responsible for template loading.
+ *
+ * Provide the actual templates via the `config.elementTemplates`.
+ *
+ * That configuration can either be an array of template
+ * descriptors or a node style callback to retrieve
+ * the templates asynchronously.
+ *
+ * @param {Array<TemplateDescriptor>|Function} loadTemplates
+ * @param {EventBus} eventBus
+ * @param {ElementTemplates} elementTemplates
+ */
+function ElementTemplatesLoader(loadTemplates, eventBus, elementTemplates) {
+  this._loadTemplates = loadTemplates;
+  this._eventBus = eventBus;
+  this._elementTemplates = elementTemplates;
+
+  var self = this;
+
+  eventBus.on('diagram.init', function() {
+    self.reload();
+  });
+}
+
+module.exports = ElementTemplatesLoader;
+
+ElementTemplatesLoader.$inject = [
+  'config.elementTemplates',
+  'eventBus',
+  'elementTemplates'
+];
+
+
+ElementTemplatesLoader.prototype.reload = function() {
+
+  var self = this;
+
+  var loadTemplates = this._loadTemplates;
+
+  // no templates specified
+  if (typeof loadTemplates === 'undefined') {
+    return;
+  }
+
+  // template loader function specified
+  if (typeof loadTemplates === 'function') {
+
+    return loadTemplates(function(err, templates) {
+
+      if (err) {
+        return self.templateErrors([ err ]);
+      }
+
+      self.setTemplates(templates);
+    });
+  }
+
+  // templates array specified
+  if (loadTemplates.length) {
+    return this.setTemplates(loadTemplates);
+  }
+
+};
+
+ElementTemplatesLoader.prototype.setTemplates = function(templates) {
+
+  var elementTemplates = this._elementTemplates;
+
+  var validator = new Validator().addAll(templates);
+
+  var errors = validator.getErrors(),
+      validTemplates = validator.getValidTemplates();
+
+  elementTemplates.set(validTemplates);
+
+  if (errors.length) {
+    this.templateErrors(errors);
+  }
+
+  this.templatesChanged();
+};
+
+ElementTemplatesLoader.prototype.templatesChanged = function() {
+  this._eventBus.fire('elementTemplates.changed');
+};
+
+ElementTemplatesLoader.prototype.templateErrors = function(errors) {
+  this._eventBus.fire('elementTemplates.errors', {
+    errors: errors
+  });
+};

+ 239 - 0
ruoyi-bpmnjs/resources/properties-panel/provider/activiti/element-templates/Helper.js

@@ -0,0 +1,239 @@
+'use strict';
+
+var getBusinessObject = require('bpmn-js/lib/util/ModelUtil').getBusinessObject;
+
+var is = require('bpmn-js/lib/util/ModelUtil').is,
+    isAny = require('bpmn-js/lib/features/modeling/util/ModelingUtil').isAny;
+
+var find = require('lodash/find');
+
+
+var TEMPLATE_ATTR = 'activiti:modelerTemplate';
+
+/**
+ * The BPMN 2.0 extension attribute name under
+ * which the element template is stored.
+ *
+ * @type {String}
+ */
+module.exports.TEMPLATE_ATTR = TEMPLATE_ATTR;
+
+
+/**
+ * Get template id for a given diagram element.
+ *
+ * @param {djs.model.Base} element
+ *
+ * @return {String}
+ */
+function getTemplateId(element) {
+
+  var bo = getBusinessObject(element);
+
+  if (bo) {
+    return bo.get(TEMPLATE_ATTR);
+  }
+}
+
+module.exports.getTemplateId = getTemplateId;
+
+
+/**
+ * Get template of a given element.
+ *
+ * @param {Element} element
+ * @param {ElementTemplates} elementTemplates
+ *
+ * @return {TemplateDefinition}
+ */
+function getTemplate(element, elementTemplates) {
+  var id = getTemplateId(element);
+
+  return id && elementTemplates.get(id);
+}
+
+module.exports.getTemplate = getTemplate;
+
+/**
+ * Get default template for a given element.
+ *
+ * @param {Element} element
+ * @param {ElementTemplates} elementTemplates
+ *
+ * @return {TemplateDefinition}
+ */
+function getDefaultTemplate(element, elementTemplates) {
+
+  // return first default template, if any exists
+  return (
+    elementTemplates.getAll().filter(function(t) {
+      return isAny(element, t.appliesTo) && t.isDefault;
+    })
+  )[0];
+}
+
+module.exports.getDefaultTemplate = getDefaultTemplate;
+
+
+/**
+ * Find extension with given type in
+ * BPMN element, diagram element or ExtensionElement.
+ *
+ * @param {ModdleElement|djs.model.Base} element
+ * @param {String} type
+ *
+ * @return {ModdleElement} the extension
+ */
+function findExtension(element, type) {
+  var bo = getBusinessObject(element);
+
+  var extensionElements;
+
+  if (is(bo, 'bpmn:ExtensionElements')) {
+    extensionElements = bo;
+  } else {
+    extensionElements = bo.extensionElements;
+  }
+
+  if (!extensionElements) {
+    return null;
+  }
+
+  return find(extensionElements.get('values'), function(e) {
+    return is(e, type);
+  });
+}
+
+module.exports.findExtension = findExtension;
+
+
+function findExtensions(element, types) {
+  var extensionElements = getExtensionElements(element);
+
+  if (!extensionElements) {
+    return [];
+  }
+
+  return extensionElements.get('values').filter(function(e) {
+    return isAny(e, types);
+  });
+}
+
+module.exports.findExtensions = findExtensions;
+
+
+function findActivitiInOut(element, binding) {
+
+  var extensionElements = getExtensionElements(element);
+
+  if (!extensionElements) {
+    return;
+  }
+
+  var matcher;
+
+  if (binding.type === 'activiti:in') {
+    matcher = function(e) {
+      return is(e, 'activiti:In') && isInOut(e, binding);
+    };
+  } else
+  if (binding.type === 'activiti:out') {
+    matcher = function(e) {
+      return is(e, 'activiti:Out') && isInOut(e, binding);
+    };
+  } else
+  if (binding.type === 'activiti:in:businessKey') {
+    matcher = function(e) {
+      return is(e, 'activiti:In') && 'businessKey' in e;
+    };
+  }
+
+  return find(extensionElements.get('values'), matcher);
+}
+
+module.exports.findActivitiInOut = findActivitiInOut;
+
+function findActivitiProperty(activitiProperties, binding) {
+  return find(activitiProperties.get('values'), function(p) {
+    return p.name === binding.name;
+  });
+}
+
+module.exports.findActivitiProperty = findActivitiProperty;
+
+
+function findInputParameter(inputOutput, binding) {
+  var parameters = inputOutput.get('inputParameters');
+
+  return find(parameters, function(p) {
+    return p.name === binding.name;
+  });
+}
+
+module.exports.findInputParameter = findInputParameter;
+
+
+function findOutputParameter(inputOutput, binding) {
+  var parameters = inputOutput.get('outputParameters');
+
+  return find(parameters, function(p) {
+    var value = p.value;
+
+    if (!binding.scriptFormat) {
+      return value === binding.source;
+    }
+
+    var definition = p.definition;
+
+    if (!definition || binding.scriptFormat !== definition.scriptFormat) {
+      return false;
+    }
+
+    return definition.value === binding.source;
+  });
+}
+
+module.exports.findOutputParameter = findOutputParameter;
+
+
+
+// helpers /////////////////////////////////
+
+function getExtensionElements(element) {
+  var bo = getBusinessObject(element);
+
+  if (is(bo, 'bpmn:ExtensionElements')) {
+    return bo;
+  } else {
+    return bo.extensionElements;
+  }
+}
+
+
+function isInOut(element, binding) {
+
+  if (binding.type === 'activiti:in') {
+    // find based on target attribute
+    if (binding.target) {
+      return element.target === binding.target;
+    }
+  }
+
+  if (binding.type === 'activiti:out') {
+    // find based on source / sourceExpression
+    if (binding.source) {
+      return element.source === binding.source;
+    }
+
+    if (binding.sourceExpression) {
+      return element.sourceExpression === binding.sourceExpression;
+    }
+  }
+
+  // find based variables / local combination
+  if (binding.variables) {
+    return element.variables === 'all' && (
+      binding.variables !== 'local' || element.local
+    );
+  }
+}

+ 303 - 0
ruoyi-bpmnjs/resources/properties-panel/provider/activiti/element-templates/Validator.js

@@ -0,0 +1,303 @@
+'use strict';
+
+var isArray = require('lodash/isArray');
+var isObject = require('lodash/isObject');
+
+var DROPDOWN_TYPE = 'Dropdown';
+
+var VALID_TYPES = [ 'String', 'Text', 'Boolean', 'Hidden', DROPDOWN_TYPE ];
+
+var PROPERTY_TYPE = 'property',
+    ACTIVITI_PROPERTY_TYPE = 'activiti:property',
+    ACTIVITI_INPUT_PARAMETER_TYPE = 'activiti:inputParameter',
+    ACTIVITI_OUTPUT_PARAMETER_TYPE = 'activiti:outputParameter',
+    ACTIVITI_IN_TYPE = 'activiti:in',
+    ACTIVITI_OUT_TYPE = 'activiti:out',
+    ACTIVITI_IN_BUSINESS_KEY_TYPE = 'activiti:in:businessKey',
+    ACTIVITI_EXECUTION_LISTENER = 'activiti:executionListener',
+    ACTIVITI_FIELD = 'activiti:field';
+
+var VALID_BINDING_TYPES = [
+  PROPERTY_TYPE,
+  ACTIVITI_PROPERTY_TYPE,
+  ACTIVITI_INPUT_PARAMETER_TYPE,
+  ACTIVITI_OUTPUT_PARAMETER_TYPE,
+  ACTIVITI_IN_TYPE,
+  ACTIVITI_OUT_TYPE,
+  ACTIVITI_IN_BUSINESS_KEY_TYPE,
+  ACTIVITI_EXECUTION_LISTENER,
+  ACTIVITI_FIELD
+];
+
+
+/**
+ * A element template validator.
+ */
+function Validator() {
+
+  this._templatesById = {};
+
+  this._validTemplates = [];
+  this._errors = [];
+
+
+  /**
+   * Adds the templates.
+   *
+   * @param {Array<TemplateDescriptor>} templates
+   *
+   * @return {Validator} self
+   */
+  this.addAll = function(templates) {
+
+    if (!isArray(templates)) {
+      this._logError('templates must be []');
+    } else {
+      templates.forEach(this.add, this);
+    }
+
+    return this;
+  };
+
+  /**
+   * Add the given element template, if it is valid.
+   *
+   * @param {TemplateDescriptor} template
+   *
+   * @return {Validator} self
+   */
+  this.add = function(template) {
+
+    var err = this._validateTemplate(template);
+
+    if (!err) {
+      this._templatesById[template.id] = template;
+
+      this._validTemplates.push(template);
+    }
+
+    return this;
+  };
+
+  /**
+   * Validate given template and return error (if any).
+   *
+   * @param {TemplateDescriptor} template
+   *
+   * @return {Error} validation error, if any
+   */
+  this._validateTemplate = function(template) {
+
+    var err,
+        id = template.id,
+        appliesTo = template.appliesTo,
+        properties = template.properties,
+        scopes = template.scopes;
+
+    if (!id) {
+      return this._logError('missing template id');
+    }
+
+    if (id in this._templatesById) {
+      return this._logError('template id <' + id + '> already used');
+    }
+
+    if (!isArray(appliesTo)) {
+      err = this._logError('missing appliesTo=[]', template);
+    }
+
+    if (!isArray(properties)) {
+      err = this._logError('missing properties=[]', template);
+    } else {
+      if (!this._validateProperties(properties)) {
+        err = new Error('invalid properties');
+      }
+    }
+
+    if (scopes) {
+      err = this._validateScopes(template, scopes);
+    }
+
+    return err;
+  };
+
+  this._validateScopes = function(template, scopes) {
+
+    var err,
+        scope,
+        scopeName;
+
+    if (!isObject(scopes) || isArray(scopes)) {
+      return this._logError('invalid scopes, should be scopes={}', template);
+    }
+
+    for (scopeName in scopes) {
+      scope = scopes[scopeName];
+
+      if (!isObject(scope) || isArray(scope)) {
+        err = this._logError('invalid scope, should be scope={}', template);
+      }
+
+      if (!isArray(scope.properties)) {
+        err = this._logError(
+          'missing properties=[] in scope <' + scopeName + '>', template
+        );
+      } else {
+        if (!this._validateProperties(scope.properties)) {
+          err = new Error('invalid properties in scope <' + scopeName + '>');
+        }
+      }
+    }
+
+    return err;
+  };
+
+  /**
+   * Validate properties and return false if any is invalid.
+   *
+   * @param {Array<PropertyDescriptor>} properties
+   *
+   * @return {Boolean} true if all properties are valid
+   */
+  this._validateProperties = function(properties) {
+    var validProperties = properties.filter(this._validateProperty, this);
+
+    return properties.length === validProperties.length;
+  };
+
+  /**
+   * Validate property and return false, if there was
+   * a validation error.
+   *
+   * @param {PropertyDescriptor} property
+   *
+   * @return {Boolean} true if property is valid
+   */
+  this._validateProperty = function(property) {
+
+    var type = property.type,
+        binding = property.binding;
+
+    var err;
+
+    var bindingType = binding.type;
+
+    if (VALID_TYPES.indexOf(type) === -1) {
+      err = this._logError(
+        'invalid property type <' + type + '>; ' +
+        'must be any of { ' + VALID_TYPES.join(', ') + ' }'
+      );
+    }
+
+    if (type === DROPDOWN_TYPE && bindingType !== ACTIVITI_EXECUTION_LISTENER) {
+      if (!isArray(property.choices)) {
+        err = this._logError(
+          'must provide choices=[] with ' + DROPDOWN_TYPE + ' type'
+        );
+      } else
+
+      if (!property.choices.every(isDropdownChoiceValid)) {
+        err = this._logError(
+          '{ name, value } must be specified for ' +
+          DROPDOWN_TYPE + ' choices'
+        );
+      }
+    }
+
+    if (!binding) {
+      return this._logError('property missing binding');
+    }
+
+    if (VALID_BINDING_TYPES.indexOf(bindingType) === -1) {
+      err = this._logError(
+        'invalid property.binding type <' + bindingType + '>; ' +
+        'must be any of { ' + VALID_BINDING_TYPES.join(', ') + ' }'
+      );
+    }
+
+    if (bindingType === PROPERTY_TYPE ||
+        bindingType === ACTIVITI_PROPERTY_TYPE ||
+        bindingType === ACTIVITI_INPUT_PARAMETER_TYPE ||
+        bindingType === ACTIVITI_FIELD) {
+
+      if (!binding.name) {
+        err = this._logError(
+          'property.binding <' + bindingType + '> requires name'
+        );
+      }
+    }
+
+    if (bindingType === ACTIVITI_OUTPUT_PARAMETER_TYPE) {
+      if (!binding.source) {
+        err = this._logError(
+          'property.binding <' + bindingType + '> requires source'
+        );
+      }
+    }
+
+    if (bindingType === ACTIVITI_IN_TYPE) {
+
+      if (!binding.variables && !binding.target) {
+        err = this._logError(
+          'property.binding <' + bindingType + '> requires ' +
+          'variables or target'
+        );
+      }
+    }
+
+    if (bindingType === ACTIVITI_OUT_TYPE) {
+
+      if (!binding.variables && !binding.source && !binding.sourceExpression) {
+        err = this._logError(
+          'property.binding <' + bindingType + '> requires ' +
+          'variables, sourceExpression or source'
+        );
+      }
+    }
+
+    if (bindingType === ACTIVITI_EXECUTION_LISTENER) {
+
+      if (type !== 'Hidden') {
+        err = this._logError(
+          'invalid property type <' + type + '> for ' + ACTIVITI_EXECUTION_LISTENER + '; ' +
+          'must be <Hidden>'
+        );
+      }
+    }
+
+    return !err;
+  };
+
+
+  this._logError = function(err, template) {
+
+    if (typeof err === 'string') {
+      if (template) {
+        err = 'template(id: ' + template.id + ') ' + err;
+      }
+
+      err = new Error(err);
+    }
+
+    this._errors.push(err);
+
+    return err;
+  };
+
+  this.getErrors = function() {
+    return this._errors;
+  };
+
+  this.getValidTemplates = function() {
+    return this._validTemplates;
+  };
+}
+
+module.exports = Validator;
+
+
+// helpers ///////////////////////////////////
+
+function isDropdownChoiceValid(c) {
+  return 'name' in c && 'value' in c;
+}

+ 471 - 0
ruoyi-bpmnjs/resources/properties-panel/provider/activiti/element-templates/cmd/ChangeElementTemplateHandler.js

@@ -0,0 +1,471 @@
+'use strict';
+
+var findExtension = require('../Helper').findExtension,
+    findExtensions = require('../Helper').findExtensions;
+
+var createActivitiProperty = require('../CreateHelper').createActivitiProperty,
+    createInputParameter = require('../CreateHelper').createInputParameter,
+    createOutputParameter = require('../CreateHelper').createOutputParameter,
+    createActivitiIn = require('../CreateHelper').createActivitiIn,
+    createActivitiOut = require('../CreateHelper').createActivitiOut,
+    createActivitiInWithBusinessKey = require('../CreateHelper').createActivitiInWithBusinessKey,
+    createActivitiExecutionListenerScript = require('../CreateHelper').createActivitiExecutionListenerScript,
+    createActivitiFieldInjection = require('../CreateHelper').createActivitiFieldInjection;
+
+var forEach = require('lodash/forEach');
+
+var ACTIVITI_SERVICE_TASK_LIKE = [
+  'activiti:class',
+  'activiti:delegateExpression',
+  'activiti:expression'
+];
+
+/**
+ * A handler that changes the modeling template of a BPMN element.
+ */
+function ChangeElementTemplateHandler(modeling, commandStack, bpmnFactory) {
+
+  function getOrCreateExtensionElements(element) {
+
+    var bo = element.businessObject;
+
+    var extensionElements = bo.extensionElements;
+
+    // add extension elements
+    if (!extensionElements) {
+      extensionElements = bpmnFactory.create('bpmn:ExtensionElements', {
+        values: []
+      });
+
+      modeling.updateProperties(element, {
+        extensionElements: extensionElements
+      });
+    }
+
+    return extensionElements;
+  }
+
+  function updateModelerTemplate(element, newTemplate) {
+    modeling.updateProperties(element, {
+      'activiti:modelerTemplate': newTemplate && newTemplate.id
+    });
+  }
+
+  function updateIoMappings(element, newTemplate, context) {
+
+    var newMappings = createInputOutputMappings(newTemplate, bpmnFactory),
+        oldMappings;
+
+    if (!newMappings) {
+      return;
+    }
+
+    if (context) {
+      commandStack.execute('properties-panel.update-businessobject', {
+        element: element,
+        businessObject: context,
+        properties: { inputOutput: newMappings }
+      });
+    } else {
+      context = getOrCreateExtensionElements(element);
+      oldMappings = findExtension(element, 'activiti:InputOutput');
+      commandStack.execute('properties-panel.update-businessobject-list', {
+        element: element,
+        currentObject: context,
+        propertyName: 'values',
+        objectsToAdd: [ newMappings ],
+        objectsToRemove: oldMappings ? [ oldMappings ] : []
+      });
+    }
+  }
+
+  function updateActivitiField(element, newTemplate, context) {
+
+    var newMappings = createActivitiFieldInjections(newTemplate, bpmnFactory),
+        oldMappings;
+
+    if (!newMappings) {
+      return;
+    }
+    if (context) {
+      commandStack.execute('properties-panel.update-businessobject', {
+        element: element,
+        businessObject: context,
+        properties: { field: newMappings }
+      });
+    } else {
+      context = getOrCreateExtensionElements(element);
+      oldMappings = findExtensions(element, ['activiti:Field']);
+
+      commandStack.execute('properties-panel.update-businessobject-list', {
+        element: element,
+        currentObject: context,
+        propertyName: 'values',
+        objectsToAdd: newMappings,
+        objectsToRemove: oldMappings ? oldMappings : []
+      });
+    }
+  }
+
+
+  function updateActivitiProperties(element, newTemplate, context) {
+
+    var newProperties = createActivitiProperties(newTemplate, bpmnFactory),
+        oldProperties;
+
+    if (!newProperties) {
+      return;
+    }
+
+    if (context) {
+      commandStack.execute('properties-panel.update-businessobject', {
+        element: element,
+        businessObject: context,
+        properties: { properties: newProperties }
+      });
+    } else {
+      context = getOrCreateExtensionElements(element);
+      oldProperties = findExtension(element, 'activiti:Properties');
+
+      commandStack.execute('properties-panel.update-businessobject-list', {
+        element: element,
+        currentObject: context,
+        propertyName: 'values',
+        objectsToAdd: [ newProperties ],
+        objectsToRemove: oldProperties ? [ oldProperties ] : []
+      });
+    }
+  }
+
+  function updateProperties(element, newTemplate, context) {
+
+    var newProperties = createBpmnPropertyUpdates(newTemplate, bpmnFactory);
+
+    var newPropertiesCount = Object.keys(newProperties).length;
+
+    if (!newPropertiesCount) {
+      return;
+    }
+
+    if (context) {
+      commandStack.execute('properties-panel.update-businessobject', {
+        element: element,
+        businessObject: context,
+        properties: newProperties
+      });
+    } else {
+      modeling.updateProperties(element, newProperties);
+    }
+  }
+
+  function updateInOut(element, newTemplate, context) {
+
+    var newInOut = createActivitiInOut(newTemplate, bpmnFactory),
+        oldInOut;
+
+    if (!newInOut) {
+      return;
+    }
+
+    if (context) {
+      commandStack.execute('properties-panel.update-businessobject', {
+        element: element,
+        businessObject: context,
+        properties: { inout: newInOut }
+      });
+    } else {
+      context = getOrCreateExtensionElements(element);
+      oldInOut = findExtensions(context, [ 'activiti:In', 'activiti:Out' ]);
+
+      commandStack.execute('properties-panel.update-businessobject-list', {
+        element: element,
+        currentObject: context,
+        propertyName: 'values',
+        objectsToAdd: newInOut,
+        objectsToRemove: oldInOut
+      });
+    }
+  }
+
+  function updateExecutionListener(element, newTemplate, context) {
+
+    var newExecutionListeners = createActivitiExecutionListeners(newTemplate, bpmnFactory),
+        oldExecutionsListeners;
+
+    if (!newExecutionListeners.length) {
+      return;
+    }
+
+    if (context) {
+      commandStack.execute('properties-panel.update-businessobject', {
+        element: element,
+        businessObject: context,
+        properties: { executionListener: newExecutionListeners }
+      });
+    } else {
+      context = getOrCreateExtensionElements(element);
+      oldExecutionsListeners = findExtensions(context, [ 'activiti:ExecutionListener' ]);
+
+      commandStack.execute('properties-panel.update-businessobject-list', {
+        element: element,
+        currentObject: context,
+        propertyName: 'values',
+        objectsToAdd: newExecutionListeners,
+        objectsToRemove: oldExecutionsListeners
+      });
+    }
+  }
+
+  /**
+   * Update / recreate a scoped element.
+   *
+   * @param {djs.model.Base} element the diagram parent element
+   * @param {String} scopeName name of the scope, i.e. activiti:Connector
+   * @param {Object} scopeDefinition
+   */
+  function updateScopeElements(element, scopeName, scopeDefinition) {
+
+    var scopeElement = bpmnFactory.create(scopeName);
+
+    // update activiti:inputOutput
+    updateIoMappings(element, scopeDefinition, scopeElement);
+
+    // update activiti:field
+    updateActivitiField(element, scopeDefinition, scopeElement);
+
+    // update activiti:properties
+    updateActivitiProperties(element, scopeDefinition, scopeElement);
+
+    // update other properties (bpmn:condition, activiti:async, ...)
+    updateProperties(element, scopeDefinition, scopeElement);
+
+    // update activiti:in and activiti:out
+    updateInOut(element, scopeDefinition, scopeElement);
+
+    // update activiti:executionListener
+    updateExecutionListener(element, scopeDefinition, scopeElement);
+
+    var extensionElements = getOrCreateExtensionElements(element);
+    var oldScope = findExtension(extensionElements, scopeName);
+
+    commandStack.execute('properties-panel.update-businessobject-list', {
+      element: element,
+      currentObject: extensionElements,
+      propertyName: 'values',
+      objectsToAdd: [ scopeElement ],
+      objectsToRemove: oldScope ? [ oldScope ] : []
+    });
+  }
+
+  /**
+   * Compose an element template change action, updating all
+   * necessary underlying properties.
+   *
+   * @param {Object} context
+   * @param {Object} context.element
+   * @param {Object} context.oldTemplate
+   * @param {Object} context.newTemplate
+   */
+  this.preExecute = function(context) {
+
+    var element = context.element,
+        newTemplate = context.newTemplate;
+
+    // update activiti:modelerTemplate attribute
+    updateModelerTemplate(element, newTemplate);
+
+    if (newTemplate) {
+
+      // update activiti:inputOutput
+      updateIoMappings(element, newTemplate);
+
+      // update activiti:field
+      updateActivitiField(element, newTemplate);
+
+      // update activiti:properties
+      updateActivitiProperties(element, newTemplate);
+
+      // update other properties (bpmn:condition, activiti:async, ...)
+      updateProperties(element, newTemplate);
+
+      // update activiti:in and activiti:out
+      updateInOut(element, newTemplate);
+
+      // update activiti:executionListener
+      updateExecutionListener(element, newTemplate);
+
+      // loop on scopes properties
+      forEach(newTemplate.scopes, function(scopeDefinition, scopeName) {
+        updateScopeElements(element, scopeName, scopeDefinition);
+      });
+
+    }
+  };
+}
+
+ChangeElementTemplateHandler.$inject = [ 'modeling', 'commandStack', 'bpmnFactory' ];
+
+module.exports = ChangeElementTemplateHandler;
+
+
+
+// helpers /////////////////////////////
+
+function createBpmnPropertyUpdates(template, bpmnFactory) {
+
+  var propertyUpdates = {};
+
+  template.properties.forEach(function(p) {
+
+    var binding = p.binding,
+        bindingTarget = binding.name,
+        propertyValue;
+
+    if (binding.type === 'property') {
+
+      if (bindingTarget === 'conditionExpression') {
+        propertyValue = bpmnFactory.create('bpmn:FormalExpression', {
+          body: p.value,
+          language: binding.scriptFormat
+        });
+      } else {
+        propertyValue = p.value;
+      }
+
+      // assigning activiti:async to true|false
+      // assigning bpmn:conditionExpression to { $type: 'bpmn:FormalExpression', ... }
+      propertyUpdates[bindingTarget] = propertyValue;
+
+      // make sure we unset other "implementation types"
+      // when applying a activiti:class template onto a preconfigured
+      // activiti:delegateExpression element
+      if (ACTIVITI_SERVICE_TASK_LIKE.indexOf(bindingTarget) !== -1) {
+        ACTIVITI_SERVICE_TASK_LIKE.forEach(function(prop) {
+          if (prop !== bindingTarget) {
+            propertyUpdates[prop] = undefined;
+          }
+        });
+      }
+    }
+  });
+
+  return propertyUpdates;
+}
+
+function createActivitiFieldInjections(template, bpmnFactory) {
+  var injections = [];
+
+  template.properties.forEach(function(p) {
+    var binding = p.binding,
+        bindingType = binding.type;
+    if (bindingType === 'activiti:field') {
+      injections.push(createActivitiFieldInjection(
+        binding, p.value, bpmnFactory
+      ));
+    }
+  });
+
+  if (injections.length) {
+    return injections;
+  }
+}
+
+function createActivitiProperties(template, bpmnFactory) {
+
+  var properties = [];
+
+  template.properties.forEach(function(p) {
+    var binding = p.binding,
+        bindingType = binding.type;
+
+    if (bindingType === 'activiti:property') {
+      properties.push(createActivitiProperty(
+        binding, p.value, bpmnFactory
+      ));
+    }
+  });
+
+  if (properties.length) {
+    return bpmnFactory.create('activiti:Properties', {
+      values: properties
+    });
+  }
+}
+
+function createInputOutputMappings(template, bpmnFactory) {
+
+  var inputParameters = [],
+      outputParameters = [];
+
+  template.properties.forEach(function(p) {
+    var binding = p.binding,
+        bindingType = binding.type;
+
+    if (bindingType === 'activiti:inputParameter') {
+      inputParameters.push(createInputParameter(
+        binding, p.value, bpmnFactory
+      ));
+    }
+
+    if (bindingType === 'activiti:outputParameter') {
+      outputParameters.push(createOutputParameter(
+        binding, p.value, bpmnFactory
+      ));
+    }
+  });
+
+  // do we need to create new ioMappings (?)
+  if (outputParameters.length || inputParameters.length) {
+    return bpmnFactory.create('activiti:InputOutput', {
+      inputParameters: inputParameters,
+      outputParameters: outputParameters
+    });
+  }
+}
+
+function createActivitiInOut(template, bpmnFactory) {
+
+  var inOuts = [];
+
+  template.properties.forEach(function(p) {
+    var binding = p.binding,
+        bindingType = binding.type;
+
+    if (bindingType === 'activiti:in') {
+      inOuts.push(createActivitiIn(
+        binding, p.value, bpmnFactory
+      ));
+    } else
+    if (bindingType === 'activiti:out') {
+      inOuts.push(createActivitiOut(
+        binding, p.value, bpmnFactory
+      ));
+    } else
+    if (bindingType === 'activiti:in:businessKey') {
+      inOuts.push(createActivitiInWithBusinessKey(
+        binding, p.value, bpmnFactory
+      ));
+    }
+  });
+
+  return inOuts;
+}
+
+
+function createActivitiExecutionListeners(template, bpmnFactory) {
+
+  var executionListener = [];
+
+  template.properties.forEach(function(p) {
+    var binding = p.binding,
+        bindingType = binding.type;
+
+    if (bindingType === 'activiti:executionListener') {
+      executionListener.push(createActivitiExecutionListenerScript(
+        binding, p.value, bpmnFactory
+      ));
+    }
+  });
+
+  return executionListener;
+}

+ 46 - 0
ruoyi-bpmnjs/resources/properties-panel/provider/activiti/element-templates/cmd/index.js

@@ -0,0 +1,46 @@
+'use strict';
+
+var ChangeElementTemplateHandler = require('./ChangeElementTemplateHandler');
+
+var getTemplate = require('../Helper').getTemplate,
+    getDefaultTemplate = require('../Helper').getDefaultTemplate;
+
+function registerHandlers(commandStack, elementTemplates, eventBus, elementRegistry) {
+  commandStack.registerHandler(
+    'propertiesPanel.activiti.changeTemplate',
+    ChangeElementTemplateHandler
+  );
+
+  // apply default element templates on shape creation
+  eventBus.on([ 'commandStack.shape.create.postExecuted' ], function(context) {
+    applyDefaultTemplate(context.context.shape, elementTemplates, commandStack);
+  });
+
+  // apply default element templates on connection creation
+  eventBus.on([ 'commandStack.connection.create.postExecuted' ], function(context) {
+    applyDefaultTemplate(context.context.connection, elementTemplates, commandStack);
+  });
+}
+
+registerHandlers.$inject = [ 'commandStack', 'elementTemplates', 'eventBus', 'elementRegistry' ];
+
+
+module.exports = {
+  __init__: [ registerHandlers ]
+};
+
+
+function applyDefaultTemplate(element, elementTemplates, commandStack) {
+
+  if (!getTemplate(element, elementTemplates)
+      && getDefaultTemplate(element, elementTemplates)) {
+
+    var command = 'propertiesPanel.activiti.changeTemplate';
+    var commandContext = {
+      element: element,
+      newTemplate: getDefaultTemplate(element, elementTemplates)
+    };
+
+    commandStack.execute(command, commandContext);
+  }
+}

+ 13 - 0
ruoyi-bpmnjs/resources/properties-panel/provider/activiti/element-templates/index.js

@@ -0,0 +1,13 @@
+module.exports = {
+  __depends__: [
+    require('./cmd/index'),
+    require('diagram-js/lib/i18n/translate').default
+  ],
+  __init__: [
+    'customElementsPropertiesActivator',
+    'elementTemplatesLoader'
+  ],
+  customElementsPropertiesActivator: [ 'type', require('./CustomElementsPropertiesActivator') ],
+  elementTemplates: [ 'type', require('./ElementTemplates') ],
+  elementTemplatesLoader: [ 'type', require('./ElementTemplatesLoader') ]
+};

+ 151 - 0
ruoyi-bpmnjs/resources/properties-panel/provider/activiti/element-templates/parts/ChooserProps.js

@@ -0,0 +1,151 @@
+'use strict';
+
+var entryFactory = require('../../../../factory/EntryFactory'),
+    is = require('bpmn-js/lib/util/ModelUtil').is,
+    getTemplate = require('../Helper').getTemplate,
+    getTemplateId = require('../Helper').getTemplateId;
+
+var find = require('lodash/find');
+
+var TEMPLATE_ATTR = require('../Helper').TEMPLATE_ATTR;
+
+function isAny(element, types) {
+  return types.reduce(function(result, type) {
+    return result || is(element, type);
+  }, false);
+}
+
+
+module.exports = function(group, element, elementTemplates, translate) {
+
+  var options = getTemplateOptions(element, elementTemplates, translate);
+
+  if (options.length === 1 && !options[0].isDefault) {
+    return;
+  }
+
+  // select element template (via dropdown)
+  group.entries.push(entryFactory.selectBox({
+    id: 'elementTemplate-chooser',
+    label: translate('Element Template'),
+    modelProperty: 'activiti:modelerTemplate',
+    selectOptions: options,
+    set: function(element, properties) {
+      return applyTemplate(element, properties[TEMPLATE_ATTR], elementTemplates);
+    },
+    disabled: function() {
+      var template = getTemplate(element, elementTemplates);
+
+      return template && isDefaultTemplate(template);
+    }
+  }));
+
+};
+
+
+// helpers //////////////////////////////////////
+
+function applyTemplate(element, newTemplateId, elementTemplates) {
+
+  // cleanup
+  // clear input output mappings
+  // undo changes to properties defined in template
+
+  // re-establish
+  // set input output mappings
+  // apply changes to properties as defined in new template
+
+  var oldTemplate = getTemplate(element, elementTemplates),
+      newTemplate = elementTemplates.get(newTemplateId);
+
+  if (oldTemplate === newTemplate) {
+    return;
+  }
+
+  return {
+    cmd: 'propertiesPanel.activiti.changeTemplate',
+    context: {
+      element: element,
+      oldTemplate: oldTemplate,
+      newTemplate: newTemplate
+    }
+  };
+}
+
+function getTemplateOptions(element, elementTemplates, translate) {
+
+  var currentTemplateId = getTemplateId(element);
+
+  var emptyOption = {
+    name: '',
+    value: ''
+  };
+
+  var allOptions = elementTemplates.getAll().reduce(function(templates, t) {
+    if (!isAny(element, t.appliesTo)) {
+      return templates;
+    }
+
+    return templates.concat({
+      name: translate(t.name),
+      value: t.id,
+      isDefault: t.isDefault
+    });
+  }, [ emptyOption ]);
+
+
+  var defaultOption = find(allOptions, function(option) {
+    return isDefaultTemplate(option);
+  });
+
+  var currentOption = find(allOptions, function(option) {
+    return option.value === currentTemplateId;
+  });
+
+  if (currentTemplateId && !currentOption) {
+    currentOption = unknownTemplate(currentTemplateId, translate);
+
+    allOptions.push(currentOption);
+  }
+
+  if (!defaultOption) {
+
+    // return all options, including empty
+    // and optionally unknownTemplate option
+    return allOptions;
+  }
+
+  // special limited handling for
+  // default options
+
+  var options = [];
+
+  // current template not set
+  if (!currentTemplateId) {
+    options.push({
+      name: '',
+      value: ''
+    });
+  }
+
+  // current template not default
+  if (currentOption && currentOption !== defaultOption) {
+    options.push(currentOption);
+  }
+
+  options.push(defaultOption);
+
+  // [ (empty), (current), defaultOption ]
+  return options;
+}
+
+function unknownTemplate(templateId, translate) {
+  return {
+    name: translate('[unknown template: {templateId}]', { templateId: templateId }),
+    value: templateId
+  };
+}
+
+function isDefaultTemplate(elementTemplate) {
+  return elementTemplate.isDefault;
+}

+ 770 - 0
ruoyi-bpmnjs/resources/properties-panel/provider/activiti/element-templates/parts/CustomProps.js

@@ -0,0 +1,770 @@
+'use strict';
+
+var assign = require('lodash/assign');
+
+var entryFactory = require('../../../../factory/EntryFactory'),
+    getBusinessObject = require('bpmn-js/lib/util/ModelUtil').getBusinessObject,
+    getTemplate = require('../Helper').getTemplate,
+    cmdHelper = require('../../../../helper/CmdHelper'),
+    elementHelper = require('../../../../helper/ElementHelper');
+
+var findExtension = require('../Helper').findExtension,
+    findExtensions = require('../Helper').findExtensions,
+    findInputParameter = require('../Helper').findInputParameter,
+    findOutputParameter = require('../Helper').findOutputParameter,
+    findActivitiProperty = require('../Helper').findActivitiProperty,
+    findActivitiInOut = require('../Helper').findActivitiInOut;
+
+var createActivitiProperty = require('../CreateHelper').createActivitiProperty,
+    createInputParameter = require('../CreateHelper').createInputParameter,
+    createOutputParameter = require('../CreateHelper').createOutputParameter,
+    createActivitiIn = require('../CreateHelper').createActivitiIn,
+    createActivitiOut = require('../CreateHelper').createActivitiOut,
+    createActivitiInWithBusinessKey = require('../CreateHelper').createActivitiInWithBusinessKey,
+    createActivitiFieldInjection = require('../CreateHelper').createActivitiFieldInjection;
+
+var ACTIVITI_PROPERTY_TYPE = 'activiti:property',
+    ACTIVITI_INPUT_PARAMETER_TYPE = 'activiti:inputParameter',
+    ACTIVITI_OUTPUT_PARAMETER_TYPE = 'activiti:outputParameter',
+    ACTIVITI_IN_TYPE = 'activiti:in',
+    ACTIVITI_OUT_TYPE = 'activiti:out',
+    ACTIVITI_IN_BUSINESS_KEY_TYPE = 'activiti:in:businessKey',
+    ACTIVITI_EXECUTION_LISTENER_TYPE = 'activiti:executionListener',
+    ACTIVITI_FIELD = 'activiti:field';
+
+var BASIC_MODDLE_TYPES = [
+  'Boolean',
+  'Integer',
+  'String'
+];
+
+var EXTENSION_BINDING_TYPES = [
+  ACTIVITI_PROPERTY_TYPE,
+  ACTIVITI_INPUT_PARAMETER_TYPE,
+  ACTIVITI_OUTPUT_PARAMETER_TYPE,
+  ACTIVITI_IN_TYPE,
+  ACTIVITI_OUT_TYPE,
+  ACTIVITI_IN_BUSINESS_KEY_TYPE,
+  ACTIVITI_FIELD
+];
+
+var IO_BINDING_TYPES = [
+  ACTIVITI_INPUT_PARAMETER_TYPE,
+  ACTIVITI_OUTPUT_PARAMETER_TYPE
+];
+
+var IN_OUT_BINDING_TYPES = [
+  ACTIVITI_IN_TYPE,
+  ACTIVITI_OUT_TYPE,
+  ACTIVITI_IN_BUSINESS_KEY_TYPE
+];
+
+/**
+ * Injects custom properties into the given group.
+ *
+ * @param {djs.model.Base} element
+ * @param {ElementTemplates} elementTemplates
+ * @param {BpmnFactory} bpmnFactory
+ * @param {Function} translate
+ */
+module.exports = function(element, elementTemplates, bpmnFactory, translate) {
+
+  var template = getTemplate(element, elementTemplates);
+
+  if (!template) {
+    return [];
+  }
+
+  var renderCustomField = function(id, p, idx) {
+    var propertyType = p.type;
+
+    var entryOptions = {
+      id: id,
+      description: p.description,
+      label: p.label ? translate(p.label) : p.label,
+      modelProperty: id,
+      get: propertyGetter(id, p),
+      set: propertySetter(id, p, bpmnFactory),
+      validate: propertyValidator(id, p, translate)
+    };
+
+    var entry;
+
+    if (propertyType === 'Boolean') {
+      entry = entryFactory.checkbox(entryOptions);
+    }
+
+    if (propertyType === 'String') {
+      entry = entryFactory.textField(entryOptions);
+    }
+
+    if (propertyType === 'Text') {
+      entry = entryFactory.textBox(entryOptions);
+    }
+
+    if (propertyType === 'Dropdown') {
+      entryOptions.selectOptions = p.choices;
+
+      entry = entryFactory.selectBox(entryOptions);
+    }
+
+    return entry;
+  };
+
+  var groups = [];
+  var id, entry;
+
+  var customFieldsGroup = {
+    id: 'customField',
+    label: translate('Custom Fields'),
+    entries: []
+  };
+  template.properties.forEach(function(p, idx) {
+
+    id = 'custom-' + template.id + '-' + idx;
+
+    entry = renderCustomField(id, p, idx);
+    if (entry) {
+      customFieldsGroup.entries.push(entry);
+    }
+  });
+  if (customFieldsGroup.entries.length > 0) {
+    groups.push(customFieldsGroup);
+  }
+
+  if (template.scopes) {
+    for (var scopeName in template.scopes) {
+
+      var scope = template.scopes[scopeName];
+      var idScopeName = scopeName.replace(/:/g, '_');
+
+      var customScopeFieldsGroup = {
+        id: 'customField-' + idScopeName,
+        label: translate('Custom Fields for scope: ') + scopeName,
+        entries: []
+      };
+
+      scope.properties.forEach(function(p, idx) {
+
+        var propertyId = 'custom-' + template.id + '-' + idScopeName + '-' + idx;
+
+        var scopedProperty = propertyWithScope(p, scopeName);
+
+        entry = renderCustomField(propertyId, scopedProperty, idx);
+        if (entry) {
+          customScopeFieldsGroup.entries.push(entry);
+        }
+      });
+
+      if (customScopeFieldsGroup.entries.length > 0) {
+        groups.push(customScopeFieldsGroup);
+      }
+    }
+  }
+
+  return groups;
+};
+
+
+// getters, setters and validators ///////////////
+
+
+/**
+ * Return a getter that retrieves the given property.
+ *
+ * @param {String} name
+ * @param {PropertyDescriptor} property
+ *
+ * @return {Function}
+ */
+function propertyGetter(name, property) {
+
+  /* getter */
+  return function get(element) {
+    var value = getPropertyValue(element, property);
+
+    return objectWithKey(name, value);
+  };
+}
+
+/**
+ * Return a setter that updates the given property.
+ *
+ * @param {String} name
+ * @param {PropertyDescriptor} property
+ * @param {BpmnFactory} bpmnFactory
+ *
+ * @return {Function}
+ */
+function propertySetter(name, property, bpmnFactory) {
+
+  /* setter */
+  return function set(element, values) {
+
+    var value = values[name];
+
+    return setPropertyValue(element, property, value, bpmnFactory);
+  };
+}
+
+/**
+ * Return a validator that ensures the property is ok.
+ *
+ * @param {String} name
+ * @param {PropertyDescriptor} property
+ * @param {Function} translate
+ *
+ * @return {Function}
+ */
+function propertyValidator(name, property, translate) {
+
+  /* validator */
+  return function validate(element, values) {
+    var value = values[name];
+
+    var error = validateValue(value, property, translate);
+
+    if (error) {
+      return objectWithKey(name, error);
+    }
+  };
+}
+
+
+// get, set and validate helpers ///////////////////
+
+/**
+ * Return the value of the specified property descriptor,
+ * on the passed diagram element.
+ *
+ * @param {djs.model.Base} element
+ * @param {PropertyDescriptor} property
+ *
+ * @return {Any}
+ */
+function getPropertyValue(element, property) {
+
+  var bo = getBusinessObject(element);
+
+  var binding = property.binding,
+      scope = property.scope;
+
+  var bindingType = binding.type,
+      bindingName = binding.name;
+
+  var propertyValue = property.value || '';
+
+  if (scope) {
+    bo = findExtension(bo, scope.name);
+    if (!bo) {
+      return propertyValue;
+    }
+  }
+
+  // property
+  if (bindingType === 'property') {
+
+    var value = bo.get(bindingName);
+
+    if (bindingName === 'conditionExpression') {
+      if (value) {
+        return value.body;
+      } else {
+        // return defined default
+        return propertyValue;
+      }
+    } else {
+      // return value; default to defined default
+      return typeof value !== 'undefined' ? value : propertyValue;
+    }
+  }
+
+  var activitiProperties,
+      activitiProperty;
+
+  if (bindingType === ACTIVITI_PROPERTY_TYPE) {
+    if (scope) {
+      activitiProperties = bo.get('properties');
+    } else {
+      activitiProperties = findExtension(bo, 'activiti:Properties');
+    }
+
+    if (activitiProperties) {
+      activitiProperty = findActivitiProperty(activitiProperties, binding);
+
+      if (activitiProperty) {
+        return activitiProperty.value;
+      }
+    }
+
+    return propertyValue;
+  }
+
+  var inputOutput,
+      ioParameter;
+
+  if (IO_BINDING_TYPES.indexOf(bindingType) !== -1) {
+
+    if (scope) {
+      inputOutput = bo.get('inputOutput');
+    } else {
+      inputOutput = findExtension(bo, 'activiti:InputOutput');
+    }
+
+    if (!inputOutput) {
+      // ioParameter cannot exist yet, return property value
+      return propertyValue;
+    }
+  }
+
+  // activiti input parameter
+  if (bindingType === ACTIVITI_INPUT_PARAMETER_TYPE) {
+    ioParameter = findInputParameter(inputOutput, binding);
+
+    if (ioParameter) {
+      if (binding.scriptFormat) {
+        if (ioParameter.definition) {
+          return ioParameter.definition.value;
+        }
+      } else {
+        return ioParameter.value || '';
+      }
+    }
+
+    return propertyValue;
+  }
+
+  // activiti output parameter
+  if (binding.type === ACTIVITI_OUTPUT_PARAMETER_TYPE) {
+    ioParameter = findOutputParameter(inputOutput, binding);
+
+    if (ioParameter) {
+      return ioParameter.name;
+    }
+
+    return propertyValue;
+  }
+
+
+  var ioElement;
+
+  if (IN_OUT_BINDING_TYPES.indexOf(bindingType) != -1) {
+    ioElement = findActivitiInOut(bo, binding);
+
+    if (ioElement) {
+      if (bindingType === ACTIVITI_IN_BUSINESS_KEY_TYPE) {
+        return ioElement.businessKey;
+      } else
+      if (bindingType === ACTIVITI_OUT_TYPE) {
+        return ioElement.target;
+      } else
+      if (bindingType === ACTIVITI_IN_TYPE) {
+        return ioElement[binding.expression ? 'sourceExpression' : 'source'];
+      }
+    }
+
+    return propertyValue;
+  }
+
+  if (bindingType === ACTIVITI_EXECUTION_LISTENER_TYPE) {
+    var executionListener;
+    if (scope) {
+      executionListener = bo.get('executionListener');
+    } else {
+      executionListener = findExtension(bo, 'activiti:ExecutionListener');
+    }
+
+    return executionListener.script.value;
+  }
+
+  var fieldInjection;
+  if (ACTIVITI_FIELD === bindingType) {
+    var fieldInjections = findExtensions(bo, [ 'activiti:Field' ]);
+    fieldInjections.forEach(function(item) {
+      if (item.name === binding.name) {
+        fieldInjection = item;
+      }
+    });
+    if (fieldInjection) {
+      return fieldInjection.string || fieldInjection.expression;
+    } else {
+      return '';
+    }
+  }
+
+  throw unknownPropertyBinding(property);
+}
+
+module.exports.getPropertyValue = getPropertyValue;
+
+
+/**
+ * Return an update operation that changes the diagram
+ * element's custom property to the given value.
+ *
+ * The response of this method will be processed via
+ * {@link PropertiesPanel#applyChanges}.
+ *
+ * @param {djs.model.Base} element
+ * @param {PropertyDescriptor} property
+ * @param {String} value
+ * @param {BpmnFactory} bpmnFactory
+ *
+ * @return {Object|Array<Object>} results to be processed
+ */
+function setPropertyValue(element, property, value, bpmnFactory) {
+  var bo = getBusinessObject(element);
+
+  var binding = property.binding,
+      scope = property.scope;
+
+  var bindingType = binding.type,
+      bindingName = binding.name;
+
+  var propertyValue;
+
+  var updates = [];
+
+  var extensionElements;
+
+  if (EXTENSION_BINDING_TYPES.indexOf(bindingType) !== -1) {
+    extensionElements = bo.get('extensionElements');
+
+    // create extension elements, if they do not exist (yet)
+    if (!extensionElements) {
+      extensionElements = elementHelper.createElement('bpmn:ExtensionElements', null, element, bpmnFactory);
+
+      updates.push(cmdHelper.updateBusinessObject(
+        element, bo, objectWithKey('extensionElements', extensionElements)
+      ));
+    }
+  }
+
+  if (scope) {
+    bo = findExtension(bo, scope.name);
+    if (!bo) {
+      bo = elementHelper.createElement(scope.name, null, element, bpmnFactory);
+
+      updates.push(cmdHelper.addElementsTolist(
+        bo, extensionElements, 'values', [ bo ]
+      ));
+    }
+  }
+
+  // property
+  if (bindingType === 'property') {
+
+    if (bindingName === 'conditionExpression') {
+
+      propertyValue = elementHelper.createElement('bpmn:FormalExpression', {
+        body: value,
+        language: binding.scriptFormat
+      }, bo, bpmnFactory);
+    } else {
+
+      var moddlePropertyDescriptor = bo.$descriptor.propertiesByName[bindingName];
+
+      var moddleType = moddlePropertyDescriptor.type;
+
+      // make sure we only update String, Integer, Real and
+      // Boolean properties (do not accidentally override complex objects...)
+      if (BASIC_MODDLE_TYPES.indexOf(moddleType) === -1) {
+        throw new Error('cannot set moddle type <' + moddleType + '>');
+      }
+
+      if (moddleType === 'Boolean') {
+        propertyValue = !!value;
+      } else
+      if (moddleType === 'Integer') {
+        propertyValue = parseInt(value, 10);
+
+        if (isNaN(propertyValue)) {
+          // do not write NaN value
+          propertyValue = undefined;
+        }
+      } else {
+        propertyValue = value;
+      }
+    }
+
+    if (propertyValue !== undefined) {
+      updates.push(cmdHelper.updateBusinessObject(
+        element, bo, objectWithKey(bindingName, propertyValue)
+      ));
+    }
+  }
+
+  // activiti:property
+  var activitiProperties,
+      existingActivitiProperty,
+      newActivitiProperty;
+
+  if (bindingType === ACTIVITI_PROPERTY_TYPE) {
+
+    if (scope) {
+      activitiProperties = bo.get('properties');
+    } else {
+      activitiProperties = findExtension(extensionElements, 'activiti:Properties');
+    }
+
+    if (!activitiProperties) {
+      activitiProperties = elementHelper.createElement('activiti:Properties', null, bo, bpmnFactory);
+
+      if (scope) {
+        updates.push(cmdHelper.updateBusinessObject(
+          element, bo, { properties: activitiProperties }
+        ));
+      }
+      else {
+        updates.push(cmdHelper.addElementsTolist(
+          element, extensionElements, 'values', [ activitiProperties ]
+        ));
+      }
+    }
+
+    existingActivitiProperty = findActivitiProperty(activitiProperties, binding);
+
+    newActivitiProperty = createActivitiProperty(binding, value, bpmnFactory);
+
+    updates.push(cmdHelper.addAndRemoveElementsFromList(
+      element,
+      activitiProperties,
+      'values',
+      null,
+      [ newActivitiProperty ],
+      existingActivitiProperty ? [ existingActivitiProperty ] : []
+    ));
+  }
+
+  // activiti:inputParameter
+  // activiti:outputParameter
+  var inputOutput,
+      existingIoParameter,
+      newIoParameter;
+
+  if (IO_BINDING_TYPES.indexOf(bindingType) !== -1) {
+
+    if (scope) {
+      inputOutput = bo.get('inputOutput');
+    } else {
+      inputOutput = findExtension(extensionElements, 'activiti:InputOutput');
+    }
+
+    // create inputOutput element, if it do not exist (yet)
+    if (!inputOutput) {
+      inputOutput = elementHelper.createElement('activiti:InputOutput', null, bo, bpmnFactory);
+
+      if (scope) {
+        updates.push(cmdHelper.updateBusinessObject(
+          element, bo, { inputOutput: inputOutput }
+        ));
+      }
+      else {
+        updates.push(cmdHelper.addElementsTolist(
+          element, extensionElements, 'values', inputOutput
+        ));
+      }
+    }
+  }
+
+  if (bindingType === ACTIVITI_INPUT_PARAMETER_TYPE) {
+
+    existingIoParameter = findInputParameter(inputOutput, binding);
+
+    newIoParameter = createInputParameter(binding, value, bpmnFactory);
+
+    updates.push(cmdHelper.addAndRemoveElementsFromList(
+      element,
+      inputOutput,
+      'inputParameters',
+      null,
+      [ newIoParameter ],
+      existingIoParameter ? [ existingIoParameter ] : []
+    ));
+  }
+
+  if (bindingType === ACTIVITI_OUTPUT_PARAMETER_TYPE) {
+
+    existingIoParameter = findOutputParameter(inputOutput, binding);
+
+    newIoParameter = createOutputParameter(binding, value, bpmnFactory);
+
+    updates.push(cmdHelper.addAndRemoveElementsFromList(
+      element,
+      inputOutput,
+      'outputParameters',
+      null,
+      [ newIoParameter ],
+      existingIoParameter ? [ existingIoParameter ] : []
+    ));
+  }
+
+
+  // activiti:in
+  // activiti:out
+  // activiti:in:businessKey
+  var existingInOut,
+      newInOut;
+
+  if (IN_OUT_BINDING_TYPES.indexOf(bindingType) !== -1) {
+
+    existingInOut = findActivitiInOut(bo, binding);
+
+    if (bindingType === ACTIVITI_IN_TYPE) {
+      newInOut = createActivitiIn(binding, value, bpmnFactory);
+    } else
+    if (bindingType === ACTIVITI_OUT_TYPE) {
+      newInOut = createActivitiOut(binding, value, bpmnFactory);
+    } else {
+      newInOut = createActivitiInWithBusinessKey(binding, value, bpmnFactory);
+    }
+
+    updates.push(cmdHelper.addAndRemoveElementsFromList(
+      element,
+      extensionElements,
+      'values',
+      null,
+      [ newInOut ],
+      existingInOut ? [ existingInOut ] : []
+    ));
+  }
+
+  if (bindingType === ACTIVITI_FIELD) {
+    var existingFieldInjections = findExtensions(bo, [ 'activiti:Field' ]);
+    var newFieldInjections = [];
+
+    if (existingFieldInjections.length > 0) {
+      existingFieldInjections.forEach(function(item) {
+        if (item.name === binding.name) {
+          newFieldInjections.push(createActivitiFieldInjection(binding, value, bpmnFactory));
+        } else {
+          newFieldInjections.push(item);
+        }
+      });
+    } else {
+      newFieldInjections.push(createActivitiFieldInjection(binding, value, bpmnFactory));
+    }
+
+    updates.push(cmdHelper.addAndRemoveElementsFromList(
+      element,
+      extensionElements,
+      'values',
+      null,
+      newFieldInjections,
+      existingFieldInjections ? existingFieldInjections : []
+    ));
+  }
+
+  if (updates.length) {
+    return updates;
+  }
+
+  // quick warning for better debugging
+  console.warn('no update', element, property, value);
+}
+
+module.exports.setPropertyValue = setPropertyValue;
+
+/**
+ * Validate value of a given property.
+ *
+ * @param {String} value
+ * @param {PropertyDescriptor} property
+ * @param {Function} translate
+ *
+ * @return {Object} with validation errors
+ */
+function validateValue(value, property, translate) {
+
+  var constraints = property.constraints || {};
+
+  if (constraints.notEmpty && isEmpty(value)) {
+    return translate('Must not be empty');
+  }
+
+  if (constraints.maxLength && value.length > constraints.maxLength) {
+    return translate('Must have max length {length}', { length: constraints.maxLength });
+  }
+
+  if (constraints.minLength && value.length < constraints.minLength) {
+    return translate('Must have min length {length}', { length: constraints.minLength });
+  }
+
+  var pattern = constraints.pattern,
+      message;
+
+  if (pattern) {
+
+    if (typeof pattern !== 'string') {
+      message = pattern.message;
+      pattern = pattern.value;
+    }
+
+    if (!matchesPattern(value, pattern)) {
+      return message || translate('Must match pattern {pattern}', { pattern: pattern });
+    }
+  }
+}
+
+
+// misc helpers ///////////////////////////////
+
+function propertyWithScope(property, scopeName) {
+  if (!scopeName) {
+    return property;
+  }
+
+  return assign({}, property, {
+    scope: {
+      name: scopeName
+    }
+  });
+}
+
+/**
+ * Return an object with a single key -> value association.
+ *
+ * @param {String} key
+ * @param {Any} value
+ *
+ * @return {Object}
+ */
+function objectWithKey(key, value) {
+  var obj = {};
+
+  obj[key] = value;
+
+  return obj;
+}
+
+/**
+ * Does the given string match the specified pattern?
+ *
+ * @param {String} str
+ * @param {String} pattern
+ *
+ * @return {Boolean}
+ */
+function matchesPattern(str, pattern) {
+  var regexp = new RegExp(pattern);
+
+  return regexp.test(str);
+}
+
+function isEmpty(str) {
+  return !str || /^\s*$/.test(str);
+}
+
+/**
+ * Create a new {@link Error} indicating an unknown
+ * property binding.
+ *
+ * @param {PropertyDescriptor} property
+ *
+ * @return {Error}
+ */
+function unknownPropertyBinding(property) {
+  var binding = property.binding;
+
+  return new Error('unknown binding: <' + binding.type + '>');
+}

+ 16 - 0
ruoyi-bpmnjs/resources/properties-panel/provider/activiti/element-templates/util/validate.js

@@ -0,0 +1,16 @@
+'use strict';
+
+var Validator = require('../Validator');
+
+/**
+ * Validate the given template descriptors and
+ * return a list of errors.
+ *
+ * @param {Array<TemplateDescriptor>} descriptors
+ *
+ * @return {Array<Error>}
+ */
+module.exports = function validate(descriptors) {
+
+  return new Validator().addAll(descriptors).getErrors();
+};

+ 9 - 0
ruoyi-bpmnjs/resources/properties-panel/provider/activiti/index.js

@@ -0,0 +1,9 @@
+module.exports = {
+  __depends__: [
+    require('./element-templates/index'),
+    require('diagram-js/lib/i18n/translate').default
+  ],
+  __init__: [ 'propertiesProvider' ],
+  propertiesProvider: [ 'type', require('./ActivitiPropertiesProvider') ]
+};
+

+ 16 - 0
ruoyi-bpmnjs/resources/properties-panel/provider/activiti/parts/AsynchronousContinuationProps.js

@@ -0,0 +1,16 @@
+'use strict';
+
+var getBusinessObject = require('bpmn-js/lib/util/ModelUtil').getBusinessObject,
+    is = require('bpmn-js/lib/util/ModelUtil').is,
+    asyncContinuation = require('./implementation/AsyncContinuation');
+
+module.exports = function(group, element, bpmnFactory, translate) {
+
+  if (is(element, 'activiti:AsyncCapable')) {
+
+    group.entries = group.entries.concat(asyncContinuation(element, bpmnFactory, {
+      getBusinessObject: getBusinessObject
+    }, translate));
+
+  }
+};

+ 90 - 0
ruoyi-bpmnjs/resources/properties-panel/provider/activiti/parts/CallActivityProps.js

@@ -0,0 +1,90 @@
+'use strict';
+
+var getBusinessObject = require('bpmn-js/lib/util/ModelUtil').getBusinessObject,
+    is = require('bpmn-js/lib/util/ModelUtil').is;
+
+var entryFactory = require('../../../factory/EntryFactory');
+
+var callable = require('./implementation/Callable');
+
+var cmdHelper = require('../../../helper/CmdHelper');
+
+var flattenDeep = require('lodash/flattenDeep');
+var assign = require('lodash/assign');
+
+function getCallableType(element) {
+  var bo = getBusinessObject(element);
+
+  var boCalledElement = bo.get('calledElement'),
+      boCaseRef = bo.get('activiti:caseRef');
+
+  var callActivityType = '';
+  if (typeof boCalledElement !== 'undefined') {
+    callActivityType = 'bpmn';
+  } else
+
+  if (typeof boCaseRef !== 'undefined') {
+    callActivityType = 'cmmn';
+  }
+
+  return callActivityType;
+}
+
+var DEFAULT_PROPS = {
+  calledElement: undefined,
+  'activiti:calledElementBinding': 'latest',
+  'activiti:calledElementVersion': undefined,
+  'activiti:calledElementTenantId': undefined,
+  'activiti:variableMappingClass' : undefined,
+  'activiti:variableMappingDelegateExpression' : undefined,
+  'activiti:caseRef': undefined,
+  'activiti:caseBinding': 'latest',
+  'activiti:caseVersion': undefined,
+  'activiti:caseTenantId': undefined
+};
+
+module.exports = function(group, element, bpmnFactory, translate) {
+
+  if (!is(element, 'activiti:CallActivity')) {
+    return;
+  }
+
+  group.entries.push(entryFactory.selectBox({
+    id : 'callActivity',
+    label: translate('CallActivity Type'),
+    selectOptions: [
+      { name: 'BPMN', value: 'bpmn' },
+      { name: 'CMMN', value: 'cmmn' }
+    ],
+    emptyParameter: true,
+    modelProperty: 'callActivityType',
+
+    get: function(element, node) {
+      return {
+        callActivityType: getCallableType(element)
+      };
+    },
+
+    set: function(element, values, node) {
+      var type = values.callActivityType;
+
+      var props = assign({}, DEFAULT_PROPS);
+
+      if (type === 'bpmn') {
+        props.calledElement = '';
+      }
+      else if (type === 'cmmn') {
+        props['activiti:caseRef'] = '';
+      }
+
+      return cmdHelper.updateProperties(element, props);
+    }
+
+  }));
+
+  group.entries.push(callable(element, bpmnFactory, {
+    getCallableType: getCallableType
+  }, translate));
+
+  group.entries = flattenDeep(group.entries);
+};

+ 27 - 0
ruoyi-bpmnjs/resources/properties-panel/provider/activiti/parts/CandidateStarterProps.js

@@ -0,0 +1,27 @@
+'use strict';
+
+var is = require('bpmn-js/lib/util/ModelUtil').is,
+    getBusinessObject = require('bpmn-js/lib/util/ModelUtil').getBusinessObject;
+
+var candidateStarter = require('./implementation/CandidateStarter');
+
+module.exports = function(group, element, bpmnFactory, translate) {
+  var businessObject = getBusinessObject(element);
+
+  if (is(element, 'activiti:Process') ||
+      is(element, 'bpmn:Participant') && businessObject.get('processRef')) {
+
+    group.entries = group.entries.concat(candidateStarter(element, bpmnFactory, {
+      getBusinessObject: function(element) {
+        var bo = getBusinessObject(element);
+
+        if (!is(bo, 'bpmn:Participant')) {
+          return bo;
+        }
+
+        return bo.get('processRef');
+      }
+    }, translate));
+
+  }
+};

+ 185 - 0
ruoyi-bpmnjs/resources/properties-panel/provider/activiti/parts/ConditionalProps.js

@@ -0,0 +1,185 @@
+'use strict';
+
+var is = require('bpmn-js/lib/util/ModelUtil').is,
+    isAny = require('bpmn-js/lib/features/modeling/util/ModelingUtil').isAny,
+    getBusinessObject = require('bpmn-js/lib/util/ModelUtil').getBusinessObject,
+    escapeHTML = require('../../../Utils').escapeHTML,
+    domQuery = require('min-dom').query,
+    cmdHelper = require('../../../helper/CmdHelper'),
+    elementHelper = require('../../../helper/ElementHelper'),
+    eventDefinitionHelper = require('../../../helper/EventDefinitionHelper'),
+    scriptImplementation = require('./implementation/Script');
+
+
+module.exports = function(group, element, bpmnFactory, translate) {
+  var bo = getBusinessObject(element);
+
+  if (!bo) {
+    return;
+  }
+
+  var conditionalEventDefinition = eventDefinitionHelper.getConditionalEventDefinition(element);
+
+  if (!(is(element, 'bpmn:SequenceFlow') && isConditionalSource(element.source))
+    && !conditionalEventDefinition) {
+    return;
+  }
+
+  var script = scriptImplementation('language', 'body', true, translate);
+  group.entries.push({
+    id: 'condition',
+    label: translate('Condition'),
+    html: '<div class="bpp-row">' +
+              '<label for="cam-condition-type">'+ escapeHTML(translate('Condition Type')) + '</label>' +
+              '<div class="bpp-field-wrapper">' +
+                '<select id="cam-condition-type" name="conditionType" data-value>' +
+                  '<option value="expression">'+ escapeHTML(translate('Expression')) + '</option>' +
+                  '<option value="script">'+ escapeHTML(translate('Script')) + '</option>' +
+                  '<option value="" selected></option>' +
+                '</select>' +
+              '</div>' +
+            '</div>' +
+
+            // expression
+            '<div class="bpp-row">' +
+              '<label for="cam-condition" data-show="isExpression">' + escapeHTML(translate('Expression')) + '</label>' +
+              '<div class="bpp-field-wrapper" data-show="isExpression">' +
+                '<input id="cam-condition" type="text" name="condition" />' +
+                '<button class="clear" data-action="clear" data-show="canClear">' +
+                  '<span>X</span>' +
+                '</button>' +
+              '</div>' +
+              '<div data-show="isScript">' +
+                script.template +
+              '</div>' +
+            '</div>',
+
+    get: function(element, propertyName) {
+      var conditionalEventDefinition = eventDefinitionHelper.getConditionalEventDefinition(element);
+
+      var conditionExpression = conditionalEventDefinition
+        ? conditionalEventDefinition.condition
+        : bo.conditionExpression;
+
+      var values = {},
+          conditionType = '';
+
+      if (conditionExpression) {
+        var conditionLanguage = conditionExpression.language;
+        if (typeof conditionLanguage !== 'undefined') {
+          conditionType = 'script';
+          values = script.get(element, conditionExpression);
+        } else {
+          conditionType = 'expression';
+          values.condition = conditionExpression.get('body');
+        }
+      }
+
+      values.conditionType = conditionType;
+
+      return values;
+
+    },
+
+    set: function(element, values, containerElement) {
+      var conditionType = values.conditionType;
+      var commands = [];
+
+      var conditionProps = {
+        body: undefined
+      };
+
+      if (conditionType === 'script') {
+        conditionProps = script.set(element, values, containerElement);
+      } else {
+        var condition = values.condition;
+
+        conditionProps.body = condition;
+      }
+
+      var conditionOrConditionExpression;
+
+      if (conditionType) {
+        conditionOrConditionExpression = elementHelper.createElement(
+          'bpmn:FormalExpression',
+          conditionProps,
+          conditionalEventDefinition || bo,
+          bpmnFactory
+        );
+
+        var source = element.source;
+
+        // if default-flow, remove default-property from source
+        if (source && source.businessObject.default === bo) {
+          commands.push(cmdHelper.updateProperties(source, { 'default': undefined }));
+        }
+      }
+
+      var update = conditionalEventDefinition
+        ? { condition: conditionOrConditionExpression }
+        : { conditionExpression: conditionOrConditionExpression };
+
+      commands.push(cmdHelper.updateBusinessObject(element, conditionalEventDefinition || bo, update));
+
+      return commands;
+    },
+
+    validate: function(element, values) {
+      var validationResult = {};
+
+      if (!values.condition && values.conditionType === 'expression') {
+        validationResult.condition = translate('Must provide a value');
+      }
+      else if (values.conditionType === 'script') {
+        validationResult = script.validate(element, values);
+      }
+
+      return validationResult;
+    },
+
+    isExpression: function(element, inputNode) {
+      var conditionType = domQuery('select[name=conditionType]', inputNode);
+      if (conditionType.selectedIndex >= 0) {
+        return conditionType.options[conditionType.selectedIndex].value === 'expression';
+      }
+    },
+
+    isScript: function(element, inputNode) {
+      var conditionType = domQuery('select[name=conditionType]', inputNode);
+      if (conditionType.selectedIndex >= 0) {
+        return conditionType.options[conditionType.selectedIndex].value === 'script';
+      }
+    },
+
+    clear: function(element, inputNode) {
+      // clear text input
+      domQuery('input[name=condition]', inputNode).value='';
+
+      return true;
+    },
+
+    canClear: function(element, inputNode) {
+      var input = domQuery('input[name=condition]', inputNode);
+
+      return input.value !== '';
+    },
+
+    script : script,
+
+    cssClasses: [ 'bpp-textfield' ]
+  });
+};
+
+
+// utilities //////////////////////////
+
+var CONDITIONAL_SOURCES = [
+  'bpmn:Activity',
+  'bpmn:ExclusiveGateway',
+  'bpmn:InclusiveGateway',
+  'bpmn:ComplexGateway'
+];
+
+function isConditionalSource(element) {
+  return isAny(element, CONDITIONAL_SOURCES);
+}

+ 57 - 0
ruoyi-bpmnjs/resources/properties-panel/provider/activiti/parts/ConnectorDetailProps.js

@@ -0,0 +1,57 @@
+'use strict';
+
+var ImplementationTypeHelper = require('../../../helper/ImplementationTypeHelper'),
+    InputOutputHelper = require('../../../helper/InputOutputHelper');
+
+var entryFactory = require('../../../factory/EntryFactory'),
+    cmdHelper = require('../../../helper/CmdHelper');
+
+function getImplementationType(element) {
+  return ImplementationTypeHelper.getImplementationType(element);
+}
+
+function getBusinessObject(element) {
+  return ImplementationTypeHelper.getServiceTaskLikeBusinessObject(element);
+}
+
+function getConnector(bo) {
+  return InputOutputHelper.getConnector(bo);
+}
+
+function isConnector(element) {
+  return getImplementationType(element) === 'connector';
+}
+
+module.exports = function(group, element, bpmnFactory, translate) {
+
+  group.entries.push(entryFactory.textField({
+    id: 'connectorId',
+    label: translate('Connector Id'),
+    modelProperty: 'connectorId',
+
+    get: function(element, node) {
+      var bo = getBusinessObject(element);
+      var connector = bo && getConnector(bo);
+      var value = connector && connector.get('connectorId');
+      return { connectorId: value };
+    },
+
+    set: function(element, values, node) {
+      var bo = getBusinessObject(element);
+      var connector = getConnector(bo);
+      return cmdHelper.updateBusinessObject(element, connector, {
+        connectorId: values.connectorId || undefined
+      });
+    },
+
+    validate: function(element, values, node) {
+      return isConnector(element) && !values.connectorId ? { connectorId: translate('Must provide a value') } : {};
+    },
+
+    hidden: function(element, node) {
+      return !isConnector(element);
+    }
+
+  }));
+
+};

+ 16 - 0
ruoyi-bpmnjs/resources/properties-panel/provider/activiti/parts/ConnectorInputOutputParameterProps.js

@@ -0,0 +1,16 @@
+'use strict';
+
+var assign = require('lodash/assign');
+
+var inputOutputParameter = require('./implementation/InputOutputParameter');
+
+module.exports = function(group, element, bpmnFactory, options, translate) {
+
+  options = assign({
+    idPrefix: 'connector-',
+    insideConnector: true
+  }, options);
+
+  group.entries = group.entries.concat(inputOutputParameter(element, bpmnFactory, options, translate));
+
+};

+ 18 - 0
ruoyi-bpmnjs/resources/properties-panel/provider/activiti/parts/ConnectorInputOutputProps.js

@@ -0,0 +1,18 @@
+'use strict';
+
+var inputOutput = require('./implementation/InputOutput');
+
+module.exports = function(group, element, bpmnFactory, translate) {
+
+  var inputOutputEntry = inputOutput(element, bpmnFactory, {
+    idPrefix: 'connector-',
+    insideConnector: true
+  }, translate);
+
+  group.entries = group.entries.concat(inputOutputEntry.entries);
+
+  return {
+    getSelectedParameter: inputOutputEntry.getSelectedParameter
+  };
+
+};

+ 40 - 0
ruoyi-bpmnjs/resources/properties-panel/provider/activiti/parts/ErrorEventProps.js

@@ -0,0 +1,40 @@
+'use strict';
+
+var is = require('bpmn-js/lib/util/ModelUtil').is,
+    eventDefinitionHelper = require('../../../helper/EventDefinitionHelper'),
+    error = require('./implementation/ErrorEventDefinition');
+
+var forEach = require('lodash/forEach');
+
+
+module.exports = function(group, element, bpmnFactory, translate) {
+
+  var errorEvents = [
+    'bpmn:StartEvent',
+    'bpmn:BoundaryEvent',
+    'bpmn:EndEvent'
+  ];
+
+  forEach(errorEvents, function(event) {
+    if (is(element, event)) {
+
+      var errorEventDefinition = eventDefinitionHelper.getErrorEventDefinition(element);
+
+      if (errorEventDefinition) {
+        var isCatchingErrorEvent = is(element, 'bpmn:StartEvent') || is (element, 'bpmn:BoundaryEvent');
+
+        var showErrorCodeVariable = isCatchingErrorEvent,
+            showErrorMessageVariable = isCatchingErrorEvent;
+
+        error(
+          group,
+          element,
+          bpmnFactory,
+          errorEventDefinition,
+          showErrorCodeVariable,
+          showErrorMessageVariable,
+          translate);
+      }
+    }
+  });
+};

Vissa filer visades inte eftersom för många filer har ändrats