Merge pull request #139 from minivv/master

【Simple设计器】流程模型->基本信息->谁可以发起,支持指定多个部门
This commit is contained in:
芋道源码 2025-03-23 17:02:52 +08:00 committed by GitHub
commit c3ad1ec30a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 279 additions and 22 deletions

View File

@ -0,0 +1,120 @@
<template>
<Dialog v-model="dialogVisible" title="部门选择" width="600">
<el-row v-loading="formLoading">
<el-col :span="24">
<ContentWrap class="h-1/1">
<el-tree
ref="treeRef"
:data="deptTree"
:props="defaultProps"
show-checkbox
:check-strictly="checkStrictly"
check-on-click-node
default-expand-all
highlight-current
node-key="id"
@check="handleCheck"
/>
</ContentWrap>
</el-col>
</el-row>
<template #footer>
<el-button
:disabled="formLoading || !selectedDeptIds?.length"
type="primary"
@click="submitForm"
>
</el-button>
<el-button @click="dialogVisible = false"> </el-button>
</template>
</Dialog>
</template>
<script lang="ts" setup>
import { defaultProps, handleTree } from '@/utils/tree'
import * as DeptApi from '@/api/system/dept'
defineOptions({ name: 'DeptSelectForm' })
const emit = defineEmits<{
confirm: [deptList: any[]]
}>()
const { t } = useI18n() //
const message = useMessage() //
const props = defineProps({
//
checkStrictly: {
type: Boolean,
default: false
},
//
multiple: {
type: Boolean,
default: true
}
})
const treeRef = ref()
const deptTree = ref<Tree[]>([]) //
const selectedDeptIds = ref<number[]>([]) // ID
const dialogVisible = ref(false) //
const formLoading = ref(false) //
/** 打开弹窗 */
const open = async (selectedList?: DeptApi.DeptVO[]) => {
resetForm()
formLoading.value = true
try {
//
const deptData = await DeptApi.getSimpleDeptList()
deptTree.value = handleTree(deptData)
} finally {
formLoading.value = false
}
dialogVisible.value = true
//
if (selectedList?.length) {
await nextTick()
const selectedIds = selectedList.map(dept => dept.id).filter((id): id is number => id !== undefined)
selectedDeptIds.value = selectedIds
treeRef.value?.setCheckedKeys(selectedIds)
}
}
/** 处理选中状态变化 */
const handleCheck = (data: any, checked: any) => {
selectedDeptIds.value = treeRef.value.getCheckedKeys()
if (!props.multiple && selectedDeptIds.value.length > 1) {
//
const lastSelectedId = selectedDeptIds.value[selectedDeptIds.value.length - 1]
selectedDeptIds.value = [lastSelectedId]
treeRef.value.setCheckedKeys([lastSelectedId])
}
}
/** 提交选择 */
const submitForm = async () => {
try {
//
const checkedNodes = treeRef.value.getCheckedNodes()
message.success(t('common.updateSuccess'))
dialogVisible.value = false
emit('confirm', checkedNodes)
} finally {
}
}
/** 重置表单 */
const resetForm = () => {
deptTree.value = []
selectedDeptIds.value = []
if (treeRef.value) {
treeRef.value.setCheckedKeys([])
}
}
defineExpose({ open }) // open
</script>

View File

@ -59,6 +59,11 @@ const props = defineProps({
startUserIds: {
type: Array,
required: false
},
//
startDeptIds: {
type: Array,
required: false
}
})
@ -82,6 +87,7 @@ provide('deptList', deptOptions)
provide('userGroupList', userGroupOptions)
provide('deptTree', deptTreeOptions)
provide('startUserIds', props.startUserIds)
provide('startDeptIds', props.startDeptIds)
provide('tasks', [])
provide('processInstance', {})
const message = useMessage() //

View File

