Procházet zdrojové kódy

【功能完善】仿钉钉流程模型浏览,增加弹窗显示用户任务信息

jason před 9 měsíci
rodič
revize
8465f8fada

+ 26 - 3
src/components/SimpleProcessDesignerV2/src/SimpleProcessViewer.vue

@@ -1,5 +1,5 @@
 <template>
-    <SimpleProcessModel :flow-node="simpleModel" :readonly="true"/>
+  <SimpleProcessModel :flow-node="simpleModel" :readonly="true" />
 </template>
 
 <script setup lang="ts">
@@ -16,10 +16,33 @@ const props = defineProps({
     required: true
   },
   // 流程任务
-  tasks : {
-    type : Array,
+  tasks: {
+    type: Array,
     default: () => [] as any[]
+  },
+  // 流程实例
+  processInstance: {
+    type: Object,
+    default: () => undefined
   }
 })
+const approveTasks = ref<any[]>(props.tasks)
+const currentProcessInstance = ref(props.processInstance)
 const simpleModel = useWatchNode(props)
+watch(
+  () => props.tasks,
+  (newValue) => {
+    approveTasks.value = newValue
+  }
+)
+watch(
+  () => props.processInstance,
+  (newValue) => {
+    currentProcessInstance.value = newValue
+  }
+)
+
+provide('tasks', approveTasks)
+provide('processInstance', currentProcessInstance)
 </script>
+p

+ 79 - 2
src/components/SimpleProcessDesignerV2/src/nodes/EndEventNode.vue

@@ -1,14 +1,69 @@
 <template>
   <div class="end-node-wrapper">
-    <div class="end-node-box" :class="`${useTaskStatusClass(currentNode?.activityStatus)}`">
+    <div class="end-node-box cursor-pointer" :class="`${useTaskStatusClass(currentNode?.activityStatus)}`" @click="nodeClick">
       <span class="node-fixed-name" title="结束">结束</span>
     </div>
   </div>
+  <el-dialog title="审批信息" v-model="dialogVisible" width="1000px" append-to-body>
+      <el-row>
+        <el-table
+          :data="processInstanceInfos"
+          size="small"
+          border
+          header-cell-class-name="table-header-gray"
+        >
+          <el-table-column
+            label="序号"
+            header-align="center"
+            align="center"
+            type="index"
+            width="50"
+          />
+          <el-table-column
+            label="发起人"
+            prop="assigneeUser.nickname"
+            min-width="100"
+            align="center"
+          />
+          <el-table-column label="部门" min-width="100" align="center">
+            <template #default="scope">
+              {{ scope.row.assigneeUser?.deptName || scope.row.ownerUser?.deptName }}
+            </template>
+          </el-table-column>
+          <el-table-column
+            :formatter="dateFormatter"
+            align="center"
+            label="开始时间"
+            prop="createTime"
+            min-width="140"
+          />
+          <el-table-column
+            :formatter="dateFormatter"
+            align="center"
+            label="结束时间"
+            prop="endTime"
+            min-width="140"
+          />
+          <el-table-column align="center" label="审批状态" prop="status" min-width="90">
+            <template #default="scope">
+              <dict-tag :type="DICT_TYPE.BPM_PROCESS_INSTANCE_STATUS" :value="scope.row.status" />
+            </template>
+          </el-table-column>
+         
+          <el-table-column align="center" label="耗时" prop="durationInMillis" width="100">
+            <template #default="scope">
+              {{ formatPast2(scope.row.durationInMillis) }}
+            </template>
+          </el-table-column>
+        </el-table>
+      </el-row>
+    </el-dialog>
 </template>
 <script setup lang="ts">
 import { SimpleFlowNode } from '../consts'
 import { useWatchNode, useTaskStatusClass } from '../node'
-
+import { dateFormatter, formatPast2 } from '@/utils/formatTime'
+import { DICT_TYPE } from '@/utils/dict'
 defineOptions({
   name: 'EndEventNode'
 })
@@ -20,6 +75,28 @@ const props = defineProps({
 })
 // 监控节点变化
 const currentNode = useWatchNode(props)
