houzhongjian
2024-08-02 57bbe04034c43a53682e7f8b24e38de1e48bf7a8
1、全局配置文件修改
2、工作流bpm功能修改
已修改39个文件
已删除1个文件
已添加5个文件
2371 ■■■■■ 文件已修改
.env.stage 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
package.json 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/bpm/processExpression.js 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/bpm/processInstance.js 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/bpm/task.js 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/bpm/userGroup.js 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/logo/login-logo.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/logo/logo.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/logo/logo_rec.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/styles/login.scss 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/SelectTree/index.vue 299 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/bpmnProcessDesigner/package/designer/ProcessViewer.vue 126 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/bpmnProcessDesigner/package/penal/PropertiesPanel.vue 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/bpmnProcessDesigner/package/penal/base/ElementBaseInfo.vue 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/bpmnProcessDesigner/package/penal/form/ElementForm.vue 352 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/bpmnProcessDesigner/package/penal/form/ElementForm_rec.vue 361 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/bpmnProcessDesigner/package/penal/task/ElementTask.vue 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/bpmnProcessDesigner/package/penal/task/task-components/ProcessExpressionDialog.vue 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/bpmnProcessDesigner/package/penal/task/task-components/UserTask.vue 321 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main.js 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/dict.js 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/formGenerator.js 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/bpm/definition/index.vue 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/bpm/model/index.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/bpm/processInstance/create/index.vue 108 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/bpm/processInstance/detail.vue 151 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/bpm/processInstance/index.vue 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/bpm/task/done/index.vue 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/bpm/task/todo/index.vue 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/infra/apiAccessLog/index.vue 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/infra/apiErrorLog/index.vue 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/infra/config/index.vue 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/infra/job/index_rec.vue 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/system/dict/data.vue 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/system/dict/index.vue 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/system/loginlog/index.vue 21 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/system/oauth2/client/index.vue 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/system/operatelog/index.vue 149 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/system/post/index.vue 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/system/role/index.vue 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/system/sms/log/index.vue 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/system/sms/template/index.vue 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/system/tenant/index.vue 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/system/tenantPackage/index.vue 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/system/user/index.vue 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.env.stage
@@ -1,25 +1,33 @@
NODE_ENV = production
# 开发环境配置
ENV = 'development'
# 页面标题
VUE_APP_TITLE = 工业互联网微服务平台
# 测试环境配置
ENV = 'staging'
# 工业互联网微服务平台/开发环境
#VUE_APP_BASE_API = '/proxy-api'
VUE_APP_BASE_API = 'http://172.16.8.100:48080'
# 工业互联网微服务平台/测试环境
VUE_APP_BASE_API = 'http://api-dashboard.iailab.iocoder.cn'
# 监控地址
VITE_APP_MONITOR_ADMIN = 'http://172.16.8.100:9111'
# 静态资源地址
PUBLIC_PATH = 'http://static.iailab.iocoder.cn/'
# xxl-job 控制台地址
VITE_APP_XXLJOB_ADMIN = 'http://172.16.8.100:9090'
# druid 控制台地址
VITE_APP_DRUID_ADMIN = 'http://172.16.8.100:48082'
# 路由懒加载
VUE_CLI_BABEL_TRANSPILE_MODULES = true
# 多租户的开关
VUE_APP_TENANT_ENABLE = true
# 验证码的开关
VUE_APP_CAPTCHA_ENABLE = true
VUE_APP_CAPTCHA_ENABLE = false
# 文档的开关
VUE_APP_DOC_ENABLE = false
VUE_APP_DOC_ENABLE = true
# 百度统计
VUE_APP_BAIDU_CODE = fadc1bd5db1a1d6f581df60a1807f8ab
package.json
@@ -42,7 +42,10 @@
  },
  "dependencies": {
    "@babel/parser": "7.18.4",
    "@riophae/vue-treeselect": "0.4.0",
    "@element-plus/icons-vue": "^2.1.0",
    "@form-create/designer": "^3.1.3",
    "@form-create/element-ui": "^3.1.24",
    "@riophae/vue-treeselect": "^0.4.0",
    "axios": "0.27.2",
    "benz-amr-recorder": "^1.1.5",
    "bpmn-js-token-simulation": "0.10.0",
@@ -50,35 +53,36 @@
    "core-js": "^3.26.0",
    "crypto-js": "^4.0.0",
    "echarts": "5.4.0",
    "element-plus": "^2.7.8",
    "element-ui": "2.15.12",
    "file-saver": "2.0.5",
    "fuse.js": "6.6.2",
    "highlight.js": "9.18.5",
    "js-beautify": "1.13.0",
    "js-cookie": "^2.2.1",
    "jsencrypt": "3.3.1",
    "min-dash": "3.5.2",
    "nprogress": "0.2.0",
    "qrcode.vue": "^1.7.0",
    "quill": "1.3.7",
    "relation-graph": "^2.1.42",
    "screenfull": "5.0.2",
    "sortablejs": "1.10.2",
    "throttle-debounce": "2.1.0",
    "vue": "2.7.14",
    "vue-count-to": "1.0.13",
    "vue-cron": "^1.0.9",
    "vue-cropper": "0.5.8",
    "vue-i18n": "^8.18.2",
    "vue-meta": "^2.4.0",
    "vue-quill-editor": "^3.0.6",
    "vue-router": "3.4.9",
    "vue-video-player": "^5.0.2",
    "vuedraggable": "2.24.3",
    "vuex": "3.6.2",
    "xml-js": "1.6.11",
    "js-cookie": "^2.2.1",
    "relation-graph": "^2.1.42",
    "vue-cron": "^1.0.9",
    "vue-i18n": "^8.18.2",
    "xlsx": "^0.18.5",
    "xlsx-style": "^0.8.13"
    "xlsx-style": "^0.8.13",
    "xml-js": "1.6.11"
  },
  "devDependencies": {
    "@vue/cli-plugin-babel": "4.5.18",
src/api/bpm/processExpression.js
对比新文件
@@ -0,0 +1,42 @@
import request from '@/utils/request'
export function getProcessExpressionPage(query) {
  return request({
    url: '/bpm/process-expression/page',
    method: 'get',
    params: query
  })
}
export function getProcessExpression(id) {
  return request({
    url: '/bpm/process-expression/get?id=' + id,
    method: 'get',
  })
}
export function createProcessExpression(data) {
  return request({
    url: '/bpm/process-expression/create',
    method: 'POST',
    data: data
  })
}
export function updateProcessExpression(data) {
  return request({
    url: '/bpm/process-expression/update',
    method: 'POST',
    data: data
  })
}
export function deleteProcessExpression(id) {
  return request({
    url: '/bpm/process-expression/delete?id=',
    method: 'DELETE',
    data: {
      id
    }
  })
}
src/api/bpm/processInstance.js
@@ -18,7 +18,7 @@
export function cancelProcessInstance(id, reason) {
  return request({
    url: '/bpm/process-instance/cancel',
    url: '/bpm/process-instance/cancel-by-start-user',
    method: 'DELETE',
    data: {
      id,
src/api/bpm/task.js
@@ -49,7 +49,7 @@
export function updateTaskAssignee(data) {
  return request({
    url: '/bpm/task/update-assignee',
    url: '/bpm/task/transfer',
    method: 'PUT',
    data: data
  })
@@ -64,7 +64,7 @@
export function getReturnList(taskId) {
  return request({
    url: '/bpm/task/return-list?taskId='+ taskId,
    url: '/bpm/task/list-by-return?id='+ taskId,
    method: 'get',
  })
}
src/api/bpm/userGroup.js
@@ -46,7 +46,7 @@
// 获取用户组精简信息列表
export function listSimpleUserGroups() {
  return request({
    url: '/bpm/user-group/list-all-simple',
    url: '/bpm/user-group/simple-list',
    method: 'get'
  })
}
src/assets/logo/login-logo.png
Binary files differ
src/assets/logo/logo.png

src/assets/logo/logo_rec.png
src/assets/styles/login.scss
@@ -14,7 +14,6 @@
// container-logo
$logoWidth: 500px;
$logoHeight: 64px;
//$logoImage: '../assets/logo/login-logo.png';
// container-content
$contentWidth: round($W / $H * 100) * 1vw;
$contentHeight: round($picH / $W * 100) / 100 * $contentWidth;
src/components/SelectTree/index.vue
对比新文件
@@ -0,0 +1,299 @@
<template>
  <!-- 单选的时候,value绑定id,多选的时候绑定展示名称字符串数组 -->
  <el-select
    :title="multiple? optionData.label : ''"
    ref="select"
    :value="multiple ? optionData.label : value"
    placeholder="请选择"
    :size="size"
    clearable
    :filterable="filterable"
    :filter-method="filterMethod"
    :disabled="disabled"
    :multiple="multiple"
    :collapse-tags="collapseTags"
    @remove-tag="removeTag"
    @clear="clear"
    @visible-change="visibleChange"
    @focus="focus"
  >
    <!-- 单选的时候,label绑定展示名称,多选的时候绑定空字符串 -->
    <el-option
      ref="option"
      class="tree-select__option"
      :value="optionData.id"
      :label="multiple ? '' : optionData.label"
    >
      <el-tree
        ref="tree"
        class="tree-select__tree"
        :class="`tree-select__tree--${multiple ? 'checked' : 'radio'}`"
        :node-key="nodeKey"
        :data="data"
        :props="props"
        v-bind="treeAttr"
        :highlight-current="!multiple"
        :show-checkbox="multiple"
        :check-strictly="checkStrictly"
        :expand-on-click-node="multiple"
        :filter-node-method="filterNode"
        @node-click="handleNodeClick"
        @current-change="handleCurrentChange"
        @check-change="handleCheckChange"
      ></el-tree>
    </el-option>
  </el-select>
</template>
<script>
export default {
  name: 'SelectTree',
  props: {
    size: {
      type: String,
      default: 'small'
    },
    // v-model绑定
    value: {
      type: [String, Number, Array],
      default: ''
    },
    // 树形的数据
    data: {
      type: Array,
      default: function() {
        return []
      }
    },
    // 每个树节点用来作为唯一标识的属性
    nodeKey: {
      type: [String, Number],
      default: 'id'
    },
    filterable: {
      type: Boolean,
      default: false
    },
    disabled: {
      type: Boolean,
      default: false
    },
    multiple: {
      type: Boolean,
      default: false
    },
    collapseTags: {
      type: Boolean,
      default: false
    },
    checkStrictly: {
      type: Boolean,
      default: false
    },
    // tree的props配置
    props: {
      type: Object,
      default: function() {
        return {
          label: 'label',
          children: 'children'
        }
      }
    }
  },
  data() {
    return {
      optionData: {
        id: '',
        name: ''
      },
      filterFlag: false,
      checkedArray: []
    }
  },
  computed: {
    treeAttr() {
      if (this.value) {
        return {
          defaultExpandedKeys: [this.value]
        }
      } else {
        // 考虑 value 可能为 null
        return {}
      }
    }
  },
  watch: {
    value: {
      handler(val) {
        if (!this.isEmpty(this.data)) {
          this.init(val)
        }
      },
      immediate: true
    },
    data: function(val) {
      if (!this.isEmpty(val)) {
        this.init(this.value)
      }
    }
  },
  methods: {
    // 是否为空
    isEmpty(val) {
      return this._.isEmpty(val)
    },
    handleNodeClick(data) {
      if (this.multiple) {
        return
      }
      if (data.disabled) {
        // 若节点 disabled === true,则取消选中
        this.$refs.tree.setCurrentKey(null)
        return
      }
      this.$emit('input', data[this.nodeKey])
      this.$emit('setLayer', data['layer'])
      this.$emit('selectNode', data[this.nodeKey])
      this.$refs.select.visible = false
    },
    handleCurrentChange(data) {
      if (this.multiple) {
        return
      }
      this.$emit('change', false)
    },
    handleCheckChange(data, checked, childrenChecked) {
      if (checked) {
        this.checkedArray.push(data)
      } else {
        let checkIndex = this._.findIndex(this.checkedArray, data)
        this.checkedArray.splice(checkIndex, 1)
      }
      // 多选绑定的值是数组
      this.optionData.id = this.checkedArray
        .map(item => item[this.nodeKey])
      // 多选展示的 label 是字符串数组
      const label = this.props.label || 'name'
      this.optionData[label] = this.checkedArray
        .map(item => item[label])
      this.$emit('input', this.optionData.id)
      this.$emit('selectNode', this.optionData.id)
    },
    init(val) {
      // 多选
      if (this.multiple) {
        if (val) {
          const arr = val.toString().split(',')
          this.$nextTick(() => {
            this.$refs.tree.setCheckedKeys(arr)
            const nodes = this.$refs.tree.getCheckedNodes()
            this.optionData.id = val
            // 多选展示的 label 是字符串数组
            const label = this.props.label || 'name'
            this.optionData[label] = nodes
              .map(item => item[label])
          })
        } else {
          this.$refs.tree.setCheckedKeys([])
        }
      } else {
        // 单选
        val = val === '' ? null : val
        this.$nextTick(() => {
          this.$refs.tree.setCurrentKey(val)
          if (!val) {
            return
          }
          const label = this.props.label || 'name'
          const node = this.$refs.tree.getNode(val)
          this.optionData.id = val
          this.optionData[label] = node.label
        })
      }
    },
    removeTag(tag) {
      // this.handleCheckChange({ id: tag }, false)
    },
    visibleChange(e) {
      if (e) {
        const tree = this.$refs.tree
        this.filterFlag && tree.filter('')
        this.filterFlag = false
        let selectDom = null
        if (this.multiple) {
          selectDom = tree.$el.querySelector('.el-tree-node.is-checked')
        } else {
          selectDom = tree.$el.querySelector('.is-current')
        }
        setTimeout(() => {
          this.$refs.select.scrollToOption({ $el: selectDom })
        }, 0)
      }
    },
    focus(e) {
      this.$emit('focus')
    },
    clear() {
      this.$emit('input', null)
    },
    filterMethod(val) {
      this.filterFlag = true
      this.$refs.tree.filter(val)
    },
    filterNode(value, data) {
      if (!value) return true
      const label = this.props.label || 'name'
      return data[label].indexOf(value) !== -1
    }
  }
}
</script>
<style lang="scss" scoped>
.tree-select__option {
  height: auto;
  line-height: 1;
  padding: 0;
  background-color: #fff;
  &.el-select-dropdown__item {
    height: auto;
    line-height: 1;
    padding: 0;
    background-color: #fff;
  }
}
.tree-select__tree {
  padding: 4px 20px;
  font-weight: 400;
  &.tree-select__tree--radio {
    ::v-deep .el-tree-node.is-current > .el-tree-node__content {
      color: #409eff;
      font-weight: 700;
    }
  }
  /*::v-deep .el-tree-node.is-current > .el-tree-node__content {
    color: #409eff;
    font-weight: 700;
  }*/
  ::v-deep .el-tree-node:focus > .el-tree-node__content {
    background-color: #ffffff;
  }
}
/* 禁止通过 tag 取消选中 */
::v-deep .el-tag.el-tag--info .el-tag__close {
  display: none;
}
</style>
src/components/bpmnProcessDesigner/package/designer/ProcessViewer.vue
@@ -1,7 +1,7 @@
<template>
  <div class="my-process-designer">
    <div class="my-process-designer__container">
      <div class="my-process-designer__canvas" ref="bpmn-canvas"></div>
      <div class="my-process-designer__canvas" style="height: 760px"  ref="bpmn-canvas"></div>
    </div>
  </div>
</template>
@@ -9,6 +9,7 @@
<script>
import BpmnViewer from "bpmn-js/lib/Viewer";
import DefaultEmptyXML from "./plugins/defaultEmpty";
import { getDictDatas } from '@/utils/dict'
export default {
  name: "MyProcessViewer",
@@ -47,13 +48,18 @@
    // 初始化
    this.initBpmnModeler();
    this.createNewDiagram(this.xml);
    this.$once("hook:beforeDestroy", () => {
      if (this.bpmnModeler) this.bpmnModeler.destroy();
      this.$emit("destroy", this.bpmnModeler);
      this.bpmnModeler = null;
    });
    // this.$once("hook:beforeDestroy", () => {
    //   if (this.bpmnModeler) this.bpmnModeler.destroy();
    //   this.$emit("destroy", this.bpmnModeler);
    //   this.bpmnModeler = null;
    // });
    // 初始模型的监听器
    this.initModelListeners();
  },
  beforeMount() {
    if (this.bpmnModeler) this.bpmnModeler.destroy()
      this.$emit("destroy", this.bpmnModeler);
      this.bpmnModeler = null;
  },
  watch: {
    value: function (newValue) { // 在 xmlString 发生变化时,重新创建,从而绘制流程图
@@ -70,6 +76,7 @@
    },
    taskData: function (newTaskListData) {
      this.taskList = newTaskListData;
      console.log(this.taskList)
      this.createNewDiagram(this.xml);
    }
  },
@@ -115,8 +122,10 @@
      let canvas = this.bpmnModeler.get('canvas');
      let todoActivity = activityList.find(m => !m.endTime) // 找到待办的任务
      let endActivity = activityList[activityList.length - 1] // 获得最后一个任务
      let findProcessTask = false //是否已经高亮了进行中的任务
      //进行中高亮之后的任务 key 集合,用于过滤掉 taskList 进行中后面的任务,避免进行中后面的数据 Hover 还有数据
      let removeTaskDefinitionKeyList = []
      // debugger
      // console.log(this.bpmnModeler.getDefinitions().rootElements[0].flowElements);
      this.bpmnModeler.getDefinitions().rootElements[0].flowElements?.forEach(n => {
        let activity = activityList.find(m => m.key === n.id) // 找到对应的活动
        if (!activity) {
@@ -128,11 +137,21 @@
          if (!task) {
            return;
          }
          // 进行中的任务已经高亮过了,则不高亮后面的任务了
          if (findProcessTask) {
            removeTaskDefinitionKeyList.push(n.id)
            return
          }
          // 高亮任务
          canvas.addMarker(n.id, this.getResultCss(task.result));
          canvas.addMarker(n.id, this.getResultCss(task.status));
          //标记是否高亮了进行中任务
          if (task.status === 1) {
            findProcessTask = true
          }
          // 如果非通过,就不走后面的线条了
          if (task.result !== 2) {
          if (task.status !== 2) {
            return;
          }
          // 处理 outgoing 出线
@@ -143,10 +162,12 @@
            // 如果目标活动存在,则根据该活动是否结束,进行【bpmn:SequenceFlow】连线的高亮设置
            if (targetActivity) {
              canvas.addMarker(nn.id, targetActivity.endTime ? 'highlight' : 'highlight-todo');
            } else if (nn.targetRef.$type === 'bpmn:ExclusiveGateway') { // TODO iailab:这个流程,暂时没走到过
            } else if (nn.targetRef.$type === 'bpmn:ExclusiveGateway') {
              // TODO iailab:这个流程,暂时没走到过
              canvas.addMarker(nn.id, activity.endTime ? 'highlight' : 'highlight-todo');
              canvas.addMarker(nn.targetRef.id, activity.endTime ? 'highlight' : 'highlight-todo');
            } else if (nn.targetRef.$type === 'bpmn:EndEvent') { // TODO iailab:这个流程,暂时没走到过
            } else if (nn.targetRef.$type === 'bpmn:EndEvent') {
              // TODO iailab:这个流程,暂时没走到过
              if (!todoActivity && endActivity.key === n.id) {
                canvas.addMarker(nn.id, 'highlight');
                canvas.addMarker(nn.targetRef.id, 'highlight');
@@ -193,7 +214,9 @@
              canvas.addMarker(nn.targetRef.id, this.getActivityHighlightCss(targetActivity));
            }
          })
        } else if (n.$type === 'bpmn:StartEvent') { // 开始节点
        } else if (n.$type === 'bpmn:StartEvent') {
          // 开始节点
          canvas.addMarker(n.id, 'highlight')
          n.outgoing?.forEach(nn => { // outgoing 例如说【bpmn:SequenceFlow】连线
            // 获得连线是否有指向目标。如果有,则进行高亮
            let targetActivity = activityList.find(m => m.key === nn.targetRef.id);
@@ -203,42 +226,64 @@
            }
          });
        } else if (n.$type === 'bpmn:EndEvent') { // 结束节点
          if (!this.processInstance || this.processInstance.result === 1) {
          if (!this.processInstance || this.processInstance.status === 1) {
            return;
          }
          canvas.addMarker(n.id, this.getResultCss(this.processInstance.result));
          canvas.addMarker(n.id, this.getResultCss(this.processInstance.status));
        } else if (n.$type === 'bpmn:ServiceTask'){ //服务任务
          if(activity.startTime>0 && activity.endTime===0){//进入执行,标识进行色
            canvas.addMarker(n.id, this.getResultCss(1));
          }
          if(activity.endTime>0){// 执行完成,节点标识完成色, 所有outgoing标识完成色。
          if(activity.endTime > 0){// 执行完成,节点标识完成色, 所有outgoing标识完成色。
            canvas.addMarker(n.id, this.getResultCss(2));
            const outgoing = this.getActivityOutgoing(activity)
            outgoing?.forEach(out=>{
              canvas.addMarker(out.id,this.getResultCss(2))
            })
          }
        } else if (n.$type === 'bpmn:SequenceFlow') {
          let targetActivity = activityList.find(m => m.key === n.targetRef.id)
          if (targetActivity) {
            canvas.addMarker(n.id, this.getActivityHighlightCss(targetActivity))
          }
        }
      })
      if (removeTaskDefinitionKeyList) {
        this.taskList = this.taskList.filter(
          (item) => !removeTaskDefinitionKeyList.includes(item.taskDefinitionKey)
        )
      }
    },
    getActivityHighlightCss(activity) {
      return activity.endTime ? 'highlight' : 'highlight-todo';
    },
    getResultCss(result) {
      if (result === 1) { // 审批中
        return 'highlight-todo';
      } else if (result === 2) { // 已通过
        return 'highlight';
      } else if (result === 3) { // 不通过
        return 'highlight-reject';
      } else if (result === 4) { // 已取消
        return 'highlight-cancel';
      } else if (result === 5) { // 已退回
        return 'highlight-back';
      } else if (result === 6) { // 已委派
        return 'highlight-todo';
    getResultCss(status) {
      if (status === 1) {
        // 审批中
        return 'highlight-todo'
      } else if (status === 2) {
        // 已通过
        return 'highlight'
      } else if (status === 3) {
        // 不通过
        return 'highlight-reject'
      } else if (status === 4) {
        // 已取消
        return 'highlight-cancel'
      } else if (status === 5) {
        // 退回
        return 'highlight-return'
      } else if (status === 6) {
        // 委派
        return 'highlight-todo'
      } else if (status === 7) {
        // 审批通过中
        return 'highlight-todo'
      } else if (status === 0) {
        // 待审批
        return 'highlight-todo'
      }
      return '';
      return ''
    },
    getActivityOutgoing(activity) {
      // 如果有 outgoing,则直接使用它
@@ -289,16 +334,22 @@
        if (element.type === 'bpmn:StartEvent' && this.processInstance) {
          html = `<p>发起人:${this.processInstance.startUser.nickname}</p>
                  <p>部门:${this.processInstance.startUser.deptName}</p>
                  <p>创建时间:${this.parseTime(this.processInstance.createTime)}`;
                  <p>创建时间:${this.parseTime(this.processInstance.startTime)}`;
        } else if (element.type === 'bpmn:UserTask') {
          // debugger
          let task = this.taskList.find(m => m.id === activity.taskId); // 找到活动对应的 taskId
          if (!task) {
            return;
          }
          let optionData = getDictDatas(this.DICT_TYPE.BPM_TASK_STATUS)
          let dataResult = ''
          optionData.forEach((element) => {
            if (element.value == task.status) {
              dataResult = element.label
            }
          })
          html = `<p>审批人:${task.assigneeUser.nickname}</p>
                  <p>部门:${task.assigneeUser.deptName}</p>
                  <p>结果:${this.getDictDataLabel(this.DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT, task.result)}</p>
                  <p>结果:${dataResult}</p>
                  <p>创建时间:${this.parseTime(task.createTime)}</p>`;
          if (task.endTime) {
            html += `<p>结束时间:${this.parseTime(task.endTime)}</p>`
@@ -307,7 +358,7 @@
            html += `<p>审批建议:${task.reason}</p>`
          }
        } else if (element.type === 'bpmn:ServiceTask' && this.processInstance) {
          if(activity.startTime>0){
          if(activity.startTime > 0){
            html = `<p>创建时间:${this.parseTime(activity.startTime)}</p>`;
          }
          if(activity.endTime>0){
@@ -315,7 +366,14 @@
          }
          console.log(html)
        } else if (element.type === 'bpmn:EndEvent' && this.processInstance) {
          html = `<p>结果:${this.getDictDataLabel(this.DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT, this.processInstance.result)}</p>`;
          let optionData = getDictDatas(this.DICT_TYPE.BPM_TASK_STATUS)
          let dataResult = ''
          optionData.forEach((element) => {
            if (element.value == this.processInstance.status) {
              dataResult = element.label
            }
          })
          html = `<p>结果:${dataResult}</p>`;
          if (this.processInstance.endTime) {
            html += `<p>结束时间:${this.parseTime(this.processInstance.endTime)}</p>`
          }
src/components/bpmnProcessDesigner/package/penal/PropertiesPanel.vue
@@ -16,9 +16,9 @@
      </el-collapse-item>
      <el-collapse-item name="condition" v-if="formVisible" key="form">
        <div slot="title" class="panel-tab__title"><i class="el-icon-s-order"></i>表单</div>
<!--        <element-form :id="elementId" :type="elementType" />-->
        友情提示:使用 <router-link target="_blank" :to="{path:'/bpm/manager/form'}"><el-link type="danger">流程表单</el-link> </router-link>
        替代,提供更好的表单设计功能
        <element-form :id="elementId" :type="elementType" />
<!--        友情提示:使用 <router-link target="_blank" :to="{path:'/bpm/manager/form'}"><el-link type="danger">流程表单</el-link> </router-link>-->
<!--        替代,提供更好的表单设计功能-->
      </el-collapse-item>
      <el-collapse-item name="task" v-if="elementType.indexOf('Task') !== -1" key="task">
        <div slot="title" class="panel-tab__title"><i class="el-icon-s-claim"></i>任务</div>
@@ -169,14 +169,14 @@
          window.bpmnInstances.elementRegistry.find(el => el.type === "bpmn:Collaboration");
      }
      if (!activatedElement) return;
      console.log(`
              ----------
      select element changed:
                id:  ${activatedElement.id}
              type:  ${activatedElement.businessObject.$type}
              ----------
              `);
      console.log("businessObject: ", activatedElement.businessObject);
      // console.log(`
      //         ----------
      // select element changed:
      //           id:  ${activatedElement.id}
      //         type:  ${activatedElement.businessObject.$type}
      //         ----------
      //         `);
      // console.log("businessObject: ", activatedElement.businessObject);
      window.bpmnInstances.bpmnElement = activatedElement;
      this.bpmnElement = activatedElement;
      this.elementId = activatedElement.id;
src/components/bpmnProcessDesigner/package/penal/base/ElementBaseInfo.vue
@@ -8,13 +8,6 @@
      @submit.native.prevent
    >
      <div v-if="elementBaseInfo.$type === 'bpmn:Process'">
        <!-- 如果是 Process 信息的时候,使用自定义表单 -->
        <el-link
          href="https://doc.iocoder.cn/bpm/#_3-%E6%B5%81%E7%A8%8B%E5%9B%BE%E7%A4%BA%E4%BE%8B"
          type="danger"
          target="_blank"
          >如何实现实现会签、或签?</el-link
        >
        <el-form-item label="流程标识" prop="key">
          <el-input
            v-model="model.key"
src/components/bpmnProcessDesigner/package/penal/form/ElementForm.vue
@@ -1,361 +1,45 @@
<template>
  <div class="panel-tab__content">
    <el-form size="mini" label-width="80px" @submit.native.prevent>
      <el-form-item label="表单标识">
        <el-input v-model="formKey" clearable @change="updateElementFormKey" />
      </el-form-item>
      <el-form-item label="业务标识">
        <el-select v-model="businessKey" @change="updateElementBusinessKey">
          <el-option v-for="i in fieldList" :key="i.id" :value="i.id" :label="i.label" />
          <el-option label="无" value="" />
    <el-form label-width="80px">
      <el-form-item label="流程表单">
        <el-select v-model="formKey" clearable @change="updateElementFormKey">
          <el-option v-for="form in formList" :key="form.id" :label="form.name" :value="form.id" />
        </el-select>
      </el-form-item>
    </el-form>
    <!--字段列表-->
    <div class="element-property list-property">
      <el-divider><i class="el-icon-coin"></i> 表单字段</el-divider>
      <el-table :data="fieldList" size="mini" max-height="240" border fit>
        <el-table-column label="序号" type="index" width="50px" />
        <el-table-column label="字段名称" prop="label" min-width="80px" show-overflow-tooltip />
        <el-table-column label="字段类型" prop="type" min-width="80px" :formatter="row => fieldType[row.type] || row.type" show-overflow-tooltip />
        <el-table-column label="默认值" prop="defaultValue" min-width="80px" show-overflow-tooltip />
        <el-table-column label="操作" width="90px">
          <template v-slot="{ row, $index }">
            <el-button size="mini" type="text" @click="openFieldForm(row, $index)">编辑</el-button>
            <el-divider direction="vertical" />
            <el-button size="mini" type="text" style="color: #ff4d4f" @click="removeField(row, $index)">移除</el-button>
          </template>
        </el-table-column>
      </el-table>
    </div>
    <div class="element-drawer__button">
      <el-button size="mini" type="primary" icon="el-icon-plus" @click="openFieldForm(null, -1)">添加字段</el-button>
    </div>
    <!--字段配置侧边栏-->
    <el-drawer :visible.sync="fieldModelVisible" title="字段配置" :size="`${width}px`" append-to-body destroy-on-close>
      <el-form :model="formFieldForm" label-width="90px" size="mini" @submit.native.prevent>
        <el-form-item label="字段ID">
          <el-input v-model="formFieldForm.id" clearable />
        </el-form-item>
        <el-form-item label="类型">
          <el-select v-model="formFieldForm.typeType" placeholder="请选择字段类型" clearable @change="changeFieldTypeType">
            <el-option v-for="(value, key) of fieldType" :label="value" :value="key" :key="key" />
          </el-select>
        </el-form-item>
        <el-form-item label="类型名称" v-if="formFieldForm.typeType === 'custom'">
          <el-input v-model="formFieldForm.type" clearable />
        </el-form-item>
        <el-form-item label="名称">
          <el-input v-model="formFieldForm.label" clearable />
        </el-form-item>
        <el-form-item label="时间格式" v-if="formFieldForm.typeType === 'date'">
          <el-input v-model="formFieldForm.datePattern" clearable />
        </el-form-item>
        <el-form-item label="默认值">
          <el-input v-model="formFieldForm.defaultValue" clearable />
        </el-form-item>
      </el-form>
      <!-- 枚举值设置 -->
      <template v-if="formFieldForm.type === 'enum'">
        <el-divider key="enum-divider" />
        <p class="listener-filed__title" key="enum-title">
          <span><i class="el-icon-menu"></i>枚举值列表:</span>
          <el-button size="mini" type="primary" @click="openFieldOptionForm(null, -1, 'enum')">添加枚举值</el-button>
        </p>
        <el-table :data="fieldEnumList" size="mini" key="enum-table" max-height="240" border fit>
          <el-table-column label="序号" width="50px" type="index" />
          <el-table-column label="枚举值编号" prop="id" min-width="100px" show-overflow-tooltip />
          <el-table-column label="枚举值名称" prop="name" min-width="100px" show-overflow-tooltip />
          <el-table-column label="操作" width="90px">
            <template v-slot="{ row, $index }">
              <el-button size="mini" type="text" @click="openFieldOptionForm(row, $index, 'enum')">编辑</el-button>
              <el-divider direction="vertical" />
              <el-button size="mini" type="text" style="color: #ff4d4f" @click="removeFieldOptionItem(row, $index, 'enum')">移除</el-button>
            </template>
          </el-table-column>
        </el-table>
      </template>
      <!-- 校验规则 -->
      <el-divider key="validation-divider" />
      <p class="listener-filed__title" key="validation-title">
        <span><i class="el-icon-menu"></i>约束条件列表:</span>
        <el-button size="mini" type="primary" @click="openFieldOptionForm(null, -1, 'constraint')">添加约束</el-button>
      </p>
      <el-table :data="fieldConstraintsList" size="mini" key="validation-table" max-height="240" border fit>
        <el-table-column label="序号" width="50px" type="index" />
        <el-table-column label="约束名称" prop="name" min-width="100px" show-overflow-tooltip />
        <el-table-column label="约束配置" prop="config" min-width="100px" show-overflow-tooltip />
        <el-table-column label="操作" width="90px">
          <template v-slot="{ row, $index }">
            <el-button size="mini" type="text" @click="openFieldOptionForm(row, $index, 'constraint')">编辑</el-button>
            <el-divider direction="vertical" />
            <el-button size="mini" type="text" style="color: #ff4d4f" @click="removeFieldOptionItem(row, $index, 'constraint')">移除</el-button>
          </template>
        </el-table-column>
      </el-table>
      <!-- 表单属性 -->
      <el-divider key="property-divider" />
      <p class="listener-filed__title" key="property-title">
        <span><i class="el-icon-menu"></i>字段属性列表:</span>
        <el-button size="mini" type="primary" @click="openFieldOptionForm(null, -1, 'property')">添加属性</el-button>
      </p>
      <el-table :data="fieldPropertiesList" size="mini" key="property-table" max-height="240" border fit>
        <el-table-column label="序号" width="50px" type="index" />
        <el-table-column label="属性编号" prop="id" min-width="100px" show-overflow-tooltip />
        <el-table-column label="属性值" prop="value" min-width="100px" show-overflow-tooltip />
        <el-table-column label="操作" width="90px">
          <template v-slot="{ row, $index }">
            <el-button size="mini" type="text" @click="openFieldOptionForm(row, $index, 'property')">编辑</el-button>
            <el-divider direction="vertical" />
            <el-button size="mini" type="text" style="color: #ff4d4f" @click="removeFieldOptionItem(row, $index, 'property')">移除</el-button>
          </template>
        </el-table-column>
      </el-table>
      <!-- 底部按钮 -->
      <div class="element-drawer__button">
        <el-button size="mini">取 消</el-button>
        <el-button size="mini" type="primary" @click="saveField">保 存</el-button>
      </div>
    </el-drawer>
    <el-dialog :visible.sync="fieldOptionModelVisible" :title="optionModelTitle" width="600px" append-to-body destroy-on-close>
      <el-form :model="fieldOptionForm" size="mini" label-width="96px" @submit.native.prevent>
        <el-form-item label="编号/ID" v-if="fieldOptionType !== 'constraint'" key="option-id">
          <el-input v-model="fieldOptionForm.id" clearable />
        </el-form-item>
        <el-form-item label="名称" v-if="fieldOptionType !== 'property'" key="option-name">
          <el-input v-model="fieldOptionForm.name" clearable />
        </el-form-item>
        <el-form-item label="配置" v-if="fieldOptionType === 'constraint'" key="option-config">
          <el-input v-model="fieldOptionForm.config" clearable />
        </el-form-item>
        <el-form-item label="值" v-if="fieldOptionType === 'property'" key="option-value">
          <el-input v-model="fieldOptionForm.value" clearable />
        </el-form-item>
      </el-form>
      <template slot="footer">
        <el-button size="mini" @click="fieldOptionModelVisible = false">取 消</el-button>
        <el-button size="mini" type="primary" @click="saveFieldOption">确 定</el-button>
      </template>
    </el-dialog>
  </div>
</template>
<script>
import { getSimpleForms } from '@/api/bpm/form'
export default {
  name: "ElementForm",
  props: {
    id: String,
    type: String
  },
  inject: {
    prefix: "prefix",
    width: "width"
  },
  data() {
    return {
      formKey: "",
      businessKey: "",
      optionModelTitle: "",
      fieldList: [],
      formFieldForm: {},
      fieldType: {
        long: "长整型",
        string: "字符串",
        boolean: "布尔类",
        date: "日期类",
        enum: "枚举类",
        custom: "自定义类型"
      },
      formFieldIndex: -1, // 编辑中的字段, -1 为新增
      formFieldOptionIndex: -1, // 编辑中的字段配置项, -1 为新增
      fieldModelVisible: false,
      fieldOptionModelVisible: false,
      fieldOptionForm: {}, // 当前激活的字段配置项数据
      fieldOptionType: "", // 当前激活的字段配置项弹窗 类型
      fieldEnumList: [], // 枚举值列表
      fieldConstraintsList: [], // 约束条件列表
      fieldPropertiesList: [] // 绑定属性列表
      formList: [],
      formData: [],
      bpmnELement: Object,
    };
  },
  watch: {
    id: {
      immediate: true,
      handler(val) {
        val && val.length && this.$nextTick(() => this.resetFormList());
      }
    }
  created() {
    this.resetFormList()
  },
  methods: {
    getSimpleForms() {
      getSimpleForms().then(response => {
        this.formList = response.data
      })
    },
    resetFormList() {
      this.getSimpleForms()
      this.bpmnELement = window.bpmnInstances.bpmnElement;
      this.formKey = this.bpmnELement.businessObject.formKey;
      // 获取元素扩展属性 或者 创建扩展属性
      this.elExtensionElements =
        this.bpmnELement.businessObject.get("extensionElements") || window.bpmnInstances.moddle.create("bpmn:ExtensionElements", { values: [] });
      // 获取元素表单配置 或者 创建新的表单配置
      this.formData =
        this.elExtensionElements.values.filter(ex => ex.$type === `${this.prefix}:FormData`)?.[0] ||
        window.bpmnInstances.moddle.create(`${this.prefix}:FormData`, { fields: [] });
      // 业务标识 businessKey, 绑定在 formData 中
      this.businessKey = this.formData.businessKey;
      // 保留剩余扩展元素,便于后面更新该元素对应属性
      this.otherExtensions = this.elExtensionElements.values.filter(ex => ex.$type !== `${this.prefix}:FormData`);
      // 复制原始值,填充表格
      this.fieldList = JSON.parse(JSON.stringify(this.formData.fields || []));
      // 更新元素扩展属性,避免后续报错
      this.updateElementExtensions();
      this.formKey = parseInt(this.bpmnELement.businessObject.formKey);
    },
    updateElementFormKey() {
      window.bpmnInstances.modeling.updateProperties(this.bpmnELement, { formKey: this.formKey });
    },
    updateElementBusinessKey() {
      window.bpmnInstances.modeling.updateModdleProperties(this.bpmnELement, this.formData, { businessKey: this.businessKey });
    },
    // 根据类型调整字段type
    changeFieldTypeType(type) {
      this.$set(this.formFieldForm, "type", type === "custom" ? "" : type);
    },
    // 打开字段详情侧边栏
    openFieldForm(field, index) {
      this.formFieldIndex = index;
      if (index !== -1) {
        const FieldObject = this.formData.fields[index];
        this.formFieldForm = JSON.parse(JSON.stringify(field));
        // 设置自定义类型
        this.$set(this.formFieldForm, "typeType", !this.fieldType[field.type] ? "custom" : field.type);
        // 初始化枚举值列表
        field.type === "enum" && (this.fieldEnumList = JSON.parse(JSON.stringify(FieldObject?.values || [])));
        // 初始化约束条件列表
        this.fieldConstraintsList = JSON.parse(JSON.stringify(FieldObject?.validation?.constraints || []));
        // 初始化自定义属性列表
        this.fieldPropertiesList = JSON.parse(JSON.stringify(FieldObject?.properties?.values || []));
      } else {
        this.formFieldForm = {};
        // 初始化枚举值列表
        this.fieldEnumList = [];
        // 初始化约束条件列表
        this.fieldConstraintsList = [];
        // 初始化自定义属性列表
        this.fieldPropertiesList = [];
      }
      this.fieldModelVisible = true;
    },
    // 打开字段 某个 配置项 弹窗
    openFieldOptionForm(option, index, type) {
      this.fieldOptionModelVisible = true;
      this.fieldOptionType = type;
      this.formFieldOptionIndex = index;
      if (type === "property") {
        this.fieldOptionForm = option ? JSON.parse(JSON.stringify(option)) : {};
        return (this.optionModelTitle = "属性配置");
      }
      if (type === "enum") {
        this.fieldOptionForm = option ? JSON.parse(JSON.stringify(option)) : {};
        return (this.optionModelTitle = "枚举值配置");
      }
      this.fieldOptionForm = option ? JSON.parse(JSON.stringify(option)) : {};
      return (this.optionModelTitle = "约束条件配置");
    },
    // 保存字段 某个 配置项
    saveFieldOption() {
      if (this.formFieldOptionIndex === -1) {
        if (this.fieldOptionType === "property") {
          this.fieldPropertiesList.push(this.fieldOptionForm);
        }
        if (this.fieldOptionType === "constraint") {
          this.fieldConstraintsList.push(this.fieldOptionForm);
        }
        if (this.fieldOptionType === "enum") {
          this.fieldEnumList.push(this.fieldOptionForm);
        }
      } else {
        this.fieldOptionType === "property" && this.fieldPropertiesList.splice(this.formFieldOptionIndex, 1, this.fieldOptionForm);
        this.fieldOptionType === "constraint" && this.fieldConstraintsList.splice(this.formFieldOptionIndex, 1, this.fieldOptionForm);
        this.fieldOptionType === "enum" && this.fieldEnumList.splice(this.formFieldOptionIndex, 1, this.fieldOptionForm);
      }
      this.fieldOptionModelVisible = false;
      this.fieldOptionForm = {};
    },
    // 保存字段配置
    saveField() {
      const { id, type, label, defaultValue, datePattern } = this.formFieldForm;
      const Field = window.bpmnInstances.moddle.create(`${this.prefix}:FormField`, { id, type, label });
      defaultValue && (Field.defaultValue = defaultValue);
      datePattern && (Field.datePattern = datePattern);
      // 构建属性
      if (this.fieldPropertiesList && this.fieldPropertiesList.length) {
        const fieldPropertyList = this.fieldPropertiesList.map(fp => {
          return window.bpmnInstances.moddle.create(`${this.prefix}:Property`, { id: fp.id, value: fp.value });
        });
        Field.properties = window.bpmnInstances.moddle.create(`${this.prefix}:Properties`, { values: fieldPropertyList });
      }
      // 构建校验规则
      if (this.fieldConstraintsList && this.fieldConstraintsList.length) {
        const fieldConstraintList = this.fieldConstraintsList.map(fc => {
          return window.bpmnInstances.moddle.create(`${this.prefix}:Constraint`, { name: fc.name, config: fc.config });
        });
        Field.validation = window.bpmnInstances.moddle.create(`${this.prefix}:Validation`, { constraints: fieldConstraintList });
      }
      // 构建枚举值
      if (this.fieldEnumList && this.fieldEnumList.length) {
        Field.values = this.fieldEnumList.map(fe => {
          return window.bpmnInstances.moddle.create(`${this.prefix}:Value`, { name: fe.name, id: fe.id });
        });
      }
      // 更新数组 与 表单配置实例
      if (this.formFieldIndex === -1) {
        this.fieldList.push(this.formFieldForm);
        this.formData.fields.push(Field);
      } else {
        this.fieldList.splice(this.formFieldIndex, 1, this.formFieldForm);
        this.formData.fields.splice(this.formFieldIndex, 1, Field);
      }
      this.updateElementExtensions();
      this.fieldModelVisible = false;
    },
    // 移除某个 字段的 配置项
    removeFieldOptionItem(option, index, type) {
      if (type === "property") {
        this.fieldPropertiesList.splice(index, 1);
        return;
      }
      if (type === "enum") {
        this.fieldEnumList.splice(index, 1);
        return;
      }
      this.fieldConstraintsList.splice(index, 1);
    },
    // 移除 字段
    removeField(field, index) {
      this.fieldList.splice(index, 1);
      this.formData.fields.splice(index, 1);
      this.updateElementExtensions();
    },
    updateElementExtensions() {
      // 更新回扩展元素
      const newElExtensionElements = window.bpmnInstances.moddle.create(`bpmn:ExtensionElements`, {
        values: this.otherExtensions.concat(this.formData)
      });
      // 更新到元素上
      window.bpmnInstances.modeling.updateProperties(this.bpmnELement, {
        extensionElements: newElExtensionElements
      });
    }
  }
};
</script>
src/components/bpmnProcessDesigner/package/penal/form/ElementForm_rec.vue
对比新文件
@@ -0,0 +1,361 @@
<template>
  <div class="panel-tab__content">
    <el-form size="mini" label-width="80px" @submit.native.prevent>
      <el-form-item label="表单标识">
        <el-input v-model="formKey" clearable @change="updateElementFormKey" />
      </el-form-item>
      <el-form-item label="业务标识">
        <el-select v-model="businessKey" @change="updateElementBusinessKey">
          <el-option v-for="i in fieldList" :key="i.id" :value="i.id" :label="i.label" />
          <el-option label="无" value="" />
        </el-select>
      </el-form-item>
    </el-form>
    <!--字段列表-->
    <div class="element-property list-property">
      <el-divider><i class="el-icon-coin"></i> 表单字段</el-divider>
      <el-table :data="fieldList" size="mini" max-height="240" border fit>
        <el-table-column label="序号" type="index" width="50px" />
        <el-table-column label="字段名称" prop="label" min-width="80px" show-overflow-tooltip />
        <el-table-column label="字段类型" prop="type" min-width="80px" :formatter="row => fieldType[row.type] || row.type" show-overflow-tooltip />
        <el-table-column label="默认值" prop="defaultValue" min-width="80px" show-overflow-tooltip />
        <el-table-column label="操作" width="90px">
          <template v-slot="{ row, $index }">
            <el-button size="mini" type="text" @click="openFieldForm(row, $index)">编辑</el-button>
            <el-divider direction="vertical" />
            <el-button size="mini" type="text" style="color: #ff4d4f" @click="removeField(row, $index)">移除</el-button>
          </template>
        </el-table-column>
      </el-table>
    </div>
    <div class="element-drawer__button">
      <el-button size="mini" type="primary" icon="el-icon-plus" @click="openFieldForm(null, -1)">添加字段</el-button>
    </div>
    <!--字段配置侧边栏-->
    <el-drawer :visible.sync="fieldModelVisible" title="字段配置" :size="`${width}px`" append-to-body destroy-on-close>
      <el-form :model="formFieldForm" label-width="90px" size="mini" @submit.native.prevent>
        <el-form-item label="字段ID">
          <el-input v-model="formFieldForm.id" clearable />
        </el-form-item>
        <el-form-item label="类型">
          <el-select v-model="formFieldForm.typeType" placeholder="请选择字段类型" clearable @change="changeFieldTypeType">
            <el-option v-for="(value, key) of fieldType" :label="value" :value="key" :key="key" />
          </el-select>
        </el-form-item>
        <el-form-item label="类型名称" v-if="formFieldForm.typeType === 'custom'">
          <el-input v-model="formFieldForm.type" clearable />
        </el-form-item>
        <el-form-item label="名称">
          <el-input v-model="formFieldForm.label" clearable />
        </el-form-item>
        <el-form-item label="时间格式" v-if="formFieldForm.typeType === 'date'">
          <el-input v-model="formFieldForm.datePattern" clearable />
        </el-form-item>
        <el-form-item label="默认值">
          <el-input v-model="formFieldForm.defaultValue" clearable />
        </el-form-item>
      </el-form>
      <!-- 枚举值设置 -->
      <template v-if="formFieldForm.type === 'enum'">
        <el-divider key="enum-divider" />
        <p class="listener-filed__title" key="enum-title">
          <span><i class="el-icon-menu"></i>枚举值列表:</span>
          <el-button size="mini" type="primary" @click="openFieldOptionForm(null, -1, 'enum')">添加枚举值</el-button>
        </p>
        <el-table :data="fieldEnumList" size="mini" key="enum-table" max-height="240" border fit>
          <el-table-column label="序号" width="50px" type="index" />
          <el-table-column label="枚举值编号" prop="id" min-width="100px" show-overflow-tooltip />
          <el-table-column label="枚举值名称" prop="name" min-width="100px" show-overflow-tooltip />
          <el-table-column label="操作" width="90px">
            <template v-slot="{ row, $index }">
              <el-button size="mini" type="text" @click="openFieldOptionForm(row, $index, 'enum')">编辑</el-button>
              <el-divider direction="vertical" />
              <el-button size="mini" type="text" style="color: #ff4d4f" @click="removeFieldOptionItem(row, $index, 'enum')">移除</el-button>
            </template>
          </el-table-column>
        </el-table>
      </template>
      <!-- 校验规则 -->
      <el-divider key="validation-divider" />
      <p class="listener-filed__title" key="validation-title">
        <span><i class="el-icon-menu"></i>约束条件列表:</span>
        <el-button size="mini" type="primary" @click="openFieldOptionForm(null, -1, 'constraint')">添加约束</el-button>
      </p>
      <el-table :data="fieldConstraintsList" size="mini" key="validation-table" max-height="240" border fit>
        <el-table-column label="序号" width="50px" type="index" />
        <el-table-column label="约束名称" prop="name" min-width="100px" show-overflow-tooltip />
        <el-table-column label="约束配置" prop="config" min-width="100px" show-overflow-tooltip />
        <el-table-column label="操作" width="90px">
          <template v-slot="{ row, $index }">
            <el-button size="mini" type="text" @click="openFieldOptionForm(row, $index, 'constraint')">编辑</el-button>
            <el-divider direction="vertical" />
            <el-button size="mini" type="text" style="color: #ff4d4f" @click="removeFieldOptionItem(row, $index, 'constraint')">移除</el-button>
          </template>
        </el-table-column>
      </el-table>
      <!-- 表单属性 -->
      <el-divider key="property-divider" />
      <p class="listener-filed__title" key="property-title">
        <span><i class="el-icon-menu"></i>字段属性列表:</span>
        <el-button size="mini" type="primary" @click="openFieldOptionForm(null, -1, 'property')">添加属性</el-button>
      </p>
      <el-table :data="fieldPropertiesList" size="mini" key="property-table" max-height="240" border fit>
        <el-table-column label="序号" width="50px" type="index" />
        <el-table-column label="属性编号" prop="id" min-width="100px" show-overflow-tooltip />
        <el-table-column label="属性值" prop="value" min-width="100px" show-overflow-tooltip />
        <el-table-column label="操作" width="90px">
          <template v-slot="{ row, $index }">
            <el-button size="mini" type="text" @click="openFieldOptionForm(row, $index, 'property')">编辑</el-button>
            <el-divider direction="vertical" />
            <el-button size="mini" type="text" style="color: #ff4d4f" @click="removeFieldOptionItem(row, $index, 'property')">移除</el-button>
          </template>
        </el-table-column>
      </el-table>
      <!-- 底部按钮 -->
      <div class="element-drawer__button">
        <el-button size="mini">取 消</el-button>
        <el-button size="mini" type="primary" @click="saveField">保 存</el-button>
      </div>
    </el-drawer>
    <el-dialog :visible.sync="fieldOptionModelVisible" :title="optionModelTitle" width="600px" append-to-body destroy-on-close>
      <el-form :model="fieldOptionForm" size="mini" label-width="96px" @submit.native.prevent>
        <el-form-item label="编号/ID" v-if="fieldOptionType !== 'constraint'" key="option-id">
          <el-input v-model="fieldOptionForm.id" clearable />
        </el-form-item>
        <el-form-item label="名称" v-if="fieldOptionType !== 'property'" key="option-name">
          <el-input v-model="fieldOptionForm.name" clearable />
        </el-form-item>
        <el-form-item label="配置" v-if="fieldOptionType === 'constraint'" key="option-config">
          <el-input v-model="fieldOptionForm.config" clearable />
        </el-form-item>
        <el-form-item label="值" v-if="fieldOptionType === 'property'" key="option-value">
          <el-input v-model="fieldOptionForm.value" clearable />
        </el-form-item>
      </el-form>
      <template slot="footer">
        <el-button size="mini" @click="fieldOptionModelVisible = false">取 消</el-button>
        <el-button size="mini" type="primary" @click="saveFieldOption">确 定</el-button>
      </template>
    </el-dialog>
  </div>
</template>
<script>
export default {
  name: "ElementForm",
  props: {
    id: String,
    type: String
  },
  inject: {
    prefix: "prefix",
    width: "width"
  },
  data() {
    return {
      formKey: "",
      businessKey: "",
      optionModelTitle: "",
      fieldList: [],
      formFieldForm: {},
      fieldType: {
        long: "长整型",
        string: "字符串",
        boolean: "布尔类",
        date: "日期类",
        enum: "枚举类",
        custom: "自定义类型"
      },
      formFieldIndex: -1, // 编辑中的字段, -1 为新增
      formFieldOptionIndex: -1, // 编辑中的字段配置项, -1 为新增
      fieldModelVisible: false,
      fieldOptionModelVisible: false,
      fieldOptionForm: {}, // 当前激活的字段配置项数据
      fieldOptionType: "", // 当前激活的字段配置项弹窗 类型
      fieldEnumList: [], // 枚举值列表
      fieldConstraintsList: [], // 约束条件列表
      fieldPropertiesList: [] // 绑定属性列表
    };
  },
  watch: {
    id: {
      immediate: true,
      handler(val) {
        val && val.length && this.$nextTick(() => this.resetFormList());
      }
    }
  },
  methods: {
    resetFormList() {
      this.bpmnELement = window.bpmnInstances.bpmnElement;
      this.formKey = this.bpmnELement.businessObject.formKey;
      // 获取元素扩展属性 或者 创建扩展属性
      this.elExtensionElements =
        this.bpmnELement.businessObject.get("extensionElements") || window.bpmnInstances.moddle.create("bpmn:ExtensionElements", { values: [] });
      // 获取元素表单配置 或者 创建新的表单配置
      this.formData =
        this.elExtensionElements.values.filter(ex => ex.$type === `${this.prefix}:FormData`)?.[0] ||
        window.bpmnInstances.moddle.create(`${this.prefix}:FormData`, { fields: [] });
      // 业务标识 businessKey, 绑定在 formData 中
      this.businessKey = this.formData.businessKey;
      // 保留剩余扩展元素,便于后面更新该元素对应属性
      this.otherExtensions = this.elExtensionElements.values.filter(ex => ex.$type !== `${this.prefix}:FormData`);
      // 复制原始值,填充表格
      this.fieldList = JSON.parse(JSON.stringify(this.formData.fields || []));
      // 更新元素扩展属性,避免后续报错
      this.updateElementExtensions();
    },
    updateElementFormKey() {
      window.bpmnInstances.modeling.updateProperties(this.bpmnELement, { formKey: this.formKey });
    },
    updateElementBusinessKey() {
      window.bpmnInstances.modeling.updateModdleProperties(this.bpmnELement, this.formData, { businessKey: this.businessKey });
    },
    // 根据类型调整字段type
    changeFieldTypeType(type) {
      this.$set(this.formFieldForm, "type", type === "custom" ? "" : type);
    },
    // 打开字段详情侧边栏
    openFieldForm(field, index) {
      this.formFieldIndex = index;
      if (index !== -1) {
        const FieldObject = this.formData.fields[index];
        this.formFieldForm = JSON.parse(JSON.stringify(field));
        // 设置自定义类型
        this.$set(this.formFieldForm, "typeType", !this.fieldType[field.type] ? "custom" : field.type);
        // 初始化枚举值列表
        field.type === "enum" && (this.fieldEnumList = JSON.parse(JSON.stringify(FieldObject?.values || [])));
        // 初始化约束条件列表
        this.fieldConstraintsList = JSON.parse(JSON.stringify(FieldObject?.validation?.constraints || []));
        // 初始化自定义属性列表
        this.fieldPropertiesList = JSON.parse(JSON.stringify(FieldObject?.properties?.values || []));
      } else {
        this.formFieldForm = {};
        // 初始化枚举值列表
        this.fieldEnumList = [];
        // 初始化约束条件列表
        this.fieldConstraintsList = [];
        // 初始化自定义属性列表
        this.fieldPropertiesList = [];
      }
      this.fieldModelVisible = true;
    },
    // 打开字段 某个 配置项 弹窗
    openFieldOptionForm(option, index, type) {
      this.fieldOptionModelVisible = true;
      this.fieldOptionType = type;
      this.formFieldOptionIndex = index;
      if (type === "property") {
        this.fieldOptionForm = option ? JSON.parse(JSON.stringify(option)) : {};
        return (this.optionModelTitle = "属性配置");
      }
      if (type === "enum") {
        this.fieldOptionForm = option ? JSON.parse(JSON.stringify(option)) : {};
        return (this.optionModelTitle = "枚举值配置");
      }
      this.fieldOptionForm = option ? JSON.parse(JSON.stringify(option)) : {};
      return (this.optionModelTitle = "约束条件配置");
    },
    // 保存字段 某个 配置项
    saveFieldOption() {
      if (this.formFieldOptionIndex === -1) {
        if (this.fieldOptionType === "property") {
          this.fieldPropertiesList.push(this.fieldOptionForm);
        }
        if (this.fieldOptionType === "constraint") {
          this.fieldConstraintsList.push(this.fieldOptionForm);
        }
        if (this.fieldOptionType === "enum") {
          this.fieldEnumList.push(this.fieldOptionForm);
        }
      } else {
        this.fieldOptionType === "property" && this.fieldPropertiesList.splice(this.formFieldOptionIndex, 1, this.fieldOptionForm);
        this.fieldOptionType === "constraint" && this.fieldConstraintsList.splice(this.formFieldOptionIndex, 1, this.fieldOptionForm);
        this.fieldOptionType === "enum" && this.fieldEnumList.splice(this.formFieldOptionIndex, 1, this.fieldOptionForm);
      }
      this.fieldOptionModelVisible = false;
      this.fieldOptionForm = {};
    },
    // 保存字段配置
    saveField() {
      const { id, type, label, defaultValue, datePattern } = this.formFieldForm;
      const Field = window.bpmnInstances.moddle.create(`${this.prefix}:FormField`, { id, type, label });
      defaultValue && (Field.defaultValue = defaultValue);
      datePattern && (Field.datePattern = datePattern);
      // 构建属性
      if (this.fieldPropertiesList && this.fieldPropertiesList.length) {
        const fieldPropertyList = this.fieldPropertiesList.map(fp => {
          return window.bpmnInstances.moddle.create(`${this.prefix}:Property`, { id: fp.id, value: fp.value });
        });
        Field.properties = window.bpmnInstances.moddle.create(`${this.prefix}:Properties`, { values: fieldPropertyList });
      }
      // 构建校验规则
      if (this.fieldConstraintsList && this.fieldConstraintsList.length) {
        const fieldConstraintList = this.fieldConstraintsList.map(fc => {
          return window.bpmnInstances.moddle.create(`${this.prefix}:Constraint`, { name: fc.name, config: fc.config });
        });
        Field.validation = window.bpmnInstances.moddle.create(`${this.prefix}:Validation`, { constraints: fieldConstraintList });
      }
      // 构建枚举值
      if (this.fieldEnumList && this.fieldEnumList.length) {
        Field.values = this.fieldEnumList.map(fe => {
          return window.bpmnInstances.moddle.create(`${this.prefix}:Value`, { name: fe.name, id: fe.id });
        });
      }
      // 更新数组 与 表单配置实例
      if (this.formFieldIndex === -1) {
        this.fieldList.push(this.formFieldForm);
        this.formData.fields.push(Field);
      } else {
        this.fieldList.splice(this.formFieldIndex, 1, this.formFieldForm);
        this.formData.fields.splice(this.formFieldIndex, 1, Field);
      }
      this.updateElementExtensions();
      this.fieldModelVisible = false;
    },
    // 移除某个 字段的 配置项
    removeFieldOptionItem(option, index, type) {
      if (type === "property") {
        this.fieldPropertiesList.splice(index, 1);
        return;
      }
      if (type === "enum") {
        this.fieldEnumList.splice(index, 1);
        return;
      }
      this.fieldConstraintsList.splice(index, 1);
    },
    // 移除 字段
    removeField(field, index) {
      this.fieldList.splice(index, 1);
      this.formData.fields.splice(index, 1);
      this.updateElementExtensions();
    },
    updateElementExtensions() {
      // 更新回扩展元素
      const newElExtensionElements = window.bpmnInstances.moddle.create(`bpmn:ExtensionElements`, {
        values: this.otherExtensions.concat(this.formData)
      });
      // 更新到元素上
      window.bpmnInstances.modeling.updateProperties(this.bpmnELement, {
        extensionElements: newElExtensionElements
      });
    }
  }
};
</script>
src/components/bpmnProcessDesigner/package/penal/task/ElementTask.vue
@@ -1,11 +1,11 @@
<template>
  <div class="panel-tab__content">
    <el-form size="mini" label-width="90px" @submit.native.prevent>
      <el-form-item label="异步延续">
        <el-checkbox v-model="taskConfigForm.asyncBefore" label="异步前" @change="changeTaskAsync" />
        <el-checkbox v-model="taskConfigForm.asyncAfter" label="异步后" @change="changeTaskAsync" />
        <el-checkbox v-model="taskConfigForm.exclusive" v-if="taskConfigForm.asyncAfter || taskConfigForm.asyncBefore" label="排除" @change="changeTaskAsync" />
      </el-form-item>
<!--      <el-form-item label="异步延续">-->
<!--        <el-checkbox v-model="taskConfigForm.asyncBefore" label="异步前" @change="changeTaskAsync" />-->
<!--        <el-checkbox v-model="taskConfigForm.asyncAfter" label="异步后" @change="changeTaskAsync" />-->
<!--        <el-checkbox v-model="taskConfigForm.exclusive" v-if="taskConfigForm.asyncAfter || taskConfigForm.asyncBefore" label="排除" @change="changeTaskAsync" />-->
<!--      </el-form-item>-->
      <component :is="witchTaskComponent" v-bind="$props" />
    </el-form>
  </div>
src/components/bpmnProcessDesigner/package/penal/task/task-components/ProcessExpressionDialog.vue
对比新文件
@@ -0,0 +1,70 @@
<!-- 表达式选择 -->
<template>
  <Dialog title="请选择表达式" v-model="dialogVisible" width="1024px">
    <ContentWrap>
      <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
        <el-table-column label="名字" align="center" prop="name" />
        <el-table-column label="表达式" align="center" prop="expression" />
        <el-table-column label="操作" align="center">
          <template #default="scope">
            <el-button link type="primary" @click="select(scope.row)"> 选择 </el-button>
          </template>
        </el-table-column>
      </el-table>
      <!-- 分页 -->
      <Pagination
        :total="total"
        v-model:page="queryParams.pageNo"
        v-model:limit="queryParams.pageSize"
        @pagination="getList"
      />
    </ContentWrap>
  </Dialog>
</template>
<script>
import { getProcessExpressionPage } from '@/api/bpm/processExpression'
import { CommonStatusEnum } from '@/utils/constants'
export default {
  name: "ProcessExpressionDialog",
  data() {
    return {
      dialogVisible: false,
      loading: true,
      list: [],
      total: 0,
      queryParams: {
        pageNo: 1,
        pageSize: 10,
        type: '',
        status: CommonStatusEnum.ENABLE
      }
    }
  },
  methods: {
    /** 打开弹窗 */
    open() {
      this.queryParams.pageNo = 1
      this.queryParams.type = type
      this.getList()
      this.dialogVisible = true
    },
    /** 查询列表 */
    getList() {
      getProcessExpressionPage(this.queryParams).then(response => {
        this.list = response.data.list;
        this.total = response.data.total;
        this.loading = false;
      })
    },
    /** 提交表单 */
    select(row) {
      this.dialogVisible = false
      const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
      emit('select', row)
    }
  },
}
</script>
src/components/bpmnProcessDesigner/package/penal/task/task-components/UserTask.vue
@@ -1,38 +1,166 @@
<template>
  <div style="margin-top: 16px">
<!--    <el-form-item label="处理用户">-->
<!--      <el-select v-model="userTaskForm.assignee" @change="updateElementTask('assignee')">-->
<!--        <el-option v-for="ak in mockData" :key="'ass-' + ak" :label="`用户${ak}`" :value="`user${ak}`" />-->
<!--      </el-select>-->
<!--    </el-form-item>-->
<!--    <el-form-item label="候选用户">-->
<!--      <el-select v-model="userTaskForm.candidateUsers" multiple collapse-tags @change="updateElementTask('candidateUsers')">-->
<!--        <el-option v-for="uk in mockData" :key="'user-' + uk" :label="`用户${uk}`" :value="`user${uk}`" />-->
<!--      </el-select>-->
<!--    </el-form-item>-->
<!--    <el-form-item label="候选分组">-->
<!--      <el-select v-model="userTaskForm.candidateGroups" multiple collapse-tags @change="updateElementTask('candidateGroups')">-->
<!--        <el-option v-for="gk in mockData" :key="'ass-' + gk" :label="`分组${gk}`" :value="`group${gk}`" />-->
<!--      </el-select>-->
<!--    </el-form-item>-->
    <el-form-item label="到期时间">
      <el-input v-model="userTaskForm.dueDate" clearable @change="updateElementTask('dueDate')" />
  <el-form label-width="100px">
    <el-form-item label="规则类型" prop="candidateStrategy">
      <el-select
        v-model="userTaskForm.candidateStrategy"
        clearable
        style="width: 100%"
        @change="changeCandidateStrategy"
      >
        <el-option
          v-for="dict in ruleOptions"
          :key="dict.value"
          :label="dict.label"
          :value="dict.value"
        />
      </el-select>
    </el-form-item>
    <el-form-item label="跟踪时间">
      <el-input v-model="userTaskForm.followUpDate" clearable @change="updateElementTask('followUpDate')" />
    <el-form-item
      v-if="userTaskForm.candidateStrategy == 10"
      label="指定角色"
      prop="candidateParam"
    >
      <el-select
        v-model="userTaskForm.candidateParam"
        clearable
        multiple
        style="width: 100%"
        @change="updateElementTask"
      >
        <el-option v-for="item in roleOptions" :key="item.id" :label="item.name" :value="item.id" />
      </el-select>
    </el-form-item>
    <el-form-item label="优先级">
      <el-input v-model="userTaskForm.priority" clearable @change="updateElementTask('priority')" />
    <el-form-item
      v-if="userTaskForm.candidateStrategy == 20 || userTaskForm.candidateStrategy == 21"
      label="指定部门"
      prop="candidateParam"
      span="24"
    >
      <!-- 多选 -->
      <el-select ref="selectForm" v-model="userTaskForm.candidateParam" placeholder="请选择">
          <el-option :value="treeDataValue" style="height: auto">
            <el-tree
              ref="treeForm"
              :data="deptTreeOptions"
              node-key="id"
              :props="defaultProps"
              highlight-current
              @node-click="selectDept"
            />
          </el-option>
      </el-select>
<!--      <el-select ref="selectForm" v-model="userTaskForm.candidateParam" placeholder="请选择" clearable multiple>-->
<!--        <el-option hidden :key="1"></el-option>-->
<!--          <el-tree-->
<!--            ref="treeForm"-->
<!--            :data="deptTreeOptions"-->
<!--            :props="defaultProps"-->
<!--            empty-text="加载中,请稍后"-->
<!--            multiple-->
<!--            node-key="id"-->
<!--            show-checkbox-->
<!--            highlight-current-->
<!--            @check-change="selectDept"-->
<!--          />-->
<!--      </el-select>-->
    </el-form-item>
    友情提示:任务的分配规则,使用
    <router-link target="_blank" :to="{path:'/bpm/manager/model'}"><el-link type="danger">流程模型</el-link> </router-link>
    下的【分配规则】替代,提供指定角色、部门负责人、部门成员、岗位、工作组、自定义脚本等 7 种维护的任务分配维度,更加灵活!
  </div>
    <el-form-item
      v-if="userTaskForm.candidateStrategy == 22"
      label="指定岗位"
      prop="candidateParam"
      span="24"
    >
      <el-select
        v-model="userTaskForm.candidateParam"
        clearable
        multiple
        style="width: 100%"
        @change="updateElementTask"
      >
        <el-option v-for="item in postOptions" :key="item.id" :label="item.name" :value="item.id" />
      </el-select>
    </el-form-item>
    <el-form-item
      v-if="userTaskForm.candidateStrategy == 30"
      label="指定用户"
      prop="candidateParam"
      span="24"
    >
      <el-select
        v-model="userTaskForm.candidateParam"
        clearable
        multiple
        style="width: 100%"
        @change="updateElementTask"
      >
        <el-option
          v-for="item in userOptions"
          :key="item.id"
          :label="item.nickname"
          :value="item.id"
        />
      </el-select>
    </el-form-item>
    <el-form-item
      v-if="userTaskForm.candidateStrategy === 40"
      label="指定用户组"
      prop="candidateParam"
    >
      <el-select
        v-model="userTaskForm.candidateParam"
        clearable
        multiple
        style="width: 100%"
        @change="updateElementTask"
      >
        <el-option
          v-for="item in userGroupOptions"
          :key="item.id"
          :label="item.name"
          :value="item.id"
        />
      </el-select>
    </el-form-item>
    <el-form-item
      v-if="userTaskForm.candidateStrategy === 60"
      label="流程表达式"
      prop="candidateParam"
    >
      <el-input
        type="textarea"
        v-model="userTaskForm.candidateParam[0]"
        clearable
        style="width: 72%"
        @change="updateElementTask"
      />
      <el-button class="ml-5px" size="small" type="success" @click="openProcessExpressionDialog"
      >选择表达式</el-button
      >
      <!-- 选择弹窗 -->
      <ProcessExpressionDialog ref="processExpressionDialogRef" @select="selectProcessExpression" />
    </el-form-item>
  </el-form>
</template>
<script>
import { DICT_TYPE, getDictDatas } from '@/utils/dict'
import { getRole, listSimpleRoles } from '@/api/system/role'
import { listSimpleDepts } from '@/api/system/dept'
import { listSimplePosts } from '@/api/system/post'
import { listSimpleUsers } from '@/api/system/user'
import { listSimpleUserGroups } from '@/api/bpm/userGroup'
import ProcessExpressionDialog from './ProcessExpressionDialog.vue'
import SelectTree from '@/components/SelectTree/index.vue'
export default {
  name: "UserTask",
  components: { SelectTree, ProcessExpressionDialog },
  computed: {
    DICT_TYPE() {
      return DICT_TYPE
    },
  },
  props: {
    id: String,
    type: String
@@ -47,8 +175,29 @@
        followUpDate: "",
        priority: ""
      },
      userTaskForm: {},
      mockData: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
      defaultProps: {
        children: 'children',
        label: 'name',
        value: 'id',
        isLeaf: 'leaf',
        emitPath: false // 用于 cascader 组件:在选中节点改变时,是否返回由该节点所在的各级菜单的值所组成的数组,若设置 false,则只返回该节点的值
      },
      userTaskForm: {
        candidateStrategy: undefined, // 分配规则
        candidateParam: [] // 分配选项
      },
      bpmnElement: Object,
      ruleOptions: [],
      roleOptions: [],
      deptTreeOptions: [],
      postOptions: [],
      userOptions: [],
      userGroupOptions: [],
      treeDataValue: '',
      form: {
        // treeData: "", // 单选
        treeData: [], // 多选
      },
    };
  },
  watch: {
@@ -60,27 +209,107 @@
      }
    }
  },
  created() {
    this.getDictDatas();
    this.getRoles();
    this.getDepts();
    this.getPosts();
    this.getUsers();
    this.getUserGroups();
  },
  methods: {
    resetTaskForm() {
      for (let key in this.defaultTaskForm) {
        let value;
        if (key === "candidateUsers" || key === "candidateGroups") {
          value = this.bpmnElement?.businessObject[key] ? this.bpmnElement.businessObject[key].split(",") : [];
        } else {
          value = this.bpmnElement?.businessObject[key] || this.defaultTaskForm[key];
        }
        this.$set(this.userTaskForm, key, value);
      }
    getDictDatas() {
      this.ruleOptions = getDictDatas(DICT_TYPE.BPM_TASK_CANDIDATE_STRATEGY)
    },
    updateElementTask(key) {
      const taskAttr = Object.create(null);
      if (key === "candidateUsers" || key === "candidateGroups") {
        taskAttr[key] = this.userTaskForm[key] && this.userTaskForm[key].length ? this.userTaskForm[key].join() : null;
      } else {
        taskAttr[key] = this.userTaskForm[key] || null;
    // 获得角色列表
    getRoles() {
      listSimpleRoles().then(response => {
        // 处理 roleOptions 参数
        this.roleOptions = [];
        this.roleOptions = response.data;
      })
    },
    getDepts() {
      // 获得部门列表
      listSimpleDepts().then(response => {
        // 处理 deptOptions 参数
        this.deptTreeOptions.push(...this.handleTree(response.data, "id"));
        // console.log(this.deptTreeOptions)
      });
    },
    getPosts() {
      listSimplePosts().then(response => {
        // 处理 postOptions 参数
        this.postOptions = [];
        this.postOptions = response.data;
      });
    },
    getUsers() {
      listSimpleUsers().then(response => {
        this.userOptions = response.data;
      });
    },
    getUserGroups() {
      listSimpleUserGroups().then(response => {
        this.userGroupOptions = response.data;
      });
    },
    changeCandidateStrategy() {
      this.userTaskForm.candidateParam = []
      this.updateElementTask()
    },
    resetTaskForm() {
      const businessObject = this.bpmnElement.businessObject
      if (!businessObject) {
        return
      }
      window.bpmnInstances.modeling.updateProperties(this.bpmnElement, taskAttr);
    }
      console.log(businessObject)
      console.log(businessObject.$attrs['flowable:candidateParam'])
      if (businessObject.$attrs.candidateStrategy != undefined) {
        this.userTaskForm.candidateStrategy = businessObject.$attrs['flowable:candidateStrategy']
      } else {
        this.userTaskForm.candidateStrategy = undefined
      }
      if (businessObject.$attrs['flowable:candidateParam'] && businessObject.$attrs['flowable:candidateParam'].length > 0) {
        this.userTaskForm.candidateStrategy = businessObject.$attrs['flowable:candidateStrategy']
        if (this.userTaskForm.candidateStrategy === 60) {
          // 特殊:流程表达式,只有一个 input 输入框
          this.userTaskForm.candidateParam = [businessObject.$attrs['flowable:candidateParam']]
        } else {
          this.userTaskForm.candidateParam = businessObject.$attrs['flowable:candidateParam']
            .split(',')
            .map((item) => +item)
        }
      } else {
        this.userTaskForm.candidateParam = []
      }
      console.log(this.userTaskForm)
    },
    selectDept(data, checked, indeterminate) {
      console.log(this.userTaskForm.candidateParam)
      console.log(data)
      console.log(checked)
      console.log(indeterminate)
      this.treeDataValue = data
      this.userTaskForm.candidateParam.push(data.name)
      this.form.treeData.push(data.name)
      this.updateElementTask()
    },
    updateElementTask() {
      console.log(this.userTaskForm)
      console.log(this.bpmnElement)
      window.bpmnInstances.modeling.updateProperties(this.bpmnElement, {
        candidateStrategy: this.userTaskForm.candidateStrategy,
        candidateParam: this.userTaskForm.candidateParam.join(',')
      });
    },
    openProcessExpressionDialog() {
      this.$refs('processExpressionDialogRef').open()
    },
    selectProcessExpression(expression) {
      this.userTaskForm.candidateParam = [expression.expression]
      this.updateElementTask()
    },
  },
  beforeDestroy() {
    this.bpmnElement = null;
src/main.js
@@ -69,6 +69,9 @@
Vue.use(VueMeta)
// Vue.use(hljs.vuePlugin);
import _ from 'lodash'
Vue.prototype._ = _
// bpmnProcessDesigner 需要引入
import MyPD from "@/components/bpmnProcessDesigner/package/index.js";
Vue.use(MyPD);
@@ -104,6 +107,11 @@
  i18n: (key, value) => i18n.t(key, value)
});
// import ElementPlus from 'element-plus'
// import 'element-plus/dist/index.css'
//
// Vue.use(ElementPlus)
Vue.config.productionTip = false
new Vue({
src/utils/dict.js
@@ -16,7 +16,6 @@
  SYSTEM_ROLE_TYPE: 'system_role_type',
  SYSTEM_DATA_SCOPE: 'system_data_scope',
  SYSTEM_NOTICE_TYPE: 'system_notice_type',
  SYSTEM_OPERATE_TYPE: 'system_operate_type',
  SYSTEM_LOGIN_TYPE: 'system_login_type',
  SYSTEM_LOGIN_RESULT: 'system_login_result',
  SYSTEM_SMS_CHANNEL_CODE: 'system_sms_channel_code',
@@ -39,12 +38,15 @@
  INFRA_CODEGEN_FRONT_TYPE: 'infra_codegen_front_type',
  INFRA_CODEGEN_SCENE: 'infra_codegen_scene',
  INFRA_FILE_STORAGE: 'infra_file_storage',
  INFRA_OPERATE_TYPE: 'infra_operate_type',
  // ========== BPM 模块 ==========
  BPM_MODEL_CATEGORY: 'bpm_model_category',
  BPM_MODEL_FORM_TYPE: 'bpm_model_form_type',
  BPM_TASK_CANDIDATE_STRATEGY: 'bpm_task_candidate_strategy',
  BPM_TASK_ASSIGN_RULE_TYPE: 'bpm_task_assign_rule_type',
  BPM_PROCESS_INSTANCE_STATUS: 'bpm_process_instance_status',
  BPM_TASK_STATUS: 'bpm_task_status',
  BPM_PROCESS_INSTANCE_RESULT: 'bpm_process_instance_result',
  BPM_TASK_ASSIGN_SCRIPT: 'bpm_task_assign_script',
  BPM_OA_LEAVE_TYPE: 'bpm_oa_leave_type',
src/utils/formGenerator.js
@@ -30,3 +30,20 @@
  return drawingList
}
// // 解码表单 Fields
// export function decodeFields2(fields) {
//   const rule = []
//   fields.forEach((item) => {
//     rule.push(JSON.parse(item))
//   })
//   return rule
// }
// // 设置表单的 Conf 和 Fields,适用 form-create 场景
// export function setConfAndFields2 (detailPreview, conf, fields, value) {
//   detailPreview = detailPreview.value
//   detailPreview.option = JSON.parse(conf)
//   detailPreview.rule = decodeFields2(fields)
//   if (value) {
//     detailPreview.value = value
//   }
// }
src/views/bpm/definition/index.vue
@@ -3,7 +3,7 @@
    <!-- 列表 -->
    <el-table v-loading="loading" :data="list">
      <el-table-column label="定义编号" align="center" prop="id" width="400" />
      <el-table-column label="定义名称" align="center" prop="name" width="100">
      <el-table-column label="流程名称" align="center" prop="name">
        <template v-slot="scope">
          <el-button type="text" @click="handleBpmnDetail(scope.row)">
            <span>{{ scope.row.name }}</span>
@@ -43,13 +43,13 @@
          <span>{{ parseTime(scope.row.deploymentTime) }}</span>
        </template>
      </el-table-column>
      <el-table-column label="定义描述" align="center" prop="description" width="300" show-overflow-tooltip />
      <el-table-column label="操作" align="center" width="150" fixed="right">
        <template v-slot="scope">
          <el-button size="mini" type="text" icon="el-icon-s-custom" @click="handleAssignRule(scope.row)"
                     v-hasPermi="['bpm:task-assign-rule:update']">分配规则</el-button>
        </template>
      </el-table-column>
      <el-table-column label="定义描述" align="center" prop="description" width="200" show-overflow-tooltip />
<!--      <el-table-column label="操作" align="center" width="150" fixed="right">-->
<!--        <template v-slot="scope">-->
<!--          <el-button size="mini" type="text" icon="el-icon-s-custom" @click="handleAssignRule(scope.row)"-->
<!--                     v-hasPermi="['bpm:task-assign-rule:update']">分配规则</el-button>-->
<!--        </template>-->
<!--      </el-table-column>-->
    </el-table>
    <!-- 流程表单配置详情 -->
src/views/bpm/model/index.vue
@@ -86,8 +86,8 @@
                     v-hasPermi="['bpm:model:update']">修改流程</el-button>
          <el-button size="mini" type="text" icon="el-icon-setting" @click="handleDesign(scope.row)"
                     v-hasPermi="['bpm:model:update']">设计流程</el-button>
          <el-button size="mini" type="text" icon="el-icon-s-custom" @click="handleAssignRule(scope.row)"
                     v-hasPermi="['bpm:task-assign-rule:query']">分配规则</el-button>
<!--          <el-button size="mini" type="text" icon="el-icon-s-custom" @click="handleAssignRule(scope.row)"-->
<!--                     v-hasPermi="['bpm:task-assign-rule:query']">分配规则</el-button>-->
          <el-button size="mini" type="text" icon="el-icon-thumb" @click="handleDeploy(scope.row)"
                     v-hasPermi="['bpm:model:deploy']">发布流程</el-button>
          <el-button size="mini" type="text" icon="el-icon-ice-cream-round" @click="handleDefinitionList(scope.row)"
src/views/bpm/processInstance/create/index.vue
@@ -1,7 +1,7 @@
<template>
  <div class="app-container">
    <!-- 第一步,通过流程定义的列表,选择对应的流程 -->
    <div v-if="!selectProcessInstance">
    <div v-if="!selectProcessDefinition">
      <el-table v-loading="loading" :data="list">
        <el-table-column label="流程名称" align="center" prop="name" width="200">
          <template v-slot="scope">
@@ -32,8 +32,8 @@
    <div v-else>
      <el-card class="box-card" >
        <div slot="header" class="clearfix">
          <span class="el-icon-document">申请信息【{{ selectProcessInstance.name }}】</span>
          <el-button style="float: right;" type="primary" @click="selectProcessInstance = undefined">选择其它流程</el-button>
          <span class="el-icon-document">申请信息【{{ selectProcessDefinition.name }}】</span>
          <el-button style="float: right;" type="primary" @click="selectProcessDefinition = undefined">选择其它流程</el-button>
        </div>
        <el-col :span="16" :offset="6">
          <div>
@@ -55,9 +55,10 @@
<script>
import {getProcessDefinitionBpmnXML, getProcessDefinitionList} from "@/api/bpm/definition";
import {DICT_TYPE, getDictDatas} from "@/utils/dict";
import {decodeFields} from "@/utils/formGenerator";
import { decodeFields, setConfAndFields2 } from '@/utils/formGenerator'
import Parser from '@/components/parser/Parser'
import {createProcessInstance} from "@/api/bpm/processInstance";
import { createProcessInstance, getProcessInstance } from '@/api/bpm/processInstance'
import { listSimpleUsers } from '@/api/system/user'
// 流程实例的发起
export default {
@@ -71,10 +72,13 @@
      loading: true,
      // 表格数据
      list: [],
      processInstanceId: this.$route.query.processInstanceId,
      // 流程表单详情
      detailForm: {
        fields: []
        fields: [],
        rule: [],
        option: {},
        value: {}
      },
      // BPMN 数据
@@ -85,6 +89,11 @@
      // 流程表单
      selectProcessInstance: undefined, // 选择的流程实例
      startUserSelectTasks: [],
      startUserSelectAssignees: {},
      startUserSelectAssigneesFormRules: {},
      selectProcessDefinition: undefined,
      userList: [],
      // 数据字典
      categoryDictDatas: getDictDatas(DICT_TYPE.BPM_MODEL_CATEGORY),
@@ -104,28 +113,93 @@
          this.loading = false
        }
      );
      // 如果 processInstanceId 非空,说明是重新发起
      if (this.processInstanceId?.length > 0) {
        let processInstance
          getProcessInstance(this.processInstanceId).then(response => {
            processInstance = response.data
            this.selectProcessInstance = response.data
            if (!processInstance) {
              this.$modal.msgError('重新发起流程失败,原因:流程实例不存在')
              return
            }
            const processDefinition = this.list.find(item => item.key == processInstance.processDefinition?.key
            )
            if (!processDefinition) {
              this.$modal.msgError('重新发起流程失败,原因:流程定义不存在')
              return
            }
            this.handleSelect(processDefinition, processInstance.formVariables)
        })
      }
    },
    /** 处理选择流程的按钮操作 **/
    handleSelect(row) {
    handleSelect(row, formVariables) {
      console.log(row)
      // 设置选择的流程
      this.selectProcessInstance = row;
      // 流程表单
      if (row.formId) {
      this.selectProcessDefinition = row
      // 重置指定审批人
      this.startUserSelectTasks = []
      this.startUserSelectAssignees = {}
      this.startUserSelectAssigneesFormRules = {}
      // 情况一:流程表单
      if (row.formType == 10) {
        // 设置表单
        // 设置对应的表单
        this.detailForm = {
          ...JSON.parse(row.formConf),
          fields: decodeFields(row.formFields)
          fields: decodeFields(row.formFields, formVariables)
        }
        // 加载流程图
        getProcessDefinitionBpmnXML(row.id).then(response => {
          this.bpmnXML = response.data.bpmnXml
          const processDefinitionDetail = response.data
          if (processDefinitionDetail) {
            this.bpmnXML = processDefinitionDetail.bpmnXml
            this.startUserSelectTasks = processDefinitionDetail.startUserSelectTasks
            // 设置指定审批人
            if (this.startUserSelectTasks?.length > 0) {
              this.detailForm.rule.push({
                type: 'startUserSelect',
                props: {
                  title: '指定审批人'
                }
              })
              // 设置校验规则
              for(const userTask of this.startUserSelectTasks) {
                this.startUserSelectAssignees[userTask.id] = []
                this.startUserSelectAssigneesFormRules[userTask.id] = [
                  { required: true, message: '请选择审批人', trigger: 'blur' }
                ]
              }
              // 加载用户列表
              listSimpleUsers().then(response => {
                this.userList = response.data
              })
            }
          }
        })
        // 情况二:业务表单
      } else if (row.formCustomCreatePath) {
        this.$router.push({ path: row.formCustomCreatePath});
          this.$router.push({ path: row.formCustomCreatePath});
        // 这里暂时无需加载流程图,因为跳出到另外个 Tab;
      }
      // 流程表单
      // if (row.formId) {
      //   // 设置对应的表单
      //   this.detailForm = {
      //     ...JSON.parse(row.formConf),
      //     fields: decodeFields(row.formFields)
      //   }
      //
      //   // 加载流程图
      //   getProcessDefinitionBpmnXML(row.id).then(response => {
      //     this.bpmnXML = response.data.bpmnXml
      //   })
      // } else if (row.formCustomCreatePath) {
      //   this.$router.push({ path: row.formCustomCreatePath});
      //   // 这里暂时无需加载流程图,因为跳出到另外个 Tab;
      // }
    },
    /** 提交按钮 */
    submitForm(params) {
@@ -140,7 +214,7 @@
      // 提交表单,创建流程
      const variables = params.values;
      createProcessInstance({
        processDefinitionId: this.selectProcessInstance.id,
        processDefinitionId: this.selectProcessDefinition.id,
        variables: variables
      }).then(response => {
        this.$modal.msgSuccess("发起流程成功");
src/views/bpm/processInstance/detail.vue
@@ -56,9 +56,31 @@
      <el-col :span="16" :offset="4">
        <div class="block">
          <el-timeline>
            <el-timeline-item v-for="(item, index) in tasks" :key="index"
                              :icon="getTimelineItemIcon(item)" :type="getTimelineItemType(item)">
              <p style="font-weight: 700">任务:{{ item.name }}</p>
            <el-timeline-item
              v-if="processInstance.endTime"
              :type="getProcessInstanceTimelineItemType(processInstance)"
            >
              <p style="font-weight: 700">
                结束流程:在 {{ parseTime(processInstance?.endTime) }} 结束
                <dict-tag
                  :type="DICT_TYPE.BPM_PROCESS_INSTANCE_STATUS"
                  :value="processInstance.status"
                />
              </p>
            </el-timeline-item>
            <el-timeline-item v-for="(item, index) in tasks" :key="index" :type="getTaskTimelineItemType(item)">
              <p style="font-weight: 700">
                审批任务:{{ item.name }}
                <dict-tag :type="DICT_TYPE.BPM_TASK_STATUS" :value="item.status" />
              </p>
<!--              <el-button-->
<!--                class="ml-10px"-->
<!--                v-if="item.children"-->
<!--                @click="openChildrenTask(item)"-->
<!--                size="small"-->
<!--              >-->
<!--                <Icon icon="ep:memo" /> 子任务-->
<!--              </el-button>-->
              <el-card :body-style="{ padding: '10px' }">
                <label v-if="item.assigneeUser" style="font-weight: normal; margin-right: 30px;">
                  审批人:{{ item.assigneeUser.nickname }}
@@ -74,9 +96,15 @@
                <label v-if="item.durationInMillis" style="color:#8a909c;font-weight: normal">
                  {{ getDateStar(item.durationInMillis) }} </label>
                <p v-if="item.reason">
                  <el-tag :type="getTimelineItemType(item)">{{ item.reason }}</el-tag>
                  <el-tag :type="getTaskTimelineItemType(item)">审批建议:{{ item.reason }}</el-tag>
                </p>
              </el-card>
            </el-timeline-item>
            <el-timeline-item type="success">
              <p style="font-weight: 700">
                发起流程:【{{ processInstance.startUser?.nickname }}】在
                {{ parseTime(processInstance?.startTime) }} 发起【 {{ processInstance.name }} 】流程
              </p>
            </el-timeline-item>
          </el-timeline>
        </div>
@@ -88,7 +116,7 @@
      <div slot="header" class="clearfix">
        <span class="el-icon-picture-outline">流程图</span>
      </div>
      <my-process-viewer key="designer" v-model="bpmnXML" v-bind="bpmnControlForm" :activityData="activityList"
      <my-process-viewer key="designer" v-model="bpmnXML" v-bind="bpmnControlForm" :prefix="bpmnControlForm.prefix" :activityData="activityList"
                         :processInstanceData="processInstance" :taskData="tasks"/>
    </el-card>
@@ -100,6 +128,9 @@
            <el-option v-for="item in userOptions" :key="parseInt(item.id)" :label="item.nickname"
                       :value="parseInt(item.id)"/>
          </el-select>
        </el-form-item>
        <el-form-item label="转派理由" prop="reason">
          <el-input v-model="updateAssignee.form.reason" clearable placeholder="请输入转派理由" />
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
@@ -128,13 +159,13 @@
    <!--退回流程-->
    <el-dialog title="退回流程" :visible.sync="returnOpen" width="40%" append-to-body>
      <el-form ref="formRef" v-loading="formLoading" :model="formData" :rules="formRules" label-width="110px">
        <el-form-item label="退回节点" prop="targetDefinitionKey">
          <el-select v-model="formData.targetDefinitionKey" clearable style="width: 100%">
        <el-form-item label="退回节点" prop="targetTaskDefinitionKey">
          <el-select v-model="formData.targetTaskDefinitionKey" clearable style="width: 100%">
            <el-option
              v-for="item in returnList"
              :key="item.definitionKey"
              :key="item.taskDefinitionKey"
              :label="item.name"
              :value="item.definitionKey"
              :value="item.taskDefinitionKey"
            />
          </el-select>
        </el-form-item>
@@ -179,7 +210,7 @@
  data() {
    return {
      // 遮罩层
      processInstanceLoading: true,
      processInstanceLoading: false,
      // 流程实例
      id: undefined, // 流程实例的编号
      processInstance: {},
@@ -192,11 +223,11 @@
      returnList: [],
      formData: {
        id: '',
        targetDefinitionKey: undefined,
        targetTaskDefinitionKey: undefined,
        reason: ''
      },
      formRules: {
        targetDefinitionKey: [{required: true, message: '必须选择回退节点', trigger: 'change'}],
        targetTaskDefinitionKey: [{required: true, message: '必须选择回退节点', trigger: 'change'}],
        reason: [{required: true, message: '回退理由不能为空', trigger: 'blur'}]
      },
      returnOpen: false,
@@ -268,12 +299,6 @@
        // 设置流程信息
        this.processInstance = response.data;
        //将业务表单,注册为动态组件
        const path = this.processInstance.processDefinition.formCustomViewPath;
        Vue.component("async-biz-form-component", function (resolve) {
          require([`@/views${path}`], resolve);
        });
        // 设置表单信息
        if (this.processInstance.processDefinition.formType === 10) {
          this.detailForm = {
@@ -289,11 +314,17 @@
              item.__config__.defaultValue = val
            }
          });
        } else {
          //将业务表单,注册为动态组件
          const path = this.processInstance.processDefinition.formCustomViewPath;
          Vue.component("async-biz-form-component", function (resolve) {
            require([`@/views${path}`], resolve);
          });
        }
        // 加载流程图
        getProcessDefinitionBpmnXML(this.processInstance.processDefinition.id).then(response => {
          this.bpmnXML = response.data
          this.bpmnXML = response.data.bpmnXml
        });
        // 加载活动列表
        getActivityList({
@@ -301,10 +332,10 @@
        }).then(response => {
          this.activityList = response.data;
        });
      }).finally(() => {
        // 取消加载中
        this.processInstanceLoading = false;
      });
      })
      // 获得流程任务列表(审批记录)
      this.tasksLoad = true;
@@ -337,7 +368,7 @@
        // 需要审核的记录
        const userId = store.getters.userId;
        this.tasks.forEach(task => {
          if (task.result !== 1 && task.result !== 6) { // 只有待处理才需要
          if (task.status !== 1 && task.status !== 6) { // 只有待处理才需要
            return;
          }
          if (!task.assigneeUser || task.assigneeUser.id !== userId) { // 自己不是处理人
@@ -353,47 +384,43 @@
        this.tasksLoad = false;
      });
    },
    /** 获得流程实例对应的颜色 */
    getProcessInstanceTimelineItemType(item) {
      if (item.status === 2) {
        return 'success'
      }
      if (item.status === 3) {
        return 'danger'
      }
      if (item.status === 4) {
        return 'warning'
      }
      return ''
    },
    /** 获得任务对应的颜色 */
    getTaskTimelineItemType(item) {
      if ([0, 1, 6, 7].includes(item.status)) {
        return 'primary'
      }
      if (item.status === 2) {
        return 'success'
      }
      if (item.status === 3) {
        return 'danger'
      }
      if (item.status === 4) {
        return 'info'
      }
      if (item.status === 5) {
        return 'warning'
      }
      return ''
    },
    getDateStar(ms) {
      return getDate(ms);
    },
    getTimelineItemIcon(item) {
      if (item.result === 1) {
        return 'el-icon-time';
      }
      if (item.result === 2) {
        return 'el-icon-check';
      }
      if (item.result === 3) {
        return 'el-icon-close';
      }
      if (item.result === 4) {
        return 'el-icon-remove-outline';
      }
      if (item.result === 5) {
        return 'el-icon-back'
      }
      return '';
    },
    getTimelineItemType(item) {
      if (item.result === 1) {
        return 'primary';
      }
      if (item.result === 2) {
        return 'success';
      }
      if (item.result === 3) {
        return 'danger';
      }
      if (item.result === 4) {
        return 'info';
      }
      if (item.result === 5) {
        return 'warning';
      }
      if (item.result === 6) {
        return 'default'
      }
      return '';
    },
    /** 处理审批通过和不通过的操作 */
    handleAudit(task, pass) {
@@ -504,7 +531,7 @@
    },
    /** 提交退回任务 */
    submitReturn() {
      if (!this.formData.targetDefinitionKey) {
      if (!this.formData.targetTaskDefinitionKey) {
        this.$modal.msgError("请选择退回节点!");
      }
      this.$refs['formRef'].validate(valid => {
src/views/bpm/processInstance/index.vue
@@ -20,13 +20,7 @@
      </el-form-item>
      <el-form-item label="状态" prop="status">
        <el-select v-model="queryParams.status" placeholder="请选择状态" clearable>
          <el-option v-for="dict in this.getDictDatas(DICT_TYPE.BPM_PROCESS_INSTANCE_STATUS)"
                     :key="dict.value" :label="dict.label" :value="dict.value"/>
        </el-select>
      </el-form-item>
      <el-form-item label="结果" prop="result">
        <el-select v-model="queryParams.result" placeholder="请选择流结果" clearable>
          <el-option v-for="dict in this.getDictDatas(DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT)"
          <el-option v-for="dict in this.getDictDatas(DICT_TYPE.BPM_TASK_STATUS)"
                     :key="dict.value" :label="dict.label" :value="dict.value"/>
        </el-select>
      </el-form-item>
@@ -40,40 +34,34 @@
    <el-row :gutter="10" class="mb8">
      <el-col :span="1.5">
        <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
                   v-hasPermi="['bpm:process-instance:query']">发起流程</el-button>
                   v-hasPermi="['bpm:process-instance:add']">发起流程</el-button>
      </el-col>
      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
    </el-row>
    <!-- 列表 -->
    <el-table v-loading="loading" :data="list">
      <el-table-column label="编号" align="center" prop="id" width="320" />
      <el-table-column label="流程名" align="center" prop="name" />
      <el-table-column label="流程名称" align="center" prop="name" />
      <el-table-column label="流程分类" align="center" prop="category">
        <template v-slot="scope">
          <dict-tag :type="DICT_TYPE.BPM_MODEL_CATEGORY" :value="scope.row.category" />
        </template>
      </el-table-column>
      <el-table-column label="当前审批任务" align="center" prop="tasks">
      <el-table-column label="当前任务" align="center" prop="tasks">
        <template v-slot="scope">
          <el-button v-for="task in scope.row.tasks" :key="task.id" type="text"">
            <span>{{ task.name }}</span>
          <el-button v-for="task in scope.row.tasks" :key="task.id" type="text">
            <span>{{ task.name }} </span>
          </el-button>
        </template>
      </el-table-column>
      <el-table-column label="状态" align="center" prop="status">
      <el-table-column label="流程状态" align="center" prop="status">
        <template v-slot="scope">
          <dict-tag :type="DICT_TYPE.BPM_PROCESS_INSTANCE_STATUS" :value="scope.row.status" />
          <dict-tag :type="DICT_TYPE.BPM_TASK_STATUS" :value="scope.row.status" />
        </template>
      </el-table-column>
      <el-table-column label="结果" align="center" prop="result">
      <el-table-column label="提交时间" align="center" prop="startTime" width="180">
        <template v-slot="scope">
          <dict-tag :type="DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT" :value="scope.row.result"/>
        </template>
      </el-table-column>
      <el-table-column label="提交时间" align="center" prop="createTime" width="180">
        <template v-slot="scope">
          <span>{{ parseTime(scope.row.createTime) }}</span>
          <span>{{ parseTime(scope.row.startTime) }}</span>
        </template>
      </el-table-column>
      <el-table-column label="结束时间" align="center" prop="createTime" width="180">
@@ -81,12 +69,20 @@
          <span>{{ parseTime(scope.row.endTime) }}</span>
        </template>
      </el-table-column>
      <el-table-column label="耗时" align="center" prop="durationInMillis" width="180">
        <template v-slot="scope">
          <span>{{ getDateStar(scope.row.durationInMillis) }}</span>
        </template>
      </el-table-column>
      <el-table-column label="流程编号" align="center" prop="id" width="120" :show-overflow-tooltip="true" />
      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
        <template v-slot="scope">
          <el-button type="text" size="small" icon="el-icon-delete" v-if="scope.row.result === 1"
                     v-hasPermi="['bpm:process-instance:cancel']" @click="handleCancel(scope.row)">取消</el-button>
          <el-button size="mini" type="text" icon="el-icon-edit" @click="handleDetail(scope.row)"
                     v-hasPermi="['bpm:process-instance:query']">详情</el-button>
          <el-button type="text" size="small" icon="el-icon-delete" v-if="scope.row.status === 1"
                     v-hasPermi="['bpm:process-instance:cancel']" @click="handleCancel(scope.row)">取消</el-button>
          <el-button size="mini" type="text" icon="el-icon-plus" v-else @click="handleAdd(scope.row)"
                     v-hasPermi="['bpm:process-instance:create']">重新发起</el-button>
        </template>
      </el-table-column>
    </el-table>
@@ -98,10 +94,17 @@
</template>
<script>
import { getMyProcessInstancePage, cancelProcessInstance } from "@/api/bpm/processInstance";
import { getMyProcessInstancePage, cancelProcessInstance, getProcessInstance } from '@/api/bpm/processInstance'
import { DICT_TYPE } from '@/utils/dict'
import { getDate } from '@/utils/dateUtils'
export default {
  name: "BpmProcessInstance",
  computed: {
    DICT_TYPE() {
      return DICT_TYPE
    }
  },
  components: {
  },
  data() {
@@ -155,8 +158,17 @@
      this.handleQuery();
    },
    /** 新增按钮操作 **/
    handleAdd() {
      this.$router.push({ name: "BpmProcessInstanceCreate"})
    handleAdd(row) {  // 如果是【业务表单】,不支持重新发起
      if (row?.id) {
          getProcessInstance(row.id).then(response => {
            let processDefinitionDetail = response.data.processDefinition
            if (processDefinitionDetail.formType === 20) {
              this.$modal.msgError("重新发起流程失败,原因:该流程使用业务表单,不支持重新发起");
              return
            }
        })
      }
      this.$router.push({ name: "BpmProcessInstanceCreate", query: {processInstanceId: row?.id}})
    },
    /** 取消按钮操作 */
    handleCancel(row) {
@@ -174,6 +186,11 @@
        this.$modal.msgSuccess("取消成功");
      })
    },
    getDateStar(ms) {
      return getDate(ms);
    },
    /** 处理详情按钮 */
    handleDetail(row) {
      this.$router.push({ name: "BpmProcessInstanceDetail", query: { id: row.id}});
src/views/bpm/task/done/index.vue
@@ -17,21 +17,20 @@
    <!-- 列表 -->
    <el-table v-loading="loading" :data="list">
      <el-table-column label="任务编号" align="center" prop="id" width="320" fixed />
      <el-table-column label="任务名称" align="center" prop="name" width="200" />
      <el-table-column label="所属流程" align="center" prop="processInstance.name" width="200" />
      <el-table-column label="流程发起人" align="center" prop="processInstance.startUserNickname" width="120" />
      <el-table-column label="结果" align="center" prop="result">
        <template v-slot="scope">
          <dict-tag :type="DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT" :value="scope.row.result"/>
        </template>
      </el-table-column>
      <el-table-column label="审批意见" align="center" prop="reason" width="200" />
      <el-table-column label="创建时间" align="center" prop="createTime" width="180">
      <el-table-column label="发起人" align="center" prop="processInstance.startUser.nickname" width="120" />
      <el-table-column label="发起时间" align="center" prop="createTime" width="180">
        <template v-slot="scope">
          <span>{{ parseTime(scope.row.createTime) }}</span>
        </template>
      </el-table-column>
      <el-table-column label="当前任务" align="center" prop="name" width="200" />
      <el-table-column label="审批状态" align="center" prop="status">
        <template v-slot="scope">
          <dict-tag :type="DICT_TYPE.BPM_TASK_STATUS" :value="scope.row.status"/>
        </template>
      </el-table-column>
      <el-table-column label="审批建议" align="center" prop="reason" width="200" />
      <el-table-column label="审批时间" align="center" prop="endTime" width="180">
        <template v-slot="scope">
          <span>{{ parseTime(scope.row.endTime) }}</span>
@@ -42,6 +41,7 @@
          <span>{{ getDateStar(scope.row.durationInMillis) }}</span>
        </template>
      </el-table-column>
      <el-table-column label="流程编号" align="center" prop="id" width="120" :show-overflow-tooltip="true" />
      <el-table-column label="操作" align="center" fixed="right" class-name="small-padding fixed-width">
        <template v-slot="scope">
          <el-button size="mini" type="text" icon="el-icon-edit" @click="handleAudit(scope.row)"
@@ -59,9 +59,15 @@
<script>
import {getDoneTaskPage} from '@/api/bpm/task'
import {getDate} from "@/utils/dateUtils";
import { DICT_TYPE } from '@/utils/dict'
export default {
  name: "BpmDoneTask",
  computed: {
    DICT_TYPE() {
      return DICT_TYPE
    }
  },
  components: {
  },
  data() {
src/views/bpm/task/todo/index.vue
@@ -17,25 +17,30 @@
    <!-- 列表 -->
    <el-table v-loading="loading" :data="list">
      <el-table-column label="任务编号" align="center" prop="id" width="320" />
      <el-table-column label="任务名称" align="center" prop="name" />
      <el-table-column label="所属流程" align="center" prop="processInstance.name" />
      <el-table-column label="流程发起人" align="center" prop="processInstance.startUserNickname" />
      <el-table-column label="创建时间" align="center" prop="createTime" width="180">
      <el-table-column label="发起人" align="center" prop="processInstance.startUser.nickname" />
      <el-table-column label="当前任务" align="center" prop="name" />
      <el-table-column label="发起时间" align="center" prop="createTime" width="180">
        <template v-slot="scope">
          <span>{{ parseTime(scope.row.createTime) }}</span>
        </template>
      </el-table-column>
      <el-table-column label="状态" align="center" prop="version" width="80">
<!--      <el-table-column label="状态" align="center" prop="version" width="80">-->
<!--        <template v-slot="scope">-->
<!--          <el-tag type="success" v-if="scope.row.suspensionState === 1">激活</el-tag>-->
<!--          <el-tag type="warning" v-if="scope.row.suspensionState === 2">挂起</el-tag>-->
<!--        </template>-->
<!--      </el-table-column>-->
      <el-table-column label="任务时间" align="center" prop="createTime" width="180">
        <template v-slot="scope">
          <el-tag type="success" v-if="scope.row.suspensionState === 1">激活</el-tag>
          <el-tag type="warning" v-if="scope.row.suspensionState === 2">挂起</el-tag>
          <span>{{ parseTime(scope.row.createTime) }}</span>
        </template>
      </el-table-column>
      <el-table-column label="流程编号" align="center" prop="id" width="120" :show-overflow-tooltip="true"/>
      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
        <template v-slot="scope">
          <el-button size="mini" type="text" icon="el-icon-edit" @click="handleAudit(scope.row)"
                     v-hasPermi="['bpm:task:update']">审批</el-button>
                     v-hasPermi="['bpm:task:update']">办理</el-button>
        </template>
      </el-table-column>
    </el-table>
src/views/infra/apiAccessLog/index.vue
@@ -69,6 +69,13 @@
          <span>{{ scope.row.resultCode === 0 ? '成功' : '失败(' + scope.row.resultMsg + ')' }}</span>
        </template>
      </el-table-column>
      <el-table-column label="操作模块" align="center" prop="operateModule" width="180" />
      <el-table-column label="操作名" align="center" prop="operateName" width="180" />
      <el-table-column label="操作类型" align="center" prop="operateType">
        <template #default="scope">
          <dict-tag :type="DICT_TYPE.INFRA_OPERATE_TYPE" :value="scope.row.operateType" />
        </template>
      </el-table-column>
      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
        <template v-slot="scope">
          <el-button size="mini" type="text" icon="el-icon-view" @click="handleView(scope.row,scope.index)"
@@ -99,6 +106,11 @@
            <el-form-item label="操作结果:">
              <div v-if="form.resultCode === 0">正常</div>
              <div v-else-if="form.resultCode > 0">失败 | {{ form.resultCode }} || {{ form.resultMsg}}</div>
            </el-form-item>
            <el-form-item label="操作模块:">{{ form.operateModule }}</el-form-item>
            <el-form-item label="操作名:">{{ form.operateName }}</el-form-item>
            <el-form-item label="操作类型:">
              <dict-tag :type="DICT_TYPE.INFRA_OPERATE_TYPE" :value="form.operateType"/>
            </el-form-item>
          </el-col>
        </el-row>
@@ -202,7 +214,9 @@
      }).then(response => {
        this.$download.excel(response, 'API 访问日志.xls');
        this.exportLoading = false;
      }).catch(() => {});
      }).finally(() => {
        this.exportLoading = false;
      });
    }
  }
};
src/views/infra/apiErrorLog/index.vue
@@ -220,7 +220,9 @@
      }).then(response => {
        this.$download.excel(response, 'API 错误日志.xls');
        this.exportLoading = false;
      }).catch(() => {});
      }).finally(() => {
        this.exportLoading = false;
      });
    }
  }
};
src/views/infra/config/index.vue
@@ -254,7 +254,9 @@
        }).then(response => {
          this.$download.excel(response, '参数配置.xls');
          this.exportLoading = false;
      }).catch(() => {});
      }).finally(() => {
        this.exportLoading = false;
      });
    },
  }
};
src/views/infra/job/index_rec.vue
@@ -363,12 +363,13 @@
    handleExport() {
      const queryParams = this.queryParams;
      this.$modal.confirm("是否确认导出所有定时任务数据项?").then(() => {
          this.exportLoading = true;
          return exportJob(queryParams);
        }).then(response => {
          this.$download.excel(response, '定时任务.xls');
          this.exportLoading = false;
      }).catch(() => {});
        this.exportLoading = true;
        return exportJob(queryParams);
      }).then(response => {
        this.$download.excel(response, '定时任务.xls');
      }).finally(() => {
        this.exportLoading = false;
      });
    }
  }
};
src/views/system/dict/data.vue
@@ -292,12 +292,13 @@
    handleExport() {
      const queryParams = this.queryParams;
      this.$modal.confirm('是否确认导出所有数据项?').then(() => {
          this.exportLoading = true;
          return exportData(queryParams);
        }).then(response => {
          this.$download.excel(response, '字典数据.xls');
          this.exportLoading = false;
      }).catch(() => {});
        this.exportLoading = true;
        return exportData(queryParams);
      }).then(response => {
        this.$download.excel(response, '字典数据.xls');
      }).finally(() => {
        this.exportLoading = false;
      });
    }
  }
};
src/views/system/dict/index.vue
@@ -244,7 +244,9 @@
      }).then(response => {
        this.$download.excel(response, '字典类型.xls');
        this.exportLoading = false;
      }).catch(() => {});
      }).finally(() => {
        this.exportLoading = false;
      });
    }
  }
};
src/views/system/loginlog/index.vue
@@ -116,16 +116,17 @@
    /** 导出按钮操作 */
    handleExport() {
      this.$modal.confirm('是否确认导出所有操作日志数据项?').then(() => {
          // 处理查询参数
          let params = {...this.queryParams};
          params.pageNo = undefined;
          params.pageSize = undefined;
          this.exportLoading = true;
          return exportLoginLog(params);
        }).then(response => {
          this.$download.excel(response, '登录日志.xls');
          this.exportLoading = false;
      }).catch(() => {});
        // 处理查询参数
        let params = {...this.queryParams};
        params.pageNo = undefined;
        params.pageSize = undefined;
        this.exportLoading = true;
        return exportLoginLog(params);
      }).then(response => {
        this.$download.excel(response, '登录日志.xls');
      }).finally(() => {
        this.exportLoading = false;
      });
    }
  }
};
src/views/system/oauth2/client/index.vue
@@ -163,8 +163,6 @@
    return {
      // 遮罩层
      loading: true,
      // 导出遮罩层
      exportLoading: false,
      // 显示搜索条件
      showSearch: true,
      // 总条数
src/views/system/operatelog/index.vue
@@ -2,29 +2,30 @@
  <div class="app-container">
    <!-- 搜索工作栏 -->
    <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
      <el-form-item label="系统模块" prop="module">
        <el-input v-model="queryParams.module" placeholder="请输入系统模块" clearable style="width: 240px;"
                  @keyup.enter.native="handleQuery"/>
      </el-form-item>
      <el-form-item label="操作人员" prop="userNickname">
        <el-input v-model="queryParams.userNickname" placeholder="请输入操作人员" clearable style="width: 240px;"
                  @keyup.enter.native="handleQuery"/>
      </el-form-item>
      <el-form-item label="类型" prop="type">
        <el-select v-model="queryParams.type" placeholder="操作类型" clearable style="width: 240px">
          <el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYSTEM_OPERATE_TYPE)" :key="parseInt(dict.value)"
                     :label="dict.label" :value="parseInt(dict.value)"/>
      <el-form-item label="操作人" prop="userId">
        <el-select v-model="queryParams.userId" placeholder="请输入操作人员" clearable style="width: 240px">
          <el-option v-for="user in this.userList" :key="user.id" :label="user.nickname" :value="user.id"/>
        </el-select>
      </el-form-item>
      <el-form-item label="状态" prop="status">
        <el-select v-model="queryParams.success" placeholder="操作状态" clearable style="width: 240px">
          <el-option :key="true" label="成功" :value="true"/>
          <el-option :key="false" label="失败" :value="false"/>
        </el-select>
      <el-form-item label="操作模块" prop="type">
        <el-input v-model="queryParams.type" placeholder="请输入操作模块" clearable style="width: 240px;"
                  @keyup.enter.native="handleQuery"/>
      </el-form-item>
      <el-form-item label="操作时间" prop="startTime">
        <el-date-picker v-model="queryParams.startTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
      <el-form-item label="操作名" prop="subType">
        <el-input v-model="queryParams.subType" placeholder="请输入操作名" clearable style="width: 240px;"
                  @keyup.enter.native="handleQuery"/>
      </el-form-item>
      <el-form-item label="操作内容" prop="action">
        <el-input v-model="queryParams.action" placeholder="请输入操作内容" clearable style="width: 240px;"
                  @keyup.enter.native="handleQuery"/>
      </el-form-item>
      <el-form-item label="操作时间" prop="createTime">
        <el-date-picker v-model="queryParams.createTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
                        range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :default-time="['00:00:00', '23:59:59']" />
      </el-form-item>
      <el-form-item label="业务编号" prop="action">
        <el-input v-model="queryParams.bizId" placeholder="请输入业务编号" clearable style="width: 240px;"
                  @keyup.enter.native="handleQuery"/>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
