改造审批设置. 整合系统中的任务的候选人策略

This commit is contained in:
jason 2024-03-24 20:35:53 +08:00
parent ee12e691be
commit 4c4d13cdd0
7 changed files with 538 additions and 74 deletions

View File

@ -32,7 +32,7 @@
</div>
</div>
</template>
<script setup>
<script lang="ts" setup>
import { ref } from 'vue'
let props = defineProps({
childNodeP: {
@ -47,23 +47,36 @@ const addType = (type) => {
if (type != 4) {
var data
if (type == 1) {
// data = {
// name: '',
// error: true,
// type: 1,
// settype: 1,
// selectMode: 0,
// selectRange: 0,
// directorLevel: 1,
// examineMode: 1,
// noHanderAction: 1,
// examineEndDirectorLevel: 0,
// childNode: props.childNodeP,
// nodeUserList: []
// }
data = {
nodeName: '审核人',
name: '审核人',
error: true,
type: 1,
settype: 1,
selectMode: 0,
selectRange: 0,
directorLevel: 1,
examineMode: 1,
noHanderAction: 1,
examineEndDirectorLevel: 0,
//
attributes : {
approveMethod : undefined,
candidateStrategy: undefined,
candidateParam: []
},
childNode: props.childNodeP,
nodeUserList: []
}
} else if (type == 2) {
data = {
nodeName: '抄送人',
name: '抄送人',
type: 2,
ccSelfSelectFlag: 1,
childNode: props.childNodeP,
@ -73,12 +86,12 @@ const addType = (type) => {
emits('update:childNodeP', data)
} else {
emits('update:childNodeP', {
nodeName: '路由',
name: '路由',
type: 4,
childNode: null,
conditionNodes: [
{
nodeName: '条件1',
name: '条件1',
error: true,
type: 3,
priorityLevel: 1,
@ -87,7 +100,7 @@ const addType = (type) => {
childNode: props.childNodeP
},
{
nodeName: '条件2',
name: '条件2',
type: 3,
priorityLevel: 2,
conditionList: [],

View File

@ -0,0 +1,47 @@
export enum NodeType {
//// 0 发起人 1审批 2抄送 3条件 4路由
/**
*
*/
START_USER_NODE = 0,
/**
*
*/
APPROVE_USER_NODE = 1,
/**
*
*/
CC_USER_NODE = 2,
/**
*
*/
CONDITION_NODE = 3,
/**
*
*/
ROUTER_NODE = 4
}
export const NODE_BG_COLOR = new Map()
NODE_BG_COLOR.set(NodeType.START_USER_NODE, '#87, 106, 149')
NODE_BG_COLOR.set(NodeType.APPROVE_USER_NODE, '#255, 148, 62')
NODE_BG_COLOR.set(NodeType.CC_USER_NODE, '#50, 150, 250')
/**
*
*/
export const NODE_TITLE = new Map()
NODE_TITLE.set(NodeType.START_USER_NODE, '发起人')
NODE_TITLE.set(NodeType.APPROVE_USER_NODE, '审批人')
NODE_TITLE.set(NodeType.CC_USER_NODE, '抄送人')
export type WorkFlowNode = {
id: string,
parentId: string,
type: NodeType,
name: string,
attributes: Map<string,any>,
// 操作人
childNode: WorkFlowNode,
conditionNodes: WorkFlowNode[]
}

View File

@ -0,0 +1,311 @@
<template>
<el-drawer
:append-to-body="true"
v-model="visible"
class="set_promoter"
:show-close="false"
:size="550"
:before-close="saveConfig"
>
<template #header>
<div class="user-task-header">审批设置</div>
</template>
<div>
<el-form label-position="top" label-width="100px">
<el-form-item label="审批方式" prop="approveMethod">
<el-select v-model="candidateConfig.approveMethod" style="width: 100%" clearable>
<el-option
v-for="dict in approveMethods"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="审批人规则类型" prop="candidateStrategy">
<el-select v-model="candidateConfig.candidateStrategy" style="width: 100%" clearable @change="changecandidateStrategy">
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.BPM_TASK_CANDIDATE_STRATEGY)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item
v-if="candidateConfig.candidateStrategy == 10"
label="指定角色"
prop="candidateParam"
>
<el-select
v-model="candidateConfig.candidateParam"
clearable
multiple
style="width: 100%"
>
<el-option
v-for="item in roleOptions"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item
v-if="candidateConfig.candidateStrategy == 20 || candidateConfig.candidateStrategy == 21"
label="指定部门"
prop="candidateParam"
span="24"
>
<el-tree-select
ref="treeRef"
v-model="candidateConfig.candidateParam"
:data="deptTreeOptions"
:props="defaultProps"
empty-text="加载中,请稍后"
multiple
node-key="id"
style="width: 100%"
show-checkbox
/>
</el-form-item>
<el-form-item
v-if="candidateConfig.candidateStrategy == 22"
label="指定岗位"
prop="candidateParam"
span="24"
>
<el-select
v-model="candidateConfig.candidateParam"
clearable
multiple
style="width: 100%"
>
<el-option
v-for="item in postOptions"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item
v-if="
candidateConfig.candidateStrategy == 30 ||
candidateConfig.candidateStrategy == 31 ||
candidateConfig.candidateStrategy == 32
"
label="指定用户"
prop="candidateParam"
span="24"
>
<el-select
v-model="candidateConfig.candidateParam"
clearable
multiple
style="width: 100%"
>
<el-option
v-for="item in userOptions"
:key="item.id"
:label="item.nickname"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item
v-if="candidateConfig.candidateStrategy === 40"
label="指定用户组"
prop="candidateParam"
>
<el-select
v-model="candidateConfig.candidateParam"
clearable
multiple
style="width: 100%"
>
<el-option
v-for="item in userGroupOptions"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item
v-if="candidateConfig.candidateStrategy === 60"
label="流程表达式"
prop="candidateParam"
>
<el-input
type="textarea"
v-model="candidateConfig.candidateParam[0]"
clearable
style="width: 100%"
/>
</el-form-item>
</el-form>
</div>
<div class="demo-drawer__footer clear">
<el-button type="primary" @click="saveConfig"> </el-button>
<el-button @click="closeDrawer"> </el-button>
</div>
</el-drawer>
</template>
<script lang="ts" setup>
import { ref, watch, computed, toRaw } from 'vue'
import { approveMethods, setApproverStr } from '../util'
import { useWorkFlowStoreWithOut } from '@/store/modules/simpleWorkflow'
import { DICT_TYPE, getIntDictOptions, getDictLabel } from '@/utils/dict'
import { defaultProps, handleTree } from '@/utils/tree'
import * as RoleApi from '@/api/system/role'
import * as DeptApi from '@/api/system/dept'
import * as PostApi from '@/api/system/post'
import * as UserApi from '@/api/system/user'
import * as UserGroupApi from '@/api/bpm/userGroup'
let props = defineProps({
directorMaxLevel: {
type: Number,
default: 0
}
})
const roleOptions = ref<RoleApi.RoleVO[]>([]) //
const postOptions = ref<PostApi.PostVO[]>([]) //
const userOptions = ref<UserApi.UserVO[]>([]) //
const deptTreeOptions = ref() //
const userGroupOptions = ref<UserGroupApi.UserGroupVO[]>([]) //
const candidateConfig = ref({
candidateStrategy: undefined,
candidateParam: [],
approveMethod: undefined
})
let approverConfig = ref({})
let approverVisible = ref(false)
let approverRoleVisible = ref(false)
let checkedRoleList = ref([])
let checkedList = ref([])
let store = useWorkFlowStoreWithOut()
let { setApproverConfig, setApprover, setUserTaskConfig } = store
let approverConfig1 = computed(() => store.approverConfig1)
let approverDrawer = computed(() => store.approverDrawer)
const userTaskConfig = computed(() => store.userTaskConfig)
let visible = computed({
get() {
return approverDrawer.value
},
set() {
closeDrawer()
}
})
watch(userTaskConfig, (val) => {
if (val.value.attributes) {
candidateConfig.value = val.value.attributes
}
})
watch(approverConfig1, (val) => {
approverConfig.value = val.value
})
let changeRange = () => {
approverConfig.value.nodeUserList = []
}
const changeType = (val) => {
approverConfig.value.nodeUserList = []
approverConfig.value.examineMode = 1
approverConfig.value.noHanderAction = 2
if (val == 2) {
approverConfig.value.directorLevel = 1
} else if (val == 4) {
approverConfig.value.selectMode = 1
approverConfig.value.selectRange = 1
} else if (val == 7) {
approverConfig.value.examineEndDirectorLevel = 1
}
}
const addApprover = () => {
approverVisible.value = true
checkedList.value = approverConfig.value.nodeUserList
}
const addRoleApprover = () => {
approverRoleVisible.value = true
checkedRoleList.value = approverConfig.value.nodeUserList
}
const sureApprover = (data) => {
approverConfig.value.nodeUserList = data
approverVisible.value = false
}
const sureRoleApprover = (data) => {
approverConfig.value.nodeUserList = data
approverRoleVisible.value = false
}
const saveApprover = () => {
approverConfig.value.error = !setApproverStr(approverConfig.value)
setApproverConfig({
value: approverConfig.value,
flag: true,
id: approverConfig1.value.id
})
closeDrawer()
}
const saveConfig = () => {
console.log('before userTaskConfig is ', userTaskConfig.value.id)
const rawConfig = toRaw(userTaskConfig.value)
rawConfig.value.attributes = toRaw(candidateConfig.value)
rawConfig.flag = true
// TODO
// setApproverConfig({
// value: approverConfig.value,
// flag: true,
// id: approverConfig1.value.id
// })
const showText = getApproverShowText()
setUserTaskConfig({
value: rawConfig.value,
flag: true,
id: userTaskConfig.value.id,
showText
})
console.log('after is userTaskConfig', userTaskConfig.value)
closeDrawer()
}
const getApproverShowText = () => {
let appoveMethodText = ''
approveMethods.forEach((item) => {
if (item.value == candidateConfig.value.approveMethod) {
appoveMethodText = item.label
}
})
const strategyText = getDictLabel(
DICT_TYPE.BPM_TASK_CANDIDATE_STRATEGY,
candidateConfig.value.candidateStrategy
)
return `审批方式:${appoveMethodText} <br/>
审批人规则类型按${strategyText}`
}
const closeDrawer = () => {
setApprover(false)
}
const changecandidateStrategy = () => {
candidateConfig.value.candidateParam = []
}
onMounted(async () => {
//
roleOptions.value = await RoleApi.getSimpleRoleList()
postOptions.value = await PostApi.getSimplePostList()
//
userOptions.value = await UserApi.getSimpleUserList()
//
const deptOptions = await DeptApi.getSimpleDeptList()
deptTreeOptions.value = handleTree(deptOptions, 'id')
//
userGroupOptions.value = await UserGroupApi.getUserGroupSimpleList()
})
</script>
<style lang="scss" scoped>
.user-task-header {
font-size: 16px !important;
}
</style>

View File

@ -6,40 +6,48 @@
* @FilePath: /Workflow-Vue3/src/components/nodeWrap.vue
-->
<template>
<div class="node-wrap" v-if="nodeConfig.type < 3">
<div class="node-wrap-box" :class="(nodeConfig.type == 0 ? 'start-node ' : '') +(isTried && nodeConfig.error ? 'active error' : '')">
<div class="title" :style="`background: rgb(${bgColors[nodeConfig.type]});`">
<span v-if="nodeConfig.type == 0">{{ nodeConfig.nodeName }}</span>
<template v-else>
<span class="iconfont">{{nodeConfig.type == 1?'':''}}</span>
<input
v-if="isInput"
type="text"
class="ant-input editable-title-input"
@blur="blurEvent()"
@focus="$event.currentTarget.select()"
v-focus
v-model="nodeConfig.nodeName"
:placeholder="defaultText"
/>
<span v-else class="editable-title" @click="clickEvent()">{{ nodeConfig.nodeName }}</span>
<i class="anticon anticon-close close" @click="delNode"></i>
</template>
</div>
<div class="content" @click="setPerson">
<div class="text">
<span class="placeholder" v-if="!showText">请选择{{defaultText}}</span>
{{showText}}
</div>
<i class="anticon anticon-right arrow"></i>
</div>
<div class="error_tip" v-if="isTried && nodeConfig.error">
<i class="anticon anticon-exclamation-circle"></i>
</div>
<div class="node-wrap" v-if="nodeConfig.type < 3">
<div
class="node-wrap-box"
:class="
(nodeConfig.type == 0 ? 'start-node ' : '') +
(isTried && nodeConfig.error ? 'active error' : '')
"
>
<div class="title" :style="`background: rgb(${bgColors[nodeConfig.type]});`">
<span v-if="nodeConfig.type == 0">{{ nodeConfig.name }}</span>
<template v-else>
<span class="iconfont">{{ nodeConfig.type == 1 ? '' : '' }}</span>
<input
v-if="isInput"
type="text"
class="ant-input editable-title-input"
@blur="blurEvent()"
@focus="$event.currentTarget.select()"
v-model="nodeConfig.name"
:placeholder="defaultText"
/>
<span v-else class="editable-title" @click="clickEvent()">{{ nodeConfig.name }}</span>
<i class="anticon anticon-close close" @click="delNode"></i>
</template>
</div>
<div class="content" @click="setPerson">
<div class="text">
<span class="placeholder" v-if="!showText">请选择{{ defaultText }}</span>
<span v-html="showText" class="ellipsis-text" v-else></span>
</div>
<div class="icon-box">
<i class="anticon anticon-edit" @click="editNode"></i>
</div>
<i class="anticon anticon-right arrow"></i>
</div>
<div class="error_tip" v-if="isTried && nodeConfig.error">
<i class="anticon anticon-exclamation-circle"></i>
</div>
<addNode v-model:childNodeP="nodeConfig.childNode" />
</div>
<div class="branch-wrap" v-if="nodeConfig.type == 4">
<addNode v-model:childNodeP="nodeConfig.childNode" />
</div>
<div class="branch-wrap" v-if="nodeConfig.type == 4">
<div class="branch-box-wrap">
<div class="branch-box">
<button class="add-branch" @click="addTerm">添加条件</button>
@ -55,16 +63,27 @@
class="ant-input editable-title-input"
@blur="blurEvent(index)"
@focus="$event.currentTarget.select()"
v-model="item.nodeName"
v-model="item.name"
/>
<span v-else class="editable-title" @click="clickEvent(index)">{{ item.nodeName }}</span>
<span class="priority-title" @click="setPerson(item.priorityLevel)">优先级{{ item.priorityLevel }}</span>
<span v-else class="editable-title" @click="clickEvent(index)">{{
item.name
}}</span>
<span class="priority-title" @click="setPerson(item.priorityLevel)"
>优先级{{ item.priorityLevel }}</span
>
<i class="anticon anticon-close close" @click="delTerm(index)"></i>
</div>
<div class="sort-right" v-if="index != nodeConfig.conditionNodes.length - 1" @click="arrTransfer(index)">&gt;</div>
<div class="content" @click="setPerson(item.priorityLevel)">{{ conditionStr(nodeConfig, index) }}</div>
<div
class="sort-right"
v-if="index != nodeConfig.conditionNodes.length - 1"
@click="arrTransfer(index)"
>&gt;</div
>
<div class="content" @click="setPerson(item.priorityLevel)">{{
conditionStr(nodeConfig, index)
}}</div>
<div class="error_tip" v-if="isTried && item.error">
<i class="anticon anticon-exclamation-circle"></i>
<i class="anticon anticon-exclamation-circle"></i>
</div>
</div>
<addNode v-model:childNodeP="item.childNode" />
@ -84,9 +103,9 @@
<addNode v-model:childNodeP="nodeConfig.childNode" />
</div>
</div>
<nodeWrap v-if="nodeConfig.childNode" v-model:nodeConfig="nodeConfig.childNode" />
<nodeWrap v-if="nodeConfig.childNode" v-model:nodeConfig="nodeConfig.childNode" />
</template>
<script setup>
<script lang="ts" setup>
import addNode from './addNode.vue'
import { onMounted, ref, watch, getCurrentInstance, computed } from 'vue'
import {
@ -95,7 +114,8 @@ import {
setApproverStr,
copyerStr,
bgColors,
placeholderList
placeholderList,
getApproverShowText
} from './util'
import { useWorkFlowStoreWithOut } from '@/store/modules/simpleWorkflow'
let _uid = getCurrentInstance().uid
@ -115,11 +135,6 @@ let props = defineProps({
let defaultText = computed(() => {
return placeholderList[props.nodeConfig.type]
})
let showText = computed(() => {
if (props.nodeConfig.type == 0) return arrToStr(props.flowPermission) || '所有人'
if (props.nodeConfig.type == 1) return setApproverStr(props.nodeConfig)
return copyerStr(props.nodeConfig)
})
let isInputList = ref([])
let isInput = ref(false)
@ -152,13 +167,32 @@ let {
setFlowPermission,
setApproverConfig,
setCopyerConfig,
setConditionsConfig
setConditionsConfig,
setUserTaskConfig
} = store
//
const userTaskConfig = computed(() => store.userTaskConfig)
let isTried = computed(() => store.isTried)
let flowPermission1 = computed(() => store.flowPermission1)
let approverConfig1 = computed(() => store.approverConfig1)
let copyerConfig1 = computed(() => store.copyerConfig1)
let conditionsConfig1 = computed(() => store.conditionsConfig1)
const showText = computed(() => {
if (props.nodeConfig.type == 0) return '发起人'
if (props.nodeConfig.type == 1) {
if(props.nodeConfig.attributes){
return getApproverShowText(props.nodeConfig.attributes.approveMethod, props.nodeConfig.attributes.candidateStrategy)
} else {
return ''
}
}
return copyerStr(props.nodeConfig)
})
watch(userTaskConfig, (approver) => {
if (approver.flag && approver.id === _uid) {
emits('update:nodeConfig', approver.value)
}
})
watch(flowPermission1, (flow) => {
if (flow.flag && flow.id === _uid) {
emits('update:flowPermission', flow.value)
@ -191,12 +225,12 @@ const blurEvent = (index) => {
if (index || index === 0) {
isInputList.value[index] = false
// eslint-disable-next-line vue/no-mutating-props
props.nodeConfig.conditionNodes[index].nodeName =
props.nodeConfig.conditionNodes[index].nodeName || '条件'
props.nodeConfig.conditionNodes[index].name =
props.nodeConfig.conditionNodes[index].name || '条件'
} else {
isInput.value = false
// eslint-disable-next-line vue/no-mutating-props
props.nodeConfig.nodeName = props.nodeConfig.nodeName || defaultText
props.nodeConfig.name = props.nodeConfig.name || defaultText
}
}
const delNode = () => {
@ -206,7 +240,7 @@ const addTerm = () => {
let len = props.nodeConfig.conditionNodes.length + 1
// eslint-disable-next-line vue/no-mutating-props
props.nodeConfig.conditionNodes.push({
nodeName: '条件' + len,
name: '条件' + len,
type: 3,
priorityLevel: len,
conditionList: [],
@ -221,7 +255,7 @@ const delTerm = (index) => {
props.nodeConfig.conditionNodes.splice(index, 1)
props.nodeConfig.conditionNodes.map((item, index) => {
item.priorityLevel = index + 1
item.nodeName = `条件${index + 1}`
item.name = `条件${index + 1}`
})
resetConditionNodesErr()
emits('update:nodeConfig', props.nodeConfig)
@ -255,13 +289,17 @@ const setPerson = (priorityLevel) => {
})
} else if (type == 1) {
setApprover(true)
setApproverConfig({
let showText = undefined;
if(_uid === userTaskConfig.value.id) {
showText = userTaskConfig.value.showText
}
setUserTaskConfig({
value: {
...JSON.parse(JSON.stringify(props.nodeConfig)),
...{ settype: props.nodeConfig.settype ? props.nodeConfig.settype : 1 }
...JSON.parse(JSON.stringify(props.nodeConfig))
},
flag: false,
id: _uid
id: _uid,
showText
})
} else if (type == 2) {
setCopyer(true)
@ -295,3 +333,10 @@ const arrTransfer = (index, type = 1) => {
emits('update:nodeConfig', props.nodeConfig)
}
</script>
<style lang="scss" scoped>
.ellipsis-text {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
</style>

View File

@ -1,3 +1,6 @@
// @ts-ignore
import { DictDataVO } from '@/api/system/dict/types'
import { DICT_TYPE, getDictLabel } from '@/utils/dict'
/**
* todo
*/
@ -51,6 +54,32 @@ export const setApproverStr = (nodeConfig: any) => {
}
}
export const approveMethods: DictDataVO [] = [
{ label: '单人审批', value: 1 },
{ label: '多人审批(所有人审批通过)', value: 2 }
// TODO 更多的类型
];
export const getApproverShowText = (approveMethod :number, candidateStrategy: number) => {
if(approveMethod && candidateStrategy) {
let appoveMethodText = ''
approveMethods.forEach((item) => {
if (item.value == approveMethod) {
appoveMethodText = item.label
}
})
const strategyText = getDictLabel(
DICT_TYPE.BPM_TASK_CANDIDATE_STRATEGY,
candidateStrategy
)
return `审批方式:${appoveMethodText} <br/>
${strategyText}`
} else {
return ''
}
}
export const copyerStr = (nodeConfig: any) => {
if (nodeConfig.nodeUserList.length != 0) {
return arrToStr(nodeConfig.nodeUserList)

View File

@ -14,7 +14,8 @@ export const useWorkFlowStore = defineStore('simpleWorkflow', {
conditionDrawer: false,
conditionsConfig1: {
conditionNodes: []
}
},
userTaskConfig: {}
}),
actions: {
setTableId(payload) {
@ -46,6 +47,9 @@ export const useWorkFlowStore = defineStore('simpleWorkflow', {
},
setConditionsConfig(payload) {
this.conditionsConfig1 = payload
},
setUserTaskConfig(payload) {
this.userTaskConfig = payload
}
}
})

View File

@ -1,6 +1,13 @@
<template>
<div>
<section class="dingflow-design">
<el-row>
<el-col :span="20"/>
<el-col :span="4">
<el-button type="primary" size="small" @click="test">测试</el-button>
</el-col>
</el-row>
<div class="box-scale">
<nodeWrap v-model:nodeConfig="nodeConfig" />
<div class="end-node">
@ -10,18 +17,26 @@
</div>
</section>
</div>
<approverDrawer />
</template>
<script lang="ts" setup>
import nodeWrap from '@/components/SimpleProcessDesigner/src/nodeWrap.vue'
import approverDrawer from '@/components/SimpleProcessDesigner/src/drawer/approverDrawer.vue'
import { ref } from 'vue'
defineOptions({ name: 'SimpleWorkflowDesignEditor' })
let nodeConfig = ref({
nodeName: '发起人',
const nodeConfig = ref({
name: '发起人',
type: 0,
id: 'root',
formPerms: {},
nodeUserList: [],
childNode: {}
attributes: {
"candidateStrategy": 70
},
childNode: {},
})
const test = ()=> {
console.log('json format', toRaw(nodeConfig.value))
}
</script>
<style>
@import url('@/components/SimpleProcessDesigner/theme/workflow.css');