ruoyi-nbcio-plus基于vue3的flowable的支持自定义业务流程处理页面detail.vue的升级修改

本文主要是介绍ruoyi-nbcio-plus基于vue3的flowable的支持自定义业务流程处理页面detail.vue的升级修改,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

更多ruoyi-nbcio功能请看演示系统

gitee源代码地址

前后端代码: https://gitee.com/nbacheng/ruoyi-nbcio

演示地址:RuoYi-Nbcio后台管理系统 http://122.227.135.243:9666/

更多nbcio-boot功能请看演示系统 

gitee源代码地址

后端代码: https://gitee.com/nbacheng/nbcio-boot

前端代码:https://gitee.com/nbacheng/nbcio-vue.git

在线演示(包括H5) : http://122.227.135.243:9888

1、因为修改vue3后也要支持自定义表单额显示处理,所以修改如下:

<template><div class="app-container"><el-tabs tab-position="top" :model-value="processed === true ? 'approval' : 'form'"><el-tab-pane label="任务办理" name="approval" v-if="processed === true"><el-card class="box-card" shadow="hover" v-if="taskFormOpen"><template #header><span>填写表单</span></template><div class="cu-content"><v-form-render :form-json="{}" :form-data="{}" ref="vfRenderRef" /></div></el-card><el-card class="box-card" shadow="hover"><template #header><span>审批流程</span></template><el-row><el-col :span="20" :offset="2"><el-form ref="taskFormRef" :model="taskForm" :rules="rules" label-width="120px"><el-form-item label="审批意见" prop="comment"><el-input type="textarea" :rows="5" v-model="taskForm.comment" placeholder="请输入 审批意见" /></el-form-item><el-form-item label="抄送人" prop="copyUserIds"><el-tag :key="index" v-for="(item, index) in copyUser" closable :disable-transitions="false" @close="handleClose('copy', item)">{{ item.nickName }}</el-tag><el-button class="button-new-tag" type="primary" icon="el-icon-plus" circle @click="onSelectCopyUsers" /></el-form-item><el-form-item label="指定审批人" prop="copyUserIds"><el-tag :key="index" v-for="(item, index) in nextUser" closable :disable-transitions="false" @close="handleClose('next', item)">{{ item.nickName }}</el-tag><el-button class="button-new-tag" type="primary" icon="el-icon-plus" circle @click="onSelectNextUsers" /></el-form-item></el-form></el-col></el-row><el-row :gutter="10" type="flex" justify="center"><el-col :span="1.5"><el-button icon="CircleCheck" type="success" @click="handleComplete">通过</el-button></el-col><el-col :span="1.5"><el-button icon="ChatLineSquare" type="primary" @click="handleDelegate">委派</el-button></el-col><el-col :span="1.5"><el-button icon="Switch" type="success" @click="handleTransfer">转办</el-button></el-col><el-col :span="1.5"><el-button icon="RefreshLeft" type="warning" @click="handleReturn">退回</el-button></el-col><el-col :span="1.5"><el-button icon="CircleClose" type="danger" @click="handleReject">拒绝</el-button></el-col></el-row></el-card></el-tab-pane><el-tab-pane label="表单信息" name="form"><div v-if="customForm.visible"> <!-- 自定义表单 --><component ref="refCustomForm" :disabled="customForm.disabled" :is="customForm.formComponent" :model="customForm.model":customFormData="customForm.customFormData" :isNew = "customForm.isNew"></component></div><div v-if="formVisible"><el-card class="box-card" shadow="never" v-for="(item, index) in processFormList" :key="index"><template #header><span>{{ item.title }}</span></template><!--流程处理表单模块--><div class="cu-content"><v-form-render :form-json="item.formModel" :form-data="item.formData" ref="vFormRenderRef" /></div></el-card></div><div style="margin-left:10%;margin-bottom: 30px"><!--对上传文件进行显示处理,临时方案 add by nbacheng 2022-07-27 --><!--   <el-upload action="#" :on-preview="handleFilePreview" :file-list="fileList" v-if="fileDisplay" /> --></div></el-tab-pane ><el-tab-pane label="流转记录" name="record"><el-card class="box-card" shadow="never"><el-col :span="20" :offset="2"><div class="block"><el-timeline><el-timeline-item v-for="(item, index) in historyProcNodeList" :key="index" :type="tagType(item.endTime)"><p style="font-weight: 700">{{ item.activityName }}</p><el-card v-if="item.activityType === 'startEvent'" class="box-card" shadow="hover">{{ item.assigneeName }} 在 {{ item.createTime }} 发起流程</el-card><el-card v-if="item.activityType === 'userTask'" class="box-card" shadow="hover"><el-descriptions :column="5" :labelStyle="{'font-weight': 'bold'}"><el-descriptions-item label="实际办理">{{ item.assigneeName || '-'}}</el-descriptions-item><el-descriptions-item label="候选办理">{{ item.candidate || '-'}}</el-descriptions-item><el-descriptions-item label="接收时间">{{ item.createTime || '-'}}</el-descriptions-item><el-descriptions-item label="办结时间">{{ item.endTime || '-' }}</el-descriptions-item><el-descriptions-item label="耗时">{{ item.duration || '-'}}</el-descriptions-item></el-descriptions><div v-if="item.commentList && item.commentList.length > 0"><div v-for="(comment, index) in item.commentList" :key="index"><el-divider content-position="left"><el-tag :type="approveTypeTag(comment.type)">{{ commentType(comment.type) }}</el-tag><el-tag type="info" effect="plain">{{ comment.time }}</el-tag></el-divider><span>{{ comment.fullMessage }}</span></div></div></el-card><el-card v-if="item.activityType === 'endEvent'" class="box-card" shadow="hover">{{ item.createTime }} 结束流程</el-card></el-timeline-item></el-timeline></div></el-col></el-card></el-tab-pane><el-tab-pane label="流程跟踪" name="track"><el-card class="box-card" shadow="never"><process-viewer:key="`designer-${loadIndex}`":style="'height:' + height":xml="processXml":finishedInfo="finishedInfo":allCommentList="historyProcNodeList"/></el-card></el-tab-pane></el-tabs><!--退回流程--><el-dialog :title="returnDialog.title" v-model="returnDialog.visible" width="40%" append-to-body><el-radio-group v-model="returnTaskKey"><el-radio-button v-for="item in returnTaskList" :key="item.id" :label="item.id">{{ item.name }}</el-radio-button></el-radio-group><template #footer><el-button @click="returnDialog.visible = false">取 消</el-button><el-button type="primary" @click="submitReturn">确 定</el-button></template></el-dialog><el-dialog :title="userSelectDialog.title" v-model="userSelectDialog.visible" width="60%" append-to-body><el-row type="flex" :gutter="20"><!--部门数据--><el-col :span="5"><el-card shadow="never" style="height: 100%"><template #header><span>部门列表</span></template><div class="head-container"><el-input v-model="deptName" placeholder="请输入部门名称" prefix-icon="Search" clearable /><el-tree:data="deptOptions":props="{ label: 'label', children: 'children' }":expand-on-click-node="false":filter-node-method="filterNode"ref="deptTreeRef"default-expand-all@node-click="handleNodeClick"/></div></el-card></el-col><el-col :span="18"><el-tableref="userTable":key="userSelectType"height="500"v-loading="userLoading":data="userList"highlight-current-row@current-change="changeCurrentUser"@selection-change="handleSelectionChange"><el-table-column v-if="userSelectType === 'copy' || userSelectType === 'next'" width="55" type="selection" /><el-table-column v-else width="30"><template #default="scope"><el-radio :label="scope.row.userId" v-model="currentUserId">{{''}}</el-radio></template></el-table-column><el-table-column label="用户名称" align="center" prop="userName" /><el-table-column label="用户昵称" align="center" prop="nickName" /><el-table-column label="手机" align="center" prop="phonenumber" /></el-table><pagination :total="userTotal" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" /></el-col></el-row><template #footer><el-button @click="userSelectDialog.visible = false">取 消</el-button><el-button type="primary" @click="submitUserData">确 定</el-button></template></el-dialog></div>
</template><script setup name="Detail" lang="ts">import { detailProcess, processIscompleted } from "@/api/workflow/process";import { getProcessVariables } from '@/api/workflow/task';import { complete, delegate, transfer, rejectTask, returnList, returnTask } from "@/api/workflow/work/task";import { deptTreeSelect, selectUser } from "@/api/workflow/identity";import { TaskForm } from "@/api/workflow/work/types";import { UserVO, DeptVO } from "@/api/workflow/identity/types";import { ComponentInternalInstance } from "vue";import {useRoute} from "vue-router";import {useFlowable} from '@/views/workflow/hooks/useFlowable'const route = useRoute();const router = useRouter();const { proxy } = getCurrentInstance() as ComponentInternalInstance;const { getFormComponent } = useFlowable()const userList = ref<UserVO[]>([]);const processed = ref(false);const taskFormOpen = ref(false)const userMultipleSelection = ref([]);const userSelectType = ref();const currentUserId = ref();const userLoading = ref(false);const userTotal = ref(0);const loadIndex = ref('');const height = ref(document.documentElement.clientHeight - 205 + 'px;');const processXml = ref('');const taskFormVisible = ref(false);const processFormList = ref([]);const taskFormData = ref([]);const historyProcNodeList = ref<any>();const formVisible = ref(false);const finishedInfo = ref({});const customForm = ref({ //自定义业务表单formId: '',title: '',disabled: false,visible: false,formComponent: null,model: {},/*流程数据*/customFormData: {},isNew: false,disableSubmit: true})const deptName = ref('');const deptOptions = ref<DeptVO[]>([]);const returnTaskList = ref();const returnTaskKey = ref();const copyUser = ref([]);const nextUser = ref([]);const taskFormRef = ref(ElForm);const vFormRenderRef = ref(null);const deptTreeRef = ref(null);const refCustomForm = ref(null);const activeName = ref(''); //获取当然tabnameconst returnDialog = reactive<DialogOption>({visible: false,title: '退回流程'});const userSelectDialog = reactive<DialogOption>({visible: false,title: ''});const taskForm = reactive<TaskForm>({comment: '',procInsId: '',taskId: '',userId: '',copyUserIds: '',nextUserIds: '',vars: '',targetKey: ''});const rules = ref({comment: [{ required: true, message: '请输入审批意见', trigger: 'blur' }]});const queryParams = ref({pageNum: 1,pageSize: 10});const tagType = (val: any) => {if (val) {return "success";} else {return "info";}}const commentType = (val: string) => {switch (val) {case '1': return '通过'case '2': return '退回'case '3': return '驳回'case '4': return '委派'case '5': return '转办'case '6': return '终止'case '7': return '撤回'case '8': return '拒绝'case '9': return '跳过'case '10': return '前加签'case '11': return '后加签'case '12': return '多实例加签'case '13': return '跳转'case '14': return '收回'}}const approveTypeTag = (val: string) => {switch (val) {case '1': return 'success'case '2': return 'warning'case '3': return 'danger'case '4': return 'primary'case '5': return 'success'case '6': return 'danger'case '7': return 'info'}}const initData = () => {taskForm.procInsId = route.params && route.params.procInsId as string;taskForm.taskId  = route.query && route.query.taskId as string;processed.value = route.query && (route.query.processed || false) === "true";// 流程任务重获取变量表单//getProcessDetails(taskForm.procInsId, taskForm.taskId);//判断流程是否结束processIscompleted({procInsId: taskForm.procInsId}).then(res => {console.log("processIscompleted res=",res);if(res.data) {processed.value = false;}// 获取流程变量processVariables(taskForm.taskId);});};/** 通过条件过滤节点  */const filterNode = (value: string, data: any) => {if (!value) return truereturn data.label.indexOf(value) !== -1}// /** 根据名称筛选部门树 */// watchEffect(//     () => {deptTreeRef.value.filter(deptName.value);},//     {//       flush: 'post' // watchEffect会在DOM挂载或者更新之前就会触发,此属性控制在DOM元素更新后运行//     }// );// 节点单击事件const handleNodeClick = (data: any) => {getList(data.id);}/** 查询部门下拉树结构 */const getTreeSelect = async () => {const res = await deptTreeSelect();deptOptions.value = res.data;};/** 查询用户列表 */const getList = async (deptId?: number) => {userLoading.value = true;const res = await selectUser({deptId: deptId});userLoading.value = false;userList.value = res.rows;userTotal.value = res.total;}/** 获取流程变量内容 */const processVariables = (taskId: string) => {console.log("processVariables taskId",taskId);if (taskId) {getProcessVariables(taskId).then(res => {console.log("getProcessVariables res=",res);if(res.code == 200) {if(res.data.hasOwnProperty('dataId') && res.data.dataId) {customForm.value.formId = res.data.dataId;// 流程任务重获取变量表单getProcessDetails(taskForm.procInsId, taskForm.taskId, res.data.dataId);loadIndex.value = taskForm.procInsId;if(processed.value) {activeName.value = "approval";}else {activeName.value = "form";}}else {// 流程任务重获取变量表单getProcessDetails(taskForm.procInsId, taskForm.taskId, "");loadIndex.value = taskForm.procInsId;if(processed.value) {activeName.value = "approval";}else {activeName.value = "form";// 回填数据,这里主要是处理文件列表显示,临时解决,以后应该在formdesigner里完成/*this.processFormList.forEach((item, i) => {if (item?.hasOwnProperty('list')) {fillFormData(item.list, item)// 更新表单this.key = +new Date().getTime()}});*/}}}});}}const fillFormData = (list, formConf) => { // for formdesignerconsole.log("fillFormData list=",list);console.log("fillFormData formConf=",formConf);list.forEach((item, i) => {// 特殊处理el-upload,包括 回显图片if(formConf.formValues[item.id] != '') {const val = formConf.formValues[item.id];if (item.ele === 'el-upload') {console.log('fillFormData val=',val)if(item['list-type'] != 'text') {//图片this.fileList = []    //隐藏加的el-upload文件列表//item['file-list'] = JSON.parse(val)if(val != '') {item['file-list'] = JSON.parse(val)}}else {  //列表console.log("列表fillFormData val",val)this.fileList = JSON.parse(val)item['file-list'] = [] //隐藏加的表单设计器的文件列表}// 回显图片this.fileDisplay = true}}if (Array.isArray(item.columns)) {this.fillFormData(item.columns, formConf)}})}const getProcessDetails = async (procInsId: string, taskId: string, dataId: string) => {const params = {procInsId: procInsId, taskId: taskId, dataId: dataId}const res = await detailProcess(params);const data = res.data;console.log("getProcessDetails data",data);processXml.value = data.bpmnXml;processFormList.value = data.processFormList;console.log("processFormList",processFormList);if(processFormList.value.length == 1 &&processFormList.value[0].formValues.hasOwnProperty('routeName')) {customForm.value.disabled = true;customForm.value.visible = true;customForm.value.formComponent = getFormComponent(processFormList.value[0].formValues.routeName).component;customForm.value.model = processFormList.value[0].formValues.formData;customForm.value.customFormData = processFormList.value[0].formValues.formData;if(data.startUserNode) {customForm.value.isNew = true;customForm.value.disabled = false;}console.log("detailProcess customForm",customForm.value);}else {taskFormVisible.value = data.existTaskForm;if (taskFormVisible.value) {taskFormData.value = data.taskFormData;}formVisible.value = true;nextTick(() => {processFormList.value.forEach((item: any, index: any) => {if (item.disabled) {vFormRenderRef.value[index].disableForm();}})})}historyProcNodeList.value = data.historyProcNodeList;finishedInfo.value = data.flowViewer;}const onSelectCopyUsers = () => {userMultipleSelection.value = copyUser;onSelectUsers('添加抄送人', 'copy')}const onSelectNextUsers = () => {userMultipleSelection.value = nextUser;onSelectUsers('指定审批人', 'next')}const onSelectUsers = (title: string, type: string) => {userSelectType.value = type;userSelectDialog.title = title;userSelectDialog.visible = true;getTreeSelect();getList()}/** 通过任务 */const handleComplete = () => {// 校验表单taskFormRef.value.validate(async (valid: boolean) => {if (valid) {const res = await complete(taskForm)proxy?.$modal.msgSuccess(res.msg);goBack();}});}/** 委派任务 */const handleDelegate = () => {userSelectType.value = 'delegate';userSelectDialog.title = '委派任务'userSelectDialog.visible = true;getTreeSelect();}/** 转办任务 */const handleTransfer = () => {userSelectType.value = 'transfer';userSelectDialog.title = '转办任务';userSelectDialog.visible = true;getTreeSelect();}/** 退回任务 */const handleReturn = async () => {// 校验表单taskFormRef.value.validate(async (valid: boolean) => {if (valid) {const res = await returnList(taskForm);returnTaskList.value = res.data;returnDialog.visible = true;}});}/** 拒绝任务 */const handleReject = async () => {await proxy?.$modal.confirm('拒绝审批单流程会终止,是否继续?');await rejectTask(taskForm);proxy?.$modal.msgSuccess("操作成功");goBack();}/** 返回页面 */const goBack = () => {// 关闭当前标签页并返回上个页面proxy?.$tab.closePage(route);router.back()}// 关闭标签const handleClose = (type: any, tag: any) => {let userObj = userMultipleSelection.value.find(item => item.userId === tag.id);userMultipleSelection.value.splice(userMultipleSelection.value.indexOf(userObj), 1);if (type === 'copy') {copyUser.value = userMultipleSelection.value;// 设置抄送人IDif (copyUser.value && copyUser.value.length > 0) {const val = copyUser.value.map(item => item.id);taskForm.copyUserIds = val instanceof Array ? val.join(',') : val;} else {taskForm.copyUserIds = '';}} else if (type === 'next') {nextUser.value = userMultipleSelection.value;// 设置抄送人IDif (nextUser.value && nextUser.value.length > 0) {const val = nextUser.value.map(item => item.id);taskForm.nextUserIds = val instanceof Array ? val.join(',') : val;} else {taskForm.nextUserIds = '';}}}const changeCurrentUser = (val: any) => {// currentUserId = val.userId}const handleSelectionChange = () => {}const submitReturn = () => {// 校验表单taskFormRef.value.validate(async (valid: boolean) => {if (valid) {if (!returnTaskKey) {proxy?.$modal.msgError("请选择退回节点!");}taskForm.targetKey = returnTaskKey.value;const res = await returnTask(taskForm);proxy?.$modal.msgSuccess(res.msg);goBack()}});console.log("taskForm => ", taskForm.targetKey);}const submitUserData = () => {let type = userSelectType.value;if (type === 'copy' || type === 'next') {if (!userMultipleSelection || userMultipleSelection.value.length <= 0) {proxy?.$modal.msgError("请选择用户");return false;}let userIds = userMultipleSelection.value.map(k => k.userId);if (type === 'copy') {// 设置抄送人ID信息copyUser.value = userMultipleSelection.value;taskForm.copyUserIds = userIds instanceof Array ? userIds.join(',') : userIds;} else if (type === 'next') {// 设置下一级审批人ID信息nextUser.value = userMultipleSelection.value;taskForm.nextUserIds = userIds instanceof Array ? userIds.join(',') : userIds;}userSelectDialog.visible = false;} else {if (!taskForm.comment) {proxy?.$modal.msgError("请输入审批意见");return false;}if (!currentUserId.value) {proxy?.$modal.msgError("请选择用户");return false;}taskForm.userId = currentUserId.value;if (type === 'delegate') {delegate(taskForm).then(res => {proxy?.$modal.msgSuccess(res.msg);goBack();});}if (type === 'transfer') {transfer(taskForm).then(res => {proxy?.$modal.msgSuccess(res.msg);goBack();});}}}onMounted(() => {initData();});
</script><style lang="scss" scoped>.clearfix:before,.clearfix:after {display: table;content: "";}.clearfix:after {clear: both}.box-card {width: 100%;margin-bottom: 20px;}.el-tag + .el-tag {margin-left: 10px;}.el-row {margin-bottom: 20px;&:last-child {margin-bottom: 0;}}.el-col {border-radius: 4px;}.button-new-tag {margin-left: 10px;}
</style>

2、其中hooks的useFlowable修改如下(原先minixs)

export const useFlowable = () => {const allFormComponent = computed(() => {return [{text:'单表示例',routeName: '@/views/workflow/demo/wf',component: defineAsyncComponent( () => import('@/views/workflow/demo/wf')),businessTable:'wf_demo'},/*{text:'主子表示例',routeName:'@/views/workflow/demo/modules/CesOrderMainForm',component:() => defineAsyncComponent(import(`@/views/workflow/demo/modules/CesOrderMainForm`)),businessTable:'ces_order_main'}*/]})const getFormComponent = (routeName) => {return allFormComponent.value.find((item) => item.routeName === routeName) || {}}return {allFormComponent,getFormComponent}
}

3、效果图如下:

这篇关于ruoyi-nbcio-plus基于vue3的flowable的支持自定义业务流程处理页面detail.vue的升级修改的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/895758

相关文章

Python使用getopt处理命令行参数示例解析(最佳实践)

《Python使用getopt处理命令行参数示例解析(最佳实践)》getopt模块是Python标准库中一个简单但强大的命令行参数处理工具,它特别适合那些需要快速实现基本命令行参数解析的场景,或者需要... 目录为什么需要处理命令行参数?getopt模块基础实际应用示例与其他参数处理方式的比较常见问http

Java Response返回值的最佳处理方案

《JavaResponse返回值的最佳处理方案》在开发Web应用程序时,我们经常需要通过HTTP请求从服务器获取响应数据,这些数据可以是JSON、XML、甚至是文件,本篇文章将详细解析Java中处理... 目录摘要概述核心问题:关键技术点:源码解析示例 1:使用HttpURLConnection获取Resp

Java中Switch Case多个条件处理方法举例

《Java中SwitchCase多个条件处理方法举例》Java中switch语句用于根据变量值执行不同代码块,适用于多个条件的处理,:本文主要介绍Java中SwitchCase多个条件处理的相... 目录前言基本语法处理多个条件示例1:合并相同代码的多个case示例2:通过字符串合并多个case进阶用法使用

Java实现优雅日期处理的方案详解

《Java实现优雅日期处理的方案详解》在我们的日常工作中,需要经常处理各种格式,各种类似的的日期或者时间,下面我们就来看看如何使用java处理这样的日期问题吧,感兴趣的小伙伴可以跟随小编一起学习一下... 目录前言一、日期的坑1.1 日期格式化陷阱1.2 时区转换二、优雅方案的进阶之路2.1 线程安全重构2

HTML5中的Microdata与历史记录管理详解

《HTML5中的Microdata与历史记录管理详解》Microdata作为HTML5新增的一个特性,它允许开发者在HTML文档中添加更多的语义信息,以便于搜索引擎和浏览器更好地理解页面内容,本文将探... 目录html5中的Mijscrodata与历史记录管理背景简介html5中的Microdata使用M

html5的响应式布局的方法示例详解

《html5的响应式布局的方法示例详解》:本文主要介绍了HTML5中使用媒体查询和Flexbox进行响应式布局的方法,简要介绍了CSSGrid布局的基础知识和如何实现自动换行的网格布局,详细内容请阅读本文,希望能对你有所帮助... 一 使用媒体查询响应式布局        使用的参数@media这是常用的

HTML5表格语法格式详解

《HTML5表格语法格式详解》在HTML语法中,表格主要通过table、tr和td3个标签构成,本文通过实例代码讲解HTML5表格语法格式,感兴趣的朋友一起看看吧... 目录一、表格1.表格语法格式2.表格属性 3.例子二、不规则表格1.跨行2.跨列3.例子一、表格在html语法中,表格主要通过< tab

Python处理函数调用超时的四种方法

《Python处理函数调用超时的四种方法》在实际开发过程中,我们可能会遇到一些场景,需要对函数的执行时间进行限制,例如,当一个函数执行时间过长时,可能会导致程序卡顿、资源占用过高,因此,在某些情况下,... 目录前言func-timeout1. 安装 func-timeout2. 基本用法自定义进程subp

Vue3组件中getCurrentInstance()获取App实例,但是返回null的解决方案

《Vue3组件中getCurrentInstance()获取App实例,但是返回null的解决方案》:本文主要介绍Vue3组件中getCurrentInstance()获取App实例,但是返回nu... 目录vue3组件中getCurrentInstajavascriptnce()获取App实例,但是返回n

Java字符串处理全解析(String、StringBuilder与StringBuffer)

《Java字符串处理全解析(String、StringBuilder与StringBuffer)》:本文主要介绍Java字符串处理全解析(String、StringBuilder与StringBu... 目录Java字符串处理全解析:String、StringBuilder与StringBuffer一、St