mirror of
https://gitee.com/myxzgzs/boyue-ui-admin-vue3
synced 2025-08-08 16:32:43 +08:00
commit
753e44ccd0
@ -36,6 +36,7 @@ export type ApprovalTaskInfo = {
|
|||||||
assigneeUser: User
|
assigneeUser: User
|
||||||
status: number
|
status: number
|
||||||
reason: string
|
reason: string
|
||||||
|
sign: string
|
||||||
}
|
}
|
||||||
|
|
||||||
// 审批节点信息
|
// 审批节点信息
|
||||||
|
@ -46,7 +46,7 @@
|
|||||||
</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)">
|
<div class="handler-item" @click="addNode(NodeType.ROUTER_BRANCH_NODE)">
|
||||||
<!-- TODO @芋艿 需要更换一下iconfont的图标 -->
|
<!-- TODO @芋艿 需要更换一下iconfont的图标 -->
|
||||||
<div class="handler-item-icon copy">
|
<div class="handler-item-icon copy">
|
||||||
<span class="iconfont icon-size icon-copy"></span>
|
<span class="iconfont icon-size icon-copy"></span>
|
||||||
@ -67,12 +67,13 @@ import {
|
|||||||
ApproveMethodType,
|
ApproveMethodType,
|
||||||
AssignEmptyHandlerType,
|
AssignEmptyHandlerType,
|
||||||
AssignStartUserHandlerType,
|
AssignStartUserHandlerType,
|
||||||
|
ConditionType,
|
||||||
NODE_DEFAULT_NAME,
|
NODE_DEFAULT_NAME,
|
||||||
NodeType,
|
NodeType,
|
||||||
RejectHandlerType,
|
RejectHandlerType,
|
||||||
SimpleFlowNode
|
SimpleFlowNode
|
||||||
} from './consts'
|
} from './consts'
|
||||||
import { generateUUID } from '@/utils'
|
import {generateUUID} from '@/utils'
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'NodeHandler'
|
name: 'NodeHandler'
|
||||||
@ -163,7 +164,7 @@ const addNode = (type: number) => {
|
|||||||
showText: '',
|
showText: '',
|
||||||
type: NodeType.CONDITION_NODE,
|
type: NodeType.CONDITION_NODE,
|
||||||
childNode: undefined,
|
childNode: undefined,
|
||||||
conditionType: 1,
|
conditionType: ConditionType.RULE,
|
||||||
defaultFlow: false
|
defaultFlow: false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -241,14 +242,13 @@ const addNode = (type: number) => {
|
|||||||
}
|
}
|
||||||
emits('update:childNode', data)
|
emits('update:childNode', data)
|
||||||
}
|
}
|
||||||
if (type === NodeType.ROUTE_BRANCH_NODE) {
|
if (type === NodeType.ROUTER_BRANCH_NODE) {
|
||||||
const data: SimpleFlowNode = {
|
const data: SimpleFlowNode = {
|
||||||
id: 'GateWay_' + generateUUID(),
|
id: 'GateWay_' + generateUUID(),
|
||||||
name: NODE_DEFAULT_NAME.get(NodeType.ROUTE_BRANCH_NODE) as string,
|
name: NODE_DEFAULT_NAME.get(NodeType.ROUTER_BRANCH_NODE) as string,
|
||||||
showText: '',
|
showText: '',
|
||||||
type: NodeType.ROUTE_BRANCH_NODE,
|
type: NodeType.ROUTER_BRANCH_NODE,
|
||||||
childNode: props.childNode,
|
childNode: props.childNode
|
||||||
defaultFlowId: 'Flow_' + generateUUID()
|
|
||||||
}
|
}
|
||||||
emits('update:childNode', data)
|
emits('update:childNode', data)
|
||||||
}
|
}
|
||||||
|
@ -45,8 +45,8 @@
|
|||||||
@update:flow-node="handleModelValueUpdate"
|
@update:flow-node="handleModelValueUpdate"
|
||||||
/>
|
/>
|
||||||
<!-- 路由分支节点 -->
|
<!-- 路由分支节点 -->
|
||||||
<RouteNode
|
<RouterNode
|
||||||
v-if="currentNode && currentNode.type === NodeType.ROUTE_BRANCH_NODE"
|
v-if="currentNode && currentNode.type === NodeType.ROUTER_BRANCH_NODE"
|
||||||
:flow-node="currentNode"
|
:flow-node="currentNode"
|
||||||
@update:flow-node="handleModelValueUpdate"
|
@update:flow-node="handleModelValueUpdate"
|
||||||
/>
|
/>
|
||||||
@ -73,7 +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 RouterNode from './nodes/RouterNode.vue'
|
||||||
import { SimpleFlowNode, NodeType } from './consts'
|
import { SimpleFlowNode, NodeType } from './consts'
|
||||||
import { useWatchNode } from './node'
|
import { useWatchNode } from './node'
|
||||||
defineOptions({
|
defineOptions({
|
||||||
|
@ -48,7 +48,7 @@ export enum NodeType {
|
|||||||
/**
|
/**
|
||||||
* 路由分支节点
|
* 路由分支节点
|
||||||
*/
|
*/
|
||||||
ROUTE_BRANCH_NODE = 54
|
ROUTER_BRANCH_NODE = 54
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum NodeId {
|
export enum NodeId {
|
||||||
@ -116,7 +116,7 @@ export interface SimpleFlowNode {
|
|||||||
// 延迟设置
|
// 延迟设置
|
||||||
delaySetting?: DelaySetting
|
delaySetting?: DelaySetting
|
||||||
// 路由分支
|
// 路由分支
|
||||||
routerGroups?: RouteCondition[]
|
routerGroups?: RouterCondition[]
|
||||||
defaultFlowId?: string
|
defaultFlowId?: string
|
||||||
// 签名
|
// 签名
|
||||||
signEnable?: boolean
|
signEnable?: boolean
|
||||||
@ -439,8 +439,6 @@ export enum OperationButtonType {
|
|||||||
* 条件规则结构定义
|
* 条件规则结构定义
|
||||||
*/
|
*/
|
||||||
export type ConditionRule = {
|
export type ConditionRule = {
|
||||||
type: number
|
|
||||||
opName: string
|
|
||||||
opCode: string
|
opCode: string
|
||||||
leftSide: string
|
leftSide: string
|
||||||
rightSide: string
|
rightSide: string
|
||||||
@ -471,7 +469,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, '请设置路由节点')
|
NODE_DEFAULT_TEXT.set(NodeType.ROUTER_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, '审批人')
|
||||||
@ -479,7 +477,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, '路由分支')
|
NODE_DEFAULT_NAME.set(NodeType.ROUTER_BRANCH_NODE, '路由分支')
|
||||||
|
|
||||||
// 候选人策略。暂时不从字典中取。 后续可能调整。控制显示顺序
|
// 候选人策略。暂时不从字典中取。 后续可能调整。控制显示顺序
|
||||||
export const CANDIDATE_STRATEGY: DictDataVO[] = [
|
export const CANDIDATE_STRATEGY: DictDataVO[] = [
|
||||||
@ -660,7 +658,7 @@ export const DELAY_TYPE = [
|
|||||||
/**
|
/**
|
||||||
* 路由分支结构定义
|
* 路由分支结构定义
|
||||||
*/
|
*/
|
||||||
export type RouteCondition = {
|
export type RouterCondition = {
|
||||||
nodeId: string
|
nodeId: string
|
||||||
conditionType: ConditionType
|
conditionType: ConditionType
|
||||||
conditionExpression: string
|
conditionExpression: string
|
||||||
|
@ -30,117 +30,7 @@
|
|||||||
>未满足其它条件时,将进入此分支(该分支不可编辑和删除)</div
|
>未满足其它条件时,将进入此分支(该分支不可编辑和删除)</div
|
||||||
>
|
>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<el-form ref="formRef" :model="currentNode" :rules="formRules" label-position="top">
|
<Condition ref="conditionRef" v-model="condition" />
|
||||||
<el-form-item label="配置方式" prop="conditionType">
|
|
||||||
<el-radio-group v-model="currentNode.conditionType" @change="changeConditionType">
|
|
||||||
<el-radio
|
|
||||||
v-for="(dict, index) in conditionConfigTypes"
|
|
||||||
:key="index"
|
|
||||||
:value="dict.value"
|
|
||||||
:label="dict.value"
|
|
||||||
>
|
|
||||||
{{ dict.label }}
|
|
||||||
</el-radio>
|
|
||||||
</el-radio-group>
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item
|
|
||||||
v-if="currentNode.conditionType === 1"
|
|
||||||
label="条件表达式"
|
|
||||||
prop="conditionExpression"
|
|
||||||
>
|
|
||||||
<el-input
|
|
||||||
type="textarea"
|
|
||||||
v-model="currentNode.conditionExpression"
|
|
||||||
clearable
|
|
||||||
style="width: 100%"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item v-if="currentNode.conditionType === 2" label="条件规则">
|
|
||||||
<div class="condition-group-tool">
|
|
||||||
<div class="flex items-center">
|
|
||||||
<div class="mr-4">条件组关系</div>
|
|
||||||
<el-switch
|
|
||||||
v-model="conditionGroups.and"
|
|
||||||
inline-prompt
|
|
||||||
active-text="且"
|
|
||||||
inactive-text="或"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<el-space direction="vertical" :spacer="conditionGroups.and ? '且' : '或'">
|
|
||||||
<el-card
|
|
||||||
class="condition-group"
|
|
||||||
style="width: 530px"
|
|
||||||
v-for="(condition, cIdx) in conditionGroups.conditions"
|
|
||||||
:key="cIdx"
|
|
||||||
>
|
|
||||||
<div class="condition-group-delete" v-if="conditionGroups.conditions.length > 1">
|
|
||||||
<Icon
|
|
||||||
color="#0089ff"
|
|
||||||
icon="ep:circle-close-filled"
|
|
||||||
:size="18"
|
|
||||||
@click="deleteConditionGroup(cIdx)"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<template #header>
|
|
||||||
<div class="flex items-center justify-between">
|
|
||||||
<div>条件组</div>
|
|
||||||
<div class="flex">
|
|
||||||
<div class="mr-4">规则关系</div>
|
|
||||||
<el-switch
|
|
||||||
v-model="condition.and"
|
|
||||||
inline-prompt
|
|
||||||
active-text="且"
|
|
||||||
inactive-text="或"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<div class="flex pt-2" v-for="(rule, rIdx) in condition.rules" :key="rIdx">
|
|
||||||
<div class="mr-2">
|
|
||||||
<el-select style="width: 160px" v-model="rule.leftSide">
|
|
||||||
<el-option
|
|
||||||
v-for="(item, index) in fieldOptions"
|
|
||||||
:key="index"
|
|
||||||
:label="item.title"
|
|
||||||
:value="item.field"
|
|
||||||
:disabled="!item.required"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</div>
|
|
||||||
<div class="mr-2">
|
|
||||||
<el-select v-model="rule.opCode" style="width: 100px">
|
|
||||||
<el-option
|
|
||||||
v-for="item in COMPARISON_OPERATORS"
|
|
||||||
:key="item.value"
|
|
||||||
:label="item.label"
|
|
||||||
:value="item.value"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</div>
|
|
||||||
<div class="mr-2">
|
|
||||||
<el-input v-model="rule.rightSide" style="width: 160px" />
|
|
||||||
</div>
|
|
||||||
<div class="mr-1 flex items-center" v-if="condition.rules.length > 1">
|
|
||||||
<Icon
|
|
||||||
icon="ep:delete"
|
|
||||||
:size="18"
|
|
||||||
@click="deleteConditionRule(condition, rIdx)"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="flex items-center">
|
|
||||||
<Icon icon="ep:plus" :size="18" @click="addConditionRule(condition, rIdx)" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</el-card>
|
|
||||||
</el-space>
|
|
||||||
<div title="添加条件组" class="mt-4 cursor-pointer">
|
|
||||||
<Icon color="#0089ff" icon="ep:plus" :size="24" @click="addConditionGroup" />
|
|
||||||
</div>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
@ -155,33 +45,17 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {
|
import {
|
||||||
SimpleFlowNode,
|
SimpleFlowNode,
|
||||||
CONDITION_CONFIG_TYPES,
|
|
||||||
ConditionType,
|
ConditionType,
|
||||||
COMPARISON_OPERATORS,
|
COMPARISON_OPERATORS,
|
||||||
ConditionGroup,
|
|
||||||
Condition,
|
|
||||||
ConditionRule,
|
|
||||||
ProcessVariableEnum
|
ProcessVariableEnum
|
||||||
} from '../consts'
|
} from '../consts'
|
||||||
import { getDefaultConditionNodeName } from '../utils'
|
import { getDefaultConditionNodeName } from '../utils'
|
||||||
import { useFormFields } from '../node'
|
import { useFormFields } from '../node'
|
||||||
import { BpmModelFormType } from '@/utils/constants'
|
import Condition from './components/Condition.vue'
|
||||||
const message = useMessage() // 消息弹窗
|
const message = useMessage() // 消息弹窗
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'ConditionNodeConfig'
|
name: 'ConditionNodeConfig'
|
||||||
})
|
})
|
||||||
const formType = inject<Ref<number>>('formType') // 表单类型
|
|
||||||
const conditionConfigTypes = computed(() => {
|
|
||||||
return CONDITION_CONFIG_TYPES.filter((item) => {
|
|
||||||
// 业务表单暂时去掉条件规则选项
|
|
||||||
if (formType?.value === BpmModelFormType.CUSTOM && item.value === ConditionType.RULE) {
|
|
||||||
return false
|
|
||||||
} else {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
conditionNode: {
|
conditionNode: {
|
||||||
type: Object as () => SimpleFlowNode,
|
type: Object as () => SimpleFlowNode,
|
||||||
@ -193,11 +67,26 @@ const props = defineProps({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
const settingVisible = ref(false)
|
const settingVisible = ref(false)
|
||||||
|
const condition = ref<any>()
|
||||||
const open = () => {
|
const open = () => {
|
||||||
if (currentNode.value.conditionType === ConditionType.RULE) {
|
condition.value = {
|
||||||
if (currentNode.value.conditionGroups) {
|
conditionType: currentNode.value.conditionType,
|
||||||
conditionGroups.value = currentNode.value.conditionGroups
|
conditionExpression: currentNode.value.conditionExpression ?? '',
|
||||||
}
|
conditionGroups: currentNode.value.conditionGroups ?? {
|
||||||
|
and: true,
|
||||||
|
conditions: [
|
||||||
|
{
|
||||||
|
and: true,
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
opCode: '==',
|
||||||
|
leftSide: '',
|
||||||
|
rightSide: ''
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
}
|
}
|
||||||
settingVisible.value = true
|
settingVisible.value = true
|
||||||
}
|
}
|
||||||
@ -239,31 +128,27 @@ const handleClose = async (done: (cancel?: boolean) => void) => {
|
|||||||
done()
|
done()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 表单校验规则
|
|
||||||
const formRules = reactive({
|
|
||||||
conditionType: [{ required: true, message: '配置方式不能为空', trigger: 'blur' }],
|
|
||||||
conditionExpression: [{ required: true, message: '条件表达式不能为空', trigger: 'blur' }]
|
|
||||||
})
|
|
||||||
const formRef = ref() // 表单 Ref
|
|
||||||
|
|
||||||
|
const conditionRef = ref()
|
||||||
// 保存配置
|
// 保存配置
|
||||||
const saveConfig = async () => {
|
const saveConfig = async () => {
|
||||||
if (!currentNode.value.defaultFlow) {
|
if (!currentNode.value.defaultFlow) {
|
||||||
// 校验表单
|
// 校验表单
|
||||||
if (!formRef) return false
|
const valid = await conditionRef.value.validate()
|
||||||
const valid = await formRef.value.validate()
|
|
||||||
if (!valid) return false
|
if (!valid) return false
|
||||||
const showText = getShowText()
|
const showText = getShowText()
|
||||||
if (!showText) {
|
if (!showText) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
currentNode.value.showText = showText
|
currentNode.value.showText = showText
|
||||||
|
currentNode.value.conditionType = condition.value.conditionType
|
||||||
if (currentNode.value.conditionType === ConditionType.EXPRESSION) {
|
if (currentNode.value.conditionType === ConditionType.EXPRESSION) {
|
||||||
currentNode.value.conditionGroups = undefined
|
currentNode.value.conditionGroups = undefined
|
||||||
|
currentNode.value.conditionExpression = condition.value.conditionExpression
|
||||||
}
|
}
|
||||||
if (currentNode.value.conditionType === ConditionType.RULE) {
|
if (currentNode.value.conditionType === ConditionType.RULE) {
|
||||||
currentNode.value.conditionExpression = undefined
|
currentNode.value.conditionExpression = undefined
|
||||||
currentNode.value.conditionGroups = conditionGroups.value
|
currentNode.value.conditionGroups = condition.value.conditionGroups
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
settingVisible.value = false
|
settingVisible.value = false
|
||||||
@ -271,16 +156,16 @@ const saveConfig = async () => {
|
|||||||
}
|
}
|
||||||
const getShowText = (): string => {
|
const getShowText = (): string => {
|
||||||
let showText = ''
|
let showText = ''
|
||||||
if (currentNode.value.conditionType === ConditionType.EXPRESSION) {
|
if (condition.value.conditionType === ConditionType.EXPRESSION) {
|
||||||
if (currentNode.value.conditionExpression) {
|
if (condition.value.conditionExpression) {
|
||||||
showText = `表达式:${currentNode.value.conditionExpression}`
|
showText = `表达式:${condition.value.conditionExpression}`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (currentNode.value.conditionType === ConditionType.RULE) {
|
if (condition.value.conditionType === ConditionType.RULE) {
|
||||||
// 条件组是否为与关系
|
// 条件组是否为与关系
|
||||||
const groupAnd = conditionGroups.value.and
|
const groupAnd = condition.value.conditionGroups.and
|
||||||
let warningMesg: undefined | string = undefined
|
let warningMesg: undefined | string = undefined
|
||||||
const conditionGroup = conditionGroups.value.conditions.map((item) => {
|
const conditionGroup = condition.value.conditionGroups.conditions.map((item) => {
|
||||||
return (
|
return (
|
||||||
'(' +
|
'(' +
|
||||||
item.rules
|
item.rules
|
||||||
@ -309,64 +194,7 @@ const getShowText = (): string => {
|
|||||||
return showText
|
return showText
|
||||||
}
|
}
|
||||||
|
|
||||||
// 改变条件配置方式
|
|
||||||
const changeConditionType = () => {}
|
|
||||||
|
|
||||||
const conditionGroups = ref<ConditionGroup>({
|
|
||||||
and: true,
|
|
||||||
conditions: [
|
|
||||||
{
|
|
||||||
and: true,
|
|
||||||
rules: [
|
|
||||||
{
|
|
||||||
type: 1,
|
|
||||||
opName: '等于',
|
|
||||||
opCode: '==',
|
|
||||||
leftSide: '',
|
|
||||||
rightSide: ''
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
})
|
|
||||||
// 添加条件组
|
|
||||||
const addConditionGroup = () => {
|
|
||||||
const condition = {
|
|
||||||
and: true,
|
|
||||||
rules: [
|
|
||||||
{
|
|
||||||
type: 1,
|
|
||||||
opName: '等于',
|
|
||||||
opCode: '==',
|
|
||||||
leftSide: '',
|
|
||||||
rightSide: ''
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
conditionGroups.value.conditions.push(condition)
|
|
||||||
}
|
|
||||||
// 删除条件组
|
|
||||||
const deleteConditionGroup = (idx: number) => {
|
|
||||||
conditionGroups.value.conditions.splice(idx, 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 添加条件规则
|
|
||||||
const addConditionRule = (condition: Condition, idx: number) => {
|
|
||||||
const rule: ConditionRule = {
|
|
||||||
type: 1,
|
|
||||||
opName: '等于',
|
|
||||||
opCode: '==',
|
|
||||||
leftSide: '',
|
|
||||||
rightSide: ''
|
|
||||||
}
|
|
||||||
condition.rules.splice(idx + 1, 0, rule)
|
|
||||||
}
|
|
||||||
|
|
||||||
const deleteConditionRule = (condition: Condition, idx: number) => {
|
|
||||||
condition.rules.splice(idx, 1)
|
|
||||||
}
|
|
||||||
const fieldsInfo = useFormFields()
|
const fieldsInfo = useFormFields()
|
||||||
|
|
||||||
/** 条件规则可选择的表单字段 */
|
/** 条件规则可选择的表单字段 */
|
||||||
const fieldOptions = computed(() => {
|
const fieldOptions = computed(() => {
|
||||||
const fieldsCopy = fieldsInfo.slice()
|
const fieldsCopy = fieldsInfo.slice()
|
||||||
|
@ -37,16 +37,19 @@
|
|||||||
:value="node.value"
|
:value="node.value"
|
||||||
/>
|
/>
|
||||||
</el-select>
|
</el-select>
|
||||||
<el-button class="mla" type="danger" link @click="deleteRouteGroup(index)"
|
<el-button class="mla" type="danger" link @click="deleteRouterGroup(index)"
|
||||||
>删除</el-button
|
>删除</el-button
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<Condition v-model="routerGroups[index]" />
|
<Condition
|
||||||
|
:ref="($event) => (conditionRef[index] = $event)"
|
||||||
|
v-model="routerGroups[index]"
|
||||||
|
/>
|
||||||
</el-card>
|
</el-card>
|
||||||
</el-form>
|
</el-form>
|
||||||
|
|
||||||
<el-button class="w-1/1" type="primary" :icon="Plus" @click="addRouteGroup">
|
<el-button class="w-1/1" type="primary" :icon="Plus" @click="addRouterGroup">
|
||||||
新增路由分支
|
新增路由分支
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
@ -61,11 +64,11 @@
|
|||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Plus } from '@element-plus/icons-vue'
|
import { Plus } from '@element-plus/icons-vue'
|
||||||
import { SimpleFlowNode, NodeType, ConditionType, RouteCondition } from '../consts'
|
import { SimpleFlowNode, NodeType, ConditionType, RouterCondition } from '../consts'
|
||||||
import { useWatchNode, useDrawer, useNodeName } from '../node'
|
import { useWatchNode, useDrawer, useNodeName } from '../node'
|
||||||
import Condition from './components/Condition.vue'
|
import Condition from './components/Condition.vue'
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'RouteNodeConfig'
|
name: 'RouterNodeConfig'
|
||||||
})
|
})
|
||||||
const message = useMessage() // 消息弹窗
|
const message = useMessage() // 消息弹窗
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@ -80,12 +83,21 @@ const { settingVisible, closeDrawer, openDrawer } = useDrawer()
|
|||||||
// 当前节点
|
// 当前节点
|
||||||
const currentNode = useWatchNode(props)
|
const currentNode = useWatchNode(props)
|
||||||
// 节点名称
|
// 节点名称
|
||||||
const { nodeName, showInput, clickIcon, blurEvent } = useNodeName(NodeType.ROUTE_BRANCH_NODE)
|
const { nodeName, showInput, clickIcon, blurEvent } = useNodeName(NodeType.ROUTER_BRANCH_NODE)
|
||||||
const routerGroups = ref<RouteCondition[]>([])
|
const routerGroups = ref<RouterCondition[]>([])
|
||||||
const nodeOptions = ref()
|
const nodeOptions = ref()
|
||||||
|
|
||||||
|
const conditionRef = ref([])
|
||||||
// 保存配置
|
// 保存配置
|
||||||
const saveConfig = async () => {
|
const saveConfig = async () => {
|
||||||
|
// 校验表单
|
||||||
|
let valid = true
|
||||||
|
for (const item of conditionRef.value) {
|
||||||
|
if (!(await item.validate())) {
|
||||||
|
valid = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!valid) return false
|
||||||
const showText = getShowText()
|
const showText = getShowText()
|
||||||
if (!showText) return false
|
if (!showText) return false
|
||||||
currentNode.value.name = nodeName.value!
|
currentNode.value.name = nodeName.value!
|
||||||
@ -96,7 +108,7 @@ const saveConfig = async () => {
|
|||||||
}
|
}
|
||||||
// 显示路由分支节点配置, 由父组件传过来
|
// 显示路由分支节点配置, 由父组件传过来
|
||||||
const showRouteNodeConfig = (node: SimpleFlowNode) => {
|
const showRouteNodeConfig = (node: SimpleFlowNode) => {
|
||||||
getRoutableNode()
|
getRouterNode()
|
||||||
routerGroups.value = []
|
routerGroups.value = []
|
||||||
nodeName.value = node.name
|
nodeName.value = node.name
|
||||||
if (node.routerGroups) {
|
if (node.routerGroups) {
|
||||||
@ -132,7 +144,7 @@ const getShowText = () => {
|
|||||||
return `${routerGroups.value.length}条路由分支`
|
return `${routerGroups.value.length}条路由分支`
|
||||||
}
|
}
|
||||||
|
|
||||||
const addRouteGroup = () => {
|
const addRouterGroup = () => {
|
||||||
routerGroups.value.push({
|
routerGroups.value.push({
|
||||||
nodeId: '',
|
nodeId: '',
|
||||||
conditionType: ConditionType.RULE,
|
conditionType: ConditionType.RULE,
|
||||||
@ -144,8 +156,6 @@ const addRouteGroup = () => {
|
|||||||
and: true,
|
and: true,
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
type: 1,
|
|
||||||
opName: '等于',
|
|
||||||
opCode: '==',
|
opCode: '==',
|
||||||
leftSide: '',
|
leftSide: '',
|
||||||
rightSide: ''
|
rightSide: ''
|
||||||
@ -157,12 +167,11 @@ const addRouteGroup = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const deleteRouteGroup = (index: number) => {
|
const deleteRouterGroup = (index: number) => {
|
||||||
routerGroups.value.splice(index, 1)
|
routerGroups.value.splice(index, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO @lesan:还有一些 router 的命名,没改过来呢
|
const getRouterNode = () => {
|
||||||
const getRoutableNode = () => {
|
|
||||||
// TODO @lesan 还需要满足以下要求
|
// TODO @lesan 还需要满足以下要求
|
||||||
// 并行分支、包容分支内部节点不能跳转到外部节点
|
// 并行分支、包容分支内部节点不能跳转到外部节点
|
||||||
// 条件分支节点可以向上跳转到外部节点
|
// 条件分支节点可以向上跳转到外部节点
|
||||||
@ -170,7 +179,7 @@ const getRoutableNode = () => {
|
|||||||
nodeOptions.value = []
|
nodeOptions.value = []
|
||||||
while (true) {
|
while (true) {
|
||||||
if (!node) break
|
if (!node) break
|
||||||
if (node.type !== NodeType.ROUTE_BRANCH_NODE) {
|
if (node.type !== NodeType.ROUTER_BRANCH_NODE) {
|
||||||
nodeOptions.value.push({
|
nodeOptions.value.push({
|
||||||
label: node.name,
|
label: node.name,
|
||||||
value: node.id
|
value: node.id
|
@ -359,11 +359,7 @@
|
|||||||
|
|
||||||
<el-divider content-position="left">是否需要签名</el-divider>
|
<el-divider content-position="left">是否需要签名</el-divider>
|
||||||
<el-form-item prop="signEnable">
|
<el-form-item prop="signEnable">
|
||||||
<el-switch
|
<el-switch v-model="configForm.signEnable" active-text="是" inactive-text="否" />
|
||||||
v-model="configForm.signEnable"
|
|
||||||
active-text="是"
|
|
||||||
inactive-text="否"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</div>
|
</div>
|
||||||
@ -445,7 +441,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
<el-tab-pane label="监听器" name="listener">
|
<el-tab-pane label="监听器" name="listener">
|
||||||
<el-form :model="configForm" label-position="top">
|
<el-form ref="listenerFormRef" :model="configForm" label-position="top">
|
||||||
<div v-for="(listener, listenerIdx) in taskListener" :key="listenerIdx">
|
<div v-for="(listener, listenerIdx) in taskListener" :key="listenerIdx">
|
||||||
<el-divider content-position="left">
|
<el-divider content-position="left">
|
||||||
<el-text tag="b" size="large">{{ listener.name }}</el-text>
|
<el-text tag="b" size="large">{{ listener.name }}</el-text>
|
||||||
@ -484,7 +480,16 @@
|
|||||||
:key="index"
|
:key="index"
|
||||||
>
|
>
|
||||||
<div class="mr-2">
|
<div class="mr-2">
|
||||||
<el-input class="w-160px" v-model="item.key" />
|
<el-form-item
|
||||||
|
:prop="`task${listener.type}ListenerHeader.${index}.key`"
|
||||||
|
:rules="{
|
||||||
|
required: true,
|
||||||
|
message: '参数名不能为空',
|
||||||
|
trigger: 'blur'
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<el-input class="w-160px" v-model="item.key" />
|
||||||
|
</el-form-item>
|
||||||
</div>
|
</div>
|
||||||
<div class="mr-2">
|
<div class="mr-2">
|
||||||
<el-select class="w-100px!" v-model="item.type">
|
<el-select class="w-100px!" v-model="item.type">
|
||||||
@ -497,24 +502,42 @@
|
|||||||
</el-select>
|
</el-select>
|
||||||
</div>
|
</div>
|
||||||
<div class="mr-2">
|
<div class="mr-2">
|
||||||
<el-input
|
<el-form-item
|
||||||
v-if="item.type === ListenerParamTypeEnum.FIXED_VALUE"
|
:prop="`task${listener.type}ListenerHeader.${index}.value`"
|
||||||
class="w-160px"
|
:rules="{
|
||||||
v-model="item.value"
|
required: true,
|
||||||
/>
|
message: '参数值不能为空',
|
||||||
<el-select
|
trigger: 'blur'
|
||||||
v-if="item.type === ListenerParamTypeEnum.FROM_FORM"
|
}"
|
||||||
class="w-160px!"
|
|
||||||
v-model="item.value"
|
|
||||||
>
|
>
|
||||||
<el-option
|
<el-input
|
||||||
v-for="(field, fIdx) in formFieldOptions"
|
v-if="item.type === ListenerParamTypeEnum.FIXED_VALUE"
|
||||||
:key="fIdx"
|
class="w-160px"
|
||||||
:label="field.title"
|
v-model="item.value"
|
||||||
:value="field.field"
|
|
||||||
:disabled="!field.required"
|
|
||||||
/>
|
/>
|
||||||
</el-select>
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
:prop="`task${listener.type}ListenerHeader.${index}.value`"
|
||||||
|
:rules="{
|
||||||
|
required: true,
|
||||||
|
message: '参数值不能为空',
|
||||||
|
trigger: 'change'
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<el-select
|
||||||
|
v-if="item.type === ListenerParamTypeEnum.FROM_FORM"
|
||||||
|
class="w-160px!"
|
||||||
|
v-model="item.value"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="(field, fIdx) in formFieldOptions"
|
||||||
|
:key="fIdx"
|
||||||
|
:label="field.title"
|
||||||
|
:value="field.field"
|
||||||
|
:disabled="!field.required"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
</div>
|
</div>
|
||||||
<div class="mr-1 flex items-center">
|
<div class="mr-1 flex items-center">
|
||||||
<Icon
|
<Icon
|
||||||
@ -544,7 +567,16 @@
|
|||||||
:key="index"
|
:key="index"
|
||||||
>
|
>
|
||||||
<div class="mr-2">
|
<div class="mr-2">
|
||||||
<el-input class="w-160px" v-model="item.key" />
|
<el-form-item
|
||||||
|
:prop="`task${listener.type}ListenerBody.${index}.key`"
|
||||||
|
:rules="{
|
||||||
|
required: true,
|
||||||
|
message: '参数名不能为空',
|
||||||
|
trigger: 'blur'
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<el-input class="w-160px" v-model="item.key" />
|
||||||
|
</el-form-item>
|
||||||
</div>
|
</div>
|
||||||
<div class="mr-2">
|
<div class="mr-2">
|
||||||
<el-select class="w-100px!" v-model="item.type">
|
<el-select class="w-100px!" v-model="item.type">
|
||||||
@ -557,24 +589,42 @@
|
|||||||
</el-select>
|
</el-select>
|
||||||
</div>
|
</div>
|
||||||
<div class="mr-2">
|
<div class="mr-2">
|
||||||
<el-input
|
<el-form-item
|
||||||
v-if="item.type === ListenerParamTypeEnum.FIXED_VALUE"
|
:prop="`task${listener.type}ListenerBody.${index}.value`"
|
||||||
class="w-160px"
|
:rules="{
|
||||||
v-model="item.value"
|
required: true,
|
||||||
/>
|
message: '参数值不能为空',
|
||||||
<el-select
|
trigger: 'blur'
|
||||||
v-if="item.type === ListenerParamTypeEnum.FROM_FORM"
|
}"
|
||||||
class="w-160px!"
|
|
||||||
v-model="item.value"
|
|
||||||
>
|
>
|
||||||
<el-option
|
<el-input
|
||||||
v-for="(field, fIdx) in formFieldOptions"
|
v-if="item.type === ListenerParamTypeEnum.FIXED_VALUE"
|
||||||
:key="fIdx"
|
class="w-160px"
|
||||||
:label="field.title"
|
v-model="item.value"
|
||||||
:value="field.field"
|
|
||||||
:disabled="!field.required"
|
|
||||||
/>
|
/>
|
||||||
</el-select>
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
:prop="`task${listener.type}ListenerBody.${index}.value`"
|
||||||
|
:rules="{
|
||||||
|
required: true,
|
||||||
|
message: '参数值不能为空',
|
||||||
|
trigger: 'change'
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<el-select
|
||||||
|
v-if="item.type === ListenerParamTypeEnum.FROM_FORM"
|
||||||
|
class="w-160px!"
|
||||||
|
v-model="item.value"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="(field, fIdx) in formFieldOptions"
|
||||||
|
:key="fIdx"
|
||||||
|
:label="field.title"
|
||||||
|
:value="field.field"
|
||||||
|
:disabled="!field.required"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
</div>
|
</div>
|
||||||
<div class="mr-1 flex items-center">
|
<div class="mr-1 flex items-center">
|
||||||
<Icon
|
<Icon
|
||||||
@ -792,6 +842,8 @@ const {
|
|||||||
cTimeoutMaxRemindCount
|
cTimeoutMaxRemindCount
|
||||||
} = useTimeoutHandler()
|
} = useTimeoutHandler()
|
||||||
|
|
||||||
|
const listenerFormRef = ref()
|
||||||
|
|
||||||
// 保存配置
|
// 保存配置
|
||||||
const saveConfig = async () => {
|
const saveConfig = async () => {
|
||||||
activeTabName.value = 'user'
|
activeTabName.value = 'user'
|
||||||
@ -807,7 +859,8 @@ const saveConfig = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!formRef) return false
|
if (!formRef) return false
|
||||||
const valid = await formRef.value.validate()
|
if (!listenerFormRef) return false
|
||||||
|
const valid = (await formRef.value.validate()) && (await listenerFormRef.value.validate())
|
||||||
if (!valid) return false
|
if (!valid) return false
|
||||||
const showText = getShowText()
|
const showText = getShowText()
|
||||||
if (!showText) return false
|
if (!showText) return false
|
||||||
@ -937,7 +990,7 @@ const showUserTaskNodeConfig = (node: SimpleFlowNode) => {
|
|||||||
configForm.value.taskCompleteListenerHeader = node.taskCompleteListener?.header ?? []
|
configForm.value.taskCompleteListenerHeader = node.taskCompleteListener?.header ?? []
|
||||||
configForm.value.taskCompleteListenerBody = node.taskCompleteListener?.body ?? []
|
configForm.value.taskCompleteListenerBody = node.taskCompleteListener?.body ?? []
|
||||||
// 6. 签名
|
// 6. 签名
|
||||||
configForm.value.signEnable = node.signEnable ?? false
|
configForm.value.signEnable = node?.signEnable ?? false
|
||||||
}
|
}
|
||||||
|
|
||||||
defineExpose({ openDrawer, showUserTaskNodeConfig }) // 暴露方法给父组件
|
defineExpose({ openDrawer, showUserTaskNodeConfig }) // 暴露方法给父组件
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
<!-- TODO @lesan:其它路由条件,可以使用这个哇? -->
|
|
||||||
<template>
|
<template>
|
||||||
<el-form ref="formRef" :model="condition" :rules="formRules" label-position="top">
|
<el-form ref="formRef" :model="condition" :rules="formRules" label-position="top">
|
||||||
<!-- TODO @lesan:1)默认选中 条件规则;2)条件规则放前面,因为更常用!-->
|
|
||||||
<el-form-item label="配置方式" prop="conditionType">
|
<el-form-item label="配置方式" prop="conditionType">
|
||||||
<el-radio-group v-model="condition.conditionType">
|
<el-radio-group v-model="condition.conditionType">
|
||||||
<el-radio
|
<el-radio
|
||||||
@ -14,18 +12,6 @@
|
|||||||
</el-radio>
|
</el-radio>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item
|
|
||||||
v-if="condition.conditionType === ConditionType.EXPRESSION"
|
|
||||||
label="条件表达式"
|
|
||||||
prop="conditionExpression"
|
|
||||||
>
|
|
||||||
<el-input
|
|
||||||
type="textarea"
|
|
||||||
v-model="condition.conditionExpression"
|
|
||||||
clearable
|
|
||||||
style="width: 100%"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item v-if="condition.conditionType === ConditionType.RULE" label="条件规则">
|
<el-form-item v-if="condition.conditionType === ConditionType.RULE" label="条件规则">
|
||||||
<div class="condition-group-tool">
|
<div class="condition-group-tool">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
@ -73,15 +59,24 @@
|
|||||||
|
|
||||||
<div class="flex pt-2" v-for="(rule, rIdx) in equation.rules" :key="rIdx">
|
<div class="flex pt-2" v-for="(rule, rIdx) in equation.rules" :key="rIdx">
|
||||||
<div class="mr-2">
|
<div class="mr-2">
|
||||||
<el-select style="width: 160px" v-model="rule.leftSide">
|
<el-form-item
|
||||||
<el-option
|
:prop="`conditionGroups.conditions.${cIdx}.rules.${rIdx}.leftSide`"
|
||||||
v-for="(field, fIdx) in fieldOptions"
|
:rules="{
|
||||||
:key="fIdx"
|
required: true,
|
||||||
:label="field.title"
|
message: '左值不能为空',
|
||||||
:value="field.field"
|
trigger: 'change'
|
||||||
:disabled="!field.required"
|
}"
|
||||||
/>
|
>
|
||||||
</el-select>
|
<el-select style="width: 160px" v-model="rule.leftSide">
|
||||||
|
<el-option
|
||||||
|
v-for="(field, fIdx) in fieldOptions"
|
||||||
|
:key="fIdx"
|
||||||
|
:label="field.title"
|
||||||
|
:value="field.field"
|
||||||
|
:disabled="!field.required"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
</div>
|
</div>
|
||||||
<div class="mr-2">
|
<div class="mr-2">
|
||||||
<el-select v-model="rule.opCode" style="width: 100px">
|
<el-select v-model="rule.opCode" style="width: 100px">
|
||||||
@ -94,7 +89,16 @@
|
|||||||
</el-select>
|
</el-select>
|
||||||
</div>
|
</div>
|
||||||
<div class="mr-2">
|
<div class="mr-2">
|
||||||
<el-input v-model="rule.rightSide" style="width: 160px" />
|
<el-form-item
|
||||||
|
:prop="`conditionGroups.conditions.${cIdx}.rules.${rIdx}.rightSide`"
|
||||||
|
:rules="{
|
||||||
|
required: true,
|
||||||
|
message: '右值不能为空',
|
||||||
|
trigger: 'blur'
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<el-input v-model="rule.rightSide" style="width: 160px" />
|
||||||
|
</el-form-item>
|
||||||
</div>
|
</div>
|
||||||
<div class="mr-1 flex items-center" v-if="equation.rules.length > 1">
|
<div class="mr-1 flex items-center" v-if="equation.rules.length > 1">
|
||||||
<Icon icon="ep:delete" :size="18" @click="deleteConditionRule(equation, rIdx)" />
|
<Icon icon="ep:delete" :size="18" @click="deleteConditionRule(equation, rIdx)" />
|
||||||
@ -114,13 +118,25 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
v-if="condition.conditionType === ConditionType.EXPRESSION"
|
||||||
|
label="条件表达式"
|
||||||
|
prop="conditionExpression"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
type="textarea"
|
||||||
|
v-model="condition.conditionExpression"
|
||||||
|
clearable
|
||||||
|
style="width: 100%"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {
|
import {
|
||||||
CONDITION_CONFIG_TYPES,
|
|
||||||
COMPARISON_OPERATORS,
|
COMPARISON_OPERATORS,
|
||||||
|
CONDITION_CONFIG_TYPES,
|
||||||
ConditionType,
|
ConditionType,
|
||||||
ProcessVariableEnum
|
ProcessVariableEnum
|
||||||
} from '../../consts'
|
} from '../../consts'
|
||||||
@ -181,8 +197,6 @@ const deleteConditionRule = (condition, index) => {
|
|||||||
|
|
||||||
const addConditionRule = (condition, index) => {
|
const addConditionRule = (condition, index) => {
|
||||||
const rule = {
|
const rule = {
|
||||||
type: 1,
|
|
||||||
opName: '等于',
|
|
||||||
opCode: '==',
|
opCode: '==',
|
||||||
leftSide: '',
|
leftSide: '',
|
||||||
rightSide: ''
|
rightSide: ''
|
||||||
@ -195,8 +209,6 @@ const addConditionGroup = (conditions) => {
|
|||||||
and: true,
|
and: true,
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
type: 1, // TODO @lesan:枚举~
|
|
||||||
opName: '等于',
|
|
||||||
opCode: '==',
|
opCode: '==',
|
||||||
leftSide: '',
|
leftSide: '',
|
||||||
rightSide: ''
|
rightSide: ''
|
||||||
@ -205,6 +217,13 @@ const addConditionGroup = (conditions) => {
|
|||||||
}
|
}
|
||||||
conditions.push(condition)
|
conditions.push(condition)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const validate = async () => {
|
||||||
|
if (!formRef) return false
|
||||||
|
return await formRef.value.validate()
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({ validate })
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
@ -31,7 +31,7 @@
|
|||||||
{{ currentNode.showText }}
|
{{ currentNode.showText }}
|
||||||
</div>
|
</div>
|
||||||
<div class="node-text" v-else>
|
<div class="node-text" v-else>
|
||||||
{{ NODE_DEFAULT_TEXT.get(NodeType.ROUTE_BRANCH_NODE) }}
|
{{ NODE_DEFAULT_TEXT.get(NodeType.ROUTER_BRANCH_NODE) }}
|
||||||
</div>
|
</div>
|
||||||
<Icon v-if="!readonly" icon="ep:arrow-right-bold" />
|
<Icon v-if="!readonly" icon="ep:arrow-right-bold" />
|
||||||
</div>
|
</div>
|
||||||
@ -49,17 +49,17 @@
|
|||||||
:current-node="currentNode"
|
:current-node="currentNode"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<RouteNodeConfig v-if="!readonly && currentNode" ref="nodeSetting" :flow-node="currentNode" />
|
<RouterNodeConfig v-if="!readonly && currentNode" ref="nodeSetting" :flow-node="currentNode" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { SimpleFlowNode, NodeType, NODE_DEFAULT_TEXT } from '../consts'
|
import { SimpleFlowNode, NodeType, NODE_DEFAULT_TEXT } from '../consts'
|
||||||
import NodeHandler from '../NodeHandler.vue'
|
import NodeHandler from '../NodeHandler.vue'
|
||||||
import { useNodeName2, useWatchNode, useTaskStatusClass } from '../node'
|
import { useNodeName2, useWatchNode, useTaskStatusClass } from '../node'
|
||||||
import RouteNodeConfig from '../nodes-config/RouteNodeConfig.vue'
|
import RouterNodeConfig from '../nodes-config/RouterNodeConfig.vue'
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'RouteNode'
|
name: 'RouterNode'
|
||||||
})
|
})
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@ -77,7 +77,7 @@ const readonly = inject<Boolean>('readonly')
|
|||||||
// 监控节点的变化
|
// 监控节点的变化
|
||||||
const currentNode = useWatchNode(props)
|
const currentNode = useWatchNode(props)
|
||||||
// 节点名称编辑
|
// 节点名称编辑
|
||||||
const { showInput, blurEvent, clickTitle } = useNodeName2(currentNode, NodeType.ROUTE_BRANCH_NODE)
|
const { showInput, blurEvent, clickTitle } = useNodeName2(currentNode, NodeType.ROUTER_BRANCH_NODE)
|
||||||
|
|
||||||
const nodeSetting = ref()
|
const nodeSetting = ref()
|
||||||
// 打开节点配置
|
// 打开节点配置
|
@ -44,6 +44,12 @@
|
|||||||
:rows="4"
|
:rows="4"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item v-if="runningTask.signEnable" label="签名" prop="sign" ref="approveSignFormRef">
|
||||||
|
<el-button @click="signRef.open()">点击签名</el-button>
|
||||||
|
<el-image class="w-90px h-40px ml-5px" v-if="approveReasonForm.sign"
|
||||||
|
:src="approveReasonForm.sign"
|
||||||
|
:preview-src-list="[approveReasonForm.sign]"/>
|
||||||
|
</el-form-item>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button :disabled="formLoading" type="success" @click="handleAudit(true, approveFormRef)">
|
<el-button :disabled="formLoading" type="success" @click="handleAudit(true, approveFormRef)">
|
||||||
{{ getButtonDisplayName(OperationButtonType.APPROVE) }}
|
{{ getButtonDisplayName(OperationButtonType.APPROVE) }}
|
||||||
@ -471,6 +477,8 @@
|
|||||||
<Icon :size="14" icon="ep:refresh" /> 再次提交
|
<Icon :size="14" icon="ep:refresh" /> 再次提交
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<SignDialog ref="signRef" @success="handleSignFinish"/>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { useUserStoreWithOut } from '@/store/modules/user'
|
import { useUserStoreWithOut } from '@/store/modules/user'
|
||||||
@ -484,6 +492,7 @@ import {
|
|||||||
} from '@/components/SimpleProcessDesignerV2/src/consts'
|
} from '@/components/SimpleProcessDesignerV2/src/consts'
|
||||||
import { BpmProcessInstanceStatus, BpmModelFormType } from '@/utils/constants'
|
import { BpmProcessInstanceStatus, BpmModelFormType } from '@/utils/constants'
|
||||||
import type { FormInstance, FormRules } from 'element-plus'
|
import type { FormInstance, FormRules } from 'element-plus'
|
||||||
|
import SignDialog from "./SignDialog.vue";
|
||||||
defineOptions({ name: 'ProcessInstanceBtnContainer' })
|
defineOptions({ name: 'ProcessInstanceBtnContainer' })
|
||||||
|
|
||||||
const router = useRouter() // 路由
|
const router = useRouter() // 路由
|
||||||
@ -522,11 +531,15 @@ const approveFormFApi = ref<any>({}) // approveForms 的 fAPi
|
|||||||
|
|
||||||
// 审批通过意见表单
|
// 审批通过意见表单
|
||||||
const approveFormRef = ref<FormInstance>()
|
const approveFormRef = ref<FormInstance>()
|
||||||
|
const signRef = ref()
|
||||||
|
const approveSignFormRef = ref()
|
||||||
const approveReasonForm = reactive({
|
const approveReasonForm = reactive({
|
||||||
reason: ''
|
reason: '',
|
||||||
|
sign: ''
|
||||||
})
|
})
|
||||||
const approveReasonRule = reactive<FormRules<typeof approveReasonForm>>({
|
const approveReasonRule = reactive<FormRules<typeof approveReasonForm>>({
|
||||||
reason: [{ required: true, message: '审批意见不能为空', trigger: 'blur' }],
|
reason: [{ required: true, message: '审批意见不能为空', trigger: 'blur' }],
|
||||||
|
sign: [{ required: true, message: '签名不能为空', trigger: 'change' }]
|
||||||
})
|
})
|
||||||
// 拒绝表单
|
// 拒绝表单
|
||||||
const rejectFormRef = ref<FormInstance>()
|
const rejectFormRef = ref<FormInstance>()
|
||||||
@ -672,6 +685,10 @@ const handleAudit = async (pass: boolean, formRef: FormInstance | undefined) =>
|
|||||||
reason: approveReasonForm.reason,
|
reason: approveReasonForm.reason,
|
||||||
variables // 审批通过, 把修改的字段值赋于流程实例变量
|
variables // 审批通过, 把修改的字段值赋于流程实例变量
|
||||||
}
|
}
|
||||||
|
// 签名
|
||||||
|
if (runningTask.value.signEnable) {
|
||||||
|
data.sign = approveReasonForm.sign
|
||||||
|
}
|
||||||
// 多表单处理,并且有额外的 approveForm 表单,需要校验 + 拼接到 data 表单里提交
|
// 多表单处理,并且有额外的 approveForm 表单,需要校验 + 拼接到 data 表单里提交
|
||||||
// TODO 芋艿 任务有多表单这里要如何处理,会和可编辑的字段冲突
|
// TODO 芋艿 任务有多表单这里要如何处理,会和可编辑的字段冲突
|
||||||
const formCreateApi = approveFormFApi.value
|
const formCreateApi = approveFormFApi.value
|
||||||
@ -966,6 +983,11 @@ const getUpdatedProcessInstanceVaiables = ()=> {
|
|||||||
return variables
|
return variables
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleSignFinish = (url) => {
|
||||||
|
approveReasonForm.sign = url
|
||||||
|
approveSignFormRef.value.validate('change')
|
||||||
|
}
|
||||||
|
|
||||||
defineExpose({ loadTodoTask })
|
defineExpose({ loadTodoTask })
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -128,7 +128,7 @@ const setSimpleModelNodeTaskStatus = (
|
|||||||
simpleModel.type === NodeType.CONDITION_BRANCH_NODE ||
|
simpleModel.type === NodeType.CONDITION_BRANCH_NODE ||
|
||||||
simpleModel.type === NodeType.PARALLEL_BRANCH_NODE ||
|
simpleModel.type === NodeType.PARALLEL_BRANCH_NODE ||
|
||||||
simpleModel.type === NodeType.INCLUSIVE_BRANCH_NODE ||
|
simpleModel.type === NodeType.INCLUSIVE_BRANCH_NODE ||
|
||||||
simpleModel.type === NodeType.ROUTE_BRANCH_NODE
|
simpleModel.type === NodeType.ROUTER_BRANCH_NODE
|
||||||
) {
|
) {
|
||||||
// 网关节点。只有通过和未执行状态
|
// 网关节点。只有通过和未执行状态
|
||||||
if (finishedActivityIds.includes(simpleModel.id)) {
|
if (finishedActivityIds.includes(simpleModel.id)) {
|
||||||
|
@ -123,6 +123,15 @@
|
|||||||
>
|
>
|
||||||
审批意见:{{ task.reason }}
|
审批意见:{{ task.reason }}
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="task.sign && activity.nodeType === NodeType.USER_TASK_NODE"
|
||||||
|
class="text-#a5a5a5 text-13px mt-1 w-full bg-#f8f8fa p2 rounded-md"
|
||||||
|
>
|
||||||
|
签名:
|
||||||
|
<el-image class="w-90px h-40px ml-5px"
|
||||||
|
:src="task.sign"
|
||||||
|
:preview-src-list="[task.sign]"/>
|
||||||
|
</div>
|
||||||
</teleport>
|
</teleport>
|
||||||
</div>
|
</div>
|
||||||
<!-- 情况二:遍历每个审批节点下的【候选的】task 任务。例如说,1)依次审批,2)未来的审批任务等 -->
|
<!-- 情况二:遍历每个审批节点下的【候选的】task 任务。例如说,1)依次审批,2)未来的审批任务等 -->
|
||||||
|
84
src/views/bpm/processInstance/detail/SignDialog.vue
Normal file
84
src/views/bpm/processInstance/detail/SignDialog.vue
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
v-model="signDialogVisible"
|
||||||
|
title="签名"
|
||||||
|
width="935"
|
||||||
|
>
|
||||||
|
<div class="position-relative">
|
||||||
|
<Vue3Signature class="b b-solid b-gray" ref="signature" w="900px" h="400px"/>
|
||||||
|
<el-button
|
||||||
|
style="position: absolute; bottom: 20px; right: 10px"
|
||||||
|
type="primary"
|
||||||
|
text
|
||||||
|
size="small"
|
||||||
|
@click="signature.clear()"
|
||||||
|
>
|
||||||
|
<Icon icon="ep:delete" class="mr-5px"/>
|
||||||
|
清除
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
<template #footer>
|
||||||
|
<div class="dialog-footer">
|
||||||
|
<el-button @click="signDialogVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" @click="submit">
|
||||||
|
提交
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import Vue3Signature from "vue3-signature"
|
||||||
|
import * as FileApi from '@/api/infra/file'
|
||||||
|
|
||||||
|
const message = useMessage() // 消息弹窗
|
||||||
|
const signDialogVisible = ref(false)
|
||||||
|
const signature = ref()
|
||||||
|
|
||||||
|
const open = async () => {
|
||||||
|
signDialogVisible.value = true
|
||||||
|
}
|
||||||
|
defineExpose({open})
|
||||||
|
|
||||||
|
const emits = defineEmits(['success'])
|
||||||
|
const submit = async () => {
|
||||||
|
message.success('签名上传中请稍等。。。')
|
||||||
|
const res = await FileApi.updateFile({file: base64ToFile(signature.value.save('image/png'), '签名')})
|
||||||
|
emits('success', res.data)
|
||||||
|
signDialogVisible.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
const base64ToFile = (base64, fileName) => {
|
||||||
|
// 将base64按照 , 进行分割 将前缀 与后续内容分隔开
|
||||||
|
let data = base64.split(',');
|
||||||
|
// 利用正则表达式 从前缀中获取图片的类型信息(image/png、image/jpeg、image/webp等)
|
||||||
|
let type = data[0].match(/:(.*?);/)[1];
|
||||||
|
// 从图片的类型信息中 获取具体的文件格式后缀(png、jpeg、webp)
|
||||||
|
let suffix = type.split('/')[1];
|
||||||
|
// 使用atob()对base64数据进行解码 结果是一个文件数据流 以字符串的格式输出
|
||||||
|
const bstr = window.atob(data[1]);
|
||||||
|
// 获取解码结果字符串的长度
|
||||||
|
let n = bstr.length
|
||||||
|
// 根据解码结果字符串的长度创建一个等长的整形数字数组
|
||||||
|
// 但在创建时 所有元素初始值都为 0
|
||||||
|
const u8arr = new Uint8Array(n)
|
||||||
|
// 将整形数组的每个元素填充为解码结果字符串对应位置字符的UTF-16 编码单元
|
||||||
|
while (n--) {
|
||||||
|
// charCodeAt():获取给定索引处字符对应的 UTF-16 代码单元
|
||||||
|
u8arr[n] = bstr.charCodeAt(n)
|
||||||
|
}
|
||||||
|
// 利用构造函数创建File文件对象
|
||||||
|
// new File(bits, name, options)
|
||||||
|
const file = new File([u8arr], `${fileName}.${suffix}`, {
|
||||||
|
type: type
|
||||||
|
})
|
||||||
|
// 将File文件对象返回给方法的调用者
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
Loading…
x
Reference in New Issue
Block a user