zhangyanpeng
2024-03-04 6a72e9e44dc7278e18d55ccd5637b81a1cb2f047
Merge branch 'dev' of http://highdatas.com:9000/r/foundation/designer.approve.2 into dev
38个文件已删除
31个文件已修改
18个文件已添加
1 文件已重命名
5749 ■■■■■ 已修改文件
README.md 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
dist/css/app.4a92317a.css 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
dist/css/chunk-1bdc1793.8a585615.css 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
dist/css/chunk-2b443e6c.100ba754.css 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
dist/css/chunk-2f84cac4.47927f08.css 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
dist/css/chunk-vendors.e6bab8b7.css 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
dist/favicon.ico 补丁 | 查看 | 原始文档 | blame | 历史
dist/fonts/element-icons.535877f5.woff 补丁 | 查看 | 原始文档 | blame | 历史
dist/fonts/element-icons.732389de.ttf 补丁 | 查看 | 原始文档 | blame | 历史
dist/fonts/iconfont.190546d2.woff2 补丁 | 查看 | 原始文档 | blame | 历史
dist/fonts/iconfont.229cda86.ttf 补丁 | 查看 | 原始文档 | blame | 历史
dist/fonts/iconfont.9c81ee89.woff 补丁 | 查看 | 原始文档 | blame | 历史
dist/img/code.09fdd434.png 补丁 | 查看 | 原始文档 | blame | 历史
dist/index.html 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
dist/js/app.d86dbfb0.js 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
dist/js/chunk-1bdc1793.bfb86fc7.js 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
dist/js/chunk-2b443e6c.839497f3.js 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
dist/js/chunk-2f84cac4.5a82d2d2.js 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
dist/js/chunk-560d3c46.d2edc74c.js 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
dist/js/chunk-vendors.230d9157.js 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
public/img.png 补丁 | 查看 | 原始文档 | blame | 历史
public/index.html 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/App.vue 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/design.js 279 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/org.js 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/process.js 补丁 | 查看 | 原始文档 | blame | 历史
src/api/request.js 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/flowDesign.scss 193 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/flowDesignVertical.scss 193 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/common/OrgPicker.vue 19 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/common/Tip.vue 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/common/flowDialog.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/common/route-tab.vue 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/config/application/application-dev.js 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/config/application/application-local.js 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/config/application/application-prod.js 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/config/application/application-uat.js 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/config/application/index.js 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/config/constants.js 358 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/config/mixins.js 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/config/request.js 86 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/config/services.js 103 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/config/shiro.js 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/config/tools.js 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main.js 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/index.js 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/utils.js 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/Index.vue 118 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/admin/FormProcessDesign.vue 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/admin/FormsPanel.vue 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/admin/LayoutHeader.vue 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/admin/layout/FormDesign.vue 533 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/admin/layout/ProcessDesign.vue 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/admin/layout/ProcessDiagramViewer.vue 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/admin/layout/form/FormDesignRender.vue 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/admin/layout/process/ProcessTree.vue 110 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/admin/layout/process/ProcessTreeViewer.vue 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/common/process/config/ApprovalNodeConfig.vue 88 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/common/process/config/ConditionGroupItemConfig.vue 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/common/process/config/ConditionNodeConfig.vue 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/common/process/config/InclusiveGroupItemConfig.vue 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/common/process/config/InclusiveNodeConfig.vue 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/common/process/config/NodeConfig.vue 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/common/process/config/RootNodeConfig.vue 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/common/process/config/SubprocessNodeConfig.vue 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/common/process/config/TaskNodeConfig.vue 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/common/process/nodes/ApprovalNode.vue 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/common/process/nodes/ConditionNode.vue 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/common/process/nodes/InclusiveNode.vue 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/common/process/nodes/Node.vue 130 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/common/process/nodes/SubprocessNode.vue 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/common/process/viewNodes/ApprovalNode.vue 122 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/common/process/viewNodes/CcNode.vue 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/common/process/viewNodes/ConcurrentNode.vue 186 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/common/process/viewNodes/ConditionNode.vue 345 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/common/process/viewNodes/DelayNode.vue 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/common/process/viewNodes/EmptyNode.vue 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/common/process/viewNodes/InclusiveNode.vue 340 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/common/process/viewNodes/Node.vue 220 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/common/process/viewNodes/RootNode.vue 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/common/process/viewNodes/SubprocessNode.vue 143 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/common/process/viewNodes/TaskNode.vue 143 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/common/process/viewNodes/TriggerNode.vue 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/workspace/InitiateProcess.vue 93 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/workspace/SponsorProcess.vue 590 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/workspace/form.js 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/workspace/index.vue 86 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/workspace/process/ProcessInstanceTabs.vue 136 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
README.md
@@ -2,28 +2,36 @@
### 项目结构
```
├─api 接口
├─assets
   |doc 系统介绍 数据结构介绍等
   |public 浏览器上面设置 图标等
   |src
    └─├─assets 资源 css等
│  └─image
├─components 通用组件
      ├─components 通用组件  弹窗
│  └─common
      |     └─ ellipsis.vue 文本超出多少自动转为省略
      |     └─orgPicker 树形架构图 人员/岗位/辖区等
      |     └─flowDialog 弹窗
      |-config 配置文件
      | └─application 应用环境的配置文件 通过index.js切换
├─router 路由
├─store vuex,设计器数据存储
├─utils
      ├─utils 工具方法
└─views 主要页面及视图
    ├─admin
    │  └─layout
    │      ├─form 表单设计
    │      └─process 流程设计
          │  └─ProcessDesign.vue 流程设计入口-组件
          │  └─ProcessDiagramViewer.vue 查看流程-组件
    ├─common
    │  ├─form 表单
    │  │  ├─components 表单组件
    │  │  ├─config 表单组件配置
    │  │  ├─expand 扩展组件
    │  │  └─settings 设置
    │  └─process 流程
    │      ├─config 流程节点设置
    │      └─nodes 流程节点
          │      └─viewNodes 查看流程图 横向
          |   └─InsertButton.vue 点击插入节点的按钮,可以选择后续节点类型,
          例如审批/条件等等
    └─workspace 工作区
          │  └─process 查看审批流的页面入口 引用上面查看流程的组件
```
dist/css/app.4a92317a.css
File was deleted
dist/css/chunk-1bdc1793.8a585615.css
File was deleted
dist/css/chunk-2b443e6c.100ba754.css
File was deleted
dist/css/chunk-2f84cac4.47927f08.css
File was deleted
dist/css/chunk-vendors.e6bab8b7.css
File was deleted
dist/favicon.ico
Binary files differ
dist/fonts/element-icons.535877f5.woff
Binary files differ
dist/fonts/element-icons.732389de.ttf
Binary files differ
dist/fonts/iconfont.190546d2.woff2
Binary files differ
dist/fonts/iconfont.229cda86.ttf
Binary files differ
dist/fonts/iconfont.9c81ee89.woff
Binary files differ
dist/img/code.09fdd434.png
Binary files differ
dist/index.html
File was deleted
dist/js/app.d86dbfb0.js
File was deleted
dist/js/chunk-1bdc1793.bfb86fc7.js
File was deleted
dist/js/chunk-2b443e6c.839497f3.js
File was deleted
dist/js/chunk-2f84cac4.5a82d2d2.js
File was deleted
dist/js/chunk-560d3c46.d2edc74c.js
File was deleted
dist/js/chunk-vendors.230d9157.js
File was deleted
public/img.png
public/index.html
@@ -4,7 +4,7 @@
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
    <link rel="icon" href="<%= BASE_URL %>img.png">
    <title><%= htmlWebpackPlugin.options.title %></title>
  </head>
  <body>