+// 是否只读
+const readonly = inject<Boolean>('readonly')
+const processInstance = inject<Ref<any>>('processInstance')
+// 审批信息的弹窗显示,用于只读模式
+const dialogVisible = ref(false) // 弹窗可见性
+const processInstanceInfos = ref<any[]>([]) // 流程的审批信息
 
+const nodeClick = () => {
+  if (readonly) { 
+    if(processInstance && processInstance.value){
+      processInstanceInfos.value = [
+      {
+        assigneeUser: processInstance.value.startUser,
+        createTime: processInstance.value.startTime,
+        endTime: processInstance.value.endTime,
+        status: processInstance.value.status,
+        durationInMillis: processInstance.value.durationInMillis
+      }
+    ]
+      dialogVisible.value = true
+    }
+  }
+}
 </script>
 <style lang="scss" scoped></style>

+ 86 - 7
src/components/SimpleProcessDesignerV2/src/nodes/StartUserNode.vue

@@ -22,7 +22,7 @@
             {{ currentNode.name }}
           </div>
         </div>
-        <div class="node-content" @click="openNodeConfig">
+        <div class="node-content" @click="nodeClick">
           <div class="node-text" :title="currentNode.showText" v-if="currentNode.showText">
             {{ currentNode.showText }}
           </div>
@@ -37,12 +37,78 @@
     </div>
   </div>
   <StartUserNodeConfig v-if="!readonly && currentNode" ref="nodeSetting" :flow-node="currentNode" />
+   <!-- 审批记录 -->
+   <el-dialog :title="dialogTitle || '审批记录'" v-model="dialogVisible" width="1000px"  append-to-body>
+      <el-row>
+        <el-table
+          :data="selectTasks"
+          size="small"
+          border
+          header-cell-class-name="table-header-gray"
+        >
+          <el-table-column
+            label="序号"
+            header-align="center"
+            align="center"
+            type="index"
+            width="50"
+          />
+          <el-table-column
+            label="审批人"
+            min-width="100"
+            align="center"
+          >
+            <template #default="scope">
+              {{ scope.row.assigneeUser?.nickname || scope.row.ownerUser?.nickname }}
+            </template>
+          </el-table-column>
+         
+          <el-table-column label="部门" min-width="100" align="center">
+            <template #default="scope">
+              {{ scope.row.assigneeUser?.deptName || scope.row.ownerUser?.deptName }}
+            </template>
+          </el-table-column>
+          <el-table-column
+            :formatter="dateFormatter"
+            align="center"
+            label="开始时间"
+            prop="createTime"
+            min-width="140"
+          />
+          <el-table-column
+            :formatter="dateFormatter"
+            align="center"
+            label="结束时间"
+            prop="endTime"
+            min-width="140"
+          />
+          <el-table-column align="center" label="审批状态" prop="status" min-width="90">
+            <template #default="scope">
+              <dict-tag :type="DICT_TYPE.BPM_TASK_STATUS" :value="scope.row.status" />
+            </template>
+          </el-table-column>
+          <el-table-column
+            align="center"
+            label="审批建议"
+            prop="reason"
+            min-width="120"
+          />
+          <el-table-column align="center" label="耗时" prop="durationInMillis" width="100">
+            <template #default="scope">
+              {{ formatPast2(scope.row.durationInMillis) }}
+            </template>
+          </el-table-column>
+        </el-table>
+      </el-row>
+  </el-dialog>
 </template>
 <script setup lang="ts">
 import NodeHandler from '../NodeHandler.vue'
 import { useWatchNode, useNodeName2, useTaskStatusClass } from '../node'
 import { SimpleFlowNode, NODE_DEFAULT_TEXT, NodeType } from '../consts'
 import StartUserNodeConfig from '../nodes-config/StartUserNodeConfig.vue'
+import { dateFormatter, formatPast2 } from '@/utils/formatTime'
+import { DICT_TYPE } from '@/utils/dict'
 defineOptions({
   name: 'StartEventNode'
 })
