!172 合并 warmflow 功能分支

* update 优化 流程定义页面 点击复制之后跳转到未发布列表
* update 优化 流程定义页面 增加加载loading层
* fix: v-model处理有延迟 需要手动处理
* update 调整流程定义查询
* Merge remote-tracking branch 'origin/dev' into warm-flw-future
* update 调整可驳回的节点
* update: 调整文案
* update: 激活/挂起改为switch操作
* update 优化 代码删除无用输出
* update 统一抄送人使用昵称展示
* update 调整分类接口
* update 统一业务id参数
* update 删除默认顶节点
* !168 优化流程分类
* update 恢复误删除代码
* update 优化流程分类
* update 修复 路由跳转未改全
* [update]
* fix 修复一些问题
* update 重构流程分类表
* update 流程定义增加表单路径与编辑功能 修复一些其他bug
* fix 修复 一些问题
* update 变量统一命名
* add 增加示例
* Merge branch 'warm-flw-future' of https://gitee.com/JavaLionLi/plus-ui…
* update 调整办理人
* Merge remote-tracking branch 'origin/dev' into warm-flw-future
* add 增加示例
* update 调整审批记录 添加流程导出
* remove 删除无用代码
* remove 删除无用代码
* [update]
* [fix]
* 流程干预删除委托
* 增加附件
* 附件修改
* 申请人查询修改
* 1.修改查询条件
* !167 fix 修复 import路径修改不全问题
* fix 修复 import路径修改不全问题
* !166 update 清除 ProcessPreview 引用
* update 清除 ProcessPreview 引用
* remove 删除 bpmn.js 设计器
* remove 删除 bpmn.js 设计器
* remove 删除 bpmn.js 设计器
* remove 删除 bpmn.js 设计器
* fix 修复 前端路径修改不全问题
* update 优化接口请求路径
* add 添加作废
* add 添加流程干预
* update 调整加签,减签
* update 调整加签,减签
* add 增加流程查看
* update 调整委托,转办
* update 调整流程变量显示
* update 调整办理人修改
* update 调整流程实例状态页面
* update 调整已办页面
* add 添加流程撤销
* update 调整任务,流程实例 ,流程定义页面
* Merge branch 'dev' into warm-flw-future
* update 调整流程定义页面
* add 添加流程变量查看
* update 调整设计器路由名称
* update 调整办理人
* update 调整设计器uri
* update 调整设计器请求uri 调整待办状态
* update 调整办理 驳回 终止等状态
* add 添加模型新增
* add 添加warm-ui设计器,删除无用代码
* Merge remote-tracking branch 'origin/dev' into warm-flw-future
* add 添加流程设计
* update 调整驳回
* update 调整视图类型错误
* Merge branch 'warm-flw-future' of https://gitee.com/JavaLionLi/plus-ui…
* 添加已办,未办
* Merge remote-tracking branch 'origin/dev' into warm-flw-future
* update 调整流程实例,待办查询
* add 添加代办人,调整提交按钮校验
* update 调整流程定义查询
* add添加流程定义激活 挂起
* add 添加流程文件部署 调整流程发布
* update 优化 时间搜索组件统一
* Merge remote-tracking branch 'origin/dev' into warm-flw-future
* fix 修复 用户管理编辑安全权限错误问题
* Merge remote-tracking branch 'origin/dev' into warm-flw-future
* update 优化 类型报错问题
* update 优化 切换租户后刷新首页
* update 优化 实现表格行选中切换
* update 优化 使用 vueuse 重构 websocket 实现
* update 优化 使用 vueuse 重构 websocket 实现
* fix 修复 登出后重新登录 sse推送报错问题
* reset 回滚 代码修改 采用其他方案
* fix 修复 登出后重新登录 sse推送报错问题
* update 优化 删除无用代码
* update element-plus 2.7.5 => 2.7.8
* reset 回滚 错误修复
* update 优化 代码生成器编辑页禁用缓存 防止同步后页面不更新问题
* fix 修复 代码生成同步点击取消报错问题
* 初始化添加warm-flow
This commit is contained in:
疯狂的狮子Li
2025-01-13 06:11:00 +00:00
parent 100f44e197
commit e423d8afcf
78 changed files with 1955 additions and 8762 deletions

View File

@ -1,71 +0,0 @@
<template>
<div class="design">
<el-dialog v-model="visible" width="100%" fullscreen :title="title">
<div class="modeler">
<bpmn-design ref="bpmnDesignRef" @save-call-back="saveCallBack"></bpmn-design>
</div>
</el-dialog>
</div>
</template>
<script lang="ts" setup name="Design">
import { getInfo, editModelXml } from '@/api/workflow/model';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
import { ModelForm } from '@/api/workflow/model/types';
import BpmnDesign from '@/bpmn/index.vue';
import useDialog from '@/hooks/useDialog';
const bpmnDesignRef = ref<InstanceType<typeof BpmnDesign>>();
const modelForm = ref<ModelForm>();
const emit = defineEmits(['closeCallBack']);
const { visible, title } = useDialog({
title: '编辑流程'
});
const modelId = ref('');
const open = async (id) => {
visible.value = true;
modelId.value = id;
const { data } = await getInfo(id);
modelForm.value = data;
bpmnDesignRef.value.initDiagram(modelForm.value.xml);
};
//保存模型
const saveCallBack = async (data) => {
await proxy?.$modal.confirm('是否确认保存?');
data.loading.value = true;
modelForm.value.id = modelId.value;
modelForm.value.xml = data.xml;
modelForm.value.svg = data.svg;
modelForm.value.key = data.key;
modelForm.value.name = data.name;
editModelXml(modelForm.value).then((res) => {
if (res.code === 200) {
visible.value = false;
proxy?.$modal.msgSuccess('保存成功');
emit('closeCallBack', data);
}
});
data.loading.value = false;
};
/**
* 对外暴露子组件方法
*/
defineExpose({
open
});
</script>
<style lang="scss" scoped>
.design {
:deep(.el-dialog .el-dialog__body) {
max-height: 100% !important;
min-height: calc(100vh - 80px);
padding: 10px 0 10px 0 !important;
}
:deep(.el-dialog__header) {
padding: 0 0 5px 0 !important;
}
}
</style>

View File

