From c9a6f7cbb5209415e626df29c7572cf40fe92b66 Mon Sep 17 00:00:00 2001 From: houzhongjian <houzhongyi@126.com> Date: 星期一, 06 一月 2025 11:44:50 +0800 Subject: [PATCH] 1、工作流程功能优化,解决流程图模拟功能simulation不生效的bug 2、system menu等页面修改 3、Footer copyright年份修改为动态 --- src/views/bpm/model/ModelForm.vue | 381 +++++++++++++++++++++++++++++++++++++++++------------ 1 files changed, 291 insertions(+), 90 deletions(-) diff --git a/src/views/bpm/model/ModelForm.vue b/src/views/bpm/model/ModelForm.vue index ce60edc..095b0ac 100644 --- a/src/views/bpm/model/ModelForm.vue +++ b/src/views/bpm/model/ModelForm.vue @@ -8,12 +8,7 @@ label-width="110px" > <el-form-item label="流程标识" prop="key"> - <el-input - v-model="formData.key" - :disabled="!!formData.id" - placeholder="请输入流标标识" - style="width: 330px" - /> + <el-input v-model="formData.key" :disabled="!!formData.id" placeholder="请输入流标标识" /> <el-tooltip v-if="!formData.id" class="item" @@ -35,7 +30,7 @@ placeholder="请输入流程名称" /> </el-form-item> - <el-form-item v-if="formData.id" label="流程分类" prop="category"> + <el-form-item label="流程分类" prop="category"> <el-select v-model="formData.category" clearable @@ -50,120 +45,223 @@ /> </el-select> </el-form-item> - <el-form-item v-if="formData.id" label="流程图标" prop="icon"> - <UploadImg v-model="formData.icon" :limit="1" height="128px" width="128px" /> + <el-form-item label="流程图标" prop="icon"> + <UploadImg v-model="formData.icon" :limit="1" height="64px" width="64px" /> </el-form-item> <el-form-item label="流程描述" prop="description"> <el-input v-model="formData.description" clearable type="textarea" /> </el-form-item> - <div v-if="formData.id"> - <el-form-item label="表单类型" prop="formType"> - <el-radio-group v-model="formData.formType"> - <el-radio - v-for="dict in getIntDictOptions(DICT_TYPE.BPM_MODEL_FORM_TYPE)" - :key="dict.value" - :label="dict.value" - > - {{ dict.label }} - </el-radio> - </el-radio-group> - </el-form-item> - <el-form-item v-if="formData.formType === 10" label="流程表单" prop="formId"> - <el-select v-model="formData.formId" clearable style="width: 100%"> - <el-option - v-for="form in formList" - :key="form.id" - :label="form.name" - :value="form.id" + <el-form-item label="流程类型" prop="type"> + <el-radio-group v-model="formData.type"> + <el-radio + v-for="dict in getIntDictOptions(DICT_TYPE.BPM_MODEL_TYPE)" + :key="dict.value" + :label="dict.value" + > + {{ dict.label }} + </el-radio> + </el-radio-group> + </el-form-item> + <el-form-item label="表单类型" prop="formType"> + <el-radio-group v-model="formData.formType"> + <el-radio + v-for="dict in getIntDictOptions(DICT_TYPE.BPM_MODEL_FORM_TYPE)" + :key="dict.value" + :label="dict.value" + > + {{ dict.label }} + </el-radio> + </el-radio-group> + </el-form-item> + <el-form-item v-if="formData.formType === 10" label="流程表单" prop="formId"> + <el-select v-model="formData.formId" clearable style="width: 100%"> + <el-option v-for="form in formList" :key="form.id" :label="form.name" :value="form.id" /> + </el-select> + </el-form-item> + <el-form-item + v-if="formData.formType === 20" + label="表单提交路由" + prop="formCustomCreatePath" + > + <el-input + v-model="formData.formCustomCreatePath" + placeholder="请输入表单提交路由" + style="width: 330px" + /> + <el-tooltip + class="item" + content="自定义表单的提交路径,使用 Vue 的路由地址,例如说:bpm/oa/leave/create.vue" + effect="light" + placement="top" + > + <i class="el-icon-question" style="padding-left: 5px"></i> + </el-tooltip> + </el-form-item> + <el-form-item v-if="formData.formType === 20" label="表单查看地址" prop="formCustomViewPath"> + <el-input + v-model="formData.formCustomViewPath" + placeholder="请输入表单查看的组件地址" + style="width: 330px" + /> + <el-tooltip + class="item" + content="自定义表单的查看组件地址,使用 Vue 的组件地址,例如说:bpm/oa/leave/detail.vue" + effect="light" + placement="top" + > + <i class="el-icon-question" style="padding-left: 5px"></i> + </el-tooltip> + </el-form-item> + <el-form-item label="是否可见" prop="visible"> + <el-radio-group v-model="formData.visible"> + <el-radio + v-for="dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)" + :key="dict.value as string" + :label="dict.value" + > + {{ dict.label }} + </el-radio> + </el-radio-group> + </el-form-item> + <el-form-item label="谁可以发起" prop="startUserType"> + <el-select + v-model="formData.startUserType" + placeholder="请选择谁可以发起" + @change="handleStartUserTypeChange" + > + <el-option label="全员" :value="0" /> + <el-option label="指定人员" :value="1" /> + <el-option label="均不可提交" :value="2" /> + </el-select> + <div v-if="formData.startUserType === 1" class="mt-2 flex flex-wrap gap-2"> + <div + v-for="user in selectedStartUsers" + :key="user.id" + class="bg-gray-100 h-35px rounded-3xl flex items-center pr-8px dark:color-gray-600 position-relative" + > + <el-avatar class="!m-5px" :size="28" v-if="user.avatar" :src="user.avatar" /> + <el-avatar class="!m-5px" :size="28" v-else> + {{ user.nickname.substring(0, 1) }} + </el-avatar> + {{ user.nickname }} + <Icon + icon="ep:close" + class="ml-2 cursor-pointer hover:text-red-500" + @click="handleRemoveStartUser(user)" /> - </el-select> - </el-form-item> - <el-form-item - v-if="formData.formType === 20" - label="表单提交路由" - prop="formCustomCreatePath" + </div> + <el-button type="primary" link @click="openStartUserSelect"> + <Icon icon="ep:plus" />选择人员 + </el-button> + </div> + </el-form-item> + <el-form-item label="流程管理员" prop="managerUserType"> + <el-select + v-model="formData.managerUserType" + placeholder="请选择流程管理员" + @change="handleManagerUserTypeChange" > - <el-input - v-model="formData.formCustomCreatePath" - placeholder="请输入表单提交路由" - style="width: 330px" - /> - <el-tooltip - class="item" - content="自定义表单的提交路径,使用 Vue 的路由地址,例如说:bpm/oa/leave/create" - effect="light" - placement="top" + <el-option label="全员" :value="0" /> + <el-option label="指定人员" :value="1" /> + <el-option label="均不可提交" :value="2" /> + </el-select> + <div v-if="formData.managerUserType === 1" class="mt-2 flex flex-wrap gap-2"> + <div + v-for="user in selectedManagerUsers" + :key="user.id" + class="bg-gray-100 h-35px rounded-3xl flex items-center pr-8px dark:color-gray-600 position-relative" > - <i class="el-icon-question" style="padding-left: 5px"></i> - </el-tooltip> - </el-form-item> - <el-form-item - v-if="formData.formType === 20" - label="表单查看地址" - prop="formCustomViewPath" - > - <el-input - v-model="formData.formCustomViewPath" - placeholder="请输入表单查看的组件地址" - style="width: 330px" - /> - <el-tooltip - class="item" - content="自定义表单的查看组件地址,使用 Vue 的组件地址,例如说:bpm/oa/leave/detail" - effect="light" - placement="top" - > - <i class="el-icon-question" style="padding-left: 5px"></i> - </el-tooltip> - </el-form-item> - </div> + <el-avatar class="!m-5px" :size="28" v-if="user.avatar" :src="user.avatar" /> + <el-avatar class="!m-5px" :size="28" v-else> + {{ user.nickname.substring(0, 1) }} + </el-avatar> + {{ user.nickname }} + <Icon + icon="ep:close" + class="ml-2 cursor-pointer hover:text-red-500" + @click="handleRemoveManagerUser(user)" + /> + </div> + <el-button type="primary" link @click="openManagerUserSelect"> + <Icon icon="ep:plus" />选择人员 + </el-button> + </div> + </el-form-item> </el-form> <template #footer> <el-button :disabled="formLoading" type="primary" @click="submitForm">确 定</el-button> <el-button @click="dialogVisible = false">取 消</el-button> </template> </Dialog> + <UserSelectForm ref="userSelectFormRef" @confirm="handleUserSelectConfirm" /> </template> <script lang="ts" setup> -import { DICT_TYPE, getIntDictOptions } from '@/utils/dict' +import { propTypes } from '@/utils/propTypes' +import { DICT_TYPE, getBoolDictOptions, getIntDictOptions } from '@/utils/dict' import { ElMessageBox } from 'element-plus' import * as ModelApi from '@/api/bpm/model' import * as FormApi from '@/api/bpm/form' -import { CategoryApi } from '@/api/bpm/category' +import { CategoryApi, CategoryVO } from '@/api/bpm/category' +import { BpmModelFormType, BpmModelType } from '@/utils/constants' +import { UserVO } from '@/api/system/user' +import * as UserApi from '@/api/system/user' +import { useUserStoreWithOut } from '@/store/modules/user' +import { FormVO } from '@/api/bpm/form' defineOptions({ name: 'ModelForm' }) const { t } = useI18n() // 国际化 const message = useMessage() // 消息弹窗 - +const userStore = useUserStoreWithOut() // 用户信息缓存 +const props = defineProps({ + categoryId: propTypes.number +}) const dialogVisible = ref(false) // 弹窗的是否展示 const dialogTitle = ref('') // 弹窗的标题 const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 const formType = ref('') // 表单的类型:create - 新增;update - 修改 -const formData = ref({ - formType: 10, +const formData: any = ref({ + id: undefined, name: '', + key: '', category: undefined, icon: undefined, description: '', + type: BpmModelType.BPMN, + formType: BpmModelFormType.NORMAL, formId: '', formCustomCreatePath: '', - formCustomViewPath: '' + formCustomViewPath: '', + visible: true, + startUserType: undefined, + managerUserType: undefined, + startUserIds: [], + managerUserIds: [] }) const formRules = reactive({ - name: [{ required: true, message: '参数名称不能为空', trigger: 'blur' }], - key: [{ required: true, message: '参数键名不能为空', trigger: 'blur' }], - category: [{ required: true, message: '参数分类不能为空', trigger: 'blur' }], - icon: [{ required: true, message: '参数图标不能为空', trigger: 'blur' }], - value: [{ required: true, message: '参数键值不能为空', trigger: 'blur' }], - visible: [{ required: true, message: '是否可见不能为空', trigger: 'blur' }] + name: [{ required: true, message: '流程名称不能为空', trigger: 'blur' }], + key: [{ required: true, message: '流程标识不能为空', trigger: 'blur' }], + category: [{ required: true, message: '流程分类不能为空', trigger: 'blur' }], + icon: [{ required: true, message: '流程图标不能为空', trigger: 'blur' }], + type: [{ required: true, message: '是否可见不能为空', trigger: 'blur' }], + formType: [{ required: true, message: '是否可见不能为空', trigger: 'blur' }], + formId: [{ required: true, message: '流程表单不能为空', trigger: 'blur' }], + formCustomCreatePath: [{ required: true, message: '表单提交路由不能为空', trigger: 'blur' }], + formCustomViewPath: [{ required: true, message: '表单查看地址不能为空', trigger: 'blur' }], + visible: [{ required: true, message: '是否可见不能为空', trigger: 'blur' }], + managerUserIds: [{ required: true, message: '流程管理员不能为空', trigger: 'blur' }] }) const formRef = ref() // 表单 Ref -const formList = ref([]) // 流程表单的下拉框的数据 -const categoryList = ref([]) // 流程分类列表 +const formList = ref<FormVO[]>([]) // 流程表单的下拉框的数据 +const categoryList = ref<CategoryVO[]>([]) // 流程分类列表 +const userList = ref<UserVO[]>([]) // 用户列表 +const selectedStartUsers = ref<UserVO[]>([]) // 已选择的发起人列表 +const selectedManagerUsers = ref<UserVO[]>([]) // 已选择的管理员列表 +const userSelectFormRef = ref() // 用户选择弹窗 ref +const currentSelectType = ref<'start' | 'manager'>('start') // 当前选择的是发起人还是管理员 /** 打开弹窗 */ -const open = async (type: string, id?: number) => { +const open = async (type: string, id?: string) => { dialogVisible.value = true dialogTitle.value = t('action.' + type) formType.value = type @@ -176,11 +274,31 @@ } finally { formLoading.value = false } + // 加载数据时,根据已有的用户ID列表初始化已选用户 + if (formData.value.startUserIds?.length) { + formData.value.startUserType = 1 + selectedStartUsers.value = userList.value.filter((user) => + formData.value.startUserIds.includes(user.id) + ) + } + if (formData.value.managerUserIds?.length) { + formData.value.managerUserType = 1 + selectedManagerUsers.value = userList.value.filter((user) => + formData.value.managerUserIds.includes(user.id) + ) + } + } else { + formData.value.managerUserIds.push(userStore.getUser.id) } // 获得流程表单的下拉框的数据 formList.value = await FormApi.getFormSimpleList() // 查询流程分类列表 categoryList.value = await CategoryApi.getCategorySimpleList() + // 查询用户列表 + userList.value = await UserApi.getSimpleUserList() + if (props.categoryId) { + formData.value.category = props.categoryId + } } defineExpose({ open }) // 提供 open 方法,用于打开弹窗 @@ -199,11 +317,10 @@ await ModelApi.createModel(data) // 提示,引导用户做后续的操作 await ElMessageBox.alert( - '<strong>新建模型成功!</strong>后续需要执行如下 3 个步骤:' + - '<div>1. 点击【修改流程】按钮,配置流程的分类、表单信息</div>' + - '<div>2. 点击【设计流程】按钮,绘制流程图</div>' + - '<div>3. 点击【发布流程】按钮,完成流程的最终发布</div>' + - '另外,每次流程修改后,都需要点击【发布流程】按钮,才能正式生效!!!', + '<strong>新建模型成功!</strong>后续需要执行如下 2 个步骤:' + + '<div>1. 点击【设计流程】按钮,绘制流程图</div>' + + '<div>2. 点击【发布流程】按钮,完成流程的最终发布</div>' + + '另外,每次流程修改后,都需要点击【发布流程】按钮,才能正式生效!!!', '重要提示', { dangerouslyUseHTMLString: true, @@ -225,15 +342,99 @@ /** 重置表单 */ const resetForm = () => { formData.value = { - formType: 10, + id: undefined, name: '', + key: '', category: undefined, - icon: '', + icon: undefined, description: '', + type: BpmModelType.BPMN, + formType: BpmModelFormType.NORMAL, formId: '', formCustomCreatePath: '', - formCustomViewPath: '' + formCustomViewPath: '', + visible: true, + startUserType: undefined, + managerUserType: undefined, + startUserIds: [], + managerUserIds: [] } formRef.value?.resetFields() + selectedStartUsers.value = [] + selectedManagerUsers.value = [] +} + +/** 处理发起人类型变化 */ +const handleStartUserTypeChange = (value: number) => { + if (value !== 1) { + selectedStartUsers.value = [] + formData.value.startUserIds = [] + } +} + +/** 处理管理员类型变化 */ +const handleManagerUserTypeChange = (value: number) => { + if (value !== 1) { + selectedManagerUsers.value = [] + formData.value.managerUserIds = [] + } +} + +/** 打开发起人选择 */ +const openStartUserSelect = () => { + currentSelectType.value = 'start' + userSelectFormRef.value.open(0, selectedStartUsers.value) +} + +/** 打开管理员选择 */ +const openManagerUserSelect = () => { + currentSelectType.value = 'manager' + userSelectFormRef.value.open(0, selectedManagerUsers.value) +} + +/** 处理用户选择确认 */ +const handleUserSelectConfirm = (_, users: UserVO[]) => { + if (currentSelectType.value === 'start') { + selectedStartUsers.value = users + formData.value.startUserIds = users.map((u) => u.id) + } else { + selectedManagerUsers.value = users + formData.value.managerUserIds = users.map((u) => u.id) + } +} + +/** 移除发起人 */ +const handleRemoveStartUser = (user: UserVO) => { + selectedStartUsers.value = selectedStartUsers.value.filter((u) => u.id !== user.id) + formData.value.startUserIds = formData.value.startUserIds.filter((id: number) => id !== user.id) +} + +/** 移除管理员 */ +const handleRemoveManagerUser = (user: UserVO) => { + selectedManagerUsers.value = selectedManagerUsers.value.filter((u) => u.id !== user.id) + formData.value.managerUserIds = formData.value.managerUserIds.filter( + (id: number) => id !== user.id + ) } </script> + +<style lang="scss" scoped> +.bg-gray-100 { + background-color: #f5f7fa; + transition: all 0.3s; + + &:hover { + background-color: #e6e8eb; + } + + .ep-close { + font-size: 14px; + color: #909399; + transition: color 0.3s; + + &:hover { + color: #f56c6c; + } + } +} +</style> -- Gitblit v1.9.3