@@ -53,6 +119,7 @@ const props = defineProps({
   }
 })
 const readonly = inject<Boolean>('readonly') // 是否只读
+const tasks = inject<Ref<any[]>>('tasks')
 // 定义事件,更新父组件。
 const emits = defineEmits<{
   'update:modelValue': [node: SimpleFlowNode | undefined]
@@ -63,15 +130,27 @@ const currentNode = useWatchNode(props)
 const { showInput, blurEvent, clickTitle } = useNodeName2(currentNode, NodeType.START_USER_NODE)
 
 const nodeSetting = ref()
-// 打开节点配置
-const openNodeConfig = () => {
+// 
+const nodeClick = () => {
   if (readonly) {
-    return
+    // 只读模式,弹窗显示任务信息
+    if(tasks && tasks.value){
+      dialogTitle.value = currentNode.value.name
+      selectTasks.value = tasks.value.filter((item: any) => item?.taskDefinitionKey === currentNode.value.id)
+      dialogVisible.value = true
+    }
+  } else {
+    // 编辑模式,打开节点配置、把当前节点传递给配置组件
+    nodeSetting.value.showStartUserNodeConfig(currentNode.value)
+    nodeSetting.value.openDrawer()
   }
-  // 把当前节点传递给配置组件
-  nodeSetting.value.showStartUserNodeConfig(currentNode.value)
-  nodeSetting.value.openDrawer()
+ 
 }
 
+// 任务的弹窗显示,用于只读模式
+const dialogVisible = ref(false) // 弹窗可见性
+const dialogTitle = ref<string | undefined>(undefined) // 弹窗标题
+const selectTasks = ref<any[]|undefined>([]) // 选中的任务数组
+
 </script>
 <style lang="scss" scoped></style>

+ 88 - 10
src/components/SimpleProcessDesignerV2/src/nodes/UserTaskNode.vue

@@ -20,7 +20,7 @@
             {{ currentNode.name }}
           </div>
         </div>
-        <div class="node-content" @click="openNodeConfig">
+        <div class="node-content" @click="nodeClick">
           <div class="node-text" :title="currentNode.showText" v-if="currentNode.showText">
             {{ currentNode.showText }}
           </div>
@@ -45,12 +45,78 @@
     :flow-node="currentNode"
     @find:return-task-nodes="findReturnTaskNodes"
   />
+  <!-- 审批记录 -->
+  <el-dialog :title="dialogTitle || '审批记录'" v-model="dialogVisible" width="1000px"  append-to-body>
+      <el-row>
+        <el-table
+          :data="selectTasks"
+          size="small"
+          border
+          header-cell-class-name="table-header-gray"
+        >
+          <el-table-column
+            label="序号"
+            header-align="center"
+            align="center"
+            type="index"
+            width="50"
+          />
+          <el-table-column
+            label="审批人"
+            min-width="100"
+            align="center"
+          >
+            <template #default="scope">
+              {{ scope.row.assigneeUser?.nickname || scope.row.ownerUser?.nickname }}
+            </template>
+          </el-table-column>
+         
+          <el-table-column label="部门" min-width="100" align="center">
+            <template #default="scope">
+              {{ scope.row.assigneeUser?.deptName || scope.row.ownerUser?.deptName }}
+            </template>
+          </el-table-column>
+          <el-table-column
+            :formatter="dateFormatter"
+            align="center"
+            label="开始时间"
+            prop="createTime"
+            min-width="140"
+          />
+          <el-table-column
+            :formatter="dateFormatter"
+            align="center"
+            label="结束时间"
+            prop="endTime"
+            min-width="140"
+          />
+          <el-table-column align="center" label="审批状态" prop="status" min-width="90">
+            <template #default="scope">
+              <dict-tag :type="DICT_TYPE.BPM_TASK_STATUS" :value="scope.row.status" />
+            </template>
+          </el-table-column>
+          <el-table-column
+            align="center"
+            label="审批建议"
+            prop="reason"
+            min-width="120"
+          />
+          <el-table-column align="center" label="耗时" prop="durationInMillis" width="100">
+            <template #default="scope">
+              {{ formatPast2(scope.row.durationInMillis) }}
+            </template>
+          </el-table-column>
+        </el-table>
+      </el-row>
+  </el-dialog>
 </template>
 <script setup lang="ts">
 import { SimpleFlowNode, NodeType, NODE_DEFAULT_TEXT } from '../consts'
 import { useWatchNode, useNodeName2, useTaskStatusClass } from '../node'
 import NodeHandler from '../NodeHandler.vue'
 import UserTaskNodeConfig from '../nodes-config/UserTaskNodeConfig.vue'
+import { dateFormatter, formatPast2 } from '@/utils/formatTime'
+import { DICT_TYPE } from '@/utils/dict'
 defineOptions({
   name: 'UserTaskNode'
 })
@@ -67,26 +133,32 @@ const emits = defineEmits<{
 
 // 是否只读
 const readonly = inject<Boolean>('readonly')
+const tasks = inject<Ref<any[]>>('tasks')
 // 监控节点变化
 const currentNode = useWatchNode(props)
-
 // 节点名称编辑
 const { showInput, blurEvent, clickTitle } = useNodeName2(currentNode, NodeType.START_USER_NODE)
 const nodeSetting = ref()
-// 打开节点配置
-const openNodeConfig = () => {
-  if (readonly) {
-    return
+
+
+const nodeClick = () => {
+  if (readonly) { 
+    if(tasks && tasks.value){
+      dialogTitle.value = currentNode.value.name
+      // 只读模式,弹窗显示任务信息
+      selectTasks.value = tasks.value.filter((item: any) => item?.taskDefinitionKey === currentNode.value.id)
+      dialogVisible.value = true
+    }
+  } else { 
+     // 编辑模式,打开节点配置、把当前节点传递给配置组件
+    nodeSetting.value.showUserTaskNodeConfig(currentNode.value)
+    nodeSetting.value.openDrawer()
   }
-  // 把当前节点传递给配置组件
-  nodeSetting.value.showUserTaskNodeConfig(currentNode.value)
-  nodeSetting.value.openDrawer()
 }
 
 const deleteNode = () => {
   emits('update:flowNode', currentNode.value.childNode)
 }
-
 // 查找可以驳回用户节点
 const findReturnTaskNodes = (
   matchNodeList: SimpleFlowNode[] // 匹配的节点
@@ -94,5 +166,11 @@ const findReturnTaskNodes = (
   // 从父节点查找
   emits('find:parentNode', matchNodeList, NodeType.USER_TASK_NODE)
 }
+
+// 任务的弹窗显示,用于只读模式
+const dialogVisible = ref(false) // 弹窗可见性
+const dialogTitle = ref<string | undefined>(undefined) // 弹窗标题
+const selectTasks = ref<any[]|undefined>([]) // 选中的任务数组
+
 </script>
 <style lang="scss" scoped></style>

+ 1 - 571
src/components/SimpleProcessDesignerV2/theme/simple-process-designer.scss

@@ -1,573 +1,3 @@
-// .simple-flow-canvas {
-//   z-index: 1;
-//   overflow: auto;
-//   background-color: #fafafa;
-//   // user-select: none;
-
-//   .simple-flow-container {
-//     position: relative;
-//     display: flex;
-//     flex-direction: column;
-//     justify-content: center;
-//     align-items: center;
-
-//     .top-area-container {
-//       position: sticky;
-//       inset: 0;
-//       display: flex;
-//       width: 100%;
-//       height: 42px;
-//       z-index: 1;
-//       // padding: 4px 0;
-//       background-color: #fff;
-//       justify-content: flex-end;
-//       align-items: center;
-
-//       .top-actions {
-//         display: flex;
-//         margin: 4px;
-//         margin-right: 8px;
-//         align-items: center;
-
-//         .canvas-control {
-//           font-size: 16px;
-
-//           .control-scale-group {
-//             display: inline-flex;
-//             align-items: center;
-//             margin-right: 8px;
-
-//             .control-scale-button {
-//               display: inline-flex;
-//               width: 28px;
-//               height: 28px;
-//               padding: 2px;
-//               text-align: center;
-//               cursor: pointer;
-//               justify-content: center;
-//               align-items: center;
-//             }
-
-//             .control-scale-label {
-//               margin: 0 4px;
-//               font-size: 14px;
-//             }
-//           }
-//         }
-//       }
-//     }
-
-//     .scale-container {
-//       display: flex;
-//       flex-direction: column;
-//       justify-content: center;
-//       align-items: center;
-//       margin-top: 16px;
-//       background-color: #fafafa;
-//       transform-origin: 50% 0 0;
-//       transform: scale(1);
-//       transition: transform 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
-
-//       // 节点容器 定义节点宽度
-//       .node-container {
-//         width: 200px;
-//       }
-//       // 节点
-//       .node-box {
-//         position: relative;
-//         display: flex;
-//         min-height: 70px;
-//         padding: 5px 10px 8px;
-//         cursor: pointer;
-//         background-color: #fff;
-//         flex-direction: column;
-//         border: 2px solid transparent;
-//         border-radius: 8px;
-//         box-shadow: 0 1px 4px 0 rgba(10, 30, 65, 0.16);
-//         transition: all 0.1s cubic-bezier(0.645, 0.045, 0.355, 1);
-
-//         &.status-pass {
-//           border-color: #67c23a;
-//           background-color: #a9da90;
-//         }
-//         &.status-pass:hover {
-//           border-color: #67c23a;
-//         }
-//         &.status-running {
-//           border-color: #5a9cf8;
-//           background-color: #e7f0fe;
-//         }
-//         &.status-running:hover {
-//           border-color: #5a9cf8;
-//         }
-//         &.status-reject {
-//           border-color: #e47470;
-//           background-color: #f6e5e5;
-//         }
-//         &.status-reject:hover {
-//           border-color: #e47470;
-//         }
-
-//         &:hover {
-//           border-color: #0089ff;
-//           .node-toolbar {
-//             opacity: 1;
-//           }
-
-//           .branch-node-move {
-//             display: flex;
-//           }
-//         }
-
-//         // 普通节点标题
-//         .node-title-container {
-//           display: flex;
-//           padding: 4px;
-//           cursor: pointer;
-//           border-radius: 4px 4px 0 0;
-//           align-items: center;
-
-//           .node-title-icon {
-//             display: flex;
-//             align-items: center;
-
-//             &.user-task {
-//               color: #ff943e;
-//             }
-//             &.copy-task {
-//               color: #3296fa;
-//             }
-//             &.start-user {
-//               color: #676565;
-//             }
-//           }
-
-//           .node-title {
-//             margin-left: 4px;
-//             font-size: 14px;
-//             font-weight: 600;
-//             white-space: nowrap;
-//             overflow: hidden;
-//             text-overflow: ellipsis;
-//             color: #1f1f1f;
-//             line-height: 18px;
-//             &:hover {
-//               border-bottom: 1px dashed #f60;
-//             }
-//           }
-//         }
-
-//         // 条件节点标题
-//         .branch-node-title-container {
-//           display: flex;
-//           padding: 4px 0;
-//           cursor: pointer;
-//           border-radius: 4px 4px 0 0;
-//           align-items: center;
-//           justify-content: space-between;
-
-//           .input-max-width {
-//             max-width: 115px !important;
-//           }
-
-//           .branch-title {
-//             font-size: 13px;
-//             font-weight: 600;
-//             white-space: nowrap;
-//             overflow: hidden;
-//             text-overflow: ellipsis;
-//             color: #f60;
-//             &:hover {
-//               border-bottom: 1px dashed #000;
-//             }
-//           }
-
-//           .branch-priority {
-//             min-width: 50px;
-//             font-size: 12px;
-//           }
-//         }
-
-//         .node-content {
-//           display: flex;
-//           min-height: 32px;
-//           padding: 4px 8px;
-//           margin-top: 4px;
-//           line-height: 32px;
-//           justify-content: space-between;
-//           align-items: center;
-//           color: #111f2c;
-//           background: rgba(0, 0, 0, 0.03);
-//           border-radius: 4px;
-
-//           .node-text {
-//             display: -webkit-box;
-//             overflow: hidden;
-//             font-size: 14px;
-//             line-height: 24px;
-//             text-overflow: ellipsis;
-//             word-break: break-all;
-//             -webkit-line-clamp: 2; /* 这将限制文本显示为两行 */
-//             -webkit-box-orient: vertical;
-//           }
-//         }
-
-//         //条件节点内容
-//         .branch-node-content {
-//           display: flex;
-//           min-height: 32px;
-//           padding: 4px 0;
-//           margin-top: 4px;
-//           line-height: 32px;
-//           align-items: center;
-//           color: #111f2c;
-//           border-radius: 4px;
-
-//           .branch-node-text {
-//             overflow: hidden;
-//             font-size: 12px;
-//             line-height: 24px;
-//             text-overflow: ellipsis;
-//             word-break: break-all;
-//             -webkit-line-clamp: 2; /* 这将限制文本显示为两行 */
-//             -webkit-box-orient: vertical;
-//           }
-//         }
-
-//         // 节点操作 :删除
-//         .node-toolbar {
-//           opacity: 0;
-//           position: absolute;
-//           top: -20px;
-//           right: 0px;
-//           display: flex;
-
-//           .toolbar-icon {
-//             text-align: center;
-//             vertical-align: middle;
-//           }
-//         }
-
-//         // 条件节点左右移动
-//         .branch-node-move {
-//           position: absolute;
-//           width: 10px;
-//           cursor: pointer;
-//           display: none;
-//           align-items: center;
-//           height: 100%;
-//           justify-content: center;
-//         }
-
-//         .move-node-left {
-//           left: -2px;
-//           top: 0px;
-//           background: rgba(126, 134, 142, 0.08);
-//           border-top-left-radius: 8px;
-//           border-bottom-left-radius: 8px;
-//         }
-
-//         .move-node-right {
-//           right: -2px;
-//           top: 0px;
-//           background: rgba(126, 134, 142, 0.08);
-//           border-top-right-radius: 6px;
-//           border-bottom-right-radius: 6px;
-//         }
-//       }
-
-//       .node-config-error {
-//         border-color: #ff5219 !important;
-//       }
-//       // 普通节点包装
-//       .node-wrapper {
-//         display: flex;
-//         flex-direction: column;
-//         justify-content: center;
-//         align-items: center;
-//       }
-//       // 节点连线处理
-//       .node-handler-wrapper {
-//         position: relative;
-//         display: flex;
-//         height: 70px;
-//         align-items: center;
-//         user-select: none;
-//         justify-content: center;
-//         flex-direction: column;
-
-//         &::before {
-//           position: absolute;
-//           top: 0;
-//           z-index: 0;
-//           width: 2px;
-//           height: 100%;
-//           margin: auto;
-//           background-color: #dedede;
-//           content: '';
-//         }
-
-//         .node-handler {
-//           .add-icon {
-//             position: relative;
-//             top: -5px;
-//             display: flex;
-//             align-items: center;
-//             justify-content: center;
-//             cursor: pointer;
-//             width: 25px;
-//             height: 25px;
-//             color: #fff;
-//             background-color: #0089ff;
-//             border-radius: 50%;
-
-//             &:hover {
-//               transform: scale(1.1);
-//             }
-//           }
-//         }
-
-//         .node-handler-arrow {
-//           position: absolute;
-//           bottom: 0;
-//           left: 50%;
-//           display: flex;
-//           transform: translateX(-50%);
-//         }
-//       }
-
-//       // 条件节点包装
-//       .branch-node-wrapper {
-//         position: relative;
-//         display: flex;
-//         flex-direction: column;
-//         justify-content: center;
-//         align-items: center;
-//         margin-top: 16px;
-
-//         .branch-node-container {
-//           position: relative;
-//           display: flex;
-
-//           &::before {
-//             position: absolute;
-//             height: 100%;
-//             width: 4px;
-//             background-color: #fafafa;
-//             content: '';
-//             left: 50%;
-//             transform: translate(-50%);
-//           }
-
-//           .branch-node-add {
-//             position: absolute;
-//             top: -18px;
-//             left: 50%;
-//             z-index: 1;
-//             height: 36px;
-//             padding: 0 10px;
-//             font-size: 12px;
-//             line-height: 36px;
-//             border: 2px solid #dedede;
-//             border-radius: 18px;
-//             transform: translateX(-50%);
-//             transform-origin: center center;
-//           }
-
-//           .branch-node-readonly {
-//             position: absolute;
-//             top: -18px;
-//             left: 50%;
-//             z-index: 1;
-//             width: 36px;
-//             height: 36px;
-//             display: flex;
-//             align-items: center;
-//             justify-content: center;
-//             border: 2px solid #dedede;
-//             background-color: #fff;
-//             border-radius: 50%;
-//             transform: translateX(-50%);
-//             transform-origin: center center;
-
-//             &.status-pass {
-//               border-color: #6bb63c;
-//               background-color: #e9f4e2;
-//             }
-//             &.status-pass:hover {
-//               border-color: #6bb63c;
-//             }
-
-//             .icon-size {
-//               font-size: 22px;
-//               color: #67c23a;
-//             }
-//           }
-
-//           .branch-node-item {
-//             position: relative;
-//             display: flex;
-//             flex-direction: column;
-//             align-items: center;
-//             min-width: 280px;
-//             padding: 40px 40px 0;
-//             background: transparent;
-//             border-top: 2px solid #dedede;
-//             border-bottom: 2px solid #dedede;
-
-//             &::before {
-//               position: absolute;
-//               width: 2px;
-//               height: 100%;
-//               margin: auto;
-//               inset: 0;
-//               background-color: #dedede;
-//               content: '';
-//             }
-//           }
-//           // 覆盖条件节点第一个节点左上角的线
-//           .branch-line-first-top {
-//             position: absolute;
-//             top: -5px;
-//             left: -1px;
-//             width: 50%;
-//             height: 7px;
-//             background-color: #fafafa;
-//             content: '';
-//           }
-//           // 覆盖条件节点第一个节点左下角的线
-//           .branch-line-first-bottom {
-//             position: absolute;
-//             bottom: -5px;
-//             left: -1px;
-//             width: 50%;
-//             height: 7px;
-//             background-color: #fafafa;
-//             content: '';
-//           }
-//           // 覆盖条件节点最后一个节点右上角的线
-//           .branch-line-last-top {
-//             position: absolute;
-//             top: -5px;
-//             right: -1px;
-//             width: 50%;
-//             height: 7px;
-//             background-color: #fafafa;
-//             content: '';
-//           }
-//           // 覆盖条件节点最后一个节点右下角的线
-//           .branch-line-last-bottom {
-//             position: absolute;
-//             right: -1px;
-//             bottom: -5px;
-//             width: 50%;
-//             height: 7px;
-//             background-color: #fafafa;
-//             content: '';
-//           }
-//         }
-//       }
-
-//       .node-fixed-name {
-//         display: inline-block;
-//         width: auto;
-//         padding: 0 4px;
-//         overflow: hidden;
-//         text-align: center;
-//         text-overflow: ellipsis;
-//         white-space: nowrap;
-//       }
-//       // 开始节点包装
-//       .start-node-wrapper {
-//         position: relative;
-//         margin-top: 16px;
-
-//         .start-node-container {
-//           display: flex;
-//           flex-direction: column;
-//           justify-content: center;
-//           align-items: center;
-
-//           .start-node-box {
-//             display: flex;
-//             justify-content: center;
-//             align-items: center;
-//             width: 90px;
-//             height: 36px;
-//             padding: 3px 4px;
-//             color: #212121;
-//             cursor: pointer;
-//             background: #fafafa;
-//             border-radius: 30px;
-//             box-shadow: 0 1px 5px 0 rgba(10, 30, 65, 0.08);
-//             box-sizing: border-box;
-//           }
-//         }
-//       }
-
-//       // 结束节点包装
-//       .end-node-wrapper {
-//         margin-bottom: 16px;
-
-//         .end-node-box {
-//           display: flex;
-//           justify-content: center;
-//           align-items: center;
-//           width: 80px;
-//           height: 36px;
-//           border: 2px solid #fafafa;
-//           color: #212121;
-//           border-radius: 30px;
-//           box-shadow: 0 1px 5px 0 rgba(10, 30, 65, 0.08);
-//           box-sizing: border-box;
-
-//           &.status-pass {
-//             border-color: #6bb63c;
-//             background-color: #a9da90;
-//           }
-
-//           &.status-pass:hover {
-//             border-color: #6bb63c;
-//           }
-
-//           &.status-reject {
-//             border-color: #e47470;
-//             background-color: #f6e5e5;
-//           }
-//           &.status-reject:hover {
-//             border-color: #e47470;
-//           }
-
-//           &.status-cancel {
-//             border-color: #919398;
-//             background-color: #eaeaeb;
-//           }
-//           &.status-cancel:hover {
-//             border-color: #919398;
-//           }
-//         }
-//       }
-
-//       // 可编辑的 title 输入框
-//       .editable-title-input {
-//         height: 20px;
-//         max-width: 145px;
-//         line-height: 20px;
-//         font-size: 12px;
-//         margin-left: 4px;
-//         border: 1px solid #d9d9d9;
-//         border-radius: 4px;
-//         transition: all 0.3s;
-
-//         &:focus {
-//           border-color: #40a9ff;
-//           outline: 0;
-//           box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
-//         }
-//       }
-//     }
-//   }
-// }
 
 // 配置节点头部
 .config-header {
@@ -739,7 +169,7 @@
     font-size: 13px;
   }
 }
-
+// Simple 流程模型样式
 .simple-process-model-container {
   height: 100%;
   overflow: auto;

+ 7 - 13
src/views/bpm/processInstance/detail/ProcessInstanceSimpleViewer.vue

@@ -1,6 +1,6 @@
 <template>
   <div v-loading="loading" class="mb-20px">
-      <SimpleProcessViewer :flow-node="simpleModel" :tasks="tasks"/>
+      <SimpleProcessViewer :flow-node="simpleModel" :tasks="tasks" :process-instance="processInstance"/>
   </div>
 </template>
 <script lang="ts" setup>
@@ -16,10 +16,11 @@ const props = defineProps({
   loading: propTypes.bool.def(false), // 是否加载中
   id: propTypes.string // 流程实例的编号
 })
-
-const tasks = ref([])
 const simpleModel = ref()
-
+// 用户任务
+const tasks = ref([])
+// 流程实例
+const processInstance = ref()
 /** 只有 loading 完成时,才去加载流程列表 */
 watch(
   () => props.loading,
@@ -27,7 +28,8 @@ watch(
     if (value && props.id) {
       const modelView = await ProcessInstanceApi.getProcessInstanceBpmnModelView(props.id)
       if (modelView) {
-        tasks.value = modelView.tasks;
+        tasks.value = modelView.tasks
+        processInstance.value = modelView.processInstance
         // 已经拒绝的活动节点编号集合,只包括 UserTask
         const rejectedTaskActivityIds: string[] = modelView.rejectedTaskActivityIds
         // 进行中的活动节点编号集合, 只包括 UserTask
@@ -44,7 +46,6 @@ watch(
           finishedActivityIds,
           finishedSequenceFlowActivityIds
         )
-        console.log('modelView.simpleModel==>', modelView.simpleModel)
         simpleModel.value = modelView.simpleModel
       }
     }
@@ -140,11 +141,4 @@ const setSimpleModelNodeTaskStatus = (
     finishedSequenceFlowActivityIds
   )
 }
-/** 监听 bpmnXml */
-// watch(
-//   () => props.bpmnXml,
-//   (value) => {
-//     view.value.bpmnXml = value
-//   }
-// )
 </script>