@ -1,411 +0,0 @@
<template>
<div v-loading="loading" class="bpmnDialogContainers">
<el-header style="border-bottom: 1px solid rgb(218 218 218); height: auto">
<div class="header-div">
<div>
<el-tooltip effect="dark" content="自适应屏幕" placement="bottom">
<el-button size="small" icon="Rank" @click="fitViewport" />
</el-tooltip>
<el-tooltip effect="dark" content="放大" placement="bottom">
<el-button size="small" icon="ZoomIn" @click="zoomViewport(true)" />
</el-tooltip>
<el-tooltip effect="dark" content="缩小" placement="bottom">
<el-button size="small" icon="ZoomOut" @click="zoomViewport(false)" />
</el-tooltip>
</div>
<div>
<div class="tips-label">
<div class="un-complete">未完成</div>
<div class="in-progress">进行中</div>
<div class="complete">已完成</div>
</div>
</div>
</div>
</el-header>
<div class="flow-containers">
<el-container class="bpmn-el-container" style="align-items: stretch">
<el-main style="padding: 0">
<div ref="canvas" class="canvas" />
</el-main>
</el-container>
</div>
</div>
</template>
<script lang="ts" setup>
import BpmnViewer from 'bpmn-js/lib/Viewer';
import MoveCanvasModule from 'diagram-js/lib/navigation/movecanvas';
import ZoomScrollModule from 'diagram-js/lib/navigation/zoomscroll';
import { ModuleDeclaration } from 'didi';
import type { Canvas, ModdleElement } from 'bpmn';
import EventBus from 'diagram-js/lib/core/EventBus';
import Overlays from 'diagram-js/lib/features/overlays/Overlays';
import processApi from '@/api/workflow/processInstance/index';
const canvas = ref<HTMLElement>();
const modeler = ref<BpmnViewer>();
const taskList = ref([]);
const zoom = ref(1);
const xml = ref('');
const loading = ref(false);
const bpmnVisible = ref(true);
const historyList = ref([]);
const init = (businessKey) => {
loading.value = true;
bpmnVisible.value = true;
nextTick(async () => {
if (modeler.value) modeler.value.destroy();
modeler.value = new BpmnViewer({
container: canvas.value,
additionalModules: [
{
//禁止滚轮滚动
zoomScroll: ['value', '']
},
ZoomScrollModule,
MoveCanvasModule
] as ModuleDeclaration[]
});
const resp = await processApi.getHistoryList(businessKey);
xml.value = resp.data.xml;
taskList.value = resp.data.taskList;
historyList.value = resp.data.historyList;
await createDiagram(xml.value);
loading.value = false;
});
};
const initXml = (xmlStr: string) => {
loading.value = true;
bpmnVisible.value = true;
nextTick(async () => {
if (modeler.value) modeler.value.destroy();
modeler.value = new BpmnViewer({
container: canvas.value,
additionalModules: [
{
//禁止滚轮滚动
zoomScroll: ['value', '']
},
ZoomScrollModule,
MoveCanvasModule
] as ModuleDeclaration[]
});
xml.value = xmlStr;
await createDiagram(xml.value);
loading.value = false;
});
};
const createDiagram = async (data) => {
try {
await modeler.value.importXML(data);
fitViewport();
fillColor();
loading.value = false;
addEventBusListener();
} catch (err) {
console.log(err);
}
};
const addEventBusListener = () => {
const eventBus = modeler.value.get<EventBus>('eventBus');
const overlays = modeler.value.get<Overlays>('overlays');
eventBus.on<ModdleElement>('element.hover', (e) => {
let data = historyList.value.find((t) => t.taskDefinitionKey === e.element.id);
if (e.element.type === 'bpmn:UserTask' && data) {
setTimeout(() => {
genNodeDetailBox(e, overlays, data);
}, 10);
}
});
eventBus.on('element.out', (e) => {
overlays.clear();
});
};
const genNodeDetailBox = (e, overlays, data) => {
overlays.add(e.element.id, {
position: { top: e.element.height, left: 0 },
html: `<div class="verlays">
<p>审批人员: ${data.nickName || ''}<p/>
<p>节点状态:${data.status || ''}</p>
<p>开始时间:${data.startTime || ''}</p>
<p>结束时间:${data.endTime || ''}</p>
<p>审批耗时:${data.runDuration || ''}</p>
<p>流程版本v${data.version || ''}</p>
</div>`
});
};
// 让图能自适应屏幕
const fitViewport = () => {
zoom.value = modeler.value.get<Canvas>('canvas').zoom('fit-viewport');
const bbox = document.querySelector<SVGGElement>('.flow-containers .viewport').getBBox();
const currentViewBox = modeler.value.get('canvas').viewbox();
const elementMid = {
x: bbox.x + bbox.width / 2 - 65,
y: bbox.y + bbox.height / 2
};
modeler.value.get<Canvas>('canvas').viewbox({
x: elementMid.x - currentViewBox.width / 2,
y: elementMid.y - currentViewBox.height / 2,
width: currentViewBox.width,
height: currentViewBox.height
});
zoom.value = (bbox.width / currentViewBox.width) * 1.8;
};
// 放大缩小
const zoomViewport = (zoomIn = true) => {
zoom.value = modeler.value.get<Canvas>('canvas').zoom();
zoom.value += zoomIn ? 0.1 : -0.1;
modeler.value.get<Canvas>('canvas').zoom(zoom.value);
};
//上色
const fillColor = () => {
const canvas = modeler.value.get<Canvas>('canvas');
bpmnNodeList(modeler.value._definitions.rootElements[0].flowElements, canvas);
};
//递归上色
const bpmnNodeList = (flowElements, canvas) => {
flowElements.forEach((n) => {
if (n.$type === 'bpmn:UserTask') {
const completeTask = taskList.value.find((m) => m.key === n.id);
if (completeTask) {
canvas.addMarker(n.id, completeTask.completed ? 'highlight' : 'highlight-todo');
n.outgoing?.forEach((nn) => {
const targetTask = taskList.value.find((m) => m.key === nn.targetRef.id);
if (targetTask) {
canvas.addMarker(nn.id, targetTask.completed ? 'highlight' : 'highlight-todo');
} else if (nn.targetRef.$type === 'bpmn:ExclusiveGateway') {
canvas.addMarker(nn.id, completeTask.completed ? 'highlight' : 'highlight-todo');
canvas.addMarker(nn.targetRef.id, completeTask.completed ? 'highlight' : 'highlight-todo');
nn.targetRef.outgoing.forEach((e) => {
gateway(e.id, e.targetRef.$type, e.targetRef.id, canvas, completeTask.completed);
});
} else if (nn.targetRef.$type === 'bpmn:ParallelGateway') {
canvas.addMarker(nn.id, completeTask.completed ? 'highlight' : 'highlight-todo');
canvas.addMarker(nn.targetRef.id, completeTask.completed ? 'highlight' : 'highlight-todo');
nn.targetRef.outgoing.forEach((e) => {
gateway(e.id, e.targetRef.$type, e.targetRef.id, canvas, completeTask.completed);
});
} else if (nn.targetRef.$type === 'bpmn:InclusiveGateway') {
canvas.addMarker(nn.id, completeTask.completed ? 'highlight' : 'highlight-todo');
canvas.addMarker(nn.targetRef.id, completeTask.completed ? 'highlight' : 'highlight-todo');
nn.targetRef.outgoing.forEach((e) => {
gateway(e.id, e.targetRef.$type, e.targetRef.id, canvas, completeTask.completed);
});
}
});
}
} else if (n.$type === 'bpmn:ExclusiveGateway') {
n.outgoing.forEach((nn) => {
const targetTask = taskList.value.find((m) => m.key === nn.targetRef.id);
if (targetTask) {
canvas.addMarker(nn.id, targetTask.completed ? 'highlight' : 'highlight-todo');
}
});
} else if (n.$type === 'bpmn:ParallelGateway') {
n.outgoing.forEach((nn) => {
const targetTask = taskList.value.find((m) => m.key === nn.targetRef.id);
if (targetTask) {
canvas.addMarker(nn.id, targetTask.completed ? 'highlight' : 'highlight-todo');
}
});
} else if (n.$type === 'bpmn:InclusiveGateway') {
n.outgoing.forEach((nn) => {
const targetTask = taskList.value.find((m) => m.key === nn.targetRef.id);
if (targetTask) {
canvas.addMarker(nn.id, targetTask.completed ? 'highlight' : 'highlight-todo');
}
});
} else if (n.$type === 'bpmn:SubProcess') {
const completeTask = taskList.value.find((m) => m.key === n.id);
if (completeTask) {
canvas.addMarker(n.id, completeTask.completed ? 'highlight' : 'highlight-todo');
}
bpmnNodeList(n.flowElements, canvas);
} else if (n.$type === 'bpmn:StartEvent') {
canvas.addMarker(n.id, 'startEvent');
if (n.outgoing) {
n.outgoing.forEach((nn) => {
const completeTask = taskList.value.find((m) => m.key === nn.targetRef.id);
if (completeTask) {
canvas.addMarker(nn.id, 'highlight');
canvas.addMarker(n.id, 'highlight');
}
});
}
} else if (n.$type === 'bpmn:EndEvent') {
canvas.addMarker(n.id, 'endEvent');
const completeTask = taskList.value.find((m) => m.key === n.id);
if (completeTask) {
canvas.addMarker(completeTask.key, 'highlight');
canvas.addMarker(n.id, 'highlight');
return;
}
}
});
};
const gateway = (id, targetRefType, targetRefId, canvas, completed) => {
if (targetRefType === 'bpmn:ExclusiveGateway') {
canvas.addMarker(id, completed ? 'highlight' : 'highlight-todo');
canvas.addMarker(targetRefId, completed ? 'highlight' : 'highlight-todo');
}
if (targetRefType === 'bpmn:ParallelGateway') {
canvas.addMarker(id, completed ? 'highlight' : 'highlight-todo');
canvas.addMarker(targetRefId, completed ? 'highlight' : 'highlight-todo');
}
if (targetRefType === 'bpmn:InclusiveGateway') {
canvas.addMarker(id, completed ? 'highlight' : 'highlight-todo');
canvas.addMarker(targetRefId, completed ? 'highlight' : 'highlight-todo');
}
};
defineExpose({
init,
initXml
});
</script>
<style lang="scss" scoped>
.canvas {
width: 100%;
height: 100%;
}
.header-div {
display: flex;
padding: 10px 0;
justify-content: space-between;
.tips-label {
display: flex;
div {
margin-right: 10px;
padding: 5px;
font-size: 12px;
}
.un-complete {
border: 1px solid #000;
}
.in-progress {
background-color: rgb(255, 237, 204);
border: 1px dashed orange;
}
.complete {
background-color: rgb(204, 230, 204);
border: 1px solid green;
}
}
}
.view-mode {
.el-header,
.el-aside,
.djs-palette,
.bjs-powered-by {
display: none;
}
.el-loading-mask {
background-color: initial;
}
.el-loading-spinner {
display: none;
}
}
.bpmn-el-container {
height: calc(100vh - 350px);
}
.flow-containers {
width: 100%;
height: 100%;
overflow-y: auto;
.canvas {
width: 100%;
height: 100%;
}
.load {
margin-right: 10px;
}
:deep(.el-form-item__label) {
font-size: 13px;
}
:deep(.djs-palette) {
left: 0 !important;
top: 0;
border-top: none;
}
:deep(.djs-container svg) {
min-height: 650px;
}
:deep(.startEvent.djs-shape .djs-visual > :nth-child(1)) {
fill: #77df6d !important;
}
:deep(.endEvent.djs-shape .djs-visual > :nth-child(1)) {
fill: #ee7b77 !important;
}
:deep(.highlight.djs-shape .djs-visual > :nth-child(1)) {
fill: green !important;
stroke: green !important;
fill-opacity: 0.2 !important;
}
:deep(.highlight.djs-shape .djs-visual > :nth-child(2)) {
fill: green !important;
}
:deep(.highlight.djs-shape .djs-visual > path) {
fill: green !important;
fill-opacity: 0.2 !important;
stroke: green !important;
}
:deep(.highlight.djs-connection > .djs-visual > path) {
stroke: green !important;
}
// 边框滚动动画
@keyframes path-animation {
from {
stroke-dashoffset: 100%;
}
to {
stroke-dashoffset: 0%;
}
}
:deep(.highlight-todo.djs-connection > .djs-visual > path) {
animation: path-animation 60s;
animation-timing-function: linear;
animation-iteration-count: infinite;
stroke-dasharray: 4px !important;
stroke: orange !important;
fill-opacity: 0.2 !important;
marker-end: url('#sequenceflow-end-_E7DFDF-_E7DFDF-803g1kf6zwzmcig1y2ulm5egr');
}
:deep(.highlight-todo.djs-shape .djs-visual > :nth-child(1)) {
animation: path-animation 60s;
animation-timing-function: linear;
animation-iteration-count: infinite;
stroke-dasharray: 4px !important;
stroke: orange !important;
fill: orange !important;
fill-opacity: 0.2 !important;
}
}
:deep(.verlays) {
width: 250px;
background: rgb(102, 102, 102);
border-radius: 4px;
border: 1px solid #ebeef5;
color: #fff;
padding: 15px 10px;
p {
line-height: 28px;
margin: 0;
padding: 0;
}
cursor: pointer;
}
</style>