@@ -41,31 +42,19 @@
    </el-row>
    <el-table v-loading="loading" :data="list">
      <el-table-column label="日志编号" align="center" prop="id" />
      <el-table-column label="操作模块" align="center" prop="module" />
      <el-table-column label="操作名" align="center" prop="name" width="180" />
      <el-table-column label="操作类型" align="center" prop="type">
      <el-table-column label="日志编号" align="center" prop="id" width="100" />
      <el-table-column label="操作人" align="center" prop="userName" width="120" />
      <el-table-column label="操作模块" align="center" prop="type" width="120" />
      <el-table-column label="操作名" align="center" prop="subType" width="160" />
      <el-table-column label="操作内容" align="center" prop="action" />
      <el-table-column label="操作日期" align="center" prop="createTime" width="180">
        <template v-slot="scope">
          <dict-tag :type="DICT_TYPE.SYSTEM_OPERATE_TYPE" :value="scope.row.type"/>
          <span>{{ parseTime(scope.row.createTime) }}</span>
        </template>
      </el-table-column>
      <el-table-column label="操作人" align="center" prop="userNickname" />
      <el-table-column label="操作结果" align="center" prop="status">
        <template v-slot="scope">
          <span>{{ scope.row.resultCode === 0 ? '成功' : '失败' }}</span>
        </template>
      </el-table-column>
      <el-table-column label="操作日期" align="center" prop="startTime" width="180">
        <template v-slot="scope">
          <span>{{ parseTime(scope.row.startTime) }}</span>
        </template>
      </el-table-column>
      <el-table-column label="执行时长" align="center" prop="startTime">
        <template v-slot="scope">
          <span>{{ scope.row.duration }}  ms</span>
        </template>
      </el-table-column>
      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
      <el-table-column label="业务编号" align="center" prop="bizId" width="120" />
      <el-table-column label="操作 IP" align="center" prop="userIp" width="120" />
      <el-table-column label="操作" align="center" width="60" class-name="small-padding fixed-width">
        <template v-slot="scope">
          <el-button size="mini" type="text" icon="el-icon-view" @click="handleView(scope.row,scope.index)"
                     v-hasPermi="['system:operate-log:query']">详细</el-button>
