【功能新增】 仿钉钉流程模型增加浏览模式

pull/582/head
jason 2024-10-28 10:02:35 +08:00
parent d477b35f83
commit 137b33e7cf
17 changed files with 436 additions and 58 deletions

View File

@ -6,6 +6,7 @@
v-model:visible="popoverShow"
placement="right-start"
width="auto"
v-if="!readonly"
>
<div class="handler-item-wrapper">
<div class="handler-item" @click="addNode(NodeType.USER_TASK_NODE)">
@ -78,6 +79,8 @@ const props = defineProps({
const emits = defineEmits(['update:childNode'])
const readonly = inject<Boolean>('readonly') //
const addNode = (type: number) => {
popoverShow.value = false
if (type === NodeType.USER_TASK_NODE) {

View File

@ -47,7 +47,7 @@
/>
<!-- 结束节点 -->
<EndEventNode v-if="currentNode && currentNode.type === NodeType.END_EVENT_NODE" />
<EndEventNode v-if="currentNode && currentNode.type === NodeType.END_EVENT_NODE" :flow-node="currentNode" />
</template>
<script setup lang="ts">
import StartUserNode from './nodes/StartUserNode.vue'

View File

@ -57,7 +57,7 @@ const props = defineProps({
required: true
}
})
const loading = ref(true)
const loading = ref(false)
const formFields = ref<string[]>([])
const formType = ref(20)
const roleOptions = ref<RoleApi.RoleVO[]>([]) //
@ -66,6 +66,7 @@ const userOptions = ref<UserApi.UserVO[]>([]) // 用户列表
const deptOptions = ref<DeptApi.DeptVO[]>([]) //
const deptTreeOptions = ref()
const userGroupOptions = ref<UserGroupApi.UserGroupVO[]>([]) //
provide('readonly', false)
provide('formFields', formFields)
provide('formType', formType)
provide('roleList', roleOptions)

View File

@ -0,0 +1,63 @@
<template>
<div class="simple-flow-canvas" v-loading="loading">
<div class="simple-flow-container">
<div class="scale-container" :style="`transform: scale(${scaleValue / 100});`">
<ProcessNodeTree v-if="processNodeTree" v-model:flow-node="processNodeTree"/>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import ProcessNodeTree from './ProcessNodeTree.vue'
import { SimpleFlowNode } from './consts'
defineOptions({
name: 'SimpleProcessRender'
})
const props = defineProps({
flowNode: {
type: Object as () => SimpleFlowNode,
required: true
}
})
const loading = ref(false)
watch(
() => props.flowNode,
(newValue) => {
processNodeTree.value = newValue
}
)
const processNodeTree = ref<SimpleFlowNode | undefined>(props.flowNode)
provide('readonly', true)
let scaleValue = ref(100)
const MAX_SCALE_VALUE = 200
const MIN_SCALE_VALUE = 50
//
const zoomOut = () => {
if (scaleValue.value == MAX_SCALE_VALUE) {
return
}
scaleValue.value += 10
}
//
const zoomIn = () => {
if (scaleValue.value == MIN_SCALE_VALUE) {
return
}
scaleValue.value -= 10
}
// onMounted(async () => {
// try {
// loading.value = true
// if (props.view) {
// processNodeTree.value = props.view.simpleModel
// }
// } finally {
// loading.value = false
// }
// })
</script>

View File

@ -1,6 +1,6 @@
// @ts-ignore
import { DictDataVO } from '@/api/system/dict/types'
import { TaskStatusEnum } from '@/api/bpm/task'
/**
*
*/
@ -96,6 +96,8 @@ export interface SimpleFlowNode {
conditionGroups?: ConditionGroup
// 是否默认的条件
defaultFlow?: boolean
// 活动的状态,用于前端节点状态展示
activityStatus? : TaskStatusEnum
}
// 候选人策略枚举 用于审批节点。抄送节点 )
export enum CandidateStrategy {

View File

@ -1,4 +1,5 @@
import SimpleProcessDesigner from './SimpleProcessDesigner.vue'
import SimpleProcessViewer from './SimpleProcessViewer.vue'
import '../theme/simple-process-designer.scss'
export { SimpleProcessDesigner }
export { SimpleProcessDesigner, SimpleProcessViewer }

View File

@ -1,4 +1,5 @@
import { cloneDeep } from 'lodash-es'
import { TaskStatusEnum } from '@/api/bpm/task'
import * as RoleApi from '@/api/system/role'
import * as DeptApi from '@/api/system/dept'
import * as PostApi from '@/api/system/post'
@ -476,3 +477,26 @@ export function useNodeName2(node: Ref<SimpleFlowNode>, nodeType: NodeType) {
blurEvent
}
}
/**
* @description
*/
export function useTaskStatusClass(taskStatus: TaskStatusEnum | undefined) : string {
if (!taskStatus) {
return ''
}
if (taskStatus === TaskStatusEnum.APPROVE ) {
return 'status-pass'
}
if (taskStatus === TaskStatusEnum.RUNNING ) {
return 'status-running'
}
if (taskStatus === TaskStatusEnum.REJECT ) {
return 'status-reject'
}
if (taskStatus === TaskStatusEnum.CANCEL ) {
return 'status-cancel'
}
return '';
}

View File

@ -5,7 +5,7 @@
<div class="node-title-container">
<div class="node-title-icon copy-task"><span class="iconfont icon-copy"></span></div>
<input
v-if="showInput"
v-if="!readonly && showInput"
type="text"
class="editable-title-input"
@blur="blurEvent()"
@ -24,9 +24,9 @@
<div class="node-text" v-else>
{{ NODE_DEFAULT_TEXT.get(NodeType.COPY_TASK_NODE) }}
</div>
<Icon icon="ep:arrow-right-bold" />
<Icon v-if="!readonly" icon="ep:arrow-right-bold" />
</div>
<div class="node-toolbar">
<div v-if="!readonly" class="node-toolbar">
<div class="toolbar-icon"
><Icon color="#0089ff" icon="ep:circle-close-filled" :size="18" @click="deleteNode"
/></div>
@ -36,7 +36,7 @@
<!-- 传递子节点给添加节点组件会在子节点前面添加节点 -->
<NodeHandler v-if="currentNode" v-model:child-node="currentNode.childNode" />
</div>
<CopyTaskNodeConfig v-if="currentNode" ref="nodeSetting" :flow-node="currentNode" />
<CopyTaskNodeConfig v-if="!readonly && currentNode" ref="nodeSetting" :flow-node="currentNode" />
</div>
</template>
<script setup lang="ts">
@ -57,7 +57,8 @@ const props = defineProps({
const emits = defineEmits<{
'update:flowNode': [node: SimpleFlowNode | undefined]
}>()
//
const readonly = inject<Boolean>('readonly')
//
const currentNode = useWatchNode(props)
//
@ -66,6 +67,9 @@ const { showInput, blurEvent, clickTitle } = useNodeName2(currentNode, NodeType.
const nodeSetting = ref()
//
const openNodeConfig = () => {
if (readonly) {
return
}
nodeSetting.value.showCopyTaskNodeConfig(currentNode.value)
nodeSetting.value.openDrawer()
}

View File

@ -1,13 +1,27 @@
<template>
<div class="end-node-wrapper">
<div class="end-node-box">
<div class="end-node-box" :class="taskStatusClass">
<span class="node-fixed-name" title="结束">结束</span>
</div>
</div>
</template>
<script setup lang="ts">
import { SimpleFlowNode } from '../consts'
import { useWatchNode, useTaskStatusClass } from '../node'
defineOptions({
name: 'EndEventNode'
})
const props = defineProps({
flowNode: {
type: Object as () => SimpleFlowNode,
default: () => null
}
})
//
const currentNode = useWatchNode(props)
//
const taskStatusClass = useTaskStatusClass(currentNode.value?.activityStatus)
</script>
<style lang="scss" scoped></style>

View File

@ -1,7 +1,13 @@
<template>
<div class="branch-node-wrapper">
<div class="branch-node-container">
<el-button class="branch-node-add" color="#67c23a" @click="addCondition" plain>添加条件</el-button>
<div v-if="readonly" class="branch-node-readonly" :class="taskStatusClass">
<span class="iconfont icon-exclusive icon-size"></span>
</div>
<el-button v-else class="branch-node-add" color="#67c23a" @click="addCondition" plain
>添加条件</el-button
>
<div
class="branch-node-item"
v-for="(item, index) in currentNode.conditionNodes"
@ -17,9 +23,9 @@
</template>
<div class="node-wrapper">
<div class="node-container">
<div class="node-box" :class="{ 'node-config-error': !item.showText }">
<div class="node-box" :class="[{ 'node-config-error': !item.showText }, `${useTaskStatusClass(item.activityStatus)}`]">
<div class="branch-node-title-container">
<div v-if="showInputs[index]">
<div v-if="!readonly && showInputs[index]">
<input
type="text"
class="input-max-width editable-title-input"
@ -39,7 +45,10 @@
{{ NODE_DEFAULT_TEXT.get(NodeType.CONDITION_NODE) }}
</div>
</div>
<div class="node-toolbar" v-if="index + 1 !== currentNode.conditionNodes?.length">
<div
class="node-toolbar"
v-if="!readonly && index + 1 !== currentNode.conditionNodes?.length"
>
<div class="toolbar-icon">
<Icon
color="#0089ff"
@ -87,6 +96,7 @@ import NodeHandler from '../NodeHandler.vue'
import ProcessNodeTree from '../ProcessNodeTree.vue'
import { SimpleFlowNode, NodeType, NODE_DEFAULT_TEXT } from '../consts'
import { getDefaultConditionNodeName } from '../utils'
import { useTaskStatusClass } from '../node'
import { generateUUID } from '@/utils'
import ConditionNodeConfig from '../nodes-config/ConditionNodeConfig.vue'
const { proxy } = getCurrentInstance() as any
@ -109,9 +119,11 @@ const emits = defineEmits<{
nodeType: number
]
}>()
//
const readonly = inject<Boolean>('readonly')
const currentNode = ref<SimpleFlowNode>(props.flowNode)
// const conditionNodes = computed(() => currentNode.value.conditionNodes);
//
const taskStatusClass = useTaskStatusClass(currentNode.value?.activityStatus)
watch(
() => props.flowNode,
@ -135,6 +147,9 @@ const clickEvent = (index: number) => {
}
const conditionNodeConfig = (nodeId: string) => {
if (readonly) {
return
}
const conditionNode = proxy.$refs[nodeId][0]
conditionNode.open()
}

View File

@ -1,7 +1,10 @@
<template>
<div class="branch-node-wrapper">
<div class="branch-node-container">
<el-button class="branch-node-add" color="#345da2" @click="addCondition" plain>添加条件</el-button>
<div v-if="readonly" class="branch-node-readonly">
<span class="iconfont icon-inclusive icon-size"></span>
</div>
<el-button v-else class="branch-node-add" color="#345da2" @click="addCondition" plain>添加条件</el-button>
<div
class="branch-node-item"
v-for="(item, index) in currentNode.conditionNodes"
@ -38,7 +41,7 @@
{{ NODE_DEFAULT_TEXT.get(NodeType.CONDITION_NODE) }}
</div>
</div>
<div class="node-toolbar" v-if="index + 1 !== currentNode.conditionNodes?.length">
<div class="node-toolbar" v-if="!readonly && index + 1 !== currentNode.conditionNodes?.length">
<div class="toolbar-icon">
<Icon
color="#0089ff"
@ -50,7 +53,7 @@
</div>
<div
class="branch-node-move move-node-left"
v-if="index != 0 && index + 1 !== currentNode.conditionNodes?.length"
v-if="!readonly && index != 0 && index + 1 !== currentNode.conditionNodes?.length"
@click="moveNode(index, -1)"
>
<Icon icon="ep:arrow-left" />
@ -58,7 +61,7 @@
<div
class="branch-node-move move-node-right"
v-if="currentNode.conditionNodes && index < currentNode.conditionNodes.length - 2"
v-if="!readonly && currentNode.conditionNodes && index < currentNode.conditionNodes.length - 2"
@click="moveNode(index, 1)"
>
<Icon icon="ep:arrow-right" />
@ -108,6 +111,8 @@ const emits = defineEmits<{
nodeType: number
]
}>()
//
const readonly = inject<Boolean>('readonly')
const currentNode = ref<SimpleFlowNode>(props.flowNode)
@ -133,6 +138,9 @@ const clickEvent = (index: number) => {
}
const conditionNodeConfig = (nodeId: string) => {
if (readonly) {
return
}
const conditionNode = proxy.$refs[nodeId][0]
conditionNode.open()
}

View File

@ -1,7 +1,10 @@
<template>
<div class="branch-node-wrapper">
<div class="branch-node-container">
<el-button class="branch-node-add" color="#626aef" @click="addCondition" plain>添加分支</el-button>
<div v-if="readonly" class="branch-node-readonly">
<span class="iconfont icon-parallel icon-size"></span>
</div>
<el-button v-else class="branch-node-add" color="#626aef" @click="addCondition" plain>添加分支</el-button>
<div
class="branch-node-item"
v-for="(item, index) in currentNode.conditionNodes"
@ -39,7 +42,7 @@
{{ NODE_DEFAULT_TEXT.get(NodeType.CONDITION_NODE) }}
</div>
</div>
<div class="node-toolbar">
<div v-if="!readonly" class="node-toolbar">
<div class="toolbar-icon">
<Icon
color="#0089ff"
@ -49,18 +52,6 @@
/>
</div>
</div>
<!-- <div
class="branch-node-move move-node-left"
v-if="index != 0 && index + 1 !== currentNode.conditionNodes?.length" @click="moveNode(index, -1)">
<Icon icon="ep:arrow-left" />
</div> -->
<!-- <div
class="branch-node-move move-node-right"
v-if="currentNode.conditionNodes && index < currentNode.conditionNodes.length - 2"
@click="moveNode(index, 1)">
<Icon icon="ep:arrow-right" />
</div> -->
</div>
<NodeHandler v-model:child-node="item.childNode" />
</div>
@ -106,6 +97,8 @@ const emits = defineEmits<{
}>()
const currentNode = ref<SimpleFlowNode>(props.flowNode)
//
const readonly = inject<Boolean>('readonly')
watch(
() => props.flowNode,

View File

@ -1,7 +1,10 @@
<template>
<div class="node-wrapper">
<div class="node-container">
<div class="node-box" :class="{ 'node-config-error': !currentNode.showText }">
<div
class="node-box"
:class="[{ 'node-config-error': !currentNode.showText }, `${taskStatusClass}`]"
>
<div class="node-title-container">
<div class="node-title-icon start-user"
><span class="iconfont icon-start-user"></span
@ -26,18 +29,18 @@
<div class="node-text" v-else>
{{ NODE_DEFAULT_TEXT.get(NodeType.START_USER_NODE) }}
</div>
<Icon icon="ep:arrow-right-bold" />
<Icon icon="ep:arrow-right-bold" v-if="!readonly" />
</div>
</div>
<!-- 传递子节点给添加节点组件会在子节点前面添加节点 -->
<NodeHandler v-if="currentNode" v-model:child-node="currentNode.childNode" />
</div>
</div>
<StartUserNodeConfig v-if="currentNode" ref="nodeSetting" :flow-node="currentNode" />
<StartUserNodeConfig v-if="!readonly && currentNode" ref="nodeSetting" :flow-node="currentNode" />
</template>
<script setup lang="ts">
import NodeHandler from '../NodeHandler.vue'
import { useWatchNode, useNodeName2 } from '../node'
import { useWatchNode, useNodeName2, useTaskStatusClass } from '../node'
import { SimpleFlowNode, NODE_DEFAULT_TEXT, NodeType } from '../consts'
import StartUserNodeConfig from '../nodes-config/StartUserNodeConfig.vue'
defineOptions({
@ -49,21 +52,28 @@ const props = defineProps({
default: () => null
}
})
const readonly = inject<Boolean>('readonly') //
//
const emits = defineEmits<{
'update:modelValue': [node: SimpleFlowNode | undefined]
}>()
//
const currentNode = useWatchNode(props)
//
const taskStatusClass = useTaskStatusClass(currentNode.value?.activityStatus)
//
const { showInput, blurEvent, clickTitle } = useNodeName2(currentNode, NodeType.START_USER_NODE)
const nodeSetting = ref()
//
const openNodeConfig = () => {
if (readonly) {
return
}
//
nodeSetting.value.showStartUserNodeConfig(currentNode.value)
nodeSetting.value.openDrawer()
}
</script>
<style lang="scss" scoped></style>

View File

@ -1,11 +1,14 @@
<template>
<div class="node-wrapper">
<div class="node-container">
<div class="node-box" :class="{ 'node-config-error': !currentNode.showText }">
<div
class="node-box"
:class="[{ 'node-config-error': !currentNode.showText }, `${taskStatusClass}`]"
>
<div class="node-title-container">
<div class="node-title-icon user-task"><span class="iconfont icon-approve"></span></div>
<input
v-if="showInput"
v-if="!readonly && showInput"
type="text"
class="editable-title-input"
@blur="blurEvent()"
@ -24,9 +27,9 @@
<div class="node-text" v-else>
{{ NODE_DEFAULT_TEXT.get(NodeType.USER_TASK_NODE) }}
</div>
<Icon icon="ep:arrow-right-bold" />
<Icon icon="ep:arrow-right-bold" v-if="!readonly" />
</div>
<div class="node-toolbar">
<div v-if="!readonly" class="node-toolbar">
<div class="toolbar-icon"
><Icon color="#0089ff" icon="ep:circle-close-filled" :size="18" @click="deleteNode"
/></div>
@ -45,7 +48,7 @@
</template>
<script setup lang="ts">
import { SimpleFlowNode, NodeType, NODE_DEFAULT_TEXT } from '../consts'
import { useWatchNode, useNodeName2 } from '../node'
import { useWatchNode, useNodeName2, useTaskStatusClass } from '../node'
import NodeHandler from '../NodeHandler.vue'
import UserTaskNodeConfig from '../nodes-config/UserTaskNodeConfig.vue'
defineOptions({
@ -61,13 +64,21 @@ const emits = defineEmits<{
'update:flowNode': [node: SimpleFlowNode | undefined]
'find:parentNode': [nodeList: SimpleFlowNode[], nodeType: NodeType]
}>()
//
const readonly = inject<Boolean>('readonly')
//
const currentNode = useWatchNode(props)
//
const taskStatusClass = useTaskStatusClass(currentNode.value?.activityStatus)
//
const { showInput, blurEvent, clickTitle } = useNodeName2(currentNode, NodeType.START_USER_NODE)
const nodeSetting = ref()
//
const openNodeConfig = () => {
if (readonly) {
return
}
//
nodeSetting.value.showUserTaskNodeConfig(currentNode.value)
nodeSetting.value.openDrawer()

View File

@ -1,10 +1,8 @@
.simple-flow-canvas {
position: absolute;
inset: 0;
z-index: 1;
overflow: auto;
background-color: #fafafa;
user-select: none;
// user-select: none;
.simple-flow-container {
position: relative;
@ -84,12 +82,32 @@
background-color: #fff;
flex-direction: column;
border: 2px solid transparent;
// border-color: #0089ff;
border-radius: 8px;
// border-color: #0089ff;
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 {
@ -280,15 +298,10 @@
&::before {
position: absolute;
top: 0;
right: 0;
left: 0;
// bottom: 5px;
bottom: 0px;
top:0;
z-index: 0;
width: 2px;
height: 100%;
// height: calc(100% - 5px);
margin: auto;
background-color: #dedede;
content: '';
@ -361,6 +374,36 @@
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;
@ -454,7 +497,6 @@
padding: 3px 4px;
color: #212121;
cursor: pointer;
// background: #2c2c2c;
background: #fafafa;
border-radius: 30px;
box-shadow: 0 1px 5px 0 rgba(10, 30, 65, 0.08);
@ -473,12 +515,36 @@
align-items: center;
width: 80px;
height: 36px;
border: 2px solid #fafafa;
color: #212121;
// background: #6e6e6e;
background: #fafafa;
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;
}
}
}

View File

@ -0,0 +1,160 @@
<template>
<el-card v-loading="loading" class="box-card">
<SimpleProcessViewer :flow-node="simpleModel" />
</el-card>
</template>
<script lang="ts" setup>
import { propTypes } from '@/utils/propTypes'
import { TaskStatusEnum } from '@/api/bpm/task'
import { BpmProcessInstanceStatus } from '@/utils/constants'
import { SimpleFlowNode, NodeType } from '@/components/SimpleProcessDesignerV2/src/consts'
import { SimpleProcessViewer } from '@/components/SimpleProcessDesignerV2/src/'
import * as ProcessInstanceApi from '@/api/bpm/processInstance'
defineOptions({ name: 'BpmProcessInstanceSimpleViewer' })
const props = defineProps({
loading: propTypes.bool.def(false), //
id: propTypes.string //
})
// const view = ref({
// simpleModel: undefined
// }) // simple
const simpleModel = ref()
/** 只有 loading 完成时,才去加载流程列表 */
watch(
() => props.loading,
async (value) => {
if (value && props.id) {
const modelView = await ProcessInstanceApi.getProcessInstanceBpmnModelView(props.id)
if (modelView) {
// UserTask
const rejectedTaskActivityIds: string[] = modelView.rejectedTaskActivityIds
// UserTask
const unfinishedTaskActivityIds: string[] = modelView.unfinishedTaskActivityIds
// UserTaskGateway
const finishedActivityIds: string[] = modelView.finishedTaskActivityIds
// 线 SequenceFlow
const finishedSequenceFlowActivityIds: string[] = modelView.finishedSequenceFlowActivityIds
setSimpleModelNodeTaskStatus(
modelView.simpleModel,
modelView.processInstance.status,
rejectedTaskActivityIds,
unfinishedTaskActivityIds,
finishedActivityIds,
finishedSequenceFlowActivityIds
)
console.log("modelView.simpleModel==>", modelView.simpleModel)
simpleModel.value = modelView.simpleModel
}
}
}
)
const setSimpleModelNodeTaskStatus = (
simpleModel: SimpleFlowNode | undefined,
processStatus: number,
rejectedTaskActivityIds: string[],
unfinishedTaskActivityIds: string[],
finishedActivityIds: string[],
finishedSequenceFlowActivityIds: string[],
) => {
if (!simpleModel) {
return
}
//
if (simpleModel.type === NodeType.END_EVENT_NODE) {
if (finishedActivityIds.includes(simpleModel.id)) {
simpleModel.activityStatus = processStatus
} else {
simpleModel.activityStatus = TaskStatusEnum.NOT_START
}
return
}
//
if (
simpleModel.type === NodeType.START_USER_NODE ||
simpleModel.type === NodeType.USER_TASK_NODE
) {
simpleModel.activityStatus = TaskStatusEnum.NOT_START
if (rejectedTaskActivityIds.includes(simpleModel.id)) {
simpleModel.activityStatus = TaskStatusEnum.REJECT
} else if(unfinishedTaskActivityIds.includes(simpleModel.id)) {
simpleModel.activityStatus = TaskStatusEnum.RUNNING
} else if (finishedActivityIds.includes(simpleModel.id)) {
simpleModel.activityStatus = TaskStatusEnum.APPROVE
}
// TODO cancel
}
//
if (simpleModel.type === NodeType.COPY_TASK_NODE) {
//
if (finishedActivityIds.includes(simpleModel.id)) {
simpleModel.activityStatus = TaskStatusEnum.APPROVE
} else {
simpleModel.activityStatus = TaskStatusEnum.NOT_START
}
}
// SequenceFlow
if (simpleModel.type === NodeType.CONDITION_NODE) {
//
if (finishedSequenceFlowActivityIds.includes(simpleModel.id)) {
simpleModel.activityStatus = TaskStatusEnum.APPROVE
} else {
simpleModel.activityStatus = TaskStatusEnum.NOT_START
}
}
//
if (
simpleModel.type === NodeType.CONDITION_BRANCH_NODE ||
simpleModel.type === NodeType.PARALLEL_BRANCH_NODE ||
simpleModel.type === NodeType.INCLUSIVE_BRANCH_NODE
) {
//
if (finishedActivityIds.includes(simpleModel.id)) {
simpleModel.activityStatus = TaskStatusEnum.APPROVE
} else {
simpleModel.activityStatus = TaskStatusEnum.NOT_START
}
simpleModel.conditionNodes?.forEach((node) => {
setSimpleModelNodeTaskStatus(
node,
processStatus,
rejectedTaskActivityIds,
unfinishedTaskActivityIds,
finishedActivityIds,
finishedSequenceFlowActivityIds
)
})
}
setSimpleModelNodeTaskStatus(
simpleModel.childNode,
processStatus,
rejectedTaskActivityIds,
unfinishedTaskActivityIds,
finishedActivityIds,
finishedSequenceFlowActivityIds
)
}
/** 监听 bpmnXml */
// watch(
// () => props.bpmnXml,
// (value) => {
// view.value.bpmnXml = value
// }
// )
</script>
<style>
.box-card {
width: 100%;
margin-bottom: 20px;
}
</style>

View File

@ -1,5 +1,8 @@
<template>
<SimpleProcessDesigner :model-id="modelId" />
<ContentWrap :bodyStyle="{ padding: '0px 0px' }" class="position-relative">
<SimpleProcessDesigner :model-id="modelId" />
</ContentWrap>
</template>
<script setup lang="ts">
import { SimpleProcessDesigner } from '@/components/SimpleProcessDesignerV2/src/'