View File

@ -2,39 +2,49 @@
<div class="container">
<el-dialog v-model="visible" draggable title="审批记录" :width="props.width" :height="props.height" :close-on-click-modal="false">
<el-tabs v-model="tabActiveName" class="demo-tabs">
<el-tab-pane label="流程图" name="bpmn">
<BpmnView ref="bpmnViewRef"></BpmnView>
<el-tab-pane v-loading="loading" label="流程图" name="bpmn">
<img :src="imgUrl" width="100%" style="margin: 0 auto" />
</el-tab-pane>
<el-tab-pane v-loading="loading" label="审批信息" name="info">
<div>
<el-table :data="historyList" style="width: 100%" border fit>
<el-table-column type="index" label="序号" align="center" width="60"></el-table-column>
<el-table-column prop="name" label="任务名称" sortable align="center"></el-table-column>
<el-table-column prop="nickName" :show-overflow-tooltip="true" label="办理人" sortable align="center">
<el-table-column prop="nodeName" label="任务名称" sortable align="center"></el-table-column>
<el-table-column prop="approveName" :show-overflow-tooltip="true" label="办理人" sortable align="center">
<template #default="scope">
<el-tag type="success">{{ scope.row.nickName || '无' }}</el-tag>
<template v-if="scope.row.approveName">
<el-tag v-for="(item, index) in scope.row.approveName.split(',')" :key="index" type="success">{{ item }}</el-tag>
</template>
<template v-else> <el-tag type="success"></el-tag></template>
</template>
</el-table-column>
<el-table-column label="状态" sortable align="center">
<el-table-column prop="flowStatus" label="状态" width="80" sortable align="center">
<template #default="scope">
<el-tag type="success">{{ scope.row.statusName }}</el-tag>
<dict-tag :options="wf_task_status" :value="scope.row.flowStatus"></dict-tag>
</template>
</el-table-column>
<el-table-column prop="comment" label="审批意见" sortable align="center"></el-table-column>
<el-table-column prop="startTime" label="开始时间" sortable align="center"></el-table-column>
<el-table-column prop="endTime" label="结束时间" sortable align="center"></el-table-column>
<el-table-column prop="runDuration" label="运行时长" sortable align="center"></el-table-column>
<el-table-column prop="attachmentList" label="附件" sortable align="center">
<el-table-column prop="message" label="审批意见" :show-overflow-tooltip="true" sortable align="center"></el-table-column>
<el-table-column prop="createTime" label="开始时间" width="160" :show-overflow-tooltip="true" sortable align="center"></el-table-column>
<el-table-column prop="updateTime" label="结束时间" width="160" :show-overflow-tooltip="true" sortable align="center"></el-table-column>
<el-table-column
prop="runDuration"
label="运行时常"
width="140"
:show-overflow-tooltip="true"
sortable
align="center"
></el-table-column>
<el-table-column prop="attachmentList" width="120" label="附件" align="center">
<template #default="scope">
<el-popover v-if="scope.row.attachmentList && scope.row.attachmentList.length > 0" placement="right" :width="310" trigger="click">
<template #reference>
<el-button style="margin-right: 16px">附件</el-button>
<el-button type="primary" style="margin-right: 16px">附件</el-button>
</template>
<el-table border :data="scope.row.attachmentList">
<el-table-column prop="name" width="202" :show-overflow-tooltip="true" label="附件名称"></el-table-column>
<el-table-column prop="originalName" width="202" :show-overflow-tooltip="true" label="附件名称"></el-table-column>
<el-table-column prop="name" width="80" align="center" :show-overflow-tooltip="true" label="操作">
<template #default="tool">
<el-button type="text" @click="handleDownload(tool.row.contentId)">下载</el-button>
<el-button type="text" @click="handleDownload(tool.row.ossId)">下载</el-button>
</template>
</el-table-column>
</el-table>
@ -49,37 +59,51 @@
</div>
</template>
<script lang="ts" setup>
import BpmnView from '@/components/BpmnView/index.vue';
import processApi from '@/api/workflow/processInstance';
import { flowImage } from '@/api/workflow/instance';
import { propTypes } from '@/utils/propTypes';
import { listByIds } from '@/api/system/oss';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { wf_task_status } = toRefs<any>(proxy?.useDict('wf_task_status'));
const props = defineProps({
width: propTypes.string.def('70%'),
width: propTypes.string.def('80%'),
height: propTypes.string.def('100%')
});
const loading = ref(false);
const visible = ref(false);
const historyList = ref<Array<any>>([]);
const tabActiveName = ref('bpmn');
const bpmnViewRef = ref<BpmnView>();
const imgUrl = ref('');
//初始化查询审批记录
const init = async (businessKey: string | number) => {
const init = async (businessId: string | number) => {
visible.value = true;
loading.value = true;
tabActiveName.value = 'bpmn';
historyList.value = [];
processApi.getHistoryRecord(businessKey).then((resp) => {
historyList.value = resp.data;
loading.value = false;
});
await nextTick(() => {
bpmnViewRef.value.init(businessKey);
flowImage(businessId).then((resp) => {
if (resp.data) {
historyList.value = resp.data.list;
imgUrl.value = 'data:image/gif;base64,' + resp.data.image;
if (historyList.value.length > 0) {
historyList.value.forEach((item) => {
if (item.ext) {
getIds(item.ext).then((res) => {
item.attachmentList = res.data;
});
} else {
item.attachmentList = [];
}
});
}
loading.value = false;
}
});
};
const getIds = async (ids: string | number) => {
const res = await listByIds(ids);
return res;
};
/** 下载按钮操作 */
const handleDownload = (ossId: string) => {

View File

@ -1,378 +0,0 @@
<template>
<el-dialog v-model="visible" draggable :title="title" :width="width" :height="height" append-to-body :close-on-click-modal="false">
<div v-if="multiInstance === 'add'" class="p-2">
<el-row :gutter="20">
<!-- 部门树 -->
<el-col :lg="4" :xs="24" style="">
<el-card shadow="hover">
<el-input v-model="deptName" placeholder="请输入部门名称" prefix-icon="Search" clearable />
<el-tree
ref="deptTreeRef"
class="mt-2"
node-key="id"
:data="deptOptions"
:props="{ label: 'label', children: 'children' }"
:expand-on-click-node="false"
:filter-node-method="filterNode"
highlight-current
default-expand-all
@node-click="handleNodeClick"
></el-tree>
</el-card>
</el-col>
<el-col :lg="20" :xs="24">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="search">
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item label="用户名称" prop="userName">
<el-input v-model="queryParams.userName" placeholder="请输入用户名称" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="手机号码" prop="phonenumber">
<el-input v-model="queryParams.phonenumber" placeholder="请输入手机号码" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</div>
</transition>
<el-card shadow="hover">
<template #header>
<el-row :gutter="10">
<right-toolbar v-model:show-search="showSearch" :search="true" @query-table="handleQuery"></right-toolbar>
</el-row>
</template>
<el-table ref="multipleTableRef" v-loading="loading" :data="userList" row-key="userId" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="50" align="center" />
<el-table-column key="userId" label="用户编号" align="center" prop="userId" />
<el-table-column key="userName" label="用户名称" align="center" prop="userName" :show-overflow-tooltip="true" />
<el-table-column key="nickName" label="用户昵称" align="center" prop="nickName" :show-overflow-tooltip="true" />
<el-table-column key="phonenumber" label="手机号码" align="center" prop="phonenumber" width="120" />
<el-table-column label="创建时间" align="center" prop="createTime" width="160">
<template #default="scope">
<span>{{ scope.row.createTime }}</span>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total > 0"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
:total="total"
@pagination="handleQuery"
/>
</el-card>
<el-card shadow="hover">
<el-tag v-for="(user, index) in chooseUserList" :key="user.userId" style="margin: 2px" closable @close="handleCloseTag(user, index)"
>{{ user.userName }}
</el-tag>
</el-card>
</el-col>
</el-row>
</div>
<div v-if="multiInstance === 'delete'" class="p-2">
<el-table v-loading="loading" :data="taskList" @selection-change="handleTaskSelection">
<el-table-column type="selection" width="55" />
<el-table-column prop="name" label="任务名称" />
<el-table-column prop="assigneeName" label="办理人" />
</el-table>
</div>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submitFileForm"> </el-button>
<el-button @click="visible = false"> </el-button>
</div>
</template>
</el-dialog>
</template>
<script setup name="User" lang="ts">
import { deptTreeSelect, listUser, optionSelect } from '@/api/system/user';
import {
addMultiInstanceExecution,
deleteMultiInstanceExecution,
getTaskUserIdsByAddMultiInstance,
getListByDeleteMultiInstance
} from '@/api/workflow/task';
import { UserVO } from '@/api/system/user/types';
import { DeptVO } from '@/api/system/dept/types';
import { ComponentInternalInstance } from 'vue';
import { ElTree, ElTable } from 'element-plus';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const props = defineProps({
// 宽
width: {
type: String,
default: '70%'
},
// 高
height: {
type: String,
default: '100%'
},
// 标题
title: {
type: String,
default: '加签人员'
},
//是否多选
multiple: {
type: Boolean,
default: true
},
//回显用户id
userIdList: {
type: Array,
default: () => []
}
});
const deptTreeRef = ref(ElTree);
const multipleTableRef = ref(ElTable);
const userList = ref<UserVO[]>();
const taskList = ref<Array<any>[]>();
const loading = ref(true);
const showSearch = ref(true);
const selectionTask = ref<Array<any>[]>();
const visible = ref(false);
const total = ref(0);
const deptName = ref('');
const deptOptions = ref<DeptVO[]>([]);
const chooseUserList = ref(ref<UserVO[]>());
const userIds = ref<Array<number | string>>([]);
//加签或者减签
const multiInstance = ref('');
const queryParams = ref<Record<string, any>>({
pageNum: 1,
pageSize: 10,
userName: '',
nickName: '',
taskId: ''
});
/** 查询用户列表 */
const getAddMultiInstanceList = async (taskId: string, userIdList: Array<number | string>) => {
deptOptions.value = [];
getTreeSelect();
multiInstance.value = 'add';
userIds.value = userIdList;
visible.value = true;
queryParams.value.taskId = taskId;
loading.value = true;
const res1 = await getTaskUserIdsByAddMultiInstance(taskId);
queryParams.value.excludeUserIds = res1.data;
const res = await listUser(queryParams.value);
loading.value = false;
userList.value = res.rows;
total.value = res.total;
if (userList.value && userIds.value.length > 0) {
const data = await optionSelect(userIds.value);
if (data.data && data.data.length > 0) {
chooseUserList.value = data.data;
data.data.forEach((user: UserVO) => {
multipleTableRef.value!.toggleRowSelection(
userList.value.find((item) => {
return item.userId == user.userId;
}),
true
);
});
}
}
};
const getList = async () => {
loading.value = true;
const res1 = await getTaskUserIdsByAddMultiInstance(queryParams.value.taskId);
queryParams.value.excludeUserIds = res1.data;
const res = await listUser(queryParams.value);
loading.value = false;
userList.value = res.rows;
total.value = res.total;
if (userList.value && userIds.value.length > 0) {
const data = await optionSelect(userIds.value);
if (data.data && data.data.length > 0) {
chooseUserList.value = data.data;
data.data.forEach((user: UserVO) => {
multipleTableRef.value!.toggleRowSelection(
userList.value.find((item) => {
return item.userId == user.userId;
}),
true
);
});
}
}
};
const getDeleteMultiInstanceList = async (taskId: string) => {
deptOptions.value = [];
loading.value = true;
queryParams.value.taskId = taskId;
multiInstance.value = 'delete';
visible.value = true;
const res = await getListByDeleteMultiInstance(taskId);
taskList.value = res.data;
loading.value = false;
};
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.value.pageNum = 1;
getAddMultiInstanceList(queryParams.value.taskId, userIds.value);
};
/** 重置按钮操作 */
const resetQuery = () => {
queryParams.value.pageNum = 1;
queryParams.value.deptId = undefined;
queryParams.value.userName = undefined;
queryParams.value.nickName = undefined;
deptTreeRef.value.setCurrentKey(null);
handleQuery();
};
/** 选择条数 */
const handleSelectionChange = (selection: UserVO[]) => {
if (props.multiple) {
chooseUserList.value = selection.filter((element, index, self) => {
return self.findIndex((x) => x.userId === element.userId) === index;
});
selection.forEach((u) => {
if (chooseUserList.value && !chooseUserList.value.includes(u)) {
multipleTableRef.value!.toggleRowSelection(u, undefined);
}
});
userIds.value = chooseUserList.value.map((item) => {
return item.userId;
});
} else {
chooseUserList.value = selection;
if (selection.length > 1) {
let delRow = selection.shift();
multipleTableRef.value!.toggleRowSelection(delRow, undefined);
}
if (selection.length === 0) {
chooseUserList.value = [];
}
}
};
/** 选择条数 */
const handleTaskSelection = (selection: any) => {
selectionTask.value = selection;
};
/** 查询部门下拉树结构 */
const getTreeSelect = async () => {
const res = await deptTreeSelect();
deptOptions.value = res.data;
};
/** 通过条件过滤节点 */
const filterNode = (value: string, data: any) => {
if (!value) return true;
return data.label.indexOf(value) !== -1;
};
/** 根据名称筛选部门树 */
watchEffect(
() => {
if (visible.value && deptOptions.value && deptOptions.value.length > 0) {
deptTreeRef.value.filter(deptName.value);
}
},
{
flush: 'post' // watchEffect会在DOM挂载或者更新之前就会触发此属性控制在DOM元素更新后运行
}
);
/** 节点单击事件 */
const handleNodeClick = (data: DeptVO) => {
queryParams.value.deptId = data.id;
getList();
};
//删除tag
const handleCloseTag = (user: UserVO, index: any) => {
if (multipleTableRef.value.selection && multipleTableRef.value.selection.length > 0) {
multipleTableRef.value.selection.forEach((u: UserVO, i: number) => {
if (user.userId === u.userId) {
multipleTableRef.value.selection.splice(i, 1);
}
});
}
if (chooseUserList.value && chooseUserList.value.length > 0) {
chooseUserList.value.splice(index, 1);
}
multipleTableRef.value.toggleRowSelection(user, undefined);
if (userIds.value && userIds.value.length > 0) {
userIds.value.forEach((userId, i) => {
if (userId === user.userId) {
userIds.value.splice(i, 1);
}
});
}
};
const submitFileForm = async () => {
if (multiInstance.value === 'add') {
if (chooseUserList.value && chooseUserList.value.length > 0) {
loading.value = true;
let userIds = chooseUserList.value.map((item) => {
return item.userId;
});
let nickNames = chooseUserList.value.map((item) => {
return item.nickName;
});
let params = {
taskId: queryParams.value.taskId,
assignees: userIds,
assigneeNames: nickNames
};
await addMultiInstanceExecution(params);
emits('submitCallback');
loading.value = false;
proxy?.$modal.msgSuccess('操作成功');
visible.value = false;
}
} else {
if (selectionTask.value && selectionTask.value.length > 0) {
loading.value = true;
let taskIds = selectionTask.value.map((item: any) => {
return item.id;
});
let executionIds = selectionTask.value.map((item: any) => {
return item.executionId;
});
let assigneeIds = selectionTask.value.map((item: any) => {
return item.assignee;
});
let assigneeNames = selectionTask.value.map((item: any) => {
return item.assigneeName;
});
let params = {
taskId: queryParams.value.taskId,
taskIds: taskIds,
executionIds: executionIds,
assigneeIds: assigneeIds,
assigneeNames: assigneeNames
};
await deleteMultiInstanceExecution(params);
emits('submitCallback');
loading.value = false;
proxy?.$modal.msgSuccess('操作成功');
visible.value = false;
}
}
};
//事件
const emits = defineEmits(['submitCallback']);
/**
* 对外暴露子组件方法
*/
defineExpose({
getAddMultiInstanceList,
getDeleteMultiInstanceList
});
</script>

View File

@ -0,0 +1,207 @@
<template>
<el-dialog v-model="visible" draggable title="流程干预" :width="props.width" :height="props.height" :close-on-click-modal="false">
<el-descriptions v-loading="loading" class="margin-top" :title="`${task.flowName}(${task.flowCode})`" :column="2" border>
<el-descriptions-item label="任务名称">{{ task.nodeName }}</el-descriptions-item>
<el-descriptions-item label="节点编码">{{ task.nodeCode }}</el-descriptions-item>
<el-descriptions-item label="开始时间">{{ task.createTime }}</el-descriptions-item>
<el-descriptions-item label="流程实例ID">{{ task.instanceId }}</el-descriptions-item>
<el-descriptions-item label="版本号">{{ task.version }}.0</el-descriptions-item>
<el-descriptions-item label="业务ID">{{ task.businessId }}</el-descriptions-item>
</el-descriptions>
<template #footer>
<span class="dialog-footer">
<el-button v-if="task.flowStatus === 'waiting'" :disabled="buttonDisabled" type="primary" @click="openTransferTask"> 转办 </el-button>
<el-button
v-if="task.flowStatus === 'waiting' && Number(task.nodeRatio) > 0"
:disabled="buttonDisabled"
type="primary"
@click="openMultiInstanceUser"
>
加签
</el-button>
<el-button
v-if="task.flowStatus === 'waiting' && Number(task.nodeRatio) > 0"
:disabled="buttonDisabled"
type="primary"
@click="handleTaskUser"
>
减签
</el-button>
<el-button v-if="task.flowStatus === 'waiting'" :disabled="buttonDisabled" type="danger" @click="handleTerminationTask"> 终止 </el-button>
</span>
</template>
<!-- 转办 -->
<UserSelect ref="transferTaskRef" :multiple="false" @confirm-call-back="handleTransferTask"></UserSelect>
<!-- 加签组件 -->
<UserSelect ref="multiInstanceUserRef" :multiple="true" @confirm-call-back="addMultiInstanceUser"></UserSelect>
<el-dialog v-model="deleteSignatureVisible" draggable title="减签人员" width="700px" height="400px" append-to-body :close-on-click-modal="false"
><div>
<el-table :data="deleteUserList" border>
<el-table-column prop="nodeName" label="任务名称" />
<el-table-column prop="nickName" label="办理人" />
<el-table-column label="操作" align="center" width="160">
<template #default="scope">
<el-button type="danger" size="small" icon="Delete" @click="deleteMultiInstanceUser(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
</div>
</el-dialog>
</el-dialog>
</template>
<script lang="ts" setup>
import { propTypes } from '@/utils/propTypes';
import { FlowTaskVO, TaskOperationBo } from '@/api/workflow/task/types';
import UserSelect from '@/components/UserSelect';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
import { getTask, taskOperation, currentTaskAllUser, terminationTask } from '@/api/workflow/task';
const props = defineProps({
width: propTypes.string.def('50%'),
height: propTypes.string.def('100%')
});
const emits = defineEmits(['submitCallback']);
const transferTaskRef = ref<InstanceType<typeof UserSelect>>();
const multiInstanceUserRef = ref<InstanceType<typeof UserSelect>>();
//遮罩层
const loading = ref(true);
//按钮
const buttonDisabled = ref(true);
const visible = ref(false);
//减签弹窗
const deleteSignatureVisible = ref(false);
//可减签的人员
const deleteUserList = ref<any>([]);
//任务
const task = ref<FlowTaskVO>({
id: undefined,
createTime: undefined,
updateTime: undefined,
tenantId: undefined,
definitionId: undefined,
instanceId: undefined,
flowName: undefined,
businessId: undefined,
nodeCode: undefined,
nodeName: undefined,
flowCode: undefined,
flowStatus: undefined,
nodeType: undefined,
nodeRatio: undefined,
version: undefined
});
const open = (taskId: string) => {
visible.value = true;
getTask(taskId).then((response) => {
loading.value = false;
buttonDisabled.value = false;
task.value = response.data;
});
};
//打开转办
const openTransferTask = () => {
transferTaskRef.value.open();
};
//转办
const handleTransferTask = async (data) => {
if (data && data.length > 0) {
const taskOperationBo = reactive<TaskOperationBo>({
userId: data[0].userId,
taskId: task.value.id,
message: ''
});
await proxy?.$modal.confirm('是否确认提交?');
loading.value = true;
buttonDisabled.value = true;
await taskOperation(taskOperationBo, 'transferTask').finally(() => {
loading.value = false;
buttonDisabled.value = false;
});
visible.value = false;
emits('submitCallback');
proxy?.$modal.msgSuccess('操作成功');
} else {
proxy?.$modal.msgWarning('请选择用户!');
}
};
//加签
const openMultiInstanceUser = async () => {
multiInstanceUserRef.value.open();
};
//加签
const addMultiInstanceUser = async (data) => {
if (data && data.length > 0) {
const taskOperationBo = reactive<TaskOperationBo>({
userIds: data.map((e) => e.userId),
taskId: task.value.id,
message: ''
});
await proxy?.$modal.confirm('是否确认提交?');
loading.value = true;
buttonDisabled.value = true;
await taskOperation(taskOperationBo, 'addSignature').finally(() => {
loading.value = false;
buttonDisabled.value = false;
});
visible.value = false;
emits('submitCallback');
proxy?.$modal.msgSuccess('操作成功');
} else {
proxy?.$modal.msgWarning('请选择用户!');
}
};
//减签
const deleteMultiInstanceUser = async (row) => {
await proxy?.$modal.confirm('是否确认提交?');
loading.value = true;
buttonDisabled.value = true;
const taskOperationBo = reactive<TaskOperationBo>({
userIds: [row.userId],
taskId: task.value.id,
message: ''
});
await taskOperation(taskOperationBo, 'reductionSignature').finally(() => {
loading.value = false;
buttonDisabled.value = false;
});
visible.value = false;
emits('submitCallback');
proxy?.$modal.msgSuccess('操作成功');
};
//获取办理人
const handleTaskUser = async () => {
let data = await currentTaskAllUser(task.value.id);
deleteUserList.value = data.data;
if (deleteUserList.value && deleteUserList.value.length > 0) {
deleteUserList.value.forEach((e) => {
e.nodeName = task.value.nodeName;
});
}
deleteSignatureVisible.value = true;
};
//终止任务
const handleTerminationTask = async () => {
let params = {
taskId: task.value.id,
comment: ''
};
await proxy?.$modal.confirm('是否确认终止?');
loading.value = true;
buttonDisabled.value = true;
await terminationTask(params).finally(() => {
loading.value = false;
buttonDisabled.value = false;
});
visible.value = false;
emits('submitCallback');
proxy?.$modal.msgSuccess('操作成功');
};
/**
* 对外暴露子组件方法
*/
defineExpose({
open
});
</script>

View File

@ -3,47 +3,47 @@
<el-form v-loading="loading" :model="form" label-width="120px">
<el-form-item label="消息提醒">
<el-checkbox-group v-model="form.messageType">
<el-checkbox label="1" name="type" disabled>站内信</el-checkbox>
<el-checkbox label="2" name="type">邮件</el-checkbox>
<el-checkbox label="3" name="type">短信</el-checkbox>
<el-checkbox value="1" name="type" disabled>站内信</el-checkbox>
<el-checkbox value="2" name="type">邮件</el-checkbox>
<el-checkbox value="3" name="type">短信</el-checkbox>
</el-checkbox-group>
</el-form-item>
<el-form-item v-if="task.businessStatus === 'waiting'" label="附件">
<fileUpload v-model="form.fileId" :file-type="['doc', 'xls', 'ppt', 'txt', 'pdf', 'xlsx', 'docx', 'zip']" :file-size="'20'" />
<el-form-item v-if="task.flowStatus === 'waiting'" label="附件">
<fileUpload v-model="form.fileId" :file-type="['png', 'jpg', 'jpeg', 'doc', 'docx', 'xlsx', 'xls', 'ppt', 'txt', 'pdf']" :file-size="20" />
</el-form-item>
<el-form-item label="抄送">
<el-button type="primary" icon="Plus" circle @click="openUserSelectCopy" />
<el-tag v-for="user in selectCopyUserList" :key="user.userId" closable style="margin: 2px" @close="handleCopyCloseTag(user)">
{{ user.userName }}
{{ user.nickName }}
</el-tag>
</el-form-item>
<el-form-item v-if="task.businessStatus === 'waiting'" label="审批意见">
<el-form-item v-if="task.flowStatus === 'waiting'" label="审批意见">
<el-input v-model="form.message" type="textarea" resize="none" />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button :disabled="buttonDisabled" type="primary" @click="handleCompleteTask"> 提交 </el-button>
<el-button v-if="task.businessStatus === 'waiting'" :disabled="buttonDisabled" type="primary" @click="openDelegateTask"> 委托 </el-button>
<el-button v-if="task.businessStatus === 'waiting'" :disabled="buttonDisabled" type="primary" @click="openTransferTask"> 转办 </el-button>
<el-button v-if="task.flowStatus === 'waiting'" :disabled="buttonDisabled" type="primary" @click="openDelegateTask"> 委托 </el-button>
<el-button v-if="task.flowStatus === 'waiting'" :disabled="buttonDisabled" type="primary" @click="openTransferTask"> 转办 </el-button>
<el-button
v-if="task.businessStatus === 'waiting' && task.multiInstance"
v-if="task.flowStatus === 'waiting' && Number(task.nodeRatio) > 0"
:disabled="buttonDisabled"
type="primary"
@click="addMultiInstanceUser"
@click="openMultiInstanceUser"
>
加签
</el-button>
<el-button
v-if="task.businessStatus === 'waiting' && task.multiInstance"
v-if="task.flowStatus === 'waiting' && Number(task.nodeRatio) > 0"
:disabled="buttonDisabled"
type="primary"
@click="deleteMultiInstanceUser"
@click="handleTaskUser"
>
减签
</el-button>
<el-button v-if="task.businessStatus === 'waiting'" :disabled="buttonDisabled" type="danger" @click="handleTerminationTask"> 终止 </el-button>
<el-button v-if="task.businessStatus === 'waiting'" :disabled="buttonDisabled" type="danger" @click="handleBackProcessOpen"> 退回 </el-button>
<el-button v-if="task.flowStatus === 'waiting'" :disabled="buttonDisabled" type="danger" @click="handleTerminationTask"> 终止 </el-button>
<el-button v-if="task.flowStatus === 'waiting'" :disabled="buttonDisabled" type="danger" @click="handleBackProcessOpen"> 退回 </el-button>
<el-button :disabled="buttonDisabled" @click="cancel">取消</el-button>
</span>
</template>
@ -54,14 +54,14 @@
<!-- 委托 -->
<UserSelect ref="delegateTaskRef" :multiple="false" @confirm-call-back="handleDelegateTask"></UserSelect>
<!-- 加签组件 -->
<multiInstanceUser ref="multiInstanceUserRef" :title="title" @submit-callback="closeDialog" />
<UserSelect ref="multiInstanceUserRef" :multiple="true" @confirm-call-back="addMultiInstanceUser"></UserSelect>
<!-- 驳回开始 -->
<el-dialog v-model="backVisible" draggable title="驳回" width="40%" :close-on-click-modal="false">
<el-form v-if="task.businessStatus === 'waiting'" v-loading="backLoading" :model="backForm" label-width="120px">
<el-form v-if="task.flowStatus === 'waiting'" v-loading="backLoading" :model="backForm" label-width="120px">
<el-form-item label="驳回节点">
<el-select v-model="backForm.targetActivityId" clearable placeholder="请选择" style="width: 300px">
<el-option v-for="item in taskNodeList" :key="item.nodeId" :label="item.nodeName" :value="item.nodeId" />
<el-select v-model="backForm.nodeCode" clearable placeholder="请选择" style="width: 300px">
<el-option v-for="item in taskNodeList" :key="item.nodeCode" :label="item.nodeName" :value="item.nodeCode" />
</el-select>
</el-form-item>
<el-form-item label="消息提醒">
@ -83,6 +83,19 @@
</template>
</el-dialog>
<!-- 驳回结束 -->
<el-dialog v-model="deleteSignatureVisible" draggable title="减签人员" width="700px" height="400px" append-to-body :close-on-click-modal="false">
<div>
<el-table :data="deleteUserList" border>
<el-table-column prop="nodeName" label="任务名称" />
<el-table-column prop="nickName" label="办理人" />
<el-table-column label="操作" align="center" width="160">
<template #default="scope">
<el-button type="danger" size="small" icon="Delete" @click="deleteMultiInstanceUser(scope.row)">删除 </el-button>
</template>
</el-table-column>
</el-table>
</div>
</el-dialog>
</el-dialog>
</template>
@ -90,18 +103,17 @@
import { ref } from 'vue';
import { ComponentInternalInstance } from 'vue';
import { ElForm } from 'element-plus';
import { completeTask, backProcess, getTaskById, transferTask, terminationTask, getTaskNodeList, delegateTask } from '@/api/workflow/task';
import { completeTask, backProcess, getTask, taskOperation, terminationTask, getBackTaskNode, currentTaskAllUser } from '@/api/workflow/task';
import UserSelect from '@/components/UserSelect';
import MultiInstanceUser from '@/components/Process/multiInstanceUser.vue';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
import { UserVO } from '@/api/system/user/types';
import { TaskVO } from '@/api/workflow/task/types';
import { FlowTaskVO, TaskOperationBo } from '@/api/workflow/task/types';
const userSelectCopyRef = ref<InstanceType<typeof UserSelect>>();
const transferTaskRef = ref<InstanceType<typeof UserSelect>>();
const delegateTaskRef = ref<InstanceType<typeof UserSelect>>();
//加签组件
const multiInstanceUserRef = ref<InstanceType<typeof MultiInstanceUser>>();
const multiInstanceUserRef = ref<InstanceType<typeof UserSelect>>();
const props = defineProps({
taskVariables: {
@ -119,65 +131,53 @@ const taskId = ref<string>('');
const selectCopyUserList = ref<UserVO[]>([]);
//抄送人id
const selectCopyUserIds = ref<string>(undefined);
// 驳回是否显示
//可减签的人员
const deleteUserList = ref<any>([]);
//驳回是否显示
const backVisible = ref(false);
const backLoading = ref(true);
const backButtonDisabled = ref(true);
// 可驳回得任务节点
const taskNodeList = ref([]);
//任务
const task = ref<TaskVO>({
const task = ref<FlowTaskVO>({
id: undefined,
name: undefined,
description: undefined,
priority: undefined,
owner: undefined,
assignee: undefined,
assigneeName: undefined,
processInstanceId: undefined,
executionId: undefined,
taskDefinitionId: undefined,
processDefinitionId: undefined,
endTime: undefined,
taskDefinitionKey: undefined,
dueDate: undefined,
category: undefined,
parentTaskId: undefined,
createTime: undefined,
updateTime: undefined,
tenantId: undefined,
claimTime: undefined,
businessStatus: undefined,
businessStatusName: undefined,
processDefinitionName: undefined,
processDefinitionKey: undefined,
participantVo: undefined,
multiInstance: undefined,
businessKey: undefined,
wfNodeConfigVo: undefined
definitionId: undefined,
instanceId: undefined,
flowName: undefined,
businessId: undefined,
nodeCode: undefined,
nodeName: undefined,
flowCode: undefined,
flowStatus: undefined,
formCustom: undefined,
formPath: undefined,
nodeType: undefined,
nodeRatio: undefined
});
//加签 减签标题
const title = ref('');
const dialog = reactive<DialogOption>({
visible: false,
title: '提示'
});
//减签弹窗
const deleteSignatureVisible = ref(false);
const form = ref<Record<string, any>>({
taskId: undefined,
message: undefined,
variables: {},
messageType: ['1'],
wfCopyList: []
flowCopyList: []
});
const backForm = ref<Record<string, any>>({
taskId: undefined,
targetActivityId: undefined,
nodeCode: undefined,
message: undefined,
variables: {},
messageType: ['1']
});
const closeDialog = () => {
dialog.visible = false;
};
//打开弹窗
const openDialog = (id?: string) => {
selectCopyUserIds.value = undefined;
@ -189,7 +189,7 @@ const openDialog = (id?: string) => {
loading.value = true;
buttonDisabled.value = true;
nextTick(() => {
getTaskById(taskId.value).then((response) => {
getTask(taskId.value).then((response) => {
task.value = response.data;
loading.value = false;
buttonDisabled.value = false;
@ -205,15 +205,15 @@ const handleCompleteTask = async () => {
form.value.taskId = taskId.value;
form.value.taskVariables = props.taskVariables;
if (selectCopyUserList.value && selectCopyUserList.value.length > 0) {
let wfCopyList = [];
let flowCopyList = [];
selectCopyUserList.value.forEach((e) => {
let copyUser = {
userId: e.userId,
userName: e.nickName
};
wfCopyList.push(copyUser);
flowCopyList.push(copyUser);
});
form.value.wfCopyList = wfCopyList;
form.value.flowCopyList = flowCopyList;
}
await proxy?.$modal.confirm('是否确认提交?');
loading.value = true;
@ -236,11 +236,11 @@ const handleBackProcessOpen = async () => {
backVisible.value = true;
backLoading.value = true;
backButtonDisabled.value = true;
let data = await getTaskNodeList(task.value.processInstanceId);
let data = await getBackTaskNode(task.value.definitionId, task.value.nodeCode);
taskNodeList.value = data.data;
backLoading.value = false;
backButtonDisabled.value = false;
backForm.value.targetActivityId = taskNodeList.value[0].nodeId;
backForm.value.nodeCode = taskNodeList.value[0].nodeCode;
};
/** 驳回流程 */
const handleBackProcess = async () => {
@ -249,7 +249,10 @@ const handleBackProcess = async () => {
loading.value = true;
backLoading.value = true;
backButtonDisabled.value = true;
await backProcess(backForm.value).finally(() => (loading.value = false));
await backProcess(backForm.value).finally(() => {
loading.value = false;
buttonDisabled.value = false;
});
dialog.visible = false;
backLoading.value = false;
backButtonDisabled.value = false;
@ -282,18 +285,48 @@ const handleCopyCloseTag = (user: UserVO) => {
selectCopyUserIds.value = selectCopyUserList.value.map((item) => item.userId).join(',');
};
//加签
const addMultiInstanceUser = () => {
if (multiInstanceUserRef.value) {
title.value = '加签人员';
multiInstanceUserRef.value.getAddMultiInstanceList(taskId.value, []);
const openMultiInstanceUser = async () => {
multiInstanceUserRef.value.open();
};
//加签
const addMultiInstanceUser = async (data) => {
if (data && data.length > 0) {
const taskOperationBo = reactive<TaskOperationBo>({
userIds: data.map((e) => e.userId),
taskId: taskId.value,
message: form.value.message
});
await proxy?.$modal.confirm('是否确认提交?');
loading.value = true;
buttonDisabled.value = true;
await taskOperation(taskOperationBo, 'addSignature').finally(() => {
loading.value = false;
buttonDisabled.value = false;
});
dialog.visible = false;
emits('submitCallback');
proxy?.$modal.msgSuccess('操作成功');
} else {
proxy?.$modal.msgWarning('请选择用户!');
}
};
//减签
const deleteMultiInstanceUser = () => {
if (multiInstanceUserRef.value) {
title.value = '减签人员';
multiInstanceUserRef.value.getDeleteMultiInstanceList(taskId.value);
}
const deleteMultiInstanceUser = async (row) => {
await proxy?.$modal.confirm('是否确认提交?');
loading.value = true;
buttonDisabled.value = true;
const taskOperationBo = reactive<TaskOperationBo>({
userIds: [row.userId],
taskId: taskId.value,
message: form.value.message
});
await taskOperation(taskOperationBo, 'reductionSignature').finally(() => {
loading.value = false;
buttonDisabled.value = false;
});
dialog.visible = false;
emits('submitCallback');
proxy?.$modal.msgSuccess('操作成功');
};
//打开转办
const openTransferTask = () => {
@ -302,15 +335,18 @@ const openTransferTask = () => {
//转办
const handleTransferTask = async (data) => {
if (data && data.length > 0) {
let params = {
taskId: taskId.value,
const taskOperationBo = reactive<TaskOperationBo>({
userId: data[0].userId,
comment: form.value.message
};
taskId: taskId.value,
message: form.value.message
});
await proxy?.$modal.confirm('是否确认提交?');
loading.value = true;
buttonDisabled.value = true;
await transferTask(params).finally(() => (loading.value = false));
await taskOperation(taskOperationBo, 'transferTask').finally(() => {
loading.value = false;
buttonDisabled.value = false;
});
dialog.visible = false;
emits('submitCallback');
proxy?.$modal.msgSuccess('操作成功');
@ -326,15 +362,18 @@ const openDelegateTask = () => {
//委托
const handleDelegateTask = async (data) => {
if (data && data.length > 0) {
let params = {
taskId: taskId.value,
const taskOperationBo = reactive<TaskOperationBo>({
userId: data[0].userId,
nickName: data[0].nickName
};
taskId: taskId.value,
message: form.value.message
});
await proxy?.$modal.confirm('是否确认提交?');
loading.value = true;
buttonDisabled.value = true;
await delegateTask(params).finally(() => (loading.value = false));
await taskOperation(taskOperationBo, 'delegateTask').finally(() => {
loading.value = false;
buttonDisabled.value = false;
});
dialog.visible = false;
emits('submitCallback');
proxy?.$modal.msgSuccess('操作成功');
@ -343,7 +382,7 @@ const handleDelegateTask = async (data) => {
}
};
//终止任务
const handleTerminationTask = async (data) => {
const handleTerminationTask = async () => {
let params = {
taskId: taskId.value,
comment: form.value.message
@ -351,11 +390,24 @@ const handleTerminationTask = async (data) => {
await proxy?.$modal.confirm('是否确认终止?');
loading.value = true;
buttonDisabled.value = true;
await terminationTask(params).finally(() => (loading.value = false));
await terminationTask(params).finally(() => {
loading.value = false;
buttonDisabled.value = false;
});
dialog.visible = false;
emits('submitCallback');
proxy?.$modal.msgSuccess('操作成功');
};
const handleTaskUser = async () => {
let data = await currentTaskAllUser(taskId.value);
deleteUserList.value = data.data;
if (deleteUserList.value && deleteUserList.value.length > 0) {
deleteUserList.value.forEach((e) => {
e.nodeName = task.value.nodeName;
});
}
deleteSignatureVisible.value = true;
};
/**
* 对外暴露子组件方法

View File

@ -43,7 +43,7 @@
<el-card shadow="hover">
<template v-if="prop.multiple" #header>
<el-tag v-for="user in selectUserList" :key="user.userId" closable style="margin: 2px" @close="handleCloseTag(user)">
{{ user.userName }}
{{ user.nickName }}
</el-tag>
</template>
@ -100,14 +100,14 @@
<script setup lang="ts">
import api from '@/api/system/user';
import { UserQuery, UserVO } from '@/api/system/user/types';
import { DeptVO } from '@/api/system/dept/types';
import { DeptTreeVO, DeptVO } from '@/api/system/dept/types';
import { VxeTableInstance } from 'vxe-table';
import useDialog from '@/hooks/useDialog';
interface PropType {
modelValue?: UserVO[] | UserVO | undefined;
multiple?: boolean;
data?: string | number | (string | number)[];
data?: string | number | (string | number)[] | undefined;
}
const prop = withDefaults(defineProps<PropType>(), {
multiple: true,
@ -125,7 +125,7 @@ const showSearch = ref(true);
const total = ref(0);
const dateRange = ref<[DateModelType, DateModelType]>(['', '']);
const deptName = ref('');
const deptOptions = ref<DeptVO[]>([]);
const deptOptions = ref<DeptTreeVO[]>([]);
const selectUserList = ref<UserVO[]>([]);
const deptTreeRef = ref<ElTreeInstance>();