Commit 0da3a9bf by Lesan

Merge remote-tracking branch 'yudao/feature/bpm' into feature/bpm-n

parents 0168f9ff eca1c83c
...@@ -46,6 +46,13 @@ ...@@ -46,6 +46,13 @@
</div> </div>
<div class="handler-item-text">延迟器</div> <div class="handler-item-text">延迟器</div>
</div> </div>
<div class="handler-item" @click="addNode(NodeType.ROUTE_BRANCH_NODE)">
<!-- TODO @芋艿 需要更换一下iconfont的图标 -->
<div class="handler-item-icon copy">
<span class="iconfont icon-size icon-copy"></span>
</div>
<div class="handler-item-text">路由分支</div>
</div>
</div> </div>
<template #reference> <template #reference>
<div class="add-icon"><Icon icon="ep:plus" /></div> <div class="add-icon"><Icon icon="ep:plus" /></div>
...@@ -120,7 +127,10 @@ const addNode = (type: number) => { ...@@ -120,7 +127,10 @@ const addNode = (type: number) => {
type: AssignEmptyHandlerType.APPROVE type: AssignEmptyHandlerType.APPROVE
}, },
assignStartUserHandlerType: AssignStartUserHandlerType.START_USER_AUDIT, assignStartUserHandlerType: AssignStartUserHandlerType.START_USER_AUDIT,
childNode: props.childNode childNode: props.childNode,
createTaskListener: {
enable: false
}
} }
emits('update:childNode', data) emits('update:childNode', data)
} }
...@@ -225,6 +235,17 @@ const addNode = (type: number) => { ...@@ -225,6 +235,17 @@ const addNode = (type: number) => {
} }
emits('update:childNode', data) emits('update:childNode', data)
} }
if (type === NodeType.ROUTE_BRANCH_NODE) {
const data: SimpleFlowNode = {
id: 'GateWay_' + generateUUID(),
name: NODE_DEFAULT_NAME.get(NodeType.ROUTE_BRANCH_NODE) as string,
showText: '',
type: NodeType.ROUTE_BRANCH_NODE,
childNode: props.childNode,
defaultFlowId: 'Flow_' + generateUUID()
}
emits('update:childNode', data)
}
} }
</script> </script>
......
...@@ -44,6 +44,12 @@ ...@@ -44,6 +44,12 @@
:flow-node="currentNode" :flow-node="currentNode"
@update:flow-node="handleModelValueUpdate" @update:flow-node="handleModelValueUpdate"
/> />
<!-- 路由分支节点 -->
<RouteNode
v-if="currentNode && currentNode.type === NodeType.ROUTE_BRANCH_NODE"
:flow-node="currentNode"
@update:flow-node="handleModelValueUpdate"
/>
<!-- 递归显示孩子节点 --> <!-- 递归显示孩子节点 -->
<ProcessNodeTree <ProcessNodeTree
v-if="currentNode && currentNode.childNode" v-if="currentNode && currentNode.childNode"
...@@ -67,6 +73,7 @@ import ExclusiveNode from './nodes/ExclusiveNode.vue' ...@@ -67,6 +73,7 @@ import ExclusiveNode from './nodes/ExclusiveNode.vue'
import ParallelNode from './nodes/ParallelNode.vue' import ParallelNode from './nodes/ParallelNode.vue'
import InclusiveNode from './nodes/InclusiveNode.vue' import InclusiveNode from './nodes/InclusiveNode.vue'
import DelayTimerNode from './nodes/DelayTimerNode.vue' import DelayTimerNode from './nodes/DelayTimerNode.vue'
import RouteNode from './nodes/RouteNode.vue'
import { SimpleFlowNode, NodeType } from './consts' import { SimpleFlowNode, NodeType } from './consts'
import { useWatchNode } from './node' import { useWatchNode } from './node'
defineOptions({ defineOptions({
......
...@@ -56,7 +56,7 @@ const props = defineProps({ ...@@ -56,7 +56,7 @@ const props = defineProps({
required: false required: false
}, },
// 可发起流程的人员编号 // 可发起流程的人员编号
startUserIds : { startUserIds: {
type: Array, type: Array,
required: false required: false
}, },
...@@ -91,6 +91,7 @@ provide('startUserIds', props.startUserIds) ...@@ -91,6 +91,7 @@ provide('startUserIds', props.startUserIds)
const message = useMessage() // 国际化 const message = useMessage() // 国际化
const processNodeTree = ref<SimpleFlowNode | undefined>() const processNodeTree = ref<SimpleFlowNode | undefined>()
provide('processNodeTree', processNodeTree)
const errorDialogVisible = ref(false) const errorDialogVisible = ref(false)
let errorNodes: SimpleFlowNode[] = [] let errorNodes: SimpleFlowNode[] = []
......
...@@ -44,7 +44,11 @@ export enum NodeType { ...@@ -44,7 +44,11 @@ export enum NodeType {
/** /**
* 包容分支节点 (对应包容网关) * 包容分支节点 (对应包容网关)
*/ */
INCLUSIVE_BRANCH_NODE = 53 INCLUSIVE_BRANCH_NODE = 53,
/**
* 路由分支节点
*/
ROUTE_BRANCH_NODE = 54
} }
export enum NodeId { export enum NodeId {
...@@ -93,6 +97,8 @@ export interface SimpleFlowNode { ...@@ -93,6 +97,8 @@ export interface SimpleFlowNode {
assignEmptyHandler?: AssignEmptyHandler assignEmptyHandler?: AssignEmptyHandler
// 审批节点的审批人与发起人相同时,对应的处理类型 // 审批节点的审批人与发起人相同时,对应的处理类型
assignStartUserHandlerType?: number assignStartUserHandlerType?: number
// 创建任务监听器
createTaskListener: ListenerHandler
// 条件类型 // 条件类型
conditionType?: ConditionType conditionType?: ConditionType
// 条件表达式 // 条件表达式
...@@ -105,6 +111,9 @@ export interface SimpleFlowNode { ...@@ -105,6 +111,9 @@ export interface SimpleFlowNode {
activityStatus?: TaskStatusEnum activityStatus?: TaskStatusEnum
// 延迟设置 // 延迟设置
delaySetting?: DelaySetting delaySetting?: DelaySetting
// 路由分支
routeGroup?: RouteCondition[]
defaultFlowId?: string
} }
// 候选人策略枚举 ( 用于审批节点。抄送节点 ) // 候选人策略枚举 ( 用于审批节点。抄送节点 )
export enum CandidateStrategy { export enum CandidateStrategy {
...@@ -222,6 +231,31 @@ export type AssignEmptyHandler = { ...@@ -222,6 +231,31 @@ export type AssignEmptyHandler = {
userIds?: number[] userIds?: number[]
} }
/**
* 监听器的结构定义
*/
export type ListenerHandler = {
enable: boolean
path: string
header: ListenerMap[]
body: ListenerMap[]
}
export type ListenerMap = {
key: string
type: number
value: string
}
export const LISTENER_MAP_TYPES = [
{
value: 1,
label: '固定值'
},
{
value: 2,
label: '表单'
}
]
// 审批拒绝类型枚举 // 审批拒绝类型枚举
export enum RejectHandlerType { export enum RejectHandlerType {
/** /**
...@@ -421,6 +455,7 @@ NODE_DEFAULT_TEXT.set(NodeType.COPY_TASK_NODE, '请配置抄送人') ...@@ -421,6 +455,7 @@ NODE_DEFAULT_TEXT.set(NodeType.COPY_TASK_NODE, '请配置抄送人')
NODE_DEFAULT_TEXT.set(NodeType.CONDITION_NODE, '请设置条件') NODE_DEFAULT_TEXT.set(NodeType.CONDITION_NODE, '请设置条件')
NODE_DEFAULT_TEXT.set(NodeType.START_USER_NODE, '请设置发起人') NODE_DEFAULT_TEXT.set(NodeType.START_USER_NODE, '请设置发起人')
NODE_DEFAULT_TEXT.set(NodeType.DELAY_TIMER_NODE, '请设置延迟器') NODE_DEFAULT_TEXT.set(NodeType.DELAY_TIMER_NODE, '请设置延迟器')
NODE_DEFAULT_TEXT.set(NodeType.ROUTE_BRANCH_NODE, '请设置路由节点')
export const NODE_DEFAULT_NAME = new Map<number, string>() export const NODE_DEFAULT_NAME = new Map<number, string>()
NODE_DEFAULT_NAME.set(NodeType.USER_TASK_NODE, '审批人') NODE_DEFAULT_NAME.set(NodeType.USER_TASK_NODE, '审批人')
...@@ -428,6 +463,7 @@ NODE_DEFAULT_NAME.set(NodeType.COPY_TASK_NODE, '抄送人') ...@@ -428,6 +463,7 @@ NODE_DEFAULT_NAME.set(NodeType.COPY_TASK_NODE, '抄送人')
NODE_DEFAULT_NAME.set(NodeType.CONDITION_NODE, '条件') NODE_DEFAULT_NAME.set(NodeType.CONDITION_NODE, '条件')
NODE_DEFAULT_NAME.set(NodeType.START_USER_NODE, '发起人') NODE_DEFAULT_NAME.set(NodeType.START_USER_NODE, '发起人')
NODE_DEFAULT_NAME.set(NodeType.DELAY_TIMER_NODE, '延迟器') NODE_DEFAULT_NAME.set(NodeType.DELAY_TIMER_NODE, '延迟器')
NODE_DEFAULT_NAME.set(NodeType.ROUTE_BRANCH_NODE, '路由分支')
// 候选人策略。暂时不从字典中取。 后续可能调整。控制显示顺序 // 候选人策略。暂时不从字典中取。 后续可能调整。控制显示顺序
export const CANDIDATE_STRATEGY: DictDataVO[] = [ export const CANDIDATE_STRATEGY: DictDataVO[] = [
...@@ -604,3 +640,13 @@ export const DELAY_TYPE = [ ...@@ -604,3 +640,13 @@ export const DELAY_TYPE = [
{ label: '固定时长', value: DelayTypeEnum.FIXED_TIME_DURATION }, { label: '固定时长', value: DelayTypeEnum.FIXED_TIME_DURATION },
{ label: '固定日期', value: DelayTypeEnum.FIXED_DATE_TIME } { label: '固定日期', value: DelayTypeEnum.FIXED_DATE_TIME }
] ]
/**
* 路由分支结构定义
*/
export type RouteCondition = {
nodeId: string
conditionType: number
conditionExpression: string
conditionGroups: ConditionGroup
}
...@@ -19,7 +19,8 @@ ...@@ -19,7 +19,8 @@
:placeholder="nodeName" :placeholder="nodeName"
/> />
<div v-else class="node-name"> <div v-else class="node-name">
{{ nodeName }} <Icon class="ml-1" icon="ep:edit-pen" :size="16" @click="clickIcon()" /> {{ nodeName }}
<Icon class="ml-1" icon="ep:edit-pen" :size="16" @click="clickIcon()" />
</div> </div>
<div class="divide-line"></div> <div class="divide-line"></div>
</div> </div>
...@@ -434,6 +435,137 @@ ...@@ -434,6 +435,137 @@
</div> </div>
</div> </div>
</el-tab-pane> </el-tab-pane>
<el-tab-pane label="监听器" name="listener">
<div>
<el-form label-position="top">
<el-divider content-position="left">
<el-text tag="b" size="large">创建任务</el-text>
</el-divider>
<!-- TODO @lesan:createTaskListenerEnable、createTaskListenerPath 等 idea 红色的告警! -->
<el-form-item prop="createTaskListenerEnable">
<el-switch
v-model="configForm.createTaskListenerEnable"
active-text="开启"
inactive-text="关闭"
/>
</el-form-item>
<div v-if="configForm.createTaskListenerEnable">
<el-form-item>
<el-alert
title="仅支持 POST 请求,以请求体方式接收参数"
type="warning"
show-icon
:closable="false"
/>
</el-form-item>
<el-form-item label="请求地址" prop="createTaskListenerPath">
<el-input v-model="configForm.createTaskListenerPath" />
</el-form-item>
<el-form-item label="请求头" prop="createTaskListenerHeader">
<div
class="flex pt-2"
v-for="(item, index) in configForm.createTaskListenerHeader"
:key="index"
>
<!-- TODO @lesan:css 尽量用 unocss 哈 -->
<div class="mr-2">
<el-input v-model="item.key" style="width: 160px" />
</div>
<div class="mr-2">
<el-select v-model="item.type" style="width: 100px">
<el-option
v-for="types in LISTENER_MAP_TYPES"
:key="types.value"
:label="types.label"
:value="types.value"
/>
</el-select>
</div>
<div class="mr-2">
<el-input v-model="item.value" style="width: 160px" />
</div>
<div class="mr-1 flex items-center">
<Icon
icon="ep:delete"
:size="18"
@click="deleteTaskListenerMap(configForm.createTaskListenerHeader, index)"
/>
</div>
</div>
<el-button
type="primary"
text
@click="addTaskListenerMap(configForm.createTaskListenerHeader)"
>
<Icon icon="ep:plus" class="mr-5px" />添加一行
</el-button>
</el-form-item>
<el-form-item label="请求体" prop="createTaskListenerBody">
<div
class="flex pt-2"
v-for="(item, index) in configForm.createTaskListenerBody"
:key="index"
>
<div class="mr-2">
<el-input v-model="item.key" style="width: 160px" />
</div>
<div class="mr-2">
<el-select v-model="item.type" style="width: 100px">
<el-option
v-for="types in LISTENER_MAP_TYPES"
:key="types.value"
:label="types.label"
:value="types.value"
/>
</el-select>
</div>
<div class="mr-2">
<el-input v-model="item.value" style="width: 160px" />
</div>
<div class="mr-1 flex items-center">
<Icon
icon="ep:delete"
:size="18"
@click="deleteTaskListenerMap(configForm.createTaskListenerBody, index)"
/>
</div>
</div>
<el-button
type="primary"
text
@click="addTaskListenerMap(configForm.createTaskListenerBody)"
>
<Icon icon="ep:plus" class="mr-5px" />添加一行
</el-button>
</el-form-item>
</div>
<!-- TODO lesan:待实现 -->
<el-divider content-position="left">
<el-text tag="b" size="large">指派任务执行人员</el-text>
</el-divider>
<el-form-item prop="assignTaskListenerEnable">
<el-switch
v-model="configForm.assignTaskListenerEnable"
active-text="开启"
inactive-text="关闭"
/>
</el-form-item>
<!-- TODO lesan:待实现 -->
<el-divider content-position="left">
<el-text tag="b" size="large">完成任务</el-text>
</el-divider>
<el-form-item prop="completeTaskListenerEnable">
<el-switch
v-model="configForm.completeTaskListenerEnable"
active-text="开启"
inactive-text="关闭"
/>
</el-form-item>
</el-form>
</div>
</el-tab-pane>
</el-tabs> </el-tabs>
<template #footer> <template #footer>
<el-divider /> <el-divider />
...@@ -469,7 +601,8 @@ import { ...@@ -469,7 +601,8 @@ import {
ASSIGN_EMPTY_HANDLER_TYPES, ASSIGN_EMPTY_HANDLER_TYPES,
AssignEmptyHandlerType, AssignEmptyHandlerType,
FieldPermissionType, FieldPermissionType,
ProcessVariableEnum ProcessVariableEnum,
LISTENER_MAP_TYPES
} from '../consts' } from '../consts'
import { import {
...@@ -662,6 +795,13 @@ const saveConfig = async () => { ...@@ -662,6 +795,13 @@ const saveConfig = async () => {
currentNode.value.fieldsPermission = fieldsPermissionConfig.value currentNode.value.fieldsPermission = fieldsPermissionConfig.value
// 设置按钮权限 // 设置按钮权限
currentNode.value.buttonsSetting = buttonsSetting.value currentNode.value.buttonsSetting = buttonsSetting.value
// 创建任务监听器
currentNode.value.createTaskListener = {
enable: configForm.value.createTaskListenerEnable,
path: configForm.value.createTaskListenerPath,
header: configForm.value.createTaskListenerHeader,
body: configForm.value.createTaskListenerBody
}
currentNode.value.showText = showText currentNode.value.showText = showText
settingVisible.value = false settingVisible.value = false
...@@ -713,6 +853,11 @@ const showUserTaskNodeConfig = (node: SimpleFlowNode) => { ...@@ -713,6 +853,11 @@ const showUserTaskNodeConfig = (node: SimpleFlowNode) => {
buttonsSetting.value = cloneDeep(node.buttonsSetting) || DEFAULT_BUTTON_SETTING buttonsSetting.value = cloneDeep(node.buttonsSetting) || DEFAULT_BUTTON_SETTING
// 4. 表单字段权限配置 // 4. 表单字段权限配置
getNodeConfigFormFields(node.fieldsPermission) getNodeConfigFormFields(node.fieldsPermission)
// 5. 创建任务监听器
configForm.value.createTaskListenerEnable = node.createTaskListener.enable
configForm.value.createTaskListenerPath = node.createTaskListener.path
configForm.value.createTaskListenerHeader = node.createTaskListener.header ?? []
configForm.value.createTaskListenerBody = node.createTaskListener.body ?? []
} }
defineExpose({ openDrawer, showUserTaskNodeConfig }) // 暴露方法给父组件 defineExpose({ openDrawer, showUserTaskNodeConfig }) // 暴露方法给父组件
...@@ -825,6 +970,17 @@ function useTimeoutHandler() { ...@@ -825,6 +970,17 @@ function useTimeoutHandler() {
cTimeoutMaxRemindCount cTimeoutMaxRemindCount
} }
} }
const addTaskListenerMap = (arr) => {
arr.push({
key: '',
type: 1,
value: ''
})
}
const deleteTaskListenerMap = (arr, index) => {
arr.splice(index, 1)
}
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
......
<template>
<div class="node-wrapper">
<div class="node-container">
<div
class="node-box"
:class="[
{ 'node-config-error': !currentNode.showText },
`${useTaskStatusClass(currentNode?.activityStatus)}`
]"
>
<div class="node-title-container">
<!-- TODO @芋艿 需要更换一下iconfont的图标 -->
<div class="node-title-icon copy-task"><span class="iconfont icon-copy"></span></div>
<input
v-if="!readonly && showInput"
type="text"
class="editable-title-input"
@blur="blurEvent()"
v-mountedFocus
v-model="currentNode.name"
:placeholder="currentNode.name"
/>
<div v-else class="node-title" @click="clickTitle">
{{ currentNode.name }}
</div>
</div>
<div class="node-content" @click="openNodeConfig">
<div class="node-text" :title="currentNode.showText" v-if="currentNode.showText">
{{ currentNode.showText }}
</div>
<div class="node-text" v-else>
{{ NODE_DEFAULT_TEXT.get(NodeType.ROUTE_BRANCH_NODE) }}
</div>
<Icon v-if="!readonly" icon="ep:arrow-right-bold" />
</div>
<div v-if="!readonly" class="node-toolbar">
<div class="toolbar-icon"
><Icon color="#0089ff" icon="ep:circle-close-filled" :size="18" @click="deleteNode"
/></div>
</div>
</div>
<!-- 传递子节点给添加节点组件。会在子节点前面添加节点 -->
<NodeHandler
v-if="currentNode"
v-model:child-node="currentNode.childNode"
:current-node="currentNode"
/>
</div>
<RouteNodeConfig
v-if="!readonly && currentNode"
ref="nodeSetting"
:flow-node="currentNode"
/>
</div>
</template>
<script setup lang="ts">
import { SimpleFlowNode, NodeType, NODE_DEFAULT_TEXT } from '../consts'
import NodeHandler from '../NodeHandler.vue'
import { useNodeName2, useWatchNode, useTaskStatusClass } from '../node'
import RouteNodeConfig from '../nodes-config/RouteNodeConfig.vue'
defineOptions({
name: 'RouteNode'
})
const props = defineProps({
flowNode: {
type: Object as () => SimpleFlowNode,
required: true
}
})
// 定义事件,更新父组件。
const emits = defineEmits<{
'update:flowNode': [node: SimpleFlowNode | undefined]
}>()
// 是否只读
const readonly = inject<Boolean>('readonly')
// 监控节点的变化
const currentNode = useWatchNode(props)
// 节点名称编辑
const { showInput, blurEvent, clickTitle } = useNodeName2(currentNode, NodeType.ROUTE_BRANCH_NODE)
const nodeSetting = ref()
// 打开节点配置
const openNodeConfig = () => {
if (readonly) {
return
}
nodeSetting.value.showRouteNodeConfig(currentNode.value)
nodeSetting.value.openDrawer()
}
// 删除节点。更新当前节点为孩子节点
const deleteNode = () => {
emits('update:flowNode', currentNode.value.childNode)
}
</script>
<style lang="scss" scoped></style>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment