From 221918bba28d2384d03c596a68256d7832e4a0e0 Mon Sep 17 00:00:00 2001 From: 潘志宝 <979469083@qq.com> Date: 星期一, 06 一月 2025 13:30:50 +0800 Subject: [PATCH] Merge remote-tracking branch 'origin/master' --- src/views/bpm/model/form/index.vue | 439 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 439 insertions(+), 0 deletions(-) diff --git a/src/views/bpm/model/form/index.vue b/src/views/bpm/model/form/index.vue new file mode 100644 index 0000000..4585fc6 --- /dev/null +++ b/src/views/bpm/model/form/index.vue @@ -0,0 +1,439 @@ +<template> + <ContentWrap> + <div class="mx-auto"> + <!-- 头部导航栏 --> + <div + class="absolute top-0 left-0 right-0 h-50px bg-white border-bottom z-10 flex items-center px-20px" + > + <!-- 左侧标题 --> + <div class="w-200px flex items-center overflow-hidden"> + <Icon icon="ep:arrow-left" class="cursor-pointer flex-shrink-0" @click="handleBack" /> + <span class="ml-10px text-16px truncate" :title="formData.name || '创建流程'"> + {{ formData.name || '创建流程' }} + </span> + </div> + + <!-- 步骤条 --> + <div class="flex-1 flex items-center justify-center h-full"> + <div class="w-400px flex items-center justify-between h-full"> + <div + v-for="(step, index) in steps" + :key="index" + class="flex items-center cursor-pointer mx-15px relative h-full" + :class="[ + currentStep === index + ? 'text-[#3473ff] border-[#3473ff] border-b-2 border-b-solid' + : 'text-gray-500' + ]" + @click="handleStepClick(index)" + > + <div + class="w-28px h-28px rounded-full flex items-center justify-center mr-8px border-2 border-solid text-15px" + :class="[ + currentStep === index + ? 'bg-[#3473ff] text-white border-[#3473ff]' + : 'border-gray-300 bg-white text-gray-500' + ]" + > + {{ index + 1 }} + </div> + <span class="text-16px font-bold whitespace-nowrap">{{ step.title }}</span> + </div> + </div> + </div> + + <!-- 右侧按钮 --> + <div class="w-200px flex items-center justify-end gap-2"> + <el-button v-if="route.params.id" type="success" @click="handleDeploy">发 布</el-button> + <el-button type="primary" @click="handleSave">保 存</el-button> + </div> + </div> + + <!-- 主体内容 --> + <div class="mt-50px"> + <!-- 第一步:基本信息 --> + <div v-if="currentStep === 0" class="mx-auto w-560px"> + <BasicInfo + v-model="formData" + :categoryList="categoryList" + :userList="userList" + ref="basicInfoRef" + /> + </div> + + <!-- 第二步:表单设计 --> + <div v-if="currentStep === 1" class="mx-auto w-560px"> + <FormDesign v-model="formData" :formList="formList" ref="formDesignRef" /> + </div> + + <!-- 第三步:流程设计 --> + <ProcessDesign + v-if="currentStep === 2" + v-model="formData" + ref="processDesignRef" + @success="handleDesignSuccess" + /> + </div> + </div> + </ContentWrap> +</template> + +<script lang="ts" setup> +import { useRoute, useRouter } from 'vue-router' +import { useMessage } from '@/hooks/web/useMessage' +import * as ModelApi from '@/api/bpm/model' +import * as FormApi from '@/api/bpm/form' +import { CategoryApi } from '@/api/bpm/category' +import * as UserApi from '@/api/system/user' +import { useUserStoreWithOut } from '@/store/modules/user' +import { BpmModelFormType, BpmModelType } from '@/utils/constants' +import BasicInfo from './BasicInfo.vue' +import FormDesign from './FormDesign.vue' +import ProcessDesign from './ProcessDesign.vue' +import { useTagsViewStore } from '@/store/modules/tagsView' + +const router = useRouter() +const { delView } = useTagsViewStore() // 视图操作 +const route = useRoute() +const message = useMessage() +const userStore = useUserStoreWithOut() + +// 组件引用 +const basicInfoRef = ref() +const formDesignRef = ref() +const processDesignRef = ref() + +/** 步骤校验函数 */ +const validateBasic = async () => { + await basicInfoRef.value?.validate() +} + +/** 表单设计校验 */ +const validateForm = async () => { + await formDesignRef.value?.validate() +} + +/** 流程设计校验 */ +const validateProcess = async () => { + await processDesignRef.value?.validate() +} + +const currentStep = ref(0) // 步骤控制 +const steps = [ + { title: '基本信息', validator: validateBasic }, + { title: '表单设计', validator: validateForm }, + { title: '流程设计', validator: validateProcess } +] + +// 表单数据 +const formData: any = ref({ + id: undefined, + name: '', + key: '', + category: undefined, + icon: undefined, + description: '', + type: BpmModelType.BPMN, + formType: BpmModelFormType.NORMAL, + formId: '', + formCustomCreatePath: '', + formCustomViewPath: '', + visible: true, + startUserType: undefined, + managerUserType: undefined, + startUserIds: [], + managerUserIds: [] +}) + +// 数据列表 +const formList = ref([]) +const categoryList = ref([]) +const userList = ref<UserApi.UserVO[]>([]) + +/** 初始化数据 */ +const initData = async () => { + const modelId = route.params.id as string + if (modelId) { + // 修改场景 + formData.value = await ModelApi.getModel(modelId) + } else { + // 新增场景 + formData.value.managerUserIds.push(userStore.getUser.id) + } + + // 获取表单列表 + formList.value = await FormApi.getFormSimpleList() + // 获取分类列表 + categoryList.value = await CategoryApi.getCategorySimpleList() + // 获取用户列表 + userList.value = await UserApi.getSimpleUserList() +} + +/** 校验所有步骤数据是否完整 */ +const validateAllSteps = async () => { + try { + // 基本信息校验 + await basicInfoRef.value?.validate() + if (!formData.value.key || !formData.value.name || !formData.value.category) { + currentStep.value = 0 + throw new Error('请完善基本信息') + } + + // 表单设计校验 + await formDesignRef.value?.validate() + if (formData.value.formType === 10 && !formData.value.formId) { + currentStep.value = 1 + throw new Error('请选择流程表单') + } + if ( + formData.value.formType === 20 && + (!formData.value.formCustomCreatePath || !formData.value.formCustomViewPath) + ) { + currentStep.value = 1 + throw new Error('请完善自定义表单信息') + } + + // 流程设计校验 + // 如果已经有流程数据,则不需要重新校验 + if (!formData.value.bpmnXml && !formData.value.simpleModel) { + // 如果当前不在第三步,需要先保存当前步骤数据 + if (currentStep.value !== 2) { + await steps[currentStep.value].validator() + // 切换到第三步 + currentStep.value = 2 + // 等待组件渲染完成 + await nextTick() + } + + // 校验流程设计 + await processDesignRef.value?.validate() + const processData = await processDesignRef.value?.getProcessData() + if (!processData) { + throw new Error('请设计流程') + } + + // 保存流程数据 + if (formData.value.type === BpmModelType.BPMN) { + formData.value.bpmnXml = processData + formData.value.simpleModel = null + } else { + formData.value.bpmnXml = null + formData.value.simpleModel = processData + } + } + + return true + } catch (error) { + throw error + } +} + +/** 保存操作 */ +const handleSave = async () => { + try { + // 保存前校验所有步骤的数据 + await validateAllSteps() + + // 更新表单数据 + const modelData = { + ...formData.value + } + + // 如果当前在第三步,获取最新的流程设计数据 + if (currentStep.value === 2) { + const processData = await processDesignRef.value?.getProcessData() + if (processData) { + if (formData.value.type === BpmModelType.BPMN) { + modelData.bpmnXml = processData + modelData.simpleModel = null + } else { + modelData.bpmnXml = null + modelData.simpleModel = processData + } + } + } + + if (formData.value.id) { + // 修改场景 + await ModelApi.updateModel(modelData) + // 询问是否发布流程 + try { + await message.confirm('修改流程成功,是否发布流程?') + // 用户点击确认,执行发布 + await handleDeploy() + } catch { + // 用户点击取消,停留在当前页面 + } + } else { + // 新增场景 + formData.value.id = await ModelApi.createModel(modelData) + message.success('新增成功') + try { + await message.confirm('创建流程成功,是否继续编辑?') + // 用户点击继续编辑,跳转到编辑页面 + await nextTick() + // 先删除当前页签 + delView(unref(router.currentRoute)) + // 跳转到编辑页面 + await router.push({ + name: 'BpmModelUpdate', + params: { id: formData.value.id } + }) + } catch { + // 先删除当前页签 + delView(unref(router.currentRoute)) + // 用户点击返回列表 + await router.push({ name: 'BpmModel' }) + } + } + } catch (error: any) { + console.error('保存失败:', error) + message.warning(error.message || '请完善所有步骤的必填信息') + } +} + +/** 发布操作 */ +const handleDeploy = async () => { + try { + // 修改场景下直接发布,新增场景下需要先确认 + if (!formData.value.id) { + await message.confirm('是否确认发布该流程?') + } + + // 校验所有步骤 + await validateAllSteps() + + // 更新表单数据 + const modelData = { + ...formData.value + } + + // 如果当前在第三步,获取最新的流程设计数据 + if (currentStep.value === 2) { + const processData = await processDesignRef.value?.getProcessData() + if (processData) { + if (formData.value.type === BpmModelType.BPMN) { + modelData.bpmnXml = processData + modelData.simpleModel = null + } else { + modelData.bpmnXml = null + modelData.simpleModel = processData + } + } + } + + // 先保存所有数据 + if (formData.value.id) { + await ModelApi.updateModel(modelData) + } else { + const result = await ModelApi.createModel(modelData) + formData.value.id = result.id + } + + // 发布 + await ModelApi.deployModel(formData.value.id) + message.success('发布成功') + // 返回列表页 + await router.push({ name: 'BpmModel' }) + } catch (error: any) { + console.error('发布失败:', error) + message.warning(error.message || '发布失败') + } +} + +/** 步骤切换处理 */ +const handleStepClick = async (index: number) => { + try { + // 如果是切换到第三步(流程设计),需要校验key和name + if (index === 2) { + if (!formData.value.key || !formData.value.name) { + message.warning('请先填写流程标识和流程名称') + return + } + } + + // 保存当前步骤的数据 + if (currentStep.value === 2) { + const processData = await processDesignRef.value?.getProcessData() + if (processData) { + if (formData.value.type === BpmModelType.BPMN) { + formData.value.bpmnXml = processData + formData.value.simpleModel = null + } else { + formData.value.bpmnXml = null + formData.value.simpleModel = processData + } + } + } else { + // 只有在向后切换时才进行校验 + if (index > currentStep.value) { + if (typeof steps[currentStep.value].validator === 'function') { + await steps[currentStep.value].validator() + } + } + } + + // 切换步骤 + currentStep.value = index + + // 如果切换到流程设计步骤,等待组件渲染完成后刷新设计器 + if (index === 2) { + await nextTick() + // 等待更长时间确保组件完全初始化 + await new Promise(resolve => setTimeout(resolve, 200)) + if (processDesignRef.value?.refresh) { + await processDesignRef.value.refresh() + } + } + } catch (error) { + console.error('步骤切换失败:', error) + message.warning('请先完善当前步骤必填信息') + } +} + +/** 处理设计器保存成功 */ +const handleDesignSuccess = (bpmnXml?: string) => { + if (bpmnXml) { + formData.value.bpmnXml = bpmnXml + } +} + +/** 返回列表页 */ +const handleBack = () => { + // 先删除当前页签 + delView(unref(router.currentRoute)) + // 跳转到列表页 + router.push({ name: 'BpmModel' }) +} + +/** 初始化 */ +onMounted(async () => { + await initData() +}) + +// 添加组件卸载前的清理代码 +onBeforeUnmount(() => { + // 清理所有的引用 + basicInfoRef.value = null + formDesignRef.value = null + processDesignRef.value = null +}) +</script> + +<style lang="scss" scoped> +.border-bottom { + border-bottom: 1px solid #dcdfe6; +} + +.text-primary { + color: #3473ff; +} + +.bg-primary { + background-color: #3473ff; +} + +.border-primary { + border-color: #3473ff; +} +</style> -- Gitblit v1.9.3