Commit 33f3bc05 by YunaiV

【功能优化】工作流:BPMN 流程图高亮的计算,切换到后端为主

parent 619491b4
...@@ -95,7 +95,7 @@ ...@@ -95,7 +95,7 @@
"@vitejs/plugin-vue": "^5.0.4", "@vitejs/plugin-vue": "^5.0.4",
"@vitejs/plugin-vue-jsx": "^3.1.0", "@vitejs/plugin-vue-jsx": "^3.1.0",
"autoprefixer": "^10.4.17", "autoprefixer": "^10.4.17",
"bpmn-js": "8.9.0", "bpmn-js": "8.10.0",
"bpmn-js-properties-panel": "0.46.0", "bpmn-js-properties-panel": "0.46.0",
"consola": "^3.2.3", "consola": "^3.2.3",
"eslint": "^8.57.0", "eslint": "^8.57.0",
......
import request from '@/config/axios'
export const getActivityList = async (params) => {
return await request.get({
url: '/bpm/activity/list',
params
})
}
...@@ -24,24 +24,23 @@ export type ProcessInstanceVO = { ...@@ -24,24 +24,23 @@ export type ProcessInstanceVO = {
// 用户信息 // 用户信息
export type User = { export type User = {
id: number, id: number
nickname: string, nickname: string
avatar: string avatar: string
} }
// 审批任务信息 // 审批任务信息
export type ApprovalTaskInfo = { export type ApprovalTaskInfo = {
id: number, id: number
ownerUser: User, ownerUser: User
assigneeUser: User, assigneeUser: User
status: number, status: number
reason: string reason: string
} }
// 审批节点信息 // 审批节点信息
export type ApprovalNodeInfo = { export type ApprovalNodeInfo = {
id : number id: number
name: string name: string
nodeType: NodeType nodeType: NodeType
status: number status: number
...@@ -88,12 +87,22 @@ export const getProcessInstanceCopyPage = async (params: any) => { ...@@ -88,12 +87,22 @@ export const getProcessInstanceCopyPage = async (params: any) => {
} }
// 获取审批详情 // 获取审批详情
export const getApprovalDetail = async (processInstanceId?:string, processDefinitionId?:string) => { export const getApprovalDetail = async (
const param = processInstanceId ? '?processInstanceId='+ processInstanceId : '?processDefinitionId='+ processDefinitionId processInstanceId?: string,
return await request.get({ url: 'bpm/process-instance/get-approval-detail'+ param }) processDefinitionId?: string
) => {
const param = processInstanceId
? '?processInstanceId=' + processInstanceId
: '?processDefinitionId=' + processDefinitionId
return await request.get({ url: 'bpm/process-instance/get-approval-detail' + param })
} }
// 获取表单字段权限 // 获取表单字段权限
export const getFormFieldsPermission = async (params: any) => { export const getFormFieldsPermission = async (params: any) => {
return await request.get({ url: '/bpm/process-instance/get-form-fields-permission', params }) return await request.get({ url: '/bpm/process-instance/get-form-fields-permission', params })
} }
// 获取流程实例的 BPMN 模型视图
export const getProcessInstanceBpmnModelView = async (id: string) => {
return await request.get({ url: '/bpm/process-instance/get-bpmn-model-view?id=' + id })
}
<template> <template>
<div class="simple-flow-canvas" v-loading="loading"> <div class="simple-flow-canvas" v-loading="loading">
<div class="simple-flow-container" > <div class="simple-flow-container">
<div class="top-area-container"> <div class="top-area-container">
<div class="top-actions"> <div class="top-actions">
<div class="canvas-control"> <div class="canvas-control">
...@@ -15,10 +15,7 @@ ...@@ -15,10 +15,7 @@
</div> </div>
</div> </div>
<div class="scale-container" :style="`transform: scale(${scaleValue / 100});`"> <div class="scale-container" :style="`transform: scale(${scaleValue / 100});`">
<ProcessNodeTree <ProcessNodeTree v-if="processNodeTree" v-model:flow-node="processNodeTree" />
v-if="processNodeTree"
v-model:flow-node="processNodeTree"
/>
</div> </div>
</div> </div>
<Dialog v-model="errorDialogVisible" title="保存失败" width="400" :fullscreen="false"> <Dialog v-model="errorDialogVisible" title="保存失败" width="400" :fullscreen="false">
...@@ -49,7 +46,7 @@ import * as DeptApi from '@/api/system/dept' ...@@ -49,7 +46,7 @@ import * as DeptApi from '@/api/system/dept'
import * as PostApi from '@/api/system/post' import * as PostApi from '@/api/system/post'
import * as UserApi from '@/api/system/user' import * as UserApi from '@/api/system/user'
import * as UserGroupApi from '@/api/bpm/userGroup' import * as UserGroupApi from '@/api/bpm/userGroup'
import { fa } from 'element-plus/es/locale'
defineOptions({ defineOptions({
name: 'SimpleProcessDesigner' name: 'SimpleProcessDesigner'
}) })
......
@import './process-designer.scss'; @import './process-designer.scss';
@import './process-panel.scss'; @import './process-panel.scss';
$success-color: #4eb819;
$primary-color: #409EFF;
$danger-color: #F56C6C;
$cancel-color: #909399;
.process-viewer {
position: relative;
border: 1px solid #EFEFEF;
background: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PHBhdHRlcm4gaWQ9ImEiIHdpZHRoPSI0MCIgaGVpZ2h0PSI0MCIgcGF0dGVyblVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHBhdGggZD0iTTAgMTBoNDBNMTAgMHY0ME0wIDIwaDQwTTIwIDB2NDBNMCAzMGg0ME0zMCAwdjQwIiBmaWxsPSJub25lIiBzdHJva2U9IiNlMGUwZTAiIG9wYWNpdHk9Ii4yIi8+PHBhdGggZD0iTTQwIDBIMHY0MCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZTBlMGUwIi8+PC9wYXR0ZXJuPjwvZGVmcz48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJ1cmwoI2EpIi8+PC9zdmc+') repeat!important;
.success-arrow {
fill: $success-color;
stroke: $success-color;
}
.success-conditional {
fill: white;
stroke: $success-color;
}
.success.djs-connection {
.djs-visual path {
stroke: $success-color!important;
//marker-end: url(#sequenceflow-end-white-success)!important;
}
}
.success.djs-connection.condition-expression {
.djs-visual path {
//marker-start: url(#conditional-flow-marker-white-success)!important;
}
}
.success.djs-shape {
.djs-visual rect {
stroke: $success-color!important;
fill: $success-color!important;
fill-opacity: 0.15!important;
}
.djs-visual polygon {
stroke: $success-color!important;
}
.djs-visual path:nth-child(2) {
stroke: $success-color!important;
fill: $success-color!important;
}
.djs-visual circle {
stroke: $success-color!important;
fill: $success-color!important;
fill-opacity: 0.15!important;
}
}
.primary.djs-shape {
.djs-visual rect {
stroke: $primary-color!important;
fill: $primary-color!important;
fill-opacity: 0.15!important;
}
.djs-visual polygon {
stroke: $primary-color!important;
}
.djs-visual circle {
stroke: $primary-color!important;
fill: $primary-color!important;
fill-opacity: 0.15!important;
}
}
.danger.djs-shape {
.djs-visual rect {
stroke: $danger-color!important;
fill: $danger-color!important;
fill-opacity: 0.15!important;
}
.djs-visual polygon {
stroke: $danger-color!important;
}
.djs-visual circle {
stroke: $danger-color!important;
fill: $danger-color!important;
fill-opacity: 0.15!important;
}
}
.cancel.djs-shape {
.djs-visual rect {
stroke: $cancel-color!important;
fill: $cancel-color!important;
fill-opacity: 0.15!important;
}
.djs-visual polygon {
stroke: $cancel-color!important;
}
.djs-visual circle {
stroke: $cancel-color!important;
fill: $cancel-color!important;
fill-opacity: 0.15!important;
}
}
}
.process-viewer .djs-tooltip-container, .process-viewer .djs-overlay-container, .process-viewer .djs-palette {
display: none;
}
...@@ -292,7 +292,8 @@ const remainingRouter: AppRouteRecordRaw[] = [ ...@@ -292,7 +292,8 @@ const remainingRouter: AppRouteRecordRaw[] = [
}, },
{ {
path: 'process-instance/detail', path: 'process-instance/detail',
component: () => import('@/views/bpm/processInstance/detail/index_new.vue'), // component: () => import('@/views/bpm/processInstance/detail/index_new.vue'),
component: () => import('@/views/bpm/processInstance/detail/index.vue'),
name: 'BpmProcessInstanceDetail', name: 'BpmProcessInstanceDetail',
meta: { meta: {
noCache: true, noCache: true,
...@@ -301,13 +302,11 @@ const remainingRouter: AppRouteRecordRaw[] = [ ...@@ -301,13 +302,11 @@ const remainingRouter: AppRouteRecordRaw[] = [
title: '流程详情', title: '流程详情',
activeMenu: '/bpm/task/my' activeMenu: '/bpm/task/my'
}, },
props: route => ( props: (route) => ({
{ id: route.query.id,
id: route.query.id, taskId: route.query.taskId,
taskId: route.query.taskId, activityId: route.query.activityId
activityId: route.query.activityId })
}
)
}, },
{ {
path: 'oa/leave/create', path: 'oa/leave/create',
......
...@@ -449,3 +449,11 @@ export const BpmModelFormType = { ...@@ -449,3 +449,11 @@ export const BpmModelFormType = {
NORMAL: 10, // 流程表单 NORMAL: 10, // 流程表单
CUSTOM: 20 // 业务表单 CUSTOM: 20 // 业务表单
} }
export const BpmProcessInstanceStatus = {
NOT_START: -1, // 未开始
RUNNING: 1, // 审批中
APPROVE: 2, // 审批通过
REJECT: 3, // 审批不通过
CANCEL: 4 // 已取消
}
...@@ -3,48 +3,43 @@ ...@@ -3,48 +3,43 @@
<template #header> <template #header>
<span class="el-icon-picture-outline">流程图</span> <span class="el-icon-picture-outline">流程图</span>
</template> </template>
<MyProcessViewer <MyProcessViewer key="designer" :xml="view.bpmnXml" :view="view" class="h-700px" />
key="designer"
:activityData="activityList"
:prefix="bpmnControlForm.prefix"
:processInstanceData="processInstance"
:taskData="tasks"
:value="bpmnXml"
v-bind="bpmnControlForm"
/>
</el-card> </el-card>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { propTypes } from '@/utils/propTypes' import { propTypes } from '@/utils/propTypes'
import { MyProcessViewer } from '@/components/bpmnProcessDesigner/package' import { MyProcessViewer } from '@/components/bpmnProcessDesigner/package'
import * as ActivityApi from '@/api/bpm/activity' import * as ProcessInstanceApi from '@/api/bpm/processInstance'
defineOptions({ name: 'BpmProcessInstanceBpmnViewer' }) defineOptions({ name: 'BpmProcessInstanceBpmnViewer' })
const props = defineProps({ const props = defineProps({
loading: propTypes.bool, // 是否加载中 loading: propTypes.bool.def(false), // 是否加载中
id: propTypes.string, // 流程实例的编号 id: propTypes.string, // 流程实例的编号
processInstance: propTypes.any, // 流程实例的信息
tasks: propTypes.array, // 流程任务的数组
bpmnXml: propTypes.string // BPMN XML bpmnXml: propTypes.string // BPMN XML
}) })
const bpmnControlForm = ref({ const view = ref({
prefix: 'flowable' bpmnXml: ''
}) }) // BPMN 流程图数据
const activityList = ref([]) // 任务列表
/** 只有 loading 完成时,才去加载流程列表 */ /** 只有 loading 完成时,才去加载流程列表 */
watch( watch(
() => props.loading, () => props.loading,
async (value) => { async (value) => {
if (value && props.id) { if (value && props.id) {
activityList.value = await ActivityApi.getActivityList({ view.value = await ProcessInstanceApi.getProcessInstanceBpmnModelView(props.id)
processInstanceId: props.id
})
} }
} }
) )
/** 监听 bpmnXml */
watch(
() => props.bpmnXml,
(value) => {
view.value.bpmnXml = value
}
)
</script> </script>
<style> <style>
.box-card { .box-card {
......
...@@ -157,13 +157,7 @@ ...@@ -157,13 +157,7 @@
/> />
<!-- 高亮流程图 --> <!-- 高亮流程图 -->
<ProcessInstanceBpmnViewer <ProcessInstanceBpmnViewer :id="`${id}`" :loading="processInstanceLoading" />
:id="`${id}`"
:bpmn-xml="bpmnXml"
:loading="processInstanceLoading"
:process-instance="processInstance"
:tasks="tasks"
/>
<!-- 弹窗:转派审批人 --> <!-- 弹窗:转派审批人 -->
<TaskTransferForm ref="taskTransferFormRef" @success="getDetail" /> <TaskTransferForm ref="taskTransferFormRef" @success="getDetail" />
......
...@@ -66,13 +66,7 @@ ...@@ -66,13 +66,7 @@
</el-tab-pane> </el-tab-pane>
<!-- 流程图 --> <!-- 流程图 -->
<el-tab-pane label="流程图" name="diagram"> <el-tab-pane label="流程图" name="diagram">
<ProcessInstanceBpmnViewer <ProcessInstanceBpmnViewer :id="`${id}`" :loading="processInstanceLoading" />
:id="`${id}`"
:bpmn-xml="bpmnXml"
:loading="processInstanceLoading"
:process-instance="processInstance"
:tasks="tasks"
/>
</el-tab-pane> </el-tab-pane>
<!-- 流转记录 --> <!-- 流转记录 -->
<el-tab-pane label="流转记录" name="record"> <el-tab-pane label="流转记录" name="record">
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment