本文主要是介绍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的升级修改的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!