@ -25,21 +25,38 @@
</template>
<el-tabs type="border-card" v-model="activeTabName">
<el-tab-pane label="权限" name="user">
<el-text v-if="!startUserIds || startUserIds.length === 0"> 全部成员可以发起流程 </el-text>
<el-text v-else-if="startUserIds.length == 1">
{{ getUserNicknames(startUserIds) }} 可发起流程
</el-text>
<el-text v-else>
<el-tooltip
class="box-item"
effect="dark"
placement="top"
:content="getUserNicknames(startUserIds)"
>
{{ getUserNicknames(startUserIds.slice(0, 2)) }}
{{ startUserIds.length }} 人可发起流程
</el-tooltip>
</el-text>
<el-text v-if="(!startUserIds || startUserIds.length === 0) && (!startDeptIds || startDeptIds.length === 0)"> 全部成员可以发起流程 </el-text>
<div v-else-if="startUserIds && startUserIds.length > 0">
<el-text v-if="startUserIds.length == 1">
{{ getUserNicknames(startUserIds) }} 可发起流程
</el-text>
<el-text v-else>
<el-tooltip
class="box-item"
effect="dark"
placement="top"
:content="getUserNicknames(startUserIds)"
>
{{ getUserNicknames(startUserIds.slice(0,2)) }} {{ startUserIds.length }} 人可发起流程
</el-tooltip>
</el-text>
</div>
<div v-else-if="startDeptIds && startDeptIds.length > 0">
<el-text v-if="startDeptIds.length == 1">
{{ getDeptNames(startDeptIds) }} 可发起流程
</el-text>
<el-text v-else>
<el-tooltip
class="box-item"
effect="dark"
placement="top"
:content="getDeptNames(startDeptIds)"
>
{{ getDeptNames(startDeptIds.slice(0,2)) }} {{ startDeptIds.length }} 个部门的人可发起流程
</el-tooltip>
</el-text>
</div>
</el-tab-pane>
<el-tab-pane label="表单字段权限" name="fields" v-if="formType === 10">
<div class="field-setting-pane">
@ -107,6 +124,7 @@
import { SimpleFlowNode, NodeType, FieldPermissionType, START_USER_BUTTON_SETTING } from '../consts'
import { useWatchNode, useDrawer, useNodeName, useFormFieldsPermission } from '../node'
import * as UserApi from '@/api/system/user'
import * as DeptApi from '@/api/system/dept'
defineOptions({
name: 'StartUserNodeConfig'
})
@ -118,8 +136,12 @@ const props = defineProps({
})
//
const startUserIds = inject<Ref<any[]>>('startUserIds')
//
const startDeptIds = inject<Ref<any[]>>('startDeptIds')
//
const userOptions = inject<Ref<UserApi.UserVO[]>>('userList')
//
const deptOptions = inject<Ref<DeptApi.DeptVO[]>>('deptList')
//
const { settingVisible, closeDrawer, openDrawer } = useDrawer()
//
@ -145,6 +167,19 @@ const getUserNicknames = (userIds: number[]): string => {
})
return nicknames.join(',')
}
const getDeptNames = (deptIds: number[]): string => {
if (!deptIds || deptIds.length === 0) {
return ''
}
const deptNames: string[] = []
deptIds.forEach((deptId) => {
const found = deptOptions?.value.find((item) => item.id === deptId)
if (found && found.name) {
deptNames.push(found.name)
}
})
return deptNames.join(',')
}
//
const saveConfig = async () => {
activeTabName.value = 'user'

View File

@ -97,10 +97,23 @@
</el-table-column>
<el-table-column label="可见范围" prop="startUserIds" min-width="150">
<template #default="{ row }">
<el-text v-if="!row.startUsers?.length"> 全部可见 </el-text>
<el-text v-if="!row.startUsers?.length && !row.startDepts?.length"> 全部可见 </el-text>
<el-text v-else-if="row.startUsers.length === 1">
{{ row.startUsers[0].nickname }}
</el-text>
<el-text v-else-if="row.startDepts?.length === 1">
{{ row.startDepts[0].name }}
</el-text>
<el-text v-else-if="row.startDepts?.length > 1">
<el-tooltip
class="box-item"
effect="dark"
placement="top"
:content="row.startDepts.map((dept: any) => dept.name).join('、')"
>
{{ row.startDepts[0].name }} {{ row.startDepts.length }} 个部门可见
</el-tooltip>
</el-text>
<el-text v-else>
<el-tooltip
class="box-item"

View File

@ -77,6 +77,7 @@
>
<el-option label="全员" :value="0" />
<el-option label="指定人员" :value="1" />
<el-option label="指定部门" :value="2" />
</el-select>
<div v-if="modelData.startUserType === 1" class="mt-2 flex flex-wrap gap-2">
<div
@ -99,6 +100,24 @@
<Icon icon="ep:plus" /> 选择人员
</el-button>
</div>
<div v-if="modelData.startUserType === 2" class="mt-2 flex flex-wrap gap-2">
<div
v-for="dept in selectedStartDepts"
:key="dept.id"
class="bg-gray-100 h-35px rounded-3xl flex items-center pr-8px dark:color-gray-600 position-relative"
>
<Icon icon="ep:office-building" class="!m-5px text-20px" />
{{ dept.name }}
<Icon
icon="ep:close"
class="ml-2 cursor-pointer hover:text-red-500"
@click="handleRemoveStartDept(dept)"
/>
</div>
<el-button type="primary" link @click="openStartDeptSelect">
<Icon icon="ep:plus" /> 选择部门
</el-button>
</div>
</el-form-item>
<el-form-item label="流程管理员" prop="managerUserIds" class="mb-20px">
<div class="flex flex-wrap gap-2">
@ -127,11 +146,19 @@
<!-- 用户选择弹窗 -->
<UserSelectForm ref="userSelectFormRef" @confirm="handleUserSelectConfirm" />
<!-- 部门选择弹窗 -->
<DeptSelectForm
ref="deptSelectFormRef"
:multiple="true"
:check-strictly="true"
@confirm="handleDeptSelectConfirm"
/>
</template>
<script lang="ts" setup>
import { DICT_TYPE, getBoolDictOptions, getIntDictOptions } from '@/utils/dict'
import { UserVO } from '@/api/system/user'
import { DeptVO } from '@/api/system/dept'
import { CategoryVO } from '@/api/bpm/category'
const props = defineProps({
@ -142,13 +169,19 @@ const props = defineProps({
userList: {
type: Array,
required: true
},
deptList: {
type: Array,
required: true
}
})
const formRef = ref()
const selectedStartUsers = ref<UserVO[]>([])
const selectedStartDepts = ref<DeptVO[]>([])
const selectedManagerUsers = ref<UserVO[]>([])
const userSelectFormRef = ref()
const deptSelectFormRef = ref()
const currentSelectType = ref<'start' | 'manager'>('start')
const rules = {
@ -174,6 +207,13 @@ watch(
} else {
selectedStartUsers.value = []
}
if (newVal.startDeptIds?.length) {
selectedStartDepts.value = props.deptList.filter((dept: DeptVO) =>
newVal.startDeptIds.includes(dept.id)
) as DeptVO[]
} else {
selectedStartDepts.value = []
}
if (newVal.managerUserIds?.length) {
selectedManagerUsers.value = props.userList.filter((user: UserVO) =>
newVal.managerUserIds.includes(user.id)
@ -193,6 +233,11 @@ const openStartUserSelect = () => {
userSelectFormRef.value.open(0, selectedStartUsers.value)
}
/** 打开部门选择 */
const openStartDeptSelect = () => {
deptSelectFormRef.value.open(selectedStartDepts.value)
}
/** 打开管理员选择 */
const openManagerUserSelect = () => {
currentSelectType.value = 'manager'
@ -214,9 +259,28 @@ const handleUserSelectConfirm = (_, users: UserVO[]) => {
}
}
/** 处理部门选择确认 */
const handleDeptSelectConfirm = (depts: DeptVO[]) => {
modelData.value = {
...modelData.value,
startDeptIds: depts.map((d) => d.id)
}
}
/** 处理发起人类型变化 */
const handleStartUserTypeChange = (value: number) => {
if (value !== 1) {
if (value === 0) {
modelData.value = {
...modelData.value,
startUserIds: [],
startDeptIds: []
}
} else if (value === 1) {
modelData.value = {
...modelData.value,
startDeptIds: []
}
} else if (value === 2) {
modelData.value = {
...modelData.value,
startUserIds: []
@ -232,6 +296,14 @@ const handleRemoveStartUser = (user: UserVO) => {
}
}
/** 移除部门 */
const handleRemoveStartDept = (dept: DeptVO) => {
modelData.value = {
...modelData.value,
startDeptIds: modelData.value.startDeptIds.filter((id: number) => id !== dept.id)
}
}
/** 移除管理员 */
const handleRemoveManagerUser = (user: UserVO) => {
modelData.value = {

View File

@ -18,6 +18,7 @@
:model-key="modelData.key"
:model-name="modelData.name"
:start-user-ids="modelData.startUserIds"
:start-dept-ids="modelData.startDeptIds"
@success="handleDesignSuccess"
/>
</template>

View File

@ -62,6 +62,7 @@
v-model="formData"
:categoryList="categoryList"
:userList="userList"
:deptList="deptList"
ref="basicInfoRef"
/>
</div>
@ -92,6 +93,8 @@ import * as ModelApi from '@/api/bpm/model'
import * as FormApi from '@/api/bpm/form'
import { CategoryApi, CategoryVO } from '@/api/bpm/category'
import * as UserApi from '@/api/system/user'
import * as DeptApi from '@/api/system/dept'
import { useUserStoreWithOut } from '@/store/modules/user'
import * as DefinitionApi from '@/api/bpm/definition'
import { BpmModelFormType, BpmModelType, BpmAutoApproveType } from '@/utils/constants'
import BasicInfo from './BasicInfo.vue'
@ -153,6 +156,7 @@ const formData: any = ref({
visible: true,
startUserType: undefined,
startUserIds: [],
startDeptIds: [],
managerUserIds: [],
allowCancelRunningProcess: true,
processIdRule: {
@ -183,6 +187,7 @@ provide('modelData', formData)
const formList = ref([])
const categoryList = ref<CategoryVO[]>([])
const userList = ref<UserApi.UserVO[]>([])
const deptList = ref<DeptApi.DeptVO[]>([])
/** 初始化数据 */
const actionType = route.params.type as string
@ -200,14 +205,15 @@ const initData = async () => {
data.simpleModel = JSON.parse(data.simpleModel)
}
formData.value = data
formData.value.startUserType = formData.value.startUserIds?.length > 0 ? 1 : 0
formData.value.startUserType = formData.value.startUserIds?.length > 0 ? 1 : formData.value?.startDeptIds?.length > 0 ? 2 : 0
} else if (['update', 'copy'].includes(actionType)) {
// /
const modelId = route.params.id as string
formData.value = await ModelApi.getModel(modelId)
formData.value.startUserType = formData.value.startUserIds?.length > 0 ? 1 : 0
//
if (actionType === 'copy') {
formData.value.startUserType = formData.value.startUserIds?.length > 0 ? 1 : formData.value?.startDeptIds?.length > 0 ? 2 : 0
//
if (route.params.type === 'copy') {
delete formData.value.id
formData.value.name += '副本'
formData.value.key += '_copy'
@ -225,7 +231,9 @@ const initData = async () => {
categoryList.value = await CategoryApi.getCategorySimpleList()
//
userList.value = await UserApi.getSimpleUserList()
//
deptList.value = await DeptApi.getSimpleDeptList()
// currentStep
currentStep.value = 0

View File

@ -6,6 +6,7 @@
:model-name="modelName"
@success="handleSuccess"
:start-user-ids="startUserIds"
:start-dept-ids="startDeptIds"
ref="designerRef"
/>
</ContentWrap>
@ -22,6 +23,7 @@ defineProps<{
modelKey?: string
modelName?: string
startUserIds?: number[]
startDeptIds?: number[]
}>()
const emit = defineEmits(['success'])