@@ -73,7 +62,7 @@
      </el-table-column>
    </el-table>
    <pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
    <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
                @pagination="getList" />
    <!-- 操作日志详细 -->
@@ -87,35 +76,37 @@
            <el-form-item label="链路追踪:">{{ form.traceId }}</el-form-item>
          </el-col>
          <el-col :span="24">
            <el-form-item label="用户信息:">{{ form.userId }} | {{ form.userNickname }} | {{ form.userIp }} | {{ form.userAgent}} </el-form-item>
            <el-form-item label="操作人编号:">{{ form.userId }} </el-form-item>
          </el-col>
          <el-col :span="24">
            <el-form-item label="操作信息:">
              {{ form.module }} | {{ form.name }}
              <dict-tag :type="DICT_TYPE.SYSTEM_OPERATE_TYPE" :value="form.type"/>
              <br /> {{ form.content }}
              <br /> {{ form.exts }}
            </el-form-item>
            <el-form-item label="操作人名字:">{{ form.userName }} </el-form-item>
          </el-col>
          <el-col :span="24">
            <el-form-item label="请求信息:">{{ form.requestMethod }} | {{ form.requestUrl }} </el-form-item>
            <el-form-item label="操作人 IP:">{{ form.userIp }} </el-form-item>
          </el-col>
          <el-col :span="24">
            <el-form-item label="方法名:">{{ form.javaMethod }}</el-form-item>
            <el-form-item label="操作人 UA:">{{ form.userAgent }} </el-form-item>
          </el-col>
          <el-col :span="24">
            <el-form-item label="方法参数:">{{ form.javaMethodArgs }}</el-form-item>
            <el-form-item label="操作模块:">{{ form.type }}</el-form-item>
          </el-col>
          <el-col :span="24">
            <el-form-item label="开始时间:">
              {{ parseTime(form.startTime) }} | {{ form.duration }} ms
            </el-form-item>
            <el-form-item label="操作名:">{{ form.subType }}</el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="操作结果:">
              <div v-if="form.resultCode === 0">正常 | {{ form.resultData}} </div>
              <div v-else-if="form.resultCode > 0">失败 | {{ form.resultCode }} || {{ form.resultMsg}}</div>
            </el-form-item>
          <el-col :span="24">
            <el-form-item label="操作内容:">{{ form.action }}</el-form-item>
          </el-col>
          <el-col :span="24">
            <el-form-item label="拓展参数:">{{ form.extra }}</el-form-item>
          </el-col>
          <el-col :span="24">
            <el-form-item label="请求 URL:">{{ form.requestMethod }} | {{ form.requestUrl }} </el-form-item>
          </el-col>
          <el-col :span="24">
            <el-form-item label="操作时间:">{{ parseTime(form.createTime) }}</el-form-item>
          </el-col>
          <el-col :span="24">
            <el-form-item label="业务编号:">{{ form.bizId }}</el-form-item>
          </el-col>
        </el-row>
      </el-form>