src/App.vue
@@ -7,19 +7,13 @@
</template>
<script>
  //import layout from './components/layout'
  export default {
    components: {},
    data() {
      return {}
    },
    mounted() {
      let beforeUrl = sessionStorage.getItem('router-path')
      if(beforeUrl === undefined || beforeUrl === ''){
        //this.$router.push("/workPanel")
      }else {
        //this.$router.push(beforeUrl)
      }
    },
    methods: {}
@@ -33,7 +27,7 @@
    body {
        margin: 0;
        min-width: 500px;
        background-color: #f5f6f6;
        //background-color: #f5f6f6;
    }
    body,html {margin:0; height:100%;}
    ul {
src/api/design.js
@@ -1,13 +1,6 @@
import request from '@/api/request.js';
// 查询表单组
export function getFormGroups(param) {
  return request({
    url: 'admin/form/group',
    method: 'get',
    params: param,
  });
}
export function getEntitySet(param) {
  param.token="admin";
  return request({
@@ -16,6 +9,7 @@
    data: param,
  });
}
//字典
export function getDict(param) {
  param.token="admin";
  return request({
@@ -24,48 +18,8 @@
    data: param,
  });
}
// 表单排序
export function groupItemsSort(param) {
  return request({
    url: 'admin/form/group/sort',
    method: 'put',
    data: param,
  });
}
// 更新表单组
export function updateGroup(param, method) {
  return request({
    url: 'admin/form/group',
    method: method,
    params: param,
  });
}
// 获取表单分组
export function getGroup() {
  return request({
    url: 'admin/form/group/list',
    method: 'get',
  });
}
// 更新表单
export function updateForm(param) {
  return request({
    url: 'admin/form',
    method: 'put',
    params: param,
  });
}
export function createForm(param) {
  return request({
    url: 'admin/form',
    method: 'post',
    data: param,
  });
}
export function createFlow(param) {
  param.token="admin";
  return request({
@@ -74,13 +28,7 @@
    data: param,
  });
}
// 查询表单详情
export function getFormDetail(id) {
  return request({
    url: 'admin/form/detail/' + id,
    method: 'get',
  });
}
// 查询审批流详情
export function getFlowDetail(param) {
  param.token="admin"
@@ -90,220 +38,13 @@
    data:param
  });
}
export function getFormDetailV2(templateId) {
// 查询审批流详情 不可编辑 横向
export function getWorkSetpsByBusinessId(param) {
  param.token="admin"
  param.dataname="md_position"
  return request({
    url: 'workspace/process/detail',
    method: 'get',
    params: {
      templateId,
    },
  });
}
// 更新表单详情
export function updateFormDetail(param) {
  return request({
    url: 'admin/form/detail',
    method: 'put',
    data: param,
  });
}
// 发起流程
export function startProcess(param) {
  return request({
    url: 'workspace/process/start',
    method: 'POST',
    data: param,
  });
}
// 查询我发起的
export function applyList(data) {
  return request({
    url: 'workspace/process/applyList',
    method: 'POST',
    data,
  });
}
// 查看我的待办
export function todoList(data) {
  return request({
    url: 'workspace/process/toDoList',
    method: 'POST',
    data,
  });
}
export function ccList(data) {
  return request({
    url: 'workspace/process/ccList',
    method: 'POST',
    data,
  });
}
export function submitedTaskList(data) {
  return request({
    url: 'workspace/process/submitedTaskList',
    method: 'POST',
    data,
  });
}
export function deleteProcessInstance(data) {
  return request({
    url: 'workspace/process/deleteProcessInstance',
    method: 'POST',
    data : { data}
  });
}
// 查看我的已办
export function doneList(data) {
  return request({
    url: 'workspace/process/doneList',
    method: 'POST',
    data,
  });
}
// 查询流程详情
export function getProcessInstanceInfo(processInstanceId, taskId) {
  return request({
    url: 'workspace/process/instanceInfo',
    method: 'POST',
    data: { processInstanceId, taskId },
  });
}
// 同意
export function agree(data) {
  return request({
    url: 'workspace/agree',
    method: 'POST',
    data: data,
  });
}
// 委派人
export function delegateTask(data) {
  return request({
    url: 'workspace/delegateTask',
    method: 'POST',
    data: data,
  });
}
// 委派人完成的按钮
export function resolveTask(data) {
  return request({
    url: 'workspace/resolveTask',
    method: 'POST',
    data: data,
  });
}
//  拒绝,驳回
export function refuse(data) {
  return request({
    url: 'workspace/refuse',
    method: 'POST',
    data: data,
  });
}
// 撤回
export function revoke(data) {
  return request({
    url: 'workspace/revoke',
    method: 'POST',
    data: data,
  });
}
// 转办
export function assignee(data) {
  return request({
    url: 'workspace/assignee',
    method: 'POST',
    data: data,
  });
}
// 回退
export function rollback(data) {
  return request({
    url: 'workspace/rollback',
    method: 'POST',
    data: data,
  });
}
// 加签
export function addMulti(data) {
  return request({
    url: 'workspace/addMulti',
    method: 'POST',
    data: data,
  });
}
// 查询加签人信息
export function queryMultiUsersInfo(data) {
  return request({
    url: 'workspace/queryMultiUsersInfo',
    method: 'POST',
    data: data,
  });
}
// 减签
export function deleteMulti(data) {
  return request({
    url: 'workspace/deleteMulti',
    method: 'POST',
    data: data,
  });
}
// 评论
export function comments(data) {
  return request({
    url: 'workspace/comments',
    method: 'POST',
    data: data,
  });
}
// 获取历史任务信息列表
export function historyTaskList(param) {
  return request({
    url: 'workspace/process/historyTaskList',
    method: 'GET',
    params: param,
  });
}
// 上传文件
export function upLoadFileApi(data) {
  return request({
    url: 'workspace/upLoadFile',
    method: 'POST',
    data: data,
  });
}
// 下载文件
export function downLoadFileApi(data) {
  return request({
    url: 'workspace/downLoadFile',
    method: 'POST',
    data: data,
    responseType: 'blob', //必须加,否则可能出现乱码或者文件错误,导致文件无法打开
  });
}
// 查询可退回的节点
export function getRollbackNodes(data) {
  return request({
    url: 'workspace/rollbackNodes',
    method: 'POST',
    data,
    url: 'root/approve/getWorkSetps',
    method: 'post',
    data:param
  });
}
src/api/org.js
File was deleted
src/api/process.js
src/api/request.js
@@ -1,5 +1,6 @@
import Vue from "vue";
import axios from "axios";
import application from '@/config/application';
import { Notification, MessageBox, Message } from "element-ui";
@@ -10,7 +11,7 @@
// 字体图标
const service = axios.create({
    baseURL: Vue.prototype.BASE_URL+":88/api" ,
    baseURL:`${application.baseURL}` ,
    //baseURL: Vue.prototype.BASE_URL + ":8090/admin",
    timeout: 50000
});
@@ -29,9 +30,6 @@
service.interceptors.response.use(
    rsp => {
        // console.log("aaaaaaa》》》rps",rsp.data.data)
        // var a =this.$Utils.decode(rsp.data.data)
        // console.log("解码后的rsp》》》rps",a)
        return rsp;
    },
    // 拦截异常的响应
src/assets/flowDesign.scss
File was deleted
src/assets/flowDesignVertical.scss
File was deleted
src/components/common/OrgPicker.vue
@@ -1,5 +1,5 @@
<template>
  <w-dialog :border="false" closeFree width="600px" @ok="selectOk" :title="title" v-model="visible">
  <flow-dialog :border="false" closeFree width="600px" @ok="selectOk" :title="title" v-model="visible">
    <div class="picker">
      <div class="candidate" v-loading="loading">
        <div class="role-header" >
@@ -36,11 +36,10 @@
        </div>
      </div>
    </div>
  </w-dialog>
  </flow-dialog>
</template>
<script>
import {getOrgTree, getUserByName} from '@/api/org'
import {getEntitySet} from "@/api/design";
export default {
@@ -143,19 +142,7 @@
      }
      return '**'
    },
    searchUser() {
      let userName = this.search.trim()
      this.searchUsers = []
      this.loading = true
      getUserByName({userName: userName}).then(rsp => {
        this.loading = false
        this.searchUsers = rsp.data
        this.selectToLeft()
      }).catch(err => {
        this.loading = false
        this.$message.error("接口异常")
      })
    },
    selectToLeft() {
      let nodes = this.search.trim() === '' ? this.nodes : this.searchUsers;
      nodes.forEach(node => {
src/components/common/Tip.vue
File was deleted
src/components/common/flowDialog.vue
File was renamed from src/components/common/WDialog.vue
@@ -28,9 +28,9 @@
<script>
export default {
  name: "WDialog",
  name: "flowDialog",
  install(Vue) {
    Vue.component("WDialog", this);
    Vue.component("flowDialog", this);
  },
  components: {},
  props: {
src/components/common/route-tab.vue
File was deleted
src/config/application/application-dev.js
New file
@@ -0,0 +1,4 @@
export default {
    baseURL: 'http://grand-dev.highdatas.com',
}
src/config/application/application-local.js
New file
@@ -0,0 +1,4 @@
export default {
    baseURL: 'http://localhost:88/api',
}
src/config/application/application-prod.js
New file
@@ -0,0 +1,9 @@
export default {
    baseURL: '',
    /*
      钉钉配置
       */
    dd: {
        corpid: 'ding6b6a824032ecd975bc961a6cb783455b'
    }
}
src/config/application/application-uat.js
New file
@@ -0,0 +1,4 @@
export default {
    baseURL: 'https://csec.jemincare.com',
}
src/config/application/index.js
New file
@@ -0,0 +1,8 @@
import local from './application-local';
import prod from './application-prod';
import dev from './application-dev';
import uat from './application-uat';
export default {
    ...local,
};
src/config/constants.js
File was deleted
src/config/mixins.js
File was deleted
src/config/request.js
File was deleted
src/config/services.js
File was deleted
src/config/shiro.js
File was deleted
src/config/tools.js
File was deleted
src/main.js
@@ -13,17 +13,14 @@
import "@/assets/iconfont/iconfont.css"
import Ellipsis from '@/components/common/Ellipsis'
import WDialog from '@/components/common/WDialog'
import Tip from '@/components/common/Tip'
import flowDialog from '@/components/common/flowDialog.vue'
import Utils from '@/utils/utils.js'
Vue.use(ElementUI);
Vue.use(Ellipsis);
Vue.use(WDialog);
Vue.use(Tip);
Vue.use(flowDialog);
Vue.use(vueEsign)
Vue.prototype.$cssSrc=require('@/assets/flowDesignVertical.scss');
Vue.prototype.$isVertical=true;
Vue.config.productionTip = false
Vue.prototype.$Utils = Utils
src/router/index.js
@@ -16,14 +16,6 @@
      redirect: "/formsPanel",
    },
    {
      path: "/index",
      name: "index",
      component: () => import("@/views/Index.vue"),
      meta: { title: "OA工作流", viewport: viewport },
    },
    {
      path: "/formsPanel",
      name: "formsPanel",
      component: () => import("@/views/admin/FormsPanel.vue"),
src/utils/utils.js
@@ -37,5 +37,77 @@
        return data;
    },
    convertToTreeData(data, parent,index,parentId) {
        const tempJson = data.find(f =>
            f.index_no === index
        )
        parent.children = {
            "id": tempJson.id,
            "parentId": parentId,
            "name": "审批人",
            "type": "APPROVAL",
            "children":{},//必须要 不然选中的值无法传递
            "props":
                {
                    "assignedType": "ASSIGN_USER",
                    "nobody": {
                        "handler": "TO_PASS",
                        "assignedUser": []
                    },
                    "refuse": {
                        "type": "TO_END",
                        "target": ""
                    },
                    "assignedUser": [{"id": tempJson.board_id||tempJson.rule_code, "name": tempJson.name,"type":tempJson.board_id!=null?"group":"staff"}],
                    "approvalGroup":tempJson.board_id!=null?{"id":tempJson.board_id,"name":tempJson.name}:"",
                    "staffGroup":tempJson.board_id!=null?"":{"id":tempJson.rule_code,"name":tempJson.name},
                }
        }
        //节点数组的大小 大于当前循环的 说明还有子项 继续循环
        if (data.length >index) {
            let getChildren = this.convertToTreeData(data, parent.children, index+1,parent.children.id)
            parent.children=getChildren
        }
        return parent;
    },
    //审批流树形结构转换为json
    dataTree(data, index, parentId) {
        let result = [];//存放结果
        let names = [];//存放审批人
        let ruleCode = [];//架构组
        data.props.assignedUser.forEach(org => names.push(org.name));
        let ids = []
        data.props.assignedUser.forEach(org => ids.push(org.id));
        // 修改的时候 根据选中类型是架构还是审批组 渲染数据
        if(data.props.assignedUser[0].type==='staff'){
            ids = [];
            data.props.assignedUser.forEach(org => ruleCode.push(org.id));
        }else{
            //判断选中的数据类型是架构还是审批组 如果是架构 将审批组清空 board_id置为空
            if (data.props.staffGroup!=undefined&& Object.keys(data.props.staffGroup).length!==0) {
                ids = [];
                data.props.assignedUser.forEach(org => ruleCode.push(org.id));
            }
        }
        //插入流程数组项
        result.push(
            {
                name: String(names).replaceAll(',', '、'),
                id: data.id||'',
                parent_id: parentId||'',
                index_no: index,
                board_id: ids.length > 0 ? String(ids).replaceAll(',', '、') : null,
                rule_code: ruleCode.length > 0 ? String(ruleCode).replaceAll(',', '、') : null
            }
        ) // 只取当前节点的信息,不包括 children
        //如果下面还有子项 继续往下读数据
        if (data.children!==undefined&&Object.keys(data.children).length !== 0) {
            let getChildren = this.dataTree(data.children, index + 1, parentId)
            result = result.concat(getChildren)
        }
        return result
    },
};
src/views/Index.vue
File was deleted
src/views/admin/FormProcessDesign.vue
@@ -6,7 +6,7 @@
    <div class="layout-body">
      <process-design ref="processDesign" v-show="activeSelect === 'processDesign'"/>
    </div>
    <w-dialog :showFooter="false" v-model="validVisible" title="设置项检查">
    <flow-dialog :showFooter="false" v-model="validVisible" title="设置项检查">
      <el-result :icon="validIcon" :title="errTitle" :subTitle="validResult.desc">
        <i slot="icon" style="font-size: 30px" v-if="!validResult.finished" class="el-icon-loading"></i>
        <div slot="subTitle" class="err-info" v-if="validResult.errs.length > 0">
@@ -27,14 +27,14 @@
          </el-form>
        </template>
      </el-result>
    </w-dialog>
    </flow-dialog>
  </el-container>
</template>
<script>
import LayoutHeader from './LayoutHeader'
import {getFormDetail, createForm, updateFormDetail, createFlow, getFlowDetail} from '@/api/design'
import {createFlow, getFlowDetail} from '@/api/design'
import ProcessDesign from '@/views/admin/layout/ProcessDesign'
@@ -105,50 +105,16 @@
  },
  methods: {
    convertToTreeData(data, parent,index,parentId) {
      //index从1开始 从index_no=1的节点开始往下一个个找
        const tempJson = data.find(f =>
            f.index_no === index
        )
        console.log("tempJson",tempJson);
        //解析子审批节点
        parent.children = {
          "id": tempJson.id,
          "parentId": parentId,
          "name": "审批人",
          "type": "APPROVAL",
          "props":
              {
                "assignedType": "ASSIGN_USER",
                "nobody": {
                  "handler": "TO_PASS",
                  "assignedUser": []
                },
                "refuse": {
                  "type": "TO_END",
                  "target": ""
                },
                "assignedUser": [{"id": tempJson.board_id||tempJson.rule_code, "name": tempJson.name,"type":tempJson.board_id!=null?"group":"staff"}],
                "approvalGroup":tempJson.board_id!=null?{"id":tempJson.board_id,"name":tempJson.name}:"",
                "staffGroup":tempJson.board_id!=null?"":{"id":tempJson.rule_code,"name":tempJson.name},
              }
        }
        //如果传过来的审批节点数组大于当前索引  说明下面还有
      if (data.length >index) {
        let getChildren = this.convertToTreeData(data, parent.children, index+1,parent.children.id)
         parent.children=getChildren
      }
      return parent;
    },
    loadFormInfo(formId) {
      let param = {"id": formId}
      //获取流程详情
      getFlowDetail(param).then(rsp => {
        let form = rsp.data.data;
        console.log("getFlowDetail-form", form)
        form.logo = ""
        form.formItems = []
        form.process =this.convertToTreeData(form.steps, {
        form.process =this.$Utils.convertToTreeData(form.steps, {
          "id": form.id, "parentId": null,
          "type": "ROOT",
          "name": "发起人",
@@ -162,7 +128,7 @@
        form.templateName = form.name
        form.groupId = null;
        console.log("输出转换后的form,",form)
        this.$store.commit('loadForm', form)
      }).catch(err => {
        this.$message.error(err)
@@ -203,7 +169,6 @@
      })
    },
    validateDesign() {
      console.log('000')
      this.validVisible = true
      this.validStep = 0
      this.showValiding()
@@ -285,47 +250,6 @@
    publishProcess() {
      this.validateDesign()
    },
    //审批流树形结构转换为json
    dataTree(data, index, parentId) {
      let result = [];
      let names = [];
      let ruleCode = [];
      console.log("data》》》",data)
      data.props.assignedUser.forEach(org => names.push(org.name));
      let ids = []
      data.props.assignedUser.forEach(org => ids.push(org.id));
      // 修改的时候 根据选中类型是架构还是审批组 渲染数据
      if(data.props.assignedUser[0].type==='staff'){
        ids = [];
        data.props.assignedUser.forEach(org => ruleCode.push(org.id));
      }else{
        //判断选中的数据类型是架构还是审批组 如果是架构 将审批组清空 board_id置为空
        if (data.props.staffGroup!=undefined&& Object.keys(data.props.staffGroup).length!==0) {
          ids = [];
          data.props.assignedUser.forEach(org => ruleCode.push(org.id));
        }
      }
      //插入流程数组项
      result.push(
          {
            name: String(names).replaceAll(',', '、'),
            id: data.id||'',
            parent_id: parentId||'',
            index_no: index,
            board_id: ids.length > 0 ? String(ids).replaceAll(',', '、') : null,
            rule_code: ruleCode.length > 0 ? String(ruleCode).replaceAll(',', '、') : null
          }
      ) // 只取当前节点的信息,不包括 children
      console.log("result>>>",result)
      //如果下面还有子项 继续往下读数据
      if (data.children!==undefined&&Object.keys(data.children).length !== 0) {
        let getChildren = this.dataTree(data.children, index + 1, parentId)
        result = result.concat(getChildren)
      }
      return result
    },
    doPublish() {
      this.$confirm('确认发布后流程立即生效,是否继续?', '提示', {
@@ -334,17 +258,14 @@
        type: 'warning'
      }).then(() => {
        let processNew = JSON.parse(JSON.stringify(this.setup.process));
        console.log("processNew", processNew)
        //判断条件分支
        this.conditionRecursion(processNew);
        let data = {};
        data.name = this.form.name;
        data.id = processNew.id||''
        var i = 1;
        let i = 1;
        //转数据结构
        data.steps = this.dataTree(processNew.children, i, processNew.id);
        data.steps = this.$Utils.dataTree(processNew.children, i, processNew.id);
        let template = {data}
        console.log("新数组结构-template", template);
        createFlow(template).then(rsp => {
          this.$message.success("创建流程成功")
          this.$router.push("/formsPanel")
src/views/admin/FormsPanel.vue
@@ -18,15 +18,10 @@
          <div style="float: right">
            <el-button type="text" icon="el-icon-edit-outline" size="mini" @click="editFrom(item, groups)">编辑
            </el-button>
            <el-button type="text" :icon="item.isStop ? 'el-icon-check':'el-icon-close'" size="mini"
                       @click="stopFrom(item)">
              {{ item.isStop ? '启用' : '停用' }}
            </el-button>
            <el-button type="text" size="mini" @click="viewGroup(item,groups)">查看流程</el-button>
            <el-button type="text" icon="el-icon-delete" size="mini" @click="moveFrom(item)" v-if="item.isStop">删除
            </el-button>
            <el-popover placement="left" trigger="click" width="400" style="margin-left: 10px"
                        @show="moveSelect === null" v-else>
                        @show="moveSelect === null" >
              <el-radio-group v-model="moveSelect" size="mini">
                <el-radio :label="g.id" border v-for="g in groups" :key="g.id" v-show="g.id > 1"
                          :disabled="g.id === groups.id" style="margin: 10px;">{{ g.name }}
@@ -46,7 +41,7 @@
<script>
import draggable from "vuedraggable";
import {
  groupItemsSort, updateForm, getEntitySet
  groupItemsSort, getEntitySet
} from '@/api/design'
export default {
@@ -67,31 +62,20 @@
    getGroups() {
      let template = {"dataname": "sys_state_approve"}
      getEntitySet(template).then(rsp => {
        console.log("getEntitySet", rsp.data.data)
        this.groups = rsp.data.data.entityset
        console.log("this.groups", this.groups)
        //this.groups = rsp.data
      }).catch(err => this.$message.error('获取审批流程异常'))
    },
    newProcess(groupId) {
      this.$store.commit("setTemplate", this.getTemplateData());
      this.$store.commit("setIsEdit", false);
      this.$router.push("/admin/design?groupId=" + groupId);
    },
    groupSort() {
      this.groupsSort = false
      groupItemsSort(this.groups).then(rsp => {
        this.$message.success("排序成功")
        this.getGroups()
      }).catch(err => {
        this.getGroups()
        this.$message.error(err.response.data)
      })
    },
    viewGroup(item, group) {
      console.log("viewGroup",item)
      this.$router.push(
          {
            path: "/workspace/process/instance/tabs",
@@ -103,53 +87,12 @@
      );
    },
    updateForm(item, type) {
      updateForm({templateId: item.templateId, type: type}).then(rsp => {
        this.$message.success(rsp.data)
        this.getGroups()
      }).catch(err => this.$message.error(err.response.data))
    },
    getTemplateData(data, group) {
      return data
    },
    editFrom(item, group) {
      console.log("item",item)
      this.$router.push("/admin/design?code=" + item.id);
    },
    stopFrom(item) {
      console.log(item)
      let tip = '是否删除此流程?';
      this.$confirm(`<span style="font-weight: bold">${item.name}</span>` + tip, '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
        dangerouslyUseHTMLString: true,
      }).then(() => {
        this.updateForm(item, (item.isStop ? 'using' : 'stop'));
      })
    },
    moveFrom(item) {
      if (item.isStop) {
        this.$confirm(`您确定要删除表单 <span style="font-weight: bold">${item.name}</span>> 吗,删除后无法恢复,是否继续?`, '提示', {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'warning',
          dangerouslyUseHTMLString: true,
        }).then(() => {
          this.updateForm(item, 'delete');
        })
      } else {
        if (this.moveSelect === null || this.moveSelect === '') {
          this.$message.error('请选择分组')
          return;
        }
        updateForm({templateId: item.templateId, type: 'move', groupId: this.moveSelect}).then(rsp => {
          this.$message.success(rsp.data)
          this.getGroups()
          this.moveSelect = null
        }).catch(err => this.$message.error(err.response.data))
      }
    },
  }
}
</script>
src/views/admin/LayoutHeader.vue
@@ -5,7 +5,6 @@
               @select="handleSelect">
        <el-menu-item index="processDesign" @click="to('processDesign')">审批流程
        </el-menu-item>
<!--        <el-menu-item index="proSetting" @click="to('proSetting')">扩展设置</el-menu-item>-->
      </el-menu>
      <div class="publish">
@@ -17,9 +16,7 @@
      </div>
    </div>
    <el-dialog title="请使用手机扫码预览" :visible.sync="viewCode" width="300px" :close-on-click-modal="false" center>
      <img src="../../assets/image/code.png" width="250" height="250">
    </el-dialog>
  </div>
</template>
@@ -83,7 +80,6 @@
      this.$emit('input', path)
    },
    handleSelect(key, keyPath) {
      console.log(key, keyPath);
    },
    listener() {
      window.onunload = this.closeBefore()
src/views/admin/layout/FormDesign.vue
File was deleted
src/views/admin/layout/ProcessDesign.vue
@@ -1,14 +1,10 @@
<template>
  <el-main>
    <div class="switchStyle">
      <el-button  size="small" @click="switchCss" >切换</el-button>
    </div>
    <div class="scale">
      <el-button icon="el-icon-plus" size="small" @click="scale += 10" :disabled="scale >= 150" circle></el-button>
      <span>{{ scale }}%</span>
      <el-button icon="el-icon-minus" size="small" @click="scale -= 10" :disabled="scale <= 40" circle></el-button>
<!--      <el-button @click="validate">校验流程</el-button>-->
    </div>
    <div class="design" :style="'transform: scale('+ scale / 100 +');'">
      <process-tree ref="process-tree" @selectedNode="nodeSelected"/>
@@ -49,7 +45,6 @@
  },
  computed:{
    selectedNode(){
      console.log("ProcessDesign-selectedNode")
      return this.$store.state.selectedNode
    }
  },
@@ -57,26 +52,11 @@
  },
  methods: {
    switchCss(){
      console.log(this.$cssSrc)
      console.log(this.$isVertical)
      if(this.$isVertical){
        this.$cssSrc=require('@/assets/flowDesign.scss')
        this.$isVertical=false;
      }else{
        this.$cssSrc=require('@/assets/flowDesignVertical.scss')
        this.$isVertical=true;
      }
      this.$forceUpdate()
    },
    validate(){
      return this.$refs["process-tree"].validateProcess()
    },
    nodeSelected(node){
      console.log('配置节点aaa', node)
      this.showConfig = true
    }
  },
src/views/admin/layout/ProcessDiagramViewer.vue
@@ -1,11 +1,11 @@
<template>
  <el-main>
  <el-main style="overflow:auto;padding: 0px;height: 90vh">
    <div class="scale">
      <el-button icon="el-icon-plus" size="small" @click="scale += 10" :disabled="scale >= 150" circle></el-button>
      <span>{{ scale }}%</span>
      <el-button icon="el-icon-minus" size="small" @click="scale -= 10" :disabled="scale <= 40" circle></el-button>
    </div>
    <div  class="design" :style="'transform: scale('+ scale / 100 +');'">
    <div  class="design" :style="'transform: scale('+ scale / 100 +');transform-origin:left top'">
      <process-tree-viewer ref="process-tree" @selectedNode="nodeSelected"/>
    </div>
    <el-drawer :title="selectedNode.name" :visible.sync="showConfig"
@@ -36,7 +36,7 @@
  components: { ProcessTreeViewer,NodeConfig },
  data() {
    return {
      scale: 100,
      scale: 90,
      selected: {},
      showInput: false,
      showConfig: false
@@ -44,7 +44,6 @@
  },
  computed:{
    selectedNode(){
      console.log("ProcessDiagramViewer-selectedNode",this.$store.state)
      return this.$store.state.selectedNode
    }
  },
@@ -56,13 +55,11 @@
  },
  methods: {
    test(){
      console.log("test")
    },
    validate(){
      return this.$refs["process-tree"].validateProcess()
    },
    nodeSelected(node){
      console.log('配置节点bbb', node)
      this.showConfig = true
    }
  },
src/views/admin/layout/form/FormDesignRender.vue
File was deleted
src/views/admin/layout/process/ProcessTree.vue
@@ -14,7 +14,6 @@
import Subprocess from '@/views/common/process/nodes/SubprocessNode.vue'
import DefaultProps from "./DefaultNodeProps"
export default {
  name: "ProcessTree",
  components: {Node, Root, Approval, Task, Cc, Trigger, Concurrent, Condition, Inclusive, Delay, Empty, Subprocess},
@@ -40,6 +39,7 @@
    ]))
    return h('div', {class:{'_root': true}, ref:'_root'}, processTrees)
  },
  methods: {
    getDomTree(h, node) {
      this.toMapping(node);
@@ -88,6 +88,7 @@
    //解码渲染的时候插入dom到同级
    decodeAppendDom(h, node, dom, props = {}){
      props.config = node
      console.log("decodeAppendDom",props)
      dom.unshift(h(node.type.toLowerCase(), {
        props: props,
        ref: node.id,
@@ -106,7 +107,6 @@
    //id映射到map,用来向上遍历
    toMapping(node){
      if (node && node.id){
        //console.log("node=> " + node.id + " name:" + node.name + " type:" + node.type)
        this.nodeMap.set(node.id, node)
      }
    },
@@ -170,28 +170,30 @@
      return node.type === 'CONCURRENTS'
    },
    getRandomId(){
      console.log("tree生成节点id")
      return '';
      //return `HighDatas_${new Date().getTime().toString().substring(5)}${Math.round(Math.random()*9000+1000)}`
    },
    //选中一个节点
    selectNode(node){
      this.$store.commit('selectedNode', node)
      this.$store.commit('selectedNode', node);
      this.$emit('selectedNode', node)
    },
    //处理节点插入逻辑
    insertNode(type, parentNode){
      this.$refs['_root'].click()
      //缓存一下后面的节点
      let afterNode = parentNode.children
      //插入新节点
      parentNode.children = {
        id: this.getRandomId(),
        parentId: parentNode.id,
        props: {},
        type: type,
        type: type
      }
      switch (type){
        case 'APPROVAL': this.insertApprovalNode(parentNode, afterNode); break;
        case 'SUBPROCESS' : this.insertApprovalNode(parentNode, afterNode); break;
        case 'TASK': this.insertTaskNode(parentNode); break;
@@ -213,15 +215,17 @@
        if (afterNode && afterNode.id){
          afterNode.parentId = parentNode.children.id
        }
        this.$set(parentNode.children, 'children', afterNode)
        this.$set(parentNode.children, 'children', Object)
      }
      this.$forceUpdate()
    },
    insertApprovalNode(parentNode){
      console.log("tree 添加通过节点",parentNode.children)
      this.$set(parentNode.children, "name", "审批人")
      console.log("tree-DefaultProps.APPROVAL_PROPS)",DefaultProps.APPROVAL_PROPS)
      this.$set(parentNode.children, "props", this.$deepCopy(DefaultProps.APPROVAL_PROPS))
    },
    insertTaskNode(parentNode){
      this.$set(parentNode.children, "name", "办理人")
@@ -255,6 +259,7 @@
          name: "条件1",
          children:{}
        },{
          id: this.getRandomId(),
          parentId: parentNode.children.id,
          type: "CONDITION",
@@ -336,7 +341,7 @@
    },
    //删除当前节点
    delNode(node){
      console.log("ProcessTree删除节点", node)
      //获取该节点的父节点
      let parentNode = this.nodeMap.get(node.parentId)
      if (parentNode){
@@ -435,6 +440,89 @@
}
</script>
<style lang="scss"  :src="$cssSrc"  >
<style lang="less" scoped>
._root{
  margin: 0 auto;
}
.process-end{
  width: 60px;
  margin: 0 auto;
  margin-bottom: 20px;
  border-radius: 15px;
  padding: 5px 10px;
  font-size: small;
  color: #747474;
  background-color: #f2f2f2;
  box-shadow: 0 0 10px 0 #bcbcbc;
}
.primary-node{
  display: flex;
  align-items: center;
  flex-direction: column;
}
.branch-node{
  display: flex;
  justify-content: center;
  /*border-top: 2px solid #cccccc;
  border-bottom: 2px solid #cccccc;*/
}
.branch-node-item{
  position: relative;
  display: flex;
  background: #f5f6f6;
  flex-direction: column;
  align-items: center;
  border-top: 2px solid #cccccc;
  border-bottom: 2px solid #cccccc;
  &:before{
    content: "";
    position: absolute;
    top: 0;
    left: calc(50% - 1px);
    margin: auto;
    width: 2px;
    height: 100%;
    background-color: #CACACA;
  }
  .line-top-left, .line-top-right, .line-bot-left, .line-bot-right{
    position: absolute;
    width: 50%;
    height: 4px;
    background-color: #f5f6f6;
  }
  .line-top-left{
    top: -2px;
    left: -1px;
  }
  .line-top-right{
    top: -2px;
    right: -1px;
  }
  .line-bot-left{
    bottom: -2px;
    left: -1px;
  }
  .line-bot-right{
    bottom: -2px;
    right: -1px;
  }
}
.add-branch-btn{
  position: absolute;
  width: 80px;
  .add-branch-btn-el{
    z-index: 999;
    position: absolute;
    top: -15px;
  }
}
.empty-node{
  display: flex;
  justify-content: center;
  flex-direction: column;
  align-items: center;
}
</style>
src/views/admin/layout/process/ProcessTreeViewer.vue
@@ -1,16 +1,16 @@
<script>
//导入所有节点组件
import Approval from '@/views/common/process/nodes/ApprovalNode.vue'
import Task from '@/views/common/process/nodes/TaskNode.vue'
import Cc from '@/views/common/process/nodes/CcNode.vue'
import Concurrent from '@/views/common/process/nodes/ConcurrentNode.vue'
import Condition from '@/views/common/process/nodes/ConditionNode.vue'
import Inclusive from '@/views/common/process/nodes/InclusiveNode.vue'
import Trigger from '@/views/common/process/nodes/TriggerNode.vue'
import Delay from '@/views/common/process/nodes/DelayNode.vue'
import Empty from '@/views/common/process/nodes/EmptyNode.vue'
import Root from '@/views/common/process/nodes/RootNode.vue'
import Node from '@/views/common/process/nodes/Node.vue'
import Approval from '@/views/common/process/viewNodes/ApprovalNode.vue'
import Task from '@/views/common/process/viewNodes/TaskNode.vue'
import Cc from '@/views/common/process/viewNodes/CcNode.vue'
import Concurrent from '@/views/common/process/viewNodes/ConcurrentNode.vue'
import Condition from '@/views/common/process/viewNodes/ConditionNode.vue'
import Inclusive from '@/views/common/process/viewNodes/InclusiveNode.vue'
import Trigger from '@/views/common/process/viewNodes/TriggerNode.vue'
import Delay from '@/views/common/process/viewNodes/DelayNode.vue'
import Empty from '@/views/common/process/viewNodes/EmptyNode.vue'
import Root from '@/views/common/process/viewNodes/RootNode.vue'
import Node from '@/views/common/process/viewNodes/Node.vue'
import DefaultProps from "./DefaultNodeProps"
@@ -34,7 +34,7 @@
    this.nodeMap.clear()
    let processTrees = this.getDomTree(h, this.dom)
    //插入末端节点
    processTrees.push(h('div', {style:{'text-align': 'center'}}, [
    processTrees.push(h('div', {class:{'end-class': true}}, [
      h('div', {class:{'process-end': true}, domProps: {innerHTML:'流程结束'}})
    ]))
    return h('div', {class:{'_root': true}, ref:'_root'}, processTrees)
@@ -46,8 +46,7 @@
        //普通业务节点
        let childDoms = this.getDomTree(h, node.children)
        let headerBgc = '#ff943e'
        let headerBgc = '#909399'
        if (this.$store.state.runningList.includes(node.id)) {
          headerBgc = '#1e90ff'
        }
@@ -189,7 +188,6 @@
    },
    //选中一个节点
    selectNode(node){
      console.log("ProcessTreeViewer-emit-selectNode")
      this.$store.commit('selectedNode', node)
      this.$emit('selectedNode', node)
    },
@@ -349,7 +347,6 @@
    },
    //删除当前节点
    delNode(node){
      console.log("删除节点", node)
      //获取该节点的父节点
      let parentNode = this.nodeMap.get(node.parentId)
      if (parentNode){
@@ -450,29 +447,58 @@
<style lang="less" scoped>
._root{
  display: flex;
  align-items:center;
 margin: 0 auto;
}
.end-class {
  display: flex;
  align-items: center;
  &:before {
    content: '';
    top: 50%;
    left: 100%;
    display: block;
    width: 0;
    height: 0;
    border-style: solid;
    border-color: #CACACA transparent transparent;
    background: #F5F5F7;
    border-top: 5px solid transparent;
    border-bottom: 5px solid transparent;
    border-left: 10px solid #CACACA; /* 左边界 */
  }
}
.process-end{
  width: 60px;
  width: 100px;
  height: 40px;
  margin: 0 auto;
  margin-bottom: 20px;
  // margin-bottom: 20px;
  border-radius: 15px;
  padding: 5px 10px;
  font-size: small;
  //font-size: small;
  color: #747474;
  background-color: #f2f2f2;
  box-shadow: 0 0 10px 0 #bcbcbc;
  display: flex;
  justify-content:center;
  align-items: center;
}
.primary-node{
  display: flex;
  align-items: center;
  flex-direction: column;
  flex-direction: row
}
.branch-node{
  display: flex;
  justify-content: center;
  /*border-top: 2px solid #cccccc;
  border-bottom: 2px solid #cccccc;*/
}
.branch-node-item{
  position: relative;
@@ -531,4 +557,5 @@
  flex-direction: column;
  align-items: center;
}
</style>
src/views/common/process/config/ApprovalNodeConfig.vue
@@ -2,38 +2,41 @@
  <div>
    <el-form label-position="top" label-width="90px">
      <el-form-item label="⚙ 选择审批组或审批架构" prop="text" class="user-type">
          <el-select @change="selected(this,'group')" value-key="id" style="width: 80%;" size="small"   v-model="nodeProps.approvalGroup" placeholder="请选择审批组">
            <el-option   v-for="approvals in approvalGroups" :label="approvals.name" :value="approvals" :key="approvals.id"></el-option>
        <el-select @change="selected(this,'group')" value-key="id" style="width: 80%;" size="small"
                   v-model="nodeProps.approvalGroup" placeholder="请选择审批组">
          <el-option v-for="approvals in approvalGroups" :label="approvals.name" :value="approvals"
                     :key="approvals.id"></el-option>
          </el-select>
      </el-form-item>
      <el-form-item label="⚙ 选择审批组或审批架构" prop="text" class="user-type">
        <el-select @change="selected(this,'staff')" value-key="id" style="width: 80%;" size="small"   v-model="nodeProps.staffGroup" placeholder="请选择审批架构">
          <el-option   v-for="staffApprovals in staffGroups" :label="staffApprovals.name" :value="staffApprovals" :key="staffApprovals.id"></el-option>
        <el-select @change="selected(this,'staff')" value-key="id" style="width: 80%;" size="small"
                   v-model="nodeProps.staffGroup" placeholder="请选择审批架构">
          <el-option v-for="staff in staffGroups" :label="staff.name" :value="staff"
                     :key="staff.id"></el-option>
        </el-select>
      </el-form-item>
      <el-divider></el-divider>
      <el-form-item label="参数名">
        <el-input style="width: 80%;"  placeholder="参数名" v-model="nodeProps.params" size="small" clearable  />
      </el-form-item>
      <el-divider></el-divider>
      <el-form-item label="触发事件">
        <div>
          <el-button type="primary" size="mini" icon="el-icon-plus" style="margin: 0 15px 15px 0" round @click="addConditionGroup">
          <el-button type="primary" size="mini" icon="el-icon-plus" style="margin: 0 15px 15px 0" round
                     @click="addConditionGroup">
            添加审批触发事件
          </el-button>
        </div>
        <div style="width: 100%" v-for="(group, index) in selectedNode.props.groups" :key="index + '_g'" class="group">
          <div class="group-header">
            <span class="group-name">触发事件{{index+1}}</span>
            <i class="el-icon-delete" @click="delGroup(index)"></i>
          </div>
          <el-row :gutter="10" >
            <el-col :span="12"><el-select @change="selected(this)"  size="small" v-model="group.action" placeholder="请选择审批组">
              <el-option   v-for="actionItem in actions" :label="actionItem.label" :value="actionItem" :key="actionItem.value"></el-option>
            <el-col :span="12">
              <el-select @change="selected(this)" size="small" v-model="group.action" placeholder="请选择审批组">
                <el-option v-for="actionItem in actions" :label="actionItem.label" :value="actionItem"
                           :key="actionItem.value"></el-option>
            </el-select>
            </el-col>
            <el-col :span="12">
@@ -45,11 +48,8 @@
            <el-col :span="20">
              <el-input  placeholder="java方法" v-model="group.method" size="small" clearable></el-input>
            </el-col>
          </el-row>
        </div>
      </el-form-item>
      <el-form-item label="🙅‍ 如果办理被驳回 👇">
        <el-radio-group @input="setValue" v-model="nodeProps.refuse.type">
@@ -59,26 +59,19 @@
        </el-radio-group>
        <div v-if="nodeProps.refuse.type === 'TO_NODE'">
          <span>指定节点:</span>
          <el-select style="margin-left: 10px; width: 150px;" placeholder="选择跳转节点" size="small" v-model="nodeProps.refuse.target">
          <el-select style="margin-left: 10px; width: 150px;" placeholder="选择跳转节点" size="small"
                     v-model="nodeProps.refuse.target">
            <el-option v-for="(node, i) in nodeOptions" :key="i" :label="node.name" :value="node.id"></el-option>
          </el-select>
        </div>
      </el-form-item>
    </el-form>
    <org-picker :title="pickerTitle" multiple :type="orgPickerType" ref="orgPicker" :selected="orgPickerSelected"
                @ok="selected"/>
  </div>
</template>
<script>
import OrgPicker from "@/components/common/OrgPicker";
import {getDict, getEntitySet} from "@/api/design";
export default {
  name: "ApprovalNodeConfig",
  components: {OrgPicker},
  props: {
    config: {
      type: Object,
@@ -91,14 +84,8 @@
    return {
      showOrgSelect: false,
      orgPickerSelected: [],
      orgPickerType: 'user',
      groupNames: ['1', '2', '3', '4', '6', 'F', 'G', 'H', 'I', 'J'],
      approvalGroups:[
        // {"value":'first',"label":'地区经理审批'},
        // {"value":'secound',"label":'大区经理审批'},
        // {"value":'third',"label":'商务经理审批'},
        // {"value":'four',"label":'财务经理审批'},
        // {"value":'five',"label":'总经理审批'},
      ],
      staffGroups:[],
      actions:[
@@ -131,76 +118,51 @@
      return this.$store.state.selectedNode
    },
    nodeProps() {
      // this.$store.state.selectedNode.props.staffGroup=this.$store.state.selectedNode.props.assignedUser[0]
      // //this.$store.state.selectedNode.props.staffGroup.code="SalesDirector"
      // //this.$store.state.selectedNode.props.staffGroup.id="position_sales_1"
      // console.log("this.$store.state.selectedNode.props",this.$store.state.selectedNode.props)
      return this.$store.state.selectedNode.props
    },
    select() {
      console.log("this.config--select",this.config)
      return this.config.assignedUser
    },
    pickerTitle() {
      switch (this.orgPickerType) {
        case 'user':
          return '请选择人员';
        case 'role':
          return '请选择系统角色';
        default:
          return null;
      }
    },
  },
  mounted() {
     this.getApprovalGroup();
     this.getStaffGroup();
    console.log("this.$store.state.selectedNode.props.staffGroup",this.$store.state.selectedNode.props.staffGroup)
  },
  methods: {
    getApprovalGroup(){
      let template = {"dataname": "sys_state_board","attachMeta":true}
      getEntitySet(template).then(rsp => {
        console.log("getEntitySet", rsp.data.data)
        this.approvalGroups = rsp.data.data.entityset
        console.log("this.approvalGroups", this.approvalGroups)
      }).catch(err => this.$message.error('获取审批流程异常'))
    },
    getStaffGroup(){
      let template = {"code":"position_sales"}
      getDict(template).then(rsp => {
        console.log("getDict", rsp.data.data.dictionary)
        this.staffGroups=this.$Utils.decode(rsp.data.data.dictionary.items)
        this.staffGroups.forEach(item=>item.name=item.value);
        this.staffGroups.forEach(item=>item.id=item.code);
        console.log("getDict", this.staffGroups)
        this.staffGroups.forEach(item => {
          item.name = item.value
          item.id = item.code
        });
        // this.staffGroups.forEach(item => );
      }).catch(err => this.$message.error('获取审批流程异常'))
    },
    //如果切换选箱不是指定节点  那就把之前选择的指定节点信息 给清除掉 避免提交上去
    setValue(){
     if(this.nodeProps.refuse.type!=='TO_NODE'){
       this.nodeProps.refuse.target='';
     }
    },
    delGroup(index) {
      this.selectedNode.props.groups.splice(index, 1)
    },
    addConditionGroup() {
      console.log("this.config",this.config)
      this.config.groups.push({
       // cids:[],
       // groupType: "OR",
        //conditions:[]
      })
    },
    selected(select,type) {
      console.log("输出选中select",this.nodeProps)
      this.nodeProps.assignedUser=[]
      if(type==='group'){
        this.nodeProps.staffGroup={}
        this.nodeProps.assignedUser.push({
@@ -218,8 +180,7 @@
        }
        )
      }
      console.log("assignedUser",this.config)
      this.orgPickerSelected.length = 0
    },
    removeOrgItem(index) {
      this.select.splice(index, 1)
@@ -231,13 +192,16 @@
<style lang="less" scoped>
.el-row {
  margin-bottom: 20px;
  &:last-child {
    margin-bottom: 0;
  }
}
.el-col {
  border-radius: 4px;
}
.user-type {
  /deep/ .el-radio {
    width: 110px;
src/views/common/process/config/ConditionGroupItemConfig.vue
@@ -234,7 +234,6 @@
      }
    },
    selected(select) {
      console.log(select)
      this.users.length = 0
      select.forEach(u => this.users.push(u))
    },
@@ -246,8 +245,6 @@
      group.conditions.splice(index, 1)
    },
    conditionChange(index, group) {
      console.log("index",index);
      console.log("group",group);
      //判断新增的
      group.cids.forEach(cid => {
        if (0 > group.conditions.findIndex(cd => cd.id === cid)){
src/views/common/process/config/ConditionNodeConfig.vue
@@ -62,7 +62,6 @@
    //条件节点
    prioritySortList() {
      let node = this.$store.state.nodeMap.get(this.selectedNode.parentId)
      console.log(this.selectedNode.id, node)
      if (node) {
        return node.branchs || []
      }
@@ -91,7 +90,6 @@
      this.showOrgSelect = true
    },
    selected(select) {
      console.log(select)
      this.showOrgSelect = false
      select.forEach(val => this.select.push(val))
    },
src/views/common/process/config/InclusiveGroupItemConfig.vue
@@ -179,7 +179,6 @@
      }
    },
    selected(select) {
      console.log(select)
      this.users.length = 0
      select.forEach(u => this.users.push(u))
    },
@@ -196,7 +195,6 @@
        if (0 > group.conditions.findIndex(cd => cd.id === cid)){
          //新增条件
          let condition = {...this.conditionList[index]}
          console.log(condition, this.conditionList, index)
          condition.compare = '';
          condition.value = []
          group.conditions.push(condition)
src/views/common/process/config/InclusiveNodeConfig.vue
@@ -62,7 +62,6 @@
    //条件节点
    prioritySortList() {
      let node = this.$store.state.nodeMap.get(this.selectedNode.parentId)
      console.log(this.selectedNode.id, node)
      if (node) {
        return node.branchs || []
      }
src/views/common/process/config/NodeConfig.vue
@@ -1,14 +1,6 @@
<template>
  <div>
    <el-tabs v-model="active" v-if="name && formConfig.length > 0">
      <el-tab-pane :label="name" name="properties">
        <component :is="(selectNode.type||'').toLowerCase()" :config="selectNode.props"/>
      </el-tab-pane>
      <el-tab-pane label="表单权限设置" name="permissions">
        <form-authority-config/>
      </el-tab-pane>
    </el-tabs>
    <component :is="(selectNode.type||'').toLowerCase()" v-else :config="selectNode.props"/>
  </div>
</template>
@@ -45,7 +37,6 @@
  },
  computed: {
    selectNode() {
      console.log("selectNode",this.$store.state)
      return this.$store.state.selectedNode
    },
    formConfig() {
src/views/common/process/config/RootNodeConfig.vue
@@ -29,7 +29,6 @@
  },
  computed:{
    select(){
      console.log("this.config.assignedUser-select",this.config.assignedUser)
      return this.config.assignedUser
    }
  },
src/views/common/process/config/SubprocessNodeConfig.vue
@@ -152,7 +152,7 @@
    }
  },
  mounted(){
    this.getGroups()
  },
  methods: {
    selectUser() {
@@ -178,24 +178,7 @@
    removeOrgItem(index) {
      this.select.splice(index, 1)
    },
    getGroups(){
      // 简便  不使用迭代方法处理
      getFormGroups().then(rsp => {
        var data = rsp.data
        this.fromGroup = data.map(group => {
          return {
            value: group.id,
            label: group.name,
            children: group.items.map(item => {
              return {
                value: item.formId,
                label: item.formName,
              };
            })
          };
        });
      }).catch(err => this.$message.error('获取分组异常'))
    },
    handleChange(key){
       // 对值进行处理
    }
src/views/common/process/config/TaskNodeConfig.vue
@@ -242,7 +242,6 @@
      this.$refs.orgPicker.show()
    },
    selected(select) {
      console.log(select)
      this.orgPickerSelected.length = 0
      select.forEach(val => this.orgPickerSelected.push(val))
    },
src/views/common/process/nodes/ApprovalNode.vue
@@ -34,26 +34,13 @@
    },
    content(){
      const config = this.config.props
      console.log("content:config.assignedUser",config)
      let texts = []
      config.assignedUser.forEach(org => texts.push(org.name))
      return String(texts).replaceAll(',', '、')
    },
    },
    // flowText(){
    //   const config = this.config
    //   console.log("flowText-config",config);
    //
    //   // return config.approvalArr.filter(ite=>ite.node_id===config.id)[0].approval_time;
    //   if(config.approvalArr!==undefined){
    //     return "发起时间:"+config.approvalArr.filter(ite=>ite.node_id===config.id)[0].approval_time;
    //   }
    //
    //   return ''
    // }
  },
  created() {
    console.log("加载ApprovalNode.vue")
  },
  methods: {
    getFormItemById(id){
src/views/common/process/nodes/ConditionNode.vue
@@ -137,9 +137,6 @@
    },
    //校验数据配置的合法性
    validate(err) {
      console.log('condition children', this.config.children)
      console.log('this.level', this.level)
      console.log('this.size', this.size)
      if (!(this.level == this.size && this.size != 0) && !this.config.children?.id) {
        this.showError = true
        this.errorInfo = '条件分支后不能为空'
src/views/common/process/nodes/InclusiveNode.vue
@@ -132,7 +132,6 @@
    },
    //校验数据配置的合法性
    validate(err) {
      console.log('inclusive children', this.config.children)
      if (!(this.level == this.size && this.size != 0) && !this.config.children?.id) {
        this.showError = true
        this.errorInfo = '条件分支后不能为空'
src/views/common/process/nodes/Node.vue
@@ -57,7 +57,7 @@
    //节点内容区域文字
    content: {
      type: String,
      default: ""
      default: "111"
    },
    title:{
      type: String,
@@ -98,18 +98,140 @@
  },
  data() {
    return {
      isV:this.$isVertical
    }
  },
  created() {
    console.log("this.isV",this.flowText)
  },
  methods: {}
}
</script>
<style lang="scss" :src="$cssSrc" scoped  >
<style lang="less" scoped>
.root {
  &:before {
    display: none !important;
  }
}
.node-error-state {
  .node-body {
    box-shadow: 0px 0px 5px 0px #F56C6C !important;
  }
}
.node {
  padding: 0 50px;
  width: 220px;
  position: relative;
  &:before {
    content: '';
    position: absolute;
    top: -12px;
    left: 50%;
    -webkit-transform: translateX(-50%);
    transform: translateX(-50%);
    width: 0;
    border-style: solid;
    border-width: 8px 6px 4px;
    border-color: #CACACA transparent transparent;
    background: #F5F5F7;
  }
  .node-body {
    cursor: pointer;
    max-height: 120px;
    position: relative;
    border-radius: 5px;
    background-color: white;
    box-shadow: 0px 0px 5px 0px #d8d8d8;
    &:hover {
      box-shadow: 0px 0px 3px 0px @theme-primary;
      .node-body-header {
        .el-icon-close {
          display: inline;
          font-size: medium;
        }
      }
    }
    .node-body-header {
      border-top-left-radius: 5px;
      border-top-right-radius: 5px;
      padding: 5px 15px;
      color: white;
      font-size: xx-small;
      .el-icon-close {
        display: none;
      }
      .name {
        height: 14px;
        width: 150px;
        display: inline-block
      }
    }
    .node-body-content {
      padding: 18px;
      color: #656363;
      font-size: 14px;
      i {
        position: absolute;
        top: 55%;
        right: 5px;
        font-size: medium;
      }
      .placeholder {
        color: #8c8c8c;
      }
    }
    .node-error {
      position: absolute;
      right: -40px;
      top: 20px;
      font-size: 25px;
      color: #F56C6C;
    }
  }
  .node-footer {
    position: relative;
    .btn {
      width: 100%;
      display: flex;
      padding: 20px 0 32px;
      justify-content: center;
    }
    /deep/ .el-button {
      height: 32px;
    }
    &::before {
      content: "";
      position: absolute;
      top: 0;
      left: 0;
      right: 0;
      bottom: 0;
      z-index: -1;
      margin: auto;
      width: 2px;
      height: 100%;
      background-color: #CACACA;
    }
  }
}
</style>
src/views/common/process/nodes/SubprocessNode.vue
@@ -33,7 +33,6 @@
    },
    content(){
      const config = this.config.props
      console.log("role,config.assignedUser",config.assignedUser)
      switch (config.assignedType){
        case "ASSIGN_USER":
          if (config.assignedUser.length > 0){
src/views/common/process/viewNodes/ApprovalNode.vue
New file
@@ -0,0 +1,122 @@
<template>
  <node :title="config.name" :show-error="showError" :content="content"  :error-info="errorInfo"
        @selected="$emit('selected')" @delNode="$emit('delNode')" @insertNode="type => $emit('insertNode', type)"
        placeholder="请设置审批人" :header-bgc="headerBgc" header-icon="el-icon-s-check"/>
</template>
<script>
import Node from './Node.vue'
export default {
  name: "ApprovalNode",
  props:{
    config:{
      type: Object,
      default: () => {
        return {}
      }
    }
  },
  components: {Node},
  data() {
    return {
      showError: false,
      errorInfo: '',
    }
  },
  computed:{
    headerBgc() {
      if (this.$store.state.diagramMode === 'viewer') {
        return this.config.props.headerBgc
      } else {
        return '#ff943e'
      }
    },
    content(){
      const config = this.config.props
      let texts = []
      config.assignedUser.forEach(org => texts.push(org.name))
      return String(texts).replaceAll(',', '、')
    },
    // flowText(){
    //   const config = this.config
    //   console.log("flowText-config",config);
    //
    //   // return config.approvalArr.filter(ite=>ite.node_id===config.id)[0].approval_time;
    //   if(config.approvalArr!==undefined){
    //     return "发起时间:"+config.approvalArr.filter(ite=>ite.node_id===config.id)[0].approval_time;
    //   }
    //
    //   return ''
    // }
  },
  created() {
  },
  methods: {
    getFormItemById(id){
      return this.$store.state.design.formItems.find(item => item.id === id)
    },
    //校验数据配置的合法性
    validate(err){
      try {
        this.showError = !this[`validate_${this.config.props.assignedType}`](err)
        if (this.config.props.nobody.handler === 'TO_USER' && this.config.props.nobody.assignedUser.length === 0) {
          this.errorInfo = '审批人为空时, 转交给指定人员:【请指定一个具体的人】'
          err.push('审批人为空时, 转交给指定人员:【请指定一个具体的人】')
          this.showError = true
        }
        return this.showError
      } catch (e) {
        return true;
      }
    },
    validate_ASSIGN_USER(err){
      if(this.config.props.assignedUser.length > 0){
        return true;
      }else {
        this.errorInfo = '请指定审批人员'
        err.push(`${this.config.name} 未指定审批人员`)
        return false
      }
    },
    validate_SELF_SELECT(err){
      return true;
    },
    validate_LEADER_TOP(err){
      return true;
    },
    validate_LEADER(err){
      return true;
    },
    validate_ROLE(err){
      if (this.config.props.assignedUser.length <= 0){
        this.errorInfo = '请指定负责审批的系统角色'
        err.push(`${this.config.name} 未指定审批角色`)
        return false
      }
      return true;
    },
    validate_SELF(err){
      return true;
    },
    validate_FORM_USER(err){
     if (this.config.props.formUser === ''){
       this.errorInfo = '请指定表单中的人员组件'
       err.push(`${this.config.name} 审批人为表单中人员,但未指定`)
       return false
     }
      return true;
    },
    validate_REFUSE(err){
      return true;
    },
  }
}
</script>
<style scoped>
</style>
src/views/common/process/viewNodes/CcNode.vue
New file
@@ -0,0 +1,61 @@
<template>
  <node :title="config.name" :show-error="showError" :content="content" :error-info="errorInfo"
        @selected="$emit('selected')" @delNode="$emit('delNode')" @insertNode="type => $emit('insertNode', type)"
        placeholder="请设置抄送人" header-bgc="#3296fa" header-icon="el-icon-s-promotion"/>
</template>
<script>
import Node from './Node.vue'
export default {
  name: "CcNode",
  props:{
    config:{
      type: Object,
      default: () => {
        return {}
      }
    }
  },
  components: {Node},
  data() {
    return {
      showError: false,
      errorInfo: '',
    }
  },
  computed:{
    content() {
      if (this.config.props.shouldAdd){
        return '由发起人指定'
      }else if (this.config.props.assignedUser.length > 0) {
        let texts = []
        this.config.props.assignedUser.forEach(org => texts.push(org.name))
        return String(texts).replaceAll(',', '、')
      } else {
        return null
      }
    }
  },
  methods: {
    //校验数据配置的合法性
    validate(err){
      this.showError = false
      if(this.config.props.shouldAdd){
        this.showError = false
      }else if(this.config.props.assignedUser.length === 0){
        this.showError = true
        this.errorInfo = '请选择需要抄送的人员'
      }
      if (this.showError){
        err.push(`抄送节点 ${this.config.name} 未设置抄送人`)
      }
      return !this.showError
    }
  }
}
</script>
<style scoped>
</style>
src/views/common/process/viewNodes/ConcurrentNode.vue
New file
@@ -0,0 +1,186 @@
<template>
  <div class="node">
    <div class="node-body" @click="$emit('selected')">
      <div class="node-body-left" @click.stop="$emit('leftMove')" v-if="level > 1" v-show="$store.state.diagramMode !== 'viewer'">
        <i class="el-icon-arrow-left"></i>
      </div>
      <div class="node-body-main">
        <div class="node-body-main-header">
          <span class="title">
            <i class="el-icon-s-operation"></i>
            <ellipsis class="name" hover-tip :content="config.name ? config.name:('并行任务' + level)"/>
          </span>
          <span class="option" v-show="$store.state.diagramMode !== 'viewer'">
            <el-tooltip effect="dark" content="复制分支" placement="top">
              <i class="el-icon-copy-document" @click="$emit('copy')"></i>
            </el-tooltip>
            <i class="el-icon-close" @click.stop="$emit('delNode')"></i>
          </span>
        </div>
        <div class="node-body-main-content">
          <span>并行任务(同时进行)</span>
        </div>
      </div>
      <div class="node-body-right" @click.stop="$emit('rightMove')" v-if="level < size" v-show="$store.state.diagramMode !== 'viewer'">
        <i class="el-icon-arrow-right"></i>
      </div>
    </div>
    <div class="node-footer">
      <div class="btn">
        <insert-button v-show="$store.state.diagramMode !== 'viewer'" @insertNode="type => $emit('insertNode', type)"></insert-button>
      </div>
    </div>
  </div>
</template>
<script>
import InsertButton from '@/views/common/InsertButton.vue'
export default {
  name: "ConcurrentNode",
  components: {InsertButton},
  props:{
    config:{
      type: Object,
      default: () => {
        return {}
      }
    },
    level:{
      type: Number,
      default: 1
    },
    //条件数
    size:{
      type: Number,
      default: 0
    }
  },
  data() {
    return {
    }
  },
  methods: {}
}
</script>
<style lang="less" scoped>
.node{
  padding: 30px 55px 0;
  width: 220px;
  .node-body{
    overflow: hidden;
    cursor: pointer;
    min-height: 80px;
    max-height: 120px;
    position: relative;
    border-radius: 5px;
    background-color: white;
    box-shadow: 0px 0px 5px 0px #d8d8d8;
    &:hover{
      .node-body-left, .node-body-right{
        i{
          display: block !important;
        }
      }
      .node-body-main {
        .option{
          display: inline-block !important;
        }
      }
      box-shadow: 0px 0px 3px 0px @theme-primary;
    }
    .node-body-left, .node-body-right{
      display: flex;
      align-items: center;
      position: absolute;
      height: 100%;
      i{
        display: none;
      }
      &:hover{
        background-color: #ececec;
      }
    }
    .node-body-left{
      left: 0;
    }
    .node-body-right{
      right: 0;
    }
    .node-body-main {
      position: absolute;
      width: 188px;
      left: 17px;
      display: inline-block;
      .node-body-main-header{
        padding: 10px 0px 5px;
        font-size: xx-small;
        position: relative;
        .title{
          color: #718dff;
          .name{
            display: inline-block;
            height: 14px;
            width: 130px;
            margin-left: 2px;
          }
        }
        .option{
          position: absolute;
          right: 0;
          display: none;
          font-size: medium;
          i{
            color: #888888;
            padding: 0 3px;
          }
        }
      }
      .node-body-main-content {
        padding: 6px;
        color: #656363;
        font-size: 14px;
        i {
          position: absolute;
          top: 55%;
          right: 10px;
          font-size: medium;
        }
      }
    }
  }
  .node-footer{
    position: relative;
    .btn{
      width: 100%;
      display: flex;
      height: 70px;
      padding: 20px 0 32px;
      justify-content: center;
    }
    /deep/ .el-button{
      height: 32px;
    }
    &::before{
      content: "";
      position: absolute;
      top: 0;
      left: 0;
      right: 0;
      bottom: 0;
      z-index: -1;
      margin: auto;
      width: 2px;
      height: 100%;
      background-color: #CACACA;
    }
  }
}
</style>
src/views/common/process/viewNodes/ConditionNode.vue
New file
@@ -0,0 +1,345 @@
<template>
  <div :class="{'node': true, 'node-error-state': showError}">
    <div :class="{'node-body': true, 'error': showError}">
      <div class="node-body-left" @click="$emit('leftMove')" v-if="level > 1 && $store.state.diagramMode !== 'viewer'">
        <i class="el-icon-arrow-left"></i>
      </div>
      <div class="node-body-main" @click="$emit('selected')">
        <div class="node-body-main-header">
          <ellipsis class="title" hover-tip :content="config.name ? config.name : ('条件' + level)"/>
          <span class="level">优先级{{ level }}</span>
          <span class="option" v-if="$store.state.diagramMode !== 'viewer'">
            <el-tooltip effect="dark" content="复制条件" placement="top">
              <i class="el-icon-copy-document" @click.stop="$emit('copy')"></i>
            </el-tooltip>
            <i class="el-icon-close" @click.stop="$emit('delNode')"></i>
          </span>
        </div>
        <div class="node-body-main-content">
          <span class="placeholder" v-if="(content || '').trim() === ''">{{
            level == size && size != 0 ? "其他条件进入此流程" : placeholder
          }}</span>
          <ellipsis hoverTip :row="4" :content="content" v-else/>
        </div>
      </div>
      <div class="node-body-right" @click="$emit('rightMove')"
           v-if="level < size && $store.state.diagramMode !== 'viewer'">
        <i class="el-icon-arrow-right"></i>
      </div>
      <div class="node-error" v-if="showError">
        <el-tooltip effect="dark" :content="errorInfo" placement="top-start">
          <i class="el-icon-warning-outline"></i>
        </el-tooltip>
      </div>
    </div>
    <div class="node-footer">
      <div class="btn">
        <insert-button v-if="$store.state.diagramMode !== 'viewer'"
                       @insertNode="type => $emit('insertNode', type)"></insert-button>
      </div>
    </div>
  </div>
</template>
<script>
import InsertButton from '@/views/common/InsertButton.vue'
import {ValueType} from '@/views/common/ComponentsConfigExport'
const groupNames = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'];
export default {
  name: "ConditionNode",
  components: {InsertButton},
  props: {
    config: {
      type: Object,
      default: () => {
        return {}
      }
    },
    //索引位置
    level: {
      type: Number,
      default: 1
    },
    //条件数
    size: {
      type: Number,
      default: 0
    }
  },
  data() {
    return {
      ValueType,
      groupNames,
      placeholder: '请设置条件',
      errorInfo: '',
      showError: false
    }
  },
  computed: {
    content() {
      const groups = this.config.props.groups
      let confitions = []
      groups.forEach(group => {
        let subConditions = []
        group.conditions.forEach(subCondition => {
          let subConditionStr = ''
          switch (subCondition.valueType) {
            case ValueType.dept:
            case ValueType.user:
              subConditionStr = `${subCondition.title}属于[${String(subCondition.value.map(u => u.name)).replaceAll(',', '. ')}]之一`
              break;
            case ValueType.number:
            case ValueType.string:
              subConditionStr = this.getOrdinaryConditionContent(subCondition)
              break;
          }
          subConditions.push(subConditionStr)
        })
        //根据子条件关系构建描述
        let subConditionsStr = String(subConditions)
            .replaceAll(',', subConditions.length > 1 ?
                (group.groupType === 'AND' ? ') 且 (' : ') 或 (') :
                (group.groupType === 'AND' ? ' 且 ' : ' 或 '))
        confitions.push(subConditions.length > 1 ? `(${subConditionsStr})` : subConditionsStr)
      })
      //构建最终描述
      return String(confitions).replaceAll(',', (this.config.props.groupsType === 'AND' ? ' 且 ' : ' 或 '))
    }
  },
  methods: {
    getDefault(val, df) {
      return val && val !== '' ? val : df;
    },
    getOrdinaryConditionContent(subCondition) {
      switch (subCondition.compare) {
        case 'IN':
          let  items = subCondition.value.map(function (item) {
            return item.label
          })
          return `${subCondition.title}为[${String(items).replaceAll(',', '、')}]中之一`
        case 'B':
          return `${subCondition.value[0]} < ${subCondition.title} < ${subCondition.value[1]}`
        case 'AB':
          return `${subCondition.value[0]} ≤ ${subCondition.title} < ${subCondition.value[1]}`
        case 'BA':
          return `${subCondition.value[0]} < ${subCondition.title} ≤ ${subCondition.value[1]}`
        case 'ABA':
          return `${subCondition.value[0]} ≤ ${subCondition.title} ≤ ${subCondition.value[1]}`
        case '<=':
          return `${subCondition.title} ≤ ${this.getDefault(subCondition.value[0], ' ?')}`
        case '>=':
          return `${subCondition.title} ≥ ${this.getDefault(subCondition.value[0], ' ?')}`
        default:
          return `${subCondition.title}${subCondition.compare}${this.getDefault(subCondition.value[0], ' ?')}`
      }
    },
    //校验数据配置的合法性
    validate(err) {
      if (!(this.level == this.size && this.size != 0) && !this.config.children?.id) {
        this.showError = true
        this.errorInfo = '条件分支后不能为空'
        err.push(`条件分支后不能为空`)
        return !this.showError
      }
      const props = this.config.props
      if (props.groups.length <= 0){
        this.showError = true
        this.errorInfo = '请设置分支条件'
        err.push(`${this.config.name} 未设置条件`)
      }else {
        if (!(this.level == this.size && this.size != 0)) {
          for (let i = 0; i < props.groups.length; i++) {
            if (props.groups[i].cids.length === 0){
              this.showError = true
              this.errorInfo = `请设置条件组${this.groupNames[i]}内的条件`
              err.push(`条件 ${this.config.name} 条件组${this.groupNames[i]}内未设置条件`)
              break
            }else {
              let conditions = props.groups[i].conditions
              for (let ci = 0; ci < conditions.length; ci++) {
                let subc = conditions[ci]
                if (subc.value.length === 0){
                  this.showError = true
                }else {
                  this.showError = false
                }
                if (this.showError){
                  this.errorInfo = `请完善条件组${this.groupNames[i]}内的${subc.title}条件`
                  err.push(`条件 ${this.config.name} 条件组${this.groupNames[i]}内${subc.title}条件未完善`)
                  return false
                }
              }
            }
          }
        }
      }
      return !this.showError;
    },
  },
};
</script>
<style lang="less" scoped>
.node-error-state {
  .node-body {
    box-shadow: 0px 0px 5px 0px #F56C6C !important;
  }
}
.node {
  padding: 30px 55px 0;
  width: 220px;
  .node-body {
    cursor: pointer;
    min-height: 80px;
    max-height: 120px;
    position: relative;
    border-radius: 5px;
    background-color: white;
    box-shadow: 0px 0px 5px 0px #d8d8d8;
    &:hover {
      .node-body-left, .node-body-right {
        i {
          display: block !important;
        }
      }
      .node-body-main {
        .level {
          display: none !important;
        }
        .option {
          display: inline-block !important;
        }
      }
      box-shadow: 0px 0px 3px 0px @theme-primary;
    }
    .node-body-left, .node-body-right {
      display: flex;
      align-items: center;
      position: absolute;
      height: 100%;
      i {
        display: none;
      }
      &:hover {
        background-color: #ececec;
      }
    }
    .node-body-left {
      left: 0;
    }
    .node-body-right {
      right: 0;
      top: 0;
    }
    .node-body-main {
      //position: absolute;
      width: 188px;
      margin-left: 17px;
      display: inline-block;
      .node-body-main-header {
        padding: 10px 0px 5px;
        font-size: xx-small;
        position: relative;
        .title {
          color: #15bca3;
          display: inline-block;
          height: 14px;
          width: 125px;
        }
        .level {
          position: absolute;
          right: 15px;
          color: #888888;
        }
        .option {
          position: absolute;
          right: 0;
          display: none;
          font-size: medium;
          i {
            color: #888888;
            padding: 0 3px;
          }
        }
      }
      .node-body-main-content {
        padding: 6px;
        color: #656363;
        font-size: 14px;
        i {
          position: absolute;
          top: 55%;
          right: 10px;
          font-size: medium;
        }
        .placeholder {
          color: #8c8c8c;
        }
      }
    }
    .node-error {
      position: absolute;
      right: -40px;
      top: 20px;
      font-size: 25px;
      color: #F56C6C;
    }
  }
  .node-footer {
    position: relative;
    .btn {
      width: 100%;
      display: flex;
      height: 70px;
      padding: 20px 0 32px;
      justify-content: center;
    }
    /deep/ .el-button {
      height: 32px;
    }
    &::before {
      content: "";
      position: absolute;
      top: 0;
      left: 0;
      right: 0;
      bottom: 0;
      z-index: -1;
      margin: auto;
      width: 2px;
      height: 100%;
      background-color: #CACACA;
    }
  }
}
</style>
src/views/common/process/viewNodes/DelayNode.vue
New file
@@ -0,0 +1,77 @@
<template>
  <node :title="config.name" :show-error="showError" :content="content" :error-info="errorInfo"
        @selected="$emit('selected')" @delNode="$emit('delNode')" @insertNode="type => $emit('insertNode', type)"
        placeholder="请设置延时时间" header-bgc="#f25643" header-icon="el-icon-time"/>
</template>
<script>
import Node from './Node.vue'
export default {
  name: "DelayNode",
  props:{
    config:{
      type: Object,
      default: () => {
        return {}
      }
    }
  },
  components: {Node},
  data() {
    return {
      showError: false,
      errorInfo: '',
    }
  },
  computed:{
    content(){
      if (this.config.props.type === 'FIXED'){
        return `等待 ${this.config.props.time} ${this.getName(this.config.props.unit)}`
      }else if(this.config.props.type === 'AUTO'){
        return `至当天 ${this.config.props.dateTime}`
      }else {
        return null
      }
    }
  },
  methods: {
    //校验数据配置的合法性
    validate(err){
      this.showError = false
      try {
        if (this.config.props.type === "AUTO") {
          if ((this.config.props.dateTime || "") === ""){
            this.showError = true
            this.errorInfo = "请选择时间点"
          }
        } else {
          if (this.config.props.time <= 0) {
            this.showError = true
            this.errorInfo = "请设置延时时长"
          }
        }
      } catch (e) {
        this.showError = true
        this.errorInfo = "配置出现问题"
      }
      if (this.showError){
        err.push(`${this.config.name} 未设置延时规则`)
      }
      return !this.showError
    },
    getName(unit){
      switch (unit){
        case 'D': return '天';
        case 'H': return '小时';
        case 'M': return '分钟';
        default: return '未知';
      }
    }
  }
}
</script>
<style scoped>
</style>
src/views/common/process/viewNodes/EmptyNode.vue
New file
@@ -0,0 +1,20 @@
<template>
  <node :show="false" @insertNode="type => $emit('insertNode', type)"/>
</template>
<script>
import Node from './Node.vue'
export default {
  name: "EmptyNode",
  components: {Node},
  data() {
    return {}
  },
  methods: {}
}
</script>
<style scoped>
</style>
src/views/common/process/viewNodes/InclusiveNode.vue
New file
@@ -0,0 +1,340 @@
<template>
  <div :class="{'node': true, 'node-error-state': showError}">
    <div :class="{'node-body': true, 'error': showError}">
      <div class="node-body-left" @click="$emit('leftMove')" v-if="level > 1 && $store.state.diagramMode !== 'viewer'">
        <i class="el-icon-arrow-left"></i>
      </div>
      <div class="node-body-main" @click="$emit('selected')">
        <div class="node-body-main-header">
          <ellipsis class="title" hover-tip :content="config.name ? config.name : ('条件' + level)"/>
          <span class="level">优先级{{ level }}</span>
          <span class="option" v-if="$store.state.diagramMode !== 'viewer'">
            <el-tooltip effect="dark" content="复制条件" placement="top">
              <i class="el-icon-copy-document" @click.stop="$emit('copy')"></i>
            </el-tooltip>
            <i class="el-icon-close" @click.stop="$emit('delNode')"></i>
          </span>
        </div>
        <div class="node-body-main-content">
          <span class="placeholder" v-if="(content || '').trim() === ''">{{
            level == size && size != 0 ? "其他条件进入此流程" : placeholder
          }}</span>
          <ellipsis hoverTip :row="4" :content="content" v-else/>
        </div>
      </div>
      <div class="node-body-right" @click="$emit('rightMove')"
           v-if="level < size && $store.state.diagramMode !== 'viewer'">
        <i class="el-icon-arrow-right"></i>
      </div>
      <div class="node-error" v-if="showError">
        <el-tooltip effect="dark" :content="errorInfo" placement="top-start">
          <i class="el-icon-warning-outline"></i>
        </el-tooltip>
      </div>
    </div>
    <div class="node-footer">
      <div class="btn">
        <insert-button v-if="$store.state.diagramMode !== 'viewer'"
                       @insertNode="type => $emit('insertNode', type)"></insert-button>
      </div>
    </div>
  </div>
</template>
<script>
import InsertButton from '@/views/common/InsertButton.vue'
import {ValueType} from '@/views/common/ComponentsConfigExport'
const groupNames = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'];
export default {
  name: "InclusiveNode",
  components: {InsertButton},
  props: {
    config: {
      type: Object,
      default: () => {
        return {}
      }
    },
    //索引位置
    level: {
      type: Number,
      default: 1
    },
    //条件数
    size: {
      type: Number,
      default: 0
    }
  },
  data() {
    return {
      ValueType,
      groupNames,
      placeholder: '请设置条件',
      errorInfo: '',
      showError: false
    }
  },
  computed: {
    content() {
      const groups = this.config.props.groups
      let confitions = []
      groups.forEach(group => {
        let subConditions = []
        group.conditions.forEach(subCondition => {
          let subConditionStr = ''
          switch (subCondition.valueType) {
            case ValueType.dept:
            case ValueType.user:
              subConditionStr = `${subCondition.title}属于[${String(subCondition.value.map(u => u.name)).replaceAll(',', '. ')}]之一`
              break;
            case ValueType.number:
            case ValueType.string:
              subConditionStr = this.getOrdinaryConditionContent(subCondition)
              break;
          }
          subConditions.push(subConditionStr)
        })
        //根据子条件关系构建描述
        let subConditionsStr = String(subConditions)
            .replaceAll(',', subConditions.length > 1 ?
                (group.groupType === 'AND' ? ') 且 (' : ') 或 (') :
                (group.groupType === 'AND' ? ' 且 ' : ' 或 '))
        confitions.push(subConditions.length > 1 ? `(${subConditionsStr})` : subConditionsStr)
      })
      //构建最终描述
      return String(confitions).replaceAll(',', (this.config.props.groupsType === 'AND' ? ' 且 ' : ' 或 '))
    }
  },
  methods: {
    getDefault(val, df) {
      return val && val !== '' ? val : df;
    },
    getOrdinaryConditionContent(subCondition) {
      switch (subCondition.compare) {
        case 'IN':
          return `${subCondition.title}为[${String(subCondition.value).replaceAll(',', '、')}]中之一`
        case 'B':
          return `${subCondition.value[0]} < ${subCondition.title} < ${subCondition.value[1]}`
        case 'AB':
          return `${subCondition.value[0]} ≤ ${subCondition.title} < ${subCondition.value[1]}`
        case 'BA':
          return `${subCondition.value[0]} < ${subCondition.title} ≤ ${subCondition.value[1]}`
        case 'ABA':
          return `${subCondition.value[0]} ≤ ${subCondition.title} ≤ ${subCondition.value[1]}`
        case '<=':
          return `${subCondition.title} ≤ ${this.getDefault(subCondition.value[0], ' ?')}`
        case '>=':
          return `${subCondition.title} ≥ ${this.getDefault(subCondition.value[0], ' ?')}`
        default:
          return `${subCondition.title}${subCondition.compare}${this.getDefault(subCondition.value[0], ' ?')}`
      }
    },
    //校验数据配置的合法性
    validate(err) {
      if (!(this.level == this.size && this.size != 0) && !this.config.children?.id) {
        this.showError = true
        this.errorInfo = '条件分支后不能为空'
        err.push(`条件分支后不能为空`)
        return !this.showError
      }
      const props = this.config.props
      if (props.groups.length <= 0){
        this.showError = true
        this.errorInfo = '请设置分支条件'
        err.push(`${this.config.name} 未设置条件`)
      }else {
        if (!(this.level == this.size && this.size != 0)) {
          for (let i = 0; i < props.groups.length; i++) {
            if (props.groups[i].cids.length === 0){
              this.showError = true
              this.errorInfo = `请设置条件组${this.groupNames[i]}内的条件`
              err.push(`条件 ${this.config.name} 条件组${this.groupNames[i]}内未设置条件`)
              break
            }else {
              let conditions = props.groups[i].conditions
              for (let ci = 0; ci < conditions.length; ci++) {
                let subc = conditions[ci]
                if (subc.value.length === 0){
                  this.showError = true
                }else {
                  this.showError = false
                }
                if (this.showError){
                  this.errorInfo = `请完善条件组${this.groupNames[i]}内的${subc.title}条件`
                  err.push(`条件 ${this.config.name} 条件组${this.groupNames[i]}内${subc.title}条件未完善`)
                  return false
                }
              }
            }
          }
        }
      }
      return !this.showError;
    },
  },
};
</script>
<style lang="less" scoped>
.node-error-state {
  .node-body {
    box-shadow: 0px 0px 5px 0px #F56C6C !important;
  }
}
.node {
  padding: 30px 55px 0;
  width: 220px;
  .node-body {
    cursor: pointer;
    min-height: 80px;
    max-height: 120px;
    position: relative;
    border-radius: 5px;
    background-color: white;
    box-shadow: 0px 0px 5px 0px #d8d8d8;
    &:hover {
      .node-body-left, .node-body-right {
        i {
          display: block !important;
        }
      }
      .node-body-main {
        .level {
          display: none !important;
        }
        .option {
          display: inline-block !important;
        }
      }
      box-shadow: 0px 0px 3px 0px @theme-primary;
    }
    .node-body-left, .node-body-right {
      display: flex;
      align-items: center;
      position: absolute;
      height: 100%;
      i {
        display: none;
      }
      &:hover {
        background-color: #ececec;
      }
    }
    .node-body-left {
      left: 0;
    }
    .node-body-right {
      right: 0;
      top: 0;
    }
    .node-body-main {
      //position: absolute;
      width: 188px;
      margin-left: 17px;
      display: inline-block;
      .node-body-main-header {
        padding: 10px 0px 5px;
        font-size: xx-small;
        position: relative;
        .title {
          color: #425c9d;
          display: inline-block;
          height: 14px;
          width: 125px;
        }
        .level {
          position: absolute;
          right: 15px;
          color: #888888;
        }
        .option {
          position: absolute;
          right: 0;
          display: none;
          font-size: medium;
          i {
            color: #888888;
            padding: 0 3px;
          }
        }
      }
      .node-body-main-content {
        padding: 6px;
        color: #656363;
        font-size: 14px;
        i {
          position: absolute;
          top: 55%;
          right: 10px;
          font-size: medium;
        }
        .placeholder {
          color: #8c8c8c;
        }
      }
    }
    .node-error {
      position: absolute;
      right: -40px;
      top: 20px;
      font-size: 25px;
      color: #F56C6C;
    }
  }
  .node-footer {
    position: relative;
    .btn {
      width: 100%;
      display: flex;
      height: 70px;
      padding: 20px 0 32px;
      justify-content: center;
    }
    /deep/ .el-button {
      height: 32px;
    }
    &::before {
      content: "";
      position: absolute;
      top: 0;
      left: 0;
      right: 0;
      bottom: 0;
      z-index: -1;
      margin: auto;
      width: 2px;
      height: 100%;
      background-color: #CACACA;
    }
  }
}
</style>
src/views/common/process/viewNodes/Node.vue
New file
@@ -0,0 +1,220 @@
<template>
  <div :class="{ 'nodeView':true, 'root': isRoot || !show, 'node-error-state': showError}">
    <div v-if="show" @click="$emit('selected')" :class="{'node-body': true, 'error': showError}" >
      <div>
        <div class="node-body-header" :style="{'background-color': headerBgc}">
          <i :class="headerIcon" style="margin-right: 5px" v-if="(headerIcon || '') !== ''"></i>
          <ellipsis class="name" hover-tip :content="title"/>
          <i class="el-icon-close" v-if="!isRoot && $store.state.diagramMode !== 'viewer'" style="float:right;" @click="$emit('delNode')"></i>
        </div>
        <div class="node-body-content">
          <i :class="leftIcon" v-if="leftIcon"></i>
          <span class="placeholder" v-if="(content || '').trim() === ''">{{placeholder}}</span>
          <ellipsis :row="3" :content="content"  v-else/>
          <i class="el-icon-arrow-right" v-if="$store.state.diagramMode !== 'viewer'" ></i>
        </div>
        <div class="node-error" v-if="showError">
          <el-tooltip effect="dark" :content="errorInfo" placement="top-start">
            <i class="el-icon-warning-outline"></i>
          </el-tooltip>
        </div>
      </div>
    </div>
    <div class="node-footer">
      <div class="btn">
        <insert-button v-show="$store.state.diagramMode !== 'viewer'" @insertNode="type => $emit('insertNode', type)"></insert-button>
      </div>
    </div>
  </div>
</template>
<script>
import InsertButton from '@/views/common/InsertButton.vue'
export default {
  name: "NodeView",
  components: {InsertButton},
  props:{
    //是否为根节点
    isRoot: {
      type: Boolean,
      default: false
    },
    //是否显示节点体
    show: {
      type: Boolean,
      default: true
    },
    //节点内容区域文字
    content: {
      type: String,
      default: ""
    },
    title:{
      type: String,
      default: "标题"
    },
    placeholder:{
      type: String,
      default: "请设置"
    },
    flowText:{
      type: String,
      default: ""
    },
    //节点体左侧图标
    leftIcon: {
      type: String,
      default: undefined
    },
    //头部图标
    headerIcon:{
      type: String,
      default: ''
    },
    //头部背景色
    headerBgc:{
      type: String,
      default: '#576a95'
    },
    //是否显示错误状态
    showError:{
      type: Boolean,
      default: false
    },
    errorInfo:{
      type: String,
      default: '无信息'
    },
  },
  data() {
    return {
    }
  },
  created() {
  },
  methods: {}
}
</script>
<style lang="less" scoped>
.root{
  &:before{
    display: none !important;
  }
}
.node-error-state{
  &.node-body{
    box-shadow: 0px 0px 5px 0px #F56C6C !important;
  }
}
.nodeView{
  display: flex;
  align-items:center;
  width: auto;
  position: relative;
  &:before{
    content: '';
    top: 50%;
    left: 100%;
    display: block;
    width: 0;
    height: 0;
    border-style: solid;
    border-color: #CACACA transparent transparent;
    background: #F5F5F7;
    border-top: 5px solid transparent;
    border-bottom: 5px solid transparent;
    border-left: 10px solid #CACACA; /* 左边界 */
  }
  .node-body{
    width: 220px;
    cursor: pointer;
    max-height: 120px;
    position: relative;
    border-radius: 5px;
    background-color: white;
    box-shadow: 0px 0px 5px 0px #d8d8d8;
    &:hover{
      box-shadow: 0px 0px 3px 0px  #1890FF;
      .node-body-header {
        .el-icon-close {
          display: inline;
          font-size: medium;
        }
      }
    }
    .node-body-header{
      border-top-left-radius: 5px;
      border-top-right-radius: 5px;
      padding: 5px 15px;
      color: white;
      font-size: xx-small;
      .el-icon-close{
        display: none;
      }
      .name{
        height: 14px;
        width: 150px;
        display: inline-block
      }
    }
    .node-body-content{
      padding: 18px;
      color: #656363;
      font-size: 14px;
      i{
        position: absolute;
        top: 55%;
        right: 5px;
        font-size: medium;
      }
      .placeholder{
        color: #8c8c8c;
      }
    }
    .node-error{
      position: absolute;
      right: -40px;
      top: 20px;
      font-size: 25px;
      color: #F56C6C;
    }
  }
  .node-footer{
    position: relative;
    .btn{
      width: 80px;
      display: flex;
      // padding: 20px 0 32px;
      justify-content: center;
    }
    /deep/ .el-button{
      height: 32px;
    }
    &::before{
      content: "";
      position: absolute;
      top: 0;
      left: 0;
      right: 0;
      bottom: 0;
      z-index: -1;
      margin: auto;
      width: 100%;
      height: 2px;
      background-color: #CACACA;
    }
  }
}
</style>
src/views/common/process/viewNodes/RootNode.vue
New file
@@ -0,0 +1,57 @@
<template>
  <node title="发起人" :is-root="true" :content="content"
        @selected="$emit('selected')" @insertNode="type => $emit('insertNode', type)"
        placeholder="所有人" :header-bgc="headerBgc" header-icon="el-icon-user-solid"/>
</template>
<script>
import Node from './Node.vue'
export default {
  name: "RootNode",
  components: {Node},
  props:{
    config:{
      type: Object,
      default: () => {
        return {}
      }
    }
  },
  computed:{
    content(){
      if (this.config.props.assignedUser.length > 0){
        let texts = []
        this.config.props.assignedUser.forEach(org => texts.push(org.writer_name))
        return String(texts).replaceAll(',', '、')
      } else {
        return '所有人'
      }
    },
    // flowText(){
    //   const config = this.config
    //   console.log("flowText-config",config);
    //   if(config.approvalArr!==undefined){
    //     return "发起时间:"+config.approvalArr.filter(ite=>ite.node_id===config.id)[0].approval_time;
    //   }
    //   return ''
    // },
    headerBgc() {
      if (this.$store.state.diagramMode === 'viewer') {
        return this.config.props.headerBgc
      } else {
        return '#576a95'
      }
    },
  },
  data() {
    return {
    }
  },
  methods: {}
}
</script>
<style scoped>
</style>
src/views/common/process/viewNodes/SubprocessNode.vue
New file
@@ -0,0 +1,143 @@
<template>
  <node :title="config.name" :show-error="showError" :content="content" :error-info="errorInfo"
        @selected="$emit('selected')" @delNode="$emit('delNode')" @insertNode="type => $emit('insertNode', type)"
        placeholder="请设置审批人" :header-bgc="headerBgc" header-icon="el-icon-s-check"/>
</template>
<script>
import Node from './Node.vue'
export default {
  name: "SubprocessNode",
  props:{
    config:{
      type: Object,
      default: () => {
        return {}
      }
    }
  },
  components: {Node},
  data() {
    return {
      showError: false,
      errorInfo: '',
    }
  },
  computed:{
    headerBgc() {
      if (this.$store.state.diagramMode === 'viewer') {
        return this.config.props.headerBgc
      } else {
        return '#ff943e'
      }
    },
    content(){
      const config = this.config.props
      switch (config.assignedType){
        case "ASSIGN_USER":
          if (config.assignedUser.length > 0){
            let texts = []
            config.assignedUser.forEach(org => texts.push(org.name))
            return String(texts).replaceAll(',', '、')
          }else {
            return '请指定审批人'
          }
        case "SELF":
          return '发起人自己'
        case "SELF_SELECT":
          return config.selfSelect.multiple ? '发起人自选多人':'发起人自选一人'
        case "LEADER_TOP":
          return '多级主管依次审批'
        case "LEADER":
          return config.leader.level > 1 ? '发起人的第 ' + config.leader.level + ' 级主管' : '发起人的直接主管'
        case "FORM_USER":
          if (!config.formUser || config.formUser === ''){
            return '表单内联系人(未选择)'
          }else {
            let text = this.getFormItemById(config.formUser)
            if (text && text.title){
              return `表单(${text.title})内的人员`
            }else {
              return '该表单已被移除😥'
            }
          }
        case "ROLE":
          if (config.assignedUser.length > 0){
            let texts = []
            config.assignedUser.forEach(org => texts.push(org.name))
            return String(texts).replaceAll(',', '、')
          }else {
            return '指定角色(未设置)'
          }
          default: return '未知设置项😥'
      }
    }
  },
  methods: {
    getFormItemById(id){
      return this.$store.state.design.formItems.find(item => item.id === id)
    },
    //校验数据配置的合法性
    validate(err){
      try {
        this.showError = !this[`validate_${this.config.props.assignedType}`](err)
        if (this.config.props.nobody.handler === 'TO_USER' && this.config.props.nobody.assignedUser.length === 0) {
          this.errorInfo = '审批人为空时, 转交给指定人员:【请指定一个具体的人】'
          err.push('审批人为空时, 转交给指定人员:【请指定一个具体的人】')
          this.showError = true
        }
        return this.showError
      } catch (e) {
        return true;
      }
    },
    validate_ASSIGN_USER(err){
      if(this.config.props.assignedUser.length > 0){
        return true;
      }else {
        this.errorInfo = '请指定审批人员'
        err.push(`${this.config.name} 未指定审批人员`)
        return false
      }
    },
    validate_SELF_SELECT(err){
      return true;
    },
    validate_LEADER_TOP(err){
      return true;
    },
    validate_LEADER(err){
      return true;
    },
    validate_ROLE(err){
      if (this.config.props.role.length <= 0){
        this.errorInfo = '请指定负责审批的系统角色'
        err.push(`${this.config.name} 未指定审批角色`)
        return false
      }
      return true;
    },
    validate_SELF(err){
      return true;
    },
    validate_FORM_USER(err){
     if (this.config.props.formUser === ''){
       this.errorInfo = '请指定表单中的人员组件'
       err.push(`${this.config.name} 审批人为表单中人员,但未指定`)
       return false
     }
      return true;
    },
    validate_REFUSE(err){
      return true;
    },
  }
}
</script>
<style scoped>
</style>
src/views/common/process/viewNodes/TaskNode.vue
New file
@@ -0,0 +1,143 @@
<template>
  <node :title="config.name" :show-error="showError" :content="content" :error-info="errorInfo"
        @selected="$emit('selected')" @delNode="$emit('delNode')" @insertNode="type => $emit('insertNode', type)"
        placeholder="请设置办理人" :header-bgc="headerBgc" header-icon="el-icon-s-check"/>
</template>
<script>
import Node from './Node.vue'
export default {
  name: "TaskNode",
  props:{
    config:{
      type: Object,
      default: () => {
        return {}
      }
    }
  },
  components: {Node},
  data() {
    return {
      showError: false,
      errorInfo: '',
    }
  },
  computed:{
    headerBgc() {
      if (this.$store.state.diagramMode === 'viewer') {
        return this.config.props.headerBgc
      } else {
        return '#e6b039'
      }
    },
    content(){
      const config = this.config.props
      switch (config.assignedType){
        case "ASSIGN_USER":
          if (config.assignedUser.length > 0){
            let texts = []
            config.assignedUser.forEach(org => texts.push(org.name))
            return String(texts).replaceAll(',', '、')
          }else {
            return '请指定办理人'
          }
        case "SELF":
          return '发起人自己'
        case "SELF_SELECT":
          return config.selfSelect.multiple ? '发起人自选多人':'发起人自选一人'
        case "LEADER_TOP":
          return '多级主管依次办理'
        case "LEADER":
          return config.leader.level > 1 ? '发起人的第 ' + config.leader.level + ' 级主管' : '发起人的直接主管'
        case "FORM_USER":
          if (!config.formUser || config.formUser === ''){
            return '表单内联系人(未选择)'
          }else {
            let text = this.getFormItemById(config.formUser)
            if (text && text.title){
              return `表单(${text.title})内的人员`
            }else {
              return '该表单已被移除😥'
            }
          }
        case "ROLE":
          if (config.assignedUser.length > 0){
            let texts = []
            config.assignedUser.forEach(org => texts.push(org.name))
            return String(texts).replaceAll(',', '、')
          }else {
            return '指定角色(未设置)'
          }
          default: return '未知设置项😥'
      }
    }
  },
  methods: {
    getFormItemById(id){
      return this.$store.state.design.formItems.find(item => item.id === id)
    },
    //校验数据配置的合法性
    validate(err){
      try {
        this.showError = !this[`validate_${this.config.props.assignedType}`](err)
        if (this.config.props.nobody.handler === 'TO_USER' && this.config.props.nobody.assignedUser.length === 0) {
          this.errorInfo = '办理人为空时, 转交给指定人员:【请指定一个具体的人】'
          err.push('办理人为空时, 转交给指定人员:【请指定一个具体的人】')
          this.showError = true
        }
        return this.showError
      } catch (e) {
        return true;
      }
    },
    validate_ASSIGN_USER(err){
      if(this.config.props.assignedUser.length > 0){
        return true;
      }else {
        this.errorInfo = '请指定办理人员'
        err.push(`${this.config.name} 未指定办理人员`)
        return false
      }
    },
    validate_SELF_SELECT(err){
      return true;
    },
    validate_LEADER_TOP(err){
      return true;
    },
    validate_LEADER(err){
      return true;
    },
    validate_ROLE(err){
      if (this.config.props.role.length <= 0){
        this.errorInfo = '请指定负责办理的系统角色'
        err.push(`${this.config.name} 未指定办理角色`)
        return false
      }
      return true;
    },
    validate_SELF(err){
      return true;
    },
    validate_FORM_USER(err){
     if (this.config.props.formUser === ''){
       this.errorInfo = '请指定表单中的人员组件'
       err.push(`${this.config.name} 办理人为表单中人员,但未指定`)
       return false
     }
      return true;
    },
    validate_REFUSE(err){
      return true;
    },
  }
}
</script>
<style scoped>
</style>
src/views/common/process/viewNodes/TriggerNode.vue
New file
@@ -0,0 +1,64 @@
<template>
  <node :title="config.name" :show-error="showError" :content="content" :error-info="errorInfo"
        @selected="$emit('selected')" @delNode="$emit('delNode')" @insertNode="type => $emit('insertNode', type)"
        placeholder="请设置触发器" header-bgc="#47bc82" header-icon="el-icon-set-up"/>
</template>
<script>
import Node from './Node.vue'
export default {
  name: "TriggerNode",
  props:{
    config:{
      type: Object,
      default: () => {
        return {}
      }
    }
  },
  components: {Node},
  data() {
    return {
      showError: false,
      errorInfo: '',
    }
  },
  computed:{
    content(){
      this.config
    }
  },
  methods: {
    //校验数据配置的合法性
    validate(err){
      this.showError = false
      if (this.config.props.type === 'WEBHOOK'){
        if(this.$isNotEmpty(this.config.props.http.url)){
          this.showError = false
        }else {
          this.showError = true
          this.errorInfo = '请设置WEBHOOK的URL地址'
        }
      }else if(this.config.props.type === 'EMAIL'){
        if(!this.$isNotEmpty(this.config.props.email.subject)
            || this.config.props.email.to.length === 0
            || !this.$isNotEmpty(this.config.props.email.content)){
          this.showError = true
          this.errorInfo = '请设置邮件发送配置'
        }else {
          this.showError = false
        }
      }
      if (this.showError){
        err.push(`${this.config.name} 触发动作未设置完善`)
      }
      return !this.showError
    }
  }
}
</script>
<style scoped>
</style>
src/views/workspace/InitiateProcess.vue
File was deleted
src/views/workspace/SponsorProcess.vue
File was deleted
src/views/workspace/form.js
File was deleted
src/views/workspace/index.vue
File was deleted
src/views/workspace/process/ProcessInstanceTabs.vue
@@ -1,18 +1,14 @@
<template>
  <div class="process-view">
    <div class="process-view__tabs" v-loading="loading">
      <el-tabs type="border-card">
        <el-tab-pane label="流程图">
          <process-diagram-viewer />
        </el-tab-pane>
      </el-tabs>
    </div>
  </div>
</template>
<script>
import { getProcessInstanceInfo,getFlowDetail } from "@/api/design";
import {getFlowDetail, getWorkSetpsByBusinessId} from "@/api/design";
import ProcessDiagramViewer from "@/views/admin/layout/ProcessDiagramViewer";
export default {
@@ -21,69 +17,50 @@
    return {
      loading: false,
      processInstanceId: "",
      taskId: "",
      formData: {},
      currentNode: {},
      processInfo: "",
    };
  },
  methods: {
    convertToTreeData(data, parent,index,parentId) {
      const tempJson = data.find(f =>
          f.index_no === index
      )
      console.log("tempJson",tempJson);
      this.$store.state.noTakeList .push(tempJson.id)
      parent.children = {
        "id": tempJson.id,
        "parentId": parentId,
        "name": "审批人",
        "type": "APPROVAL",
        "props":
            {
              "assignedType": "ASSIGN_USER",
              "nobody": {
                "handler": "TO_PASS",
                "assignedUser": []
              },
              "refuse": {
                "type": "TO_END",
                "target": ""
              },
              "assignedUser": [{"id": tempJson.board_id||tempJson.rule_code, "name": tempJson.name,"type":tempJson.board_id!=null?"group":"staff"}],
              "approvalGroup":tempJson.board_id!=null?{"id":tempJson.board_id,"name":tempJson.name}:"",
              "staffGroup":tempJson.board_id!=null?"":{"id":tempJson.rule_code,"name":tempJson.name},
            }
      }
      if (data.length >index) {
        let getChildren = this.convertToTreeData(data, parent.children, index+1,parent.children.id)
        parent.children=getChildren
      }
      return parent;
    },
    getProcessInfo() {
      this.loading = true;
      let param = {"id": this.processInstanceId}
      getWorkSetpsByBusinessId(param).then(rsp => {
        let workSetps=rsp.data.data
        if(workSetps.length>0){
          let index=-1;
          let resultProcess;
          let noApprovalArr=workSetps.filter(item=>item.end_time===null);
          ////如果所有节点都没审批 那么就取index_no=1的id为当前运行的节点
          if(noApprovalArr.length===workSetps.length){
            resultProcess=workSetps.find(item=>item.index_no===1)
          }else{
            //否则就取遍历查询 审批节点不为空 index_no最大的
            workSetps.forEach(item => {
              if (item.index_no > index && item.end_time!==null) {
                index = item.index_no;
                resultProcess = item;
              }
            })
          }
          this.$store.state.runningList .push(resultProcess.approve_step_id)
          param.id=resultProcess.approve_id
          this.getFlowDetail(param)
        }else{
          this.$message.error("未查询到审批流数据!")
        }
      }).catch(err => {
        this.$message.error(err)
      })
    },
    getFlowDetail(param){
      getFlowDetail(param).then(rsp => {
        let form = rsp.data.data;
        console.log("getFlowDetail-form", form)
        form.logo = ""
        form.settings = {
          "commiter": [],
          "admin": [],
          "sign": false,
          "notify": {
            "types": [
              "APP"
            ],
            " title": "消息通知标题"
          }
        }
        form.formItems = []
        form.process =this.convertToTreeData(form.steps, {
        form.process =this.$Utils.convertToTreeData(form.steps, {
          "id": form.id, "parentId": null,
          "type": "ROOT",
          "name": "发起人",
@@ -92,57 +69,20 @@
            "formPerms": []
          },
        },1,form.id)
        this.$store.state.runningList .push(form.id)
        form.name=this.$Utils.decode(form.name);
        form.templateName = form.name
        form.groupId = null;
        form.icon = "{\"icon\":\"el-icon-eleme\",\"background\":\"#1e90ff\"}";
        form.notify = "";
        form.remark = "备注说明";
        form.isStop = false
        form.whoCommit = "[]"
        form.whoEdit = "[]"
        form.whoExport = "[]"
        form.templateId = "Steps-B-Director"
        form.formId = "Steps-B-Director"
        form.processDefinitionId = null
        this.$store.state.design = form;
        console.log("this.$store.state.design",this.$store.state.design)
        console.log("输出转换后的form,",form)
        this.$store.commit('loadForm', form)
      }).catch(err => {
        this.$message.error(err)
      }) .finally(() => {
            this.loading = false;
          });
      // getProcessInstanceInfo(this.processInstanceId, this.taskId)
      //   .then((rsp) => {
      //     console.log("流程详情", rsp.data);
      //     const form = { ...rsp.data.result.processTemplates };
      //     const currentNode = { ...rsp.data.result?.currentNode };
      //
      //     form.logo = JSON.parse(form.logo);
      //     form.settings = JSON.parse(form.settings);
      //     form.process = JSON.parse(form.process);
      //     this.$store.state.design = form;
      //     this.$store.state.endList = rsp.data.result.endList;
      //     this.$store.state.runningList = rsp.data.result.runningList;
      //     this.$store.state.noTakeList = rsp.data.result.noTakeList;
      //     this.$store.state.detailVOList = rsp.data.result.detailVOList;
      //
      //     this.currentNode = currentNode;
      //     this.form = form;
      //   })
      //   .finally(() => {
      //     this.loading = false;
      //   });
    },
    }
  },
  beforeMount() {
    this.processInstanceId = this.$route.query.processInstanceId;
    this.taskId = this.$route.query.taskId;
  },
  mounted() {
    this.getProcessInfo();
@@ -154,8 +94,8 @@
.process-view {
  padding: 20px;
  height: 100%;
  display: flex;
  flex-direction: column;
  //display: flex;
  //flex-direction: column;
  box-sizing: border-box;
  &__header {