修改为next、分支修改为branches、新增ServiceNode节点

This commit is contained in:
caixiaofeng 2024-12-11 13:31:44 +08:00
parent 48cbcf35ed
commit 3fe2c36607
11 changed files with 193 additions and 74 deletions

View File

@ -1,7 +1,7 @@
<script setup lang="ts">
import TreeNode from './nodes/TreeNode.vue'
import Panel from './panels/index.vue'
import type { ErrorInfo, FlowNode, TimerNode } from './nodes/type'
import type { ErrorInfo, FlowNode, ServiceNode, TimerNode } from './nodes/type'
import type {
ApprovalNode,
BranchNode,
@ -73,13 +73,13 @@ const nextId = (): string => {
if (node.id === id) {
return true
}
if (node.child) {
return findId(node.child, id)
if (node.next) {
return findId(node.next, id)
}
if ('children' in node) {
if ('branches' in node) {
const branchNode = node as BranchNode
if (branchNode.children && branchNode.children.length > 0) {
return branchNode.children.some((item) => {
if (branchNode.branches && branchNode.branches.length > 0) {
return branchNode.branches.some((item) => {
return findId(item, id)
})
}
@ -92,53 +92,53 @@ const nextId = (): string => {
return id
}
const addExclusive = (node: FlowNode) => {
const child = node.child
const next = node.next
const id = nextId()
const exclusiveNode = {
id: id,
pid: node.id,
type: 'exclusive',
name: '独占网关',
child: child,
children: []
next: next,
branches: []
} as ExclusiveNode
if (child) {
child.pid = id
if (next) {
next.pid = id
}
addCondition(exclusiveNode)
addCondition(exclusiveNode)
node.child = exclusiveNode
if (exclusiveNode.children.length > 0) {
const condition = exclusiveNode.children[exclusiveNode.children.length - 1] as ConditionNode
node.next = exclusiveNode
if (exclusiveNode.branches.length > 0) {
const condition = exclusiveNode.branches[exclusiveNode.branches.length - 1] as ConditionNode
condition.def = true
condition.name = '默认条件'
}
}
const addCondition = (node: FlowNode) => {
const exclusive = node as ExclusiveNode
exclusive.children.splice(exclusive.children.length - 1, 0, {
exclusive.branches.splice(exclusive.branches.length - 1, 0, {
id: nextId(),
pid: exclusive.id,
type: 'condition',
def: false,
name: `条件${exclusive.children.length + 1}`,
name: `条件${exclusive.branches.length + 1}`,
conditions: {
operator: 'and',
conditions: [],
groups: []
} as FilterRules,
child: undefined
next: undefined
})
}
const addCc = (node: FlowNode) => {
const child = node.child
const next = node.next
const id = nextId()
node.child = {
node.next = {
id: id,
pid: node.id,
type: 'cc',
name: '抄送人',
child: child,
next: next,
assigneeType: 'user',
formUser: '',
formRole: '',
@ -150,38 +150,38 @@ const addCc = (node: FlowNode) => {
self: false,
formProperties: []
} as CcNode
if (child) {
child.pid = id
if (next) {
next.pid = id
}
}
const addTimer = (node: FlowNode) => {
const child = node.child
const next = node.next
const id = nextId()
node.child = {
node.next = {
id: id,
pid: node.id,
name: '计时等待',
type: 'timer',
child: child,
next: next,
waitType: 'duration',
unit: 'PT%sS',
duration: 0,
timeDate: undefined
} as TimerNode
if (child) {
child.pid = id
if (next) {
next.pid = id
}
}
const addNotify = (node: FlowNode) => {
const child = node.child
const next = node.next
const id = nextId()
node.child = {
node.next = {
id: id,
pid: node.id,
name: '消息通知',
type: 'notify',
child: child,
next: next,
assigneeType: 'user',
formUser: '',
formRole: '',
@ -195,20 +195,36 @@ const addNotify = (node: FlowNode) => {
subject: '',
content: ''
} as NotifyNode
if (child) {
child.pid = id
if (next) {
next.pid = id
}
}
const addService = (node: FlowNode) => {
const next = node.next
const id = nextId()
node.next = {
id: id,
pid: node.id,
type: 'service',
name: '服务节点',
next: next,
implementationType: '',
implementation: '',
} as ServiceNode
if (next) {
next.pid = id
}
}
const addApproval = (node: FlowNode) => {
const child = node.child
const next = node.next
const id = nextId()
node.child = {
node.next = {
id: id,
pid: node.id,
type: 'approval',
name: '审批人',
executionListeners: [],
child: child,
next: next,
//
assigneeType: 'user',
formUser: '',
@ -234,8 +250,8 @@ const addApproval = (node: FlowNode) => {
minusMulti: false
}
} as ApprovalNode
if (child) {
child.pid = id
if (next) {
next.pid = id
}
}
const addNode = (type: NodeType, node: FlowNode) => {
@ -245,6 +261,7 @@ const addNode = (type: NodeType, node: FlowNode) => {
cc: addCc,
timer: addTimer,
notify: addNotify,
service: addService,
approval: addApproval
}
const fun = addMap[type]
@ -257,32 +274,32 @@ const delNode = (del: FlowNode) => {
const delNodeNext = (next: FlowNode, del: FlowNode) => {
delete nodesError.value[del.id]
if (next.id === del.pid) {
if ('children' in next && next.child?.id !== del.id) {
if ('branches' in next && next.next?.id !== del.id) {
const branchNode = next as BranchNode
const index = branchNode.children.findIndex((item) => item.id === del.id)
const index = branchNode.branches.findIndex((item) => item.id === del.id)
if (index !== -1) {
if (branchNode.children.length <= 2) {
if (branchNode.branches.length <= 2) {
delError(branchNode)
delNode(branchNode)
} else {
delError(del)
branchNode.children.splice(index, 1)
branchNode.branches.splice(index, 1)
}
}
} else {
if (del.child && del.child.pid) {
del.child.pid = next.id
if (del.next && del.next.pid) {
del.next.pid = next.id
}
next.child = del.child
next.next = del.next
}
} else {
if (next.child) {
delNodeNext(next.child, del)
if (next.next) {
delNodeNext(next.next, del)
}
if ('children' in next) {
if ('branches' in next) {
const nextBranch = next as BranchNode
if (nextBranch.children && nextBranch.children.length > 0) {
nextBranch.children.forEach((item) => {
if (nextBranch.branches && nextBranch.branches.length > 0) {
nextBranch.branches.forEach((item) => {
delNodeNext(item, del)
})
}
@ -291,13 +308,13 @@ const delNodeNext = (next: FlowNode, del: FlowNode) => {
}
const delError = (node: FlowNode) => {
delete nodesError.value[node.id]
if (node.child) {
delError(node.child)
if (node.next) {
delError(node.next)
}
if ('children' in node) {
if ('branches' in node) {
const branchNode = node as BranchNode
if (branchNode.children && branchNode.children.length > 0) {
branchNode.children.forEach((item) => {
if (branchNode.branches && branchNode.branches.length > 0) {
branchNode.branches.forEach((item) => {
delError(item)
})
}

View File

@ -30,6 +30,10 @@ const addNotifyNode = () => {
$emits('addNode', 'notify')
popoverRef.value?.hide()
}
const addServiceNode = () => {
$emits('addNode', 'service')
popoverRef.value?.hide()
}
</script>
<template>
@ -62,6 +66,10 @@ const addNotifyNode = () => {
<svg-icon name="el:BellFilled" />
<el-text>消息通知</el-text>
</div>
<div class="node-select" @click="addServiceNode">
<svg-icon name="el:Tools" />
<el-text>服务节点</el-text>
</div>
</el-space>
<template #reference>
<el-button
@ -117,6 +125,10 @@ const addNotifyNode = () => {
&.BellFilled {
background-color: #95d475;
}
&.Tools {
background-color: #ffc107;
}
}
.el-text {

View File

@ -13,13 +13,13 @@ const { nodesError } = inject<{
const content = ref<string>('')
watchEffect(() => {
const errors: ErrorInfo[] = []
const { id, name, def, conditions, child } = props.node
const { id, name, def, conditions, next } = props.node
if (def) {
content.value = '不满足其他条件,进入此分支'
} else if (conditions.conditions.length > 0 || (conditions.groups?.length || 0) > 0) {
const count = conditions.conditions.length + (conditions.groups?.length || 0)
content.value = `已设置(${count})个条件`
if (!child) {
if (!next) {
errors.push({ id: id, name: name, message: '分支下节点为空' })
}
} else {

View File

@ -17,14 +17,14 @@ const addNode = (type: NodeType, node?: FlowNode) => {
$emits('addNode', type, node || props.node)
}
const moveRight = (index: number) => {
const node = props.node.children[index]
props.node.children.splice(index, 1)
props.node.children.splice(index + 1, 0, node)
const node = props.node.branches[index]
props.node.branches.splice(index, 1)
props.node.branches.splice(index + 1, 0, node)
}
const moveLeft = (index: number) => {
const node = props.node.children[index]
props.node.children.splice(index, 1)
props.node.children.splice(index - 1, 0, node)
const node = props.node.branches[index]
props.node.branches.splice(index, 1)
props.node.branches.splice(index - 1, 0, node)
}
</script>
@ -33,12 +33,12 @@ const moveLeft = (index: number) => {
<div class="add-branch">
<slot :addNode="addNode" :readOnly="readOnly"></slot>
</div>
<div v-for="(item, index) in node.children" :key="item.id" class="col-box">
<div v-for="(item, index) in node.branches" :key="item.id" class="col-box">
<template v-if="index === 0">
<div class="top-left-border"></div>
<div class="bottom-left-border" />
</template>
<template v-else-if="node.children.length === index + 1">
<template v-else-if="node.branches.length === index + 1">
<div class="top-right-border"></div>
<div class="bottom-right-border" />
</template>
@ -47,14 +47,14 @@ const moveLeft = (index: number) => {
<div
class="move-left"
@click.stop="moveLeft(index)"
v-show="index !== 0 && node.children.length !== index + 1 && !readOnly"
v-show="index !== 0 && node.branches.length !== index + 1 && !readOnly"
>
<svg-icon name="el:ArrowLeft" />
</div>
<div
class="move-right"
@click.stop="moveRight(index)"
v-show="![index + 1, index + 2].includes(node.children.length) && !readOnly"
v-show="![index + 1, index + 2].includes(node.branches.length) && !readOnly"
>
<svg-icon name="el:ArrowRight" />
</div>

View File

@ -0,0 +1,40 @@
<script setup lang="ts">
import Node from './Node.vue'
import type { ErrorInfo, ServiceNode } from './type'
import type { Ref } from 'vue'
const props = defineProps<{
node: ServiceNode
}>()
const { nodesError } = inject<{
nodesError: Ref<Recordable<ErrorInfo[]>>
}>('flowDesign', { nodesError: ref({}) })
const content = ref<string>('')
watchEffect(() => {
const errors: ErrorInfo[] = []
const { id, name, implementationType, implementation } = props.node
if (!implementationType) {
errors.push({ id: id, name: name, message: '执行类型为空' })
content.value = '执行类型为空'
} else if (!implementation) {
errors.push({ id: id, name: name, message: '执行值为空' })
content.value = '执行值为空'
} else {
content.value = `执行服务`
}
//
if (errors.length > 0) {
nodesError.value[id] = errors
} else {
delete nodesError.value[id]
}
})
</script>
<template>
<Node v-bind="$attrs" icon="el:Tools" color="#ffc107" :node="node">
<el-text>{{ content }}</el-text>
</Node>
</template>
<style scoped lang="scss"></style>

View File

@ -12,8 +12,8 @@ const { nodesError } = inject<{
}>('flowDesign', { nodesError: ref({}) })
watchEffect(() => {
const errors: ErrorInfo[] = []
const { id, name, child } = props.node
if (child?.type === 'end') {
const { id, name, next } = props.node
if (next?.type === 'end') {
errors.push({ id: id, name: name, message: '发起下节点为空' })
}
//

View File

@ -7,6 +7,7 @@ import Approval from './ApprovalNode.vue'
import Cc from './CcNode.vue'
import Timer from './TimerNode.vue'
import Notify from './NotifyNode.vue'
import Service from './ServiceNode.vue'
import Exclusive from './ExclusiveNode.vue'
import Condition from './ConditionNode.vue'
@ -19,6 +20,7 @@ const nodes: Recordable<Component> = {
cc: Cc,
timer: Timer,
notify: Notify,
service: Service,
exclusive: Exclusive,
condition: Condition,
end: End
@ -32,7 +34,7 @@ const nodes: Recordable<Component> = {
<slot :name="name" v-bind="scope || {}"></slot>
</template>
</component>
<TreeNode v-if="node.child" :node="node.child" v-bind="$attrs" />
<TreeNode v-if="node.next" :node="node.next" v-bind="$attrs" />
</template>
<style scoped lang="scss"></style>

View File

@ -7,6 +7,7 @@ export type NodeType =
| 'exclusive'
| 'timer'
| 'notify'
| 'service'
| 'condition'
| 'end'
@ -16,7 +17,7 @@ export interface FlowNode {
name: string
type: NodeType
executionListeners?: NodeListener[]
child?: FlowNode
next?: FlowNode
}
export interface NodeListener {
@ -88,6 +89,11 @@ export interface ApprovalNode extends AssigneeNode {
taskListeners?: NodeListener[]
}
export interface ServiceNode extends FlowNode {
implementationType: string
implementation: string
}
export interface TimerNode extends FlowNode {
waitType: 'duration' | 'date'
unit: 'PT%sS' | 'PT%sM' | 'PT%sH' | 'P%sD' | 'P%sW' | 'P%sM'
@ -101,11 +107,11 @@ export interface ConditionNode extends FlowNode {
}
export interface BranchNode extends FlowNode {
children: FlowNode[]
branches: FlowNode[]
}
export interface ExclusiveNode extends BranchNode {
children: ConditionNode[]
branches: ConditionNode[]
}
export interface ErrorInfo {

View File

@ -0,0 +1,40 @@
<script setup lang="ts">
import type { ServiceNode } from '../nodes/type'
defineProps<{
activeData: ServiceNode
}>()
</script>
<template>
<el-form label-position="top">
<el-form-item prop="implementationType" label="执行类型">
<el-select v-model="activeData.implementationType" placeholder="请选择执行类型">
<el-option label="类" value="class" />
<el-option label="表达式" value="expression" />
<el-option label="委托表达式" value="delegateExpression" />
</el-select>
</el-form-item>
<el-form-item prop="implementation" label="执行值">
<template #label>
<div class="flex-items-center gap3px">
<span>执行值</span>
<el-tooltip placement="top-start">
<template #content>
实现 JavaDelegate 接口 <br />
${com.example.delegate.MyServiceDelegate} <br />
表达式: ${myServiceDelegate.execute(execution)} <br />
委托表达式${myServiceDelegate}
</template>
<el-icon>
<QuestionFilled />
</el-icon>
</el-tooltip>
</div>
</template>
<el-input v-model="activeData.implementation" placeholder="请输入执行值" clearable />
</el-form-item>
</el-form>
</template>
<style scoped lang="scss"></style>

View File

@ -6,6 +6,7 @@ import Approval from './ApprovalPanel.vue'
import Cc from './CcPanel.vue'
import Timer from './TimerPanel.vue'
import Notify from './NotifyPanel.vue'
import Service from './ServicePanel.vue'
import Condition from './ConditionPanel.vue'
import End from './EndPanel.vue'
import type { FlowNode } from '../nodes/type'
@ -20,6 +21,7 @@ const panels: Recordable<Component> = {
cc: Cc,
timer: Timer,
notify: Notify,
service: Service,
condition: Condition,
end: End
}

View File

@ -12,13 +12,13 @@ const process = ref<FlowNode>({
name: '发起人',
executionListeners: [],
formProperties: [],
child: {
next: {
id: 'end',
pid: 'root',
type: 'end',
name: '流程结束',
executionListeners: [],
child: undefined
next: undefined
} as EndNode
} as StartNode)
//