@@ -128,6 +119,7 @@
<script>
import { listOperateLog, exportOperateLog } from "@/api/system/operatelog";
import { listSimpleUsers } from "@/api/system/user";
export default {
  name: "SystemOperateLog",
@@ -153,16 +145,22 @@
      queryParams: {
        pageNo: 1,
        pageSize: 10,
        module: undefined,
        userNickname: undefined,
        businessType: undefined,
        status: undefined,
        startTime: []
        userId: undefined,
        type: undefined,
        subType: undefined,
        action: undefined,
        createTime: [],
        bizId: undefined
      },
      userList: [], // 用户列表
    };
  },
  created() {
    this.getList();
    // 获取用户精简信息列表
    listSimpleUsers().then(res => {
      this.userList = res.data;
    });
  },
  methods: {
    /** 查询登录日志 */
@@ -193,16 +191,17 @@
    /** 导出按钮操作 */
    handleExport() {
      this.$modal.confirm('是否确认导出所有操作日志数据项?').then(() => {
          // 处理查询参数
          let params = {...this.queryParams};
          params.pageNo = undefined;
          params.pageSize = undefined;
          this.exportLoading = true;
          return exportOperateLog(params);
        }).then(response => {
          this.$download.excel(response, '操作日志.xls');
          this.exportLoading = false;
      }).catch(() => {});
        // 处理查询参数
        let params = {...this.queryParams};
        params.pageNo = undefined;
        params.pageSize = undefined;
        this.exportLoading = true;
        return exportOperateLog(params);
      }).then(response => {
        this.$download.excel(response, '操作日志.xls');
      }).finally(() => {
        this.exportLoading = false;
      });
    }
  }
};
src/views/system/post/index.vue
@@ -231,12 +231,13 @@
    handleExport() {
      const queryParams = this.queryParams;
      this.$modal.confirm('是否确认导出所有岗位数据项?').then(() => {
          this.exportLoading = true;
          return exportPost(queryParams);
        }).then(response => {
          this.$download.excel(response, '岗位数据.xls');
          this.exportLoading = false;
      }).catch(() => {});
        this.exportLoading = true;
        return exportPost(queryParams);
      }).then(response => {
        this.$download.excel(response, '岗位数据.xls');
      }).finally(() => {
        this.exportLoading = false;
      });
    }
  }
};
src/views/system/role/index.vue
@@ -480,13 +480,14 @@
    /** 导出按钮操作 */
    handleExport() {
      const queryParams = this.queryParams;
      this.$modal.confirm('是否确认导出所有角色数据项?').then(function() {
          this.exportLoading = true;
          return exportRole(queryParams);
        }).then(response => {
          this.$download.excel(response, '角色数据.xls');
          this.exportLoading = false;
      }).catch(() => {});
      this.$modal.confirm('是否确认导出所有角色数据项?').then(() => {
        this.exportLoading = true;
        return exportRole(queryParams);
      }).then(response => {
        this.$download.excel(response, '角色数据.xls');
      }).finally(() => {
        this.exportLoading = false;
      });
    }
  }
};
src/views/system/sms/log/index.vue
@@ -267,7 +267,9 @@
      }).then(response => {
        this.$download.excel(response, '短信日志.xls');
        this.exportLoading = false;
      }).catch(() => {});
      }).finally(() => {
        this.exportLoading = false;
      });
    },
    /** 详细按钮操作 */
    handleView(row) {
src/views/system/sms/template/index.vue
@@ -330,7 +330,9 @@
      }).then(response => {
        this.$download.excel(response, '短信模板.xls');
        this.exportLoading = false;
      }).catch(() => {});
      }).finally(() => {
        this.exportLoading = false;
      });
    },
    /** 发送短息按钮 */
    handleSendSms(row) {
src/views/system/tenant/index.vue
@@ -292,12 +292,13 @@
      params.pageSize = undefined;
      // 执行导出
      this.$modal.confirm('是否确认导出所有租户数据项?').then(() => {
          this.exportLoading = true;
          return exportTenantExcel(params);
        }).then(response => {
          this.$download.excel(response, '租户.xls');
          this.exportLoading = false;
      }).catch(() => {});
        this.exportLoading = true;
        return exportTenantExcel(params);
      }).then(response => {
        this.$download.excel(response, '租户.xls');
      }).catch(() => {
        this.exportLoading = false;
      });
    },
    /** 套餐名格式化 */
    getPackageName(packageId) {
src/views/system/tenantPackage/index.vue
@@ -101,8 +101,6 @@
    return {
      // 遮罩层
      loading: true,
      // 导出遮罩层
      exportLoading: false,
      // 显示搜索条件
      showSearch: true,
      // 总条数
src/views/system/user/index.vue
@@ -576,8 +576,9 @@
          return exportUser(params);
        }).then(response => {
          this.$download.excel(response, '用户数据.xls');
          this.exportLoading = false;
      }).catch(() => {});
      }).finally(() => {
        this.exportLoading = false;
      })
    },
    /** 导入按钮操作 */
    handleImport() {