!88 合并flowable工作流功能

* merge 合并dev
* add 添加抄送查询
* add 添加我的已办
* add 添加抄送
* add 添加附件下载
* update 优化类型
* Merge remote-tracking branch 'origin/future/flowable' into future/flowable
* fix 修复 流程设计器打包部署报错问题
* add 添加审批附件上传
* update 修复固定值未在xml显示问题
* update 修复跳转条件回显Object问题
* update 优化面板文件名称
* update 调整key
* remove 移除旧设计器,添加xml保存
* update 增加任务面板提示
* update 优化xml预览和svg预览
* update 优化xml预览和svg预览
* update 优化模型设计
* update 优化设计器
* update 优化设计器
* update 优化设计器
* update 删除旧设计器
* update 删除console.log
* add 添加模型接口
* update 优化预览xml和svg样式被修改问题
* update 优化属性面板,增加展开动画
* update 去除开发模式
* update 优化任务栏样式
* update 优化图标渲染样式
* update 增加BpmnFactory类型
* update 增加BpmnFactory
* update 移除users和group
* update 移除无用类型
* update 优化页面类型
* update 去除多余属性
* update 完善流程线
* update 增加复杂网关
* update 完善流程
* update 完善网关
* update 优化网关汉化
* update 优化过期时间选择
* update 支持多实例
* update 增加类容提示
* update 支持选择组
* update 新增角色api
* update 优化roleSelect 选中未确定,再次打开还保留选中的问题
* update 优化userSelect 选中未确定,再次打开还保留选中的问题
* update 优化userSelect 选中未确定,再次打开还保留选中的问题
* update 去掉modeler store多余属性 bpmnModel
* update 优化属性面板,当面板未选中时默认展示流程面板
* update 优化TaskPanel类型,去掉roles属性
* update 优化用户api
* update 优化用户选择器
* update 优化执行监听器
* update 优化任务监听器
* update 优化usePanel方法
* update 选人优化
* update 增加扩展节点信息
* update 增加usePanel默认方法
* update 去除处理事件
* update 扩展flowable  userinfo属性
* update 全局modeler 改为非响应式
* update 增加hooks方法
* update 修改命名
* update 修改面板formData来源
* update 重写用户任务面板选择逻辑
* update 重写用户任务面板选择逻辑
* update 修改用户选择组件获取数据逻辑
* update 修改枚举类型
* update 修改默认配置列
* update 增加修改节点方法
* update 调整预览窗口大小
* update 优化用户选择组件 返回值
* update 优化用户选择组件
* update 新增通过ids 获取用户信息
* update 重写task面板选人 未完成
* update 升级用户选择 支持多选配置
* update 升级bpmnjs依赖版本
* update 增加useDialog类型
* update 调整全局样式
* update 代码高亮设置
* update 优化领用,归还加载
* update 增加选择角色
* update  新增角色选择组件
* update 新增过期时间选择组件
* update 调整任务面板样式
* update  调整全局dialog header 增加分割线
* update 代码高亮设置
* update  调整面板位置
* update 封装用户选择组件
* update 移除所有的节点描述
* update 删除分类
* update  调整面板位置
* update  修改命名,增加自定义渲染
* update  修改命名,增加自定义渲染
* update 增加 Element类型定义
* update 调整样式
* update 移除bpmn panel依赖,升级bpmn.js依赖到最新,修改汉化包
* update 调整类型声明文件
* update 调整类型声明文件
* update 优化面板工具
* update 优化面板工具
* Merge remote-tracking branch 'origin/future/flowable' into future/flowable
* update 优化面板工具
* Merge branch 'future/flowable' of https://gitee.com/JavaLionLi/plus-ui…
* add 添加修改办理人
* update 优化面板工具
* update 初始化流程数据
* Merge remote-tracking branch 'origin/future/flowable' into future/flowable
* add 流程设计面板
* update 调整初始化xml
* add 任务面板
* add 新增bpmn.js
* update 优化request请求类判断请求头方式
* update 流程定义预览 优化
* update 流程定义预览 优化
* update 去掉console.log
* update 优化工作流代码
* fix 修复待办任务 重置查询条件失效问题
* add 增加待办任务 接口类型,优化页面
* add 增加vite 启动预编译css
* fix 修复i18n无感刷新问题
* Merge branch 'dev' into future/flowable
* update 调整选择请假事件
* Merge branch 'dev' into future/flowable
* 同步dev代码
* Merge branch 'dev' into future/flowable
* 合并dev
* remove 设计器无用代码 调整请假查询
* update 调整请假申请
* update 移动请假表单包结构,调整设计器选择引用表单请求错误
* remove 移除动态表单
* update 调整流程办理
* Merge branch 'dev' into future/flowable
* Merge branch 'dev' into future/flowable
* Merge branch 'dev' into future/flowable
* Merge branch 'dev' into future/flowable
* 合并
* update 调整请假申请流程提交
* update 修改业务单据流程提交
* update 调整业务单据流程提交
* 优化代码
* 优化工作流代码缩进
* 项目格式化配置修改
* 调整代码缩进
* Merge branch 'ts' into future/flowable
* add 添加动态表单提交流程
* add 添加动态表单单据
* add 新增流程定义与表单关联
* update 调整点击左侧部门查询人员,部门刷新问题
* update 调整按钮图标
* 调整错别字
* update 调整流程定义图片预览
* add 添加流程实例迁移版本
* fix 修复我的单据无法提交问题
* Merge branch 'ts' into future/flowable
* remove 还原代码后端解决
* update 流程设计器中分配发起人变量错误,先移除
* fix 修复设计器无法编辑问题
* update 调整设计器请求头
* Merge branch 'ts' into future/flowable
* Merge branch 'ts' into future/flowable
* add 添加流程定义历史列表
* update 审批记录v2改为v3
* update 调整请假必填项
* add 添加请假申请示例,添加流程定义文件部署
* update 移除流程表单 formConfig 属性,表单配置信息都放一起便于使用。
* add 添加任务加签,减签
* update 调整流程作废
* update 优化流程状态
* add 添加任务驳回
* add 添加查询当前租户所有待办,已办任务
* add 增加审批意见
* add 添加流程办理弹窗确认组件
* add 添加任务作废理由
* update 调整流程实例,流程定义检索
* add 添加我的单据页面
* add 添加任务归还认领
* add 添加流程实例,流程定义分类查询
* add 添加模型分类查询
* add 添加流程分类
* Merge remote-tracking branch 'origin/future/flowable' into future/flowable
* add 添加流程表单管理页面
* add 集成vForm动态表单组件
* update xml调整超出滚动
* add 添加已办列表
* add 添加单据状态
* update 优化流程实例删除
* fix 修复流程实例查询挂起状态错误
* update 调整流程实例挂起激活状态
* add 添加流程实例列表
* update 调整流程定义弹窗提示
* add 添加流程定义列表,添加流程图,xml预览,添加简单流程启动,办理
* 调整审批记录悬浮逻辑
* 删除无用代码
* add 添加节点悬浮信息
* 调整流程预览
* 调整流程追踪
* add 添加审批记录
* add 模型设计的types
* update 修改排版
* add lang=ts
* update 修改ele的废弃api
* fix调整审批记录图片不显示问题
* add 添加任务待办,流程图
* 调整设计器关闭
* add 添加待办
* remove 删除无用代码
* update types
* 添加模型token验证
* 隐藏设计器验证按钮,隐藏表单,案例,应用程序等
* 添加模型部署
* 添加画图接口token,优化画图接口
* 添加工作流模型新增,修改,查询,删除,画图工具
This commit is contained in:
疯狂的狮子Li
2024-03-05 14:57:51 +00:00
parent 542f73f0e6
commit 0108df1334
70 changed files with 9502 additions and 9 deletions

View File

@ -0,0 +1,263 @@
<template>
<div class="p-2">
<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" label-width="68px">
<el-form-item label="分类名称" prop="categoryName">
<el-input v-model="queryParams.categoryName" placeholder="请输入分类名称" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="分类编码" prop="categoryCode">
<el-input v-model="queryParams.categoryCode" 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="never">
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button v-hasPermi="['workflow:category:add']" type="primary" plain icon="Plus" @click="handleAdd()">新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="info" plain icon="Sort" @click="handleToggleExpandAll">展开/折叠</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @query-table="getList"></right-toolbar>
</el-row>
</template>
<el-table
ref="categoryTableRef"
v-loading="loading"
:data="categoryList"
row-key="id"
:default-expand-all="isExpandAll"
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
>
<el-table-column label="分类名称" prop="categoryName" />
<el-table-column label="分类编码" align="center" prop="categoryCode" />
<el-table-column label="排序" align="center" prop="sortNum" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip content="修改" placement="top">
<el-button v-hasPermi="['workflow:category:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)" />
</el-tooltip>
<el-tooltip content="新增" placement="top">
<el-button v-hasPermi="['workflow:category:add']" link type="primary" icon="Plus" @click="handleAdd(scope.row)" />
</el-tooltip>
<el-tooltip content="删除" placement="top">
<el-button v-hasPermi="['workflow:category:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)" />
</el-tooltip>
</template>
</el-table-column>
</el-table>
</el-card>
<!-- 添加或修改流程分类对话框 -->
<el-dialog v-model="dialog.visible" :title="dialog.title" width="500px" append-to-body>
<el-form ref="categoryFormRef" v-loading="loading" :model="form" :rules="rules" label-width="80px">
<el-form-item label="父级分类" prop="parentId">
<el-tree-select
v-model="form.parentId"
:data="categoryOptions"
:props="{ value: 'id', label: 'categoryName', children: 'children' }"
value-key="id"
placeholder="请选择父级id"
check-strictly
/>
</el-form-item>
<el-form-item label="分类名称" prop="categoryName">
<el-input v-model="form.categoryName" placeholder="请输入分类名称" />
</el-form-item>
<el-form-item label="分类编码" prop="categoryCode">
<el-input v-model="form.categoryCode" placeholder="请输入分类编码" />
</el-form-item>
<el-form-item label="排序" prop="sortNum">
<el-input-number v-model="form.sortNum" placeholder="请输入排序" controls-position="right" :min="0" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button :loading="buttonLoading" type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup name="Category" lang="ts">
import { listCategory, getCategory, delCategory, addCategory, updateCategory } from '@/api/workflow/category';
import { CategoryVO, CategoryQuery, CategoryForm } from '@/api/workflow/category/types';
type CategoryOption = {
id: number;
categoryName: string;
children?: CategoryOption[];
};
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const categoryList = ref<CategoryVO[]>([]);
const categoryOptions = ref<CategoryOption[]>([]);
const buttonLoading = ref(false);
const showSearch = ref(true);
const isExpandAll = ref(true);
const loading = ref(false);
const queryFormRef = ref<ElFormInstance>();
const categoryFormRef = ref<ElFormInstance>();
const categoryTableRef = ref<ElTableInstance>();
const dialog = reactive<DialogOption>({
visible: false,
title: ''
});
const initFormData: CategoryForm = {
id: undefined,
categoryName: undefined,
categoryCode: undefined,
parentId: undefined,
sortNum: 0
};
const data = reactive<PageData<CategoryForm, CategoryQuery>>({
form: { ...initFormData },
queryParams: {
pageNum: 1,
pageSize: 10,
categoryName: undefined,
categoryCode: undefined
},
rules: {
id: [{ required: true, message: '主键不能为空', trigger: 'blur' }],
categoryName: [{ required: true, message: '分类名称不能为空', trigger: 'blur' }],
categoryCode: [{ required: true, message: '分类编码不能为空', trigger: 'blur' }],
parentId: [{ required: true, message: '父级id不能为空', trigger: 'blur' }]
}
});
const { queryParams, form, rules } = toRefs(data);
/** 查询流程分类列表 */
const getList = async () => {
loading.value = true;
const res = await listCategory(queryParams.value);
const data = proxy?.handleTree<CategoryVO>(res.data, 'id', 'parentId');
if (data) {
categoryList.value = data;
loading.value = false;
}
};
/** 查询流程分类下拉树结构 */
const getTreeselect = async () => {
const res = await listCategory();
categoryOptions.value = [];
const data: CategoryOption = { id: 0, categoryName: '顶级节点', children: [] };
data.children = proxy?.handleTree<CategoryOption>(res.data, 'id', 'parentId');
categoryOptions.value.push(data);
};
// 取消按钮
const cancel = () => {
reset();
dialog.visible = false;
};
// 表单重置
const reset = () => {
form.value = { ...initFormData };
categoryFormRef.value?.resetFields();
};
/** 搜索按钮操作 */
const handleQuery = () => {
getList();
};
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value?.resetFields();
handleQuery();
};
/** 新增按钮操作 */
const handleAdd = (row?: CategoryVO) => {
dialog.visible = true;
dialog.title = '添加流程分类';
nextTick(() => {
reset();
getTreeselect();
if (row != null && row.id) {
form.value.parentId = row.id;
} else {
form.value.parentId = 0;
}
});
};
/** 展开/折叠操作 */
const handleToggleExpandAll = () => {
isExpandAll.value = !isExpandAll.value;
toggleExpandAll(categoryList.value, isExpandAll.value);
};
/** 展开/折叠操作 */
const toggleExpandAll = (data: CategoryVO[], status: boolean) => {
data.forEach((item) => {
categoryTableRef.value?.toggleRowExpansion(item, status);
if (item.children && item.children.length > 0) toggleExpandAll(item.children, status);
});
};
/** 修改按钮操作 */
const handleUpdate = (row: CategoryVO) => {
loading.value = true;
dialog.visible = true;
dialog.title = '修改流程分类';
nextTick(async () => {
reset();
await getTreeselect();
if (row != null) {
form.value.parentId = row.id;
}
const res = await getCategory(row.id);
loading.value = false;
Object.assign(form.value, res.data);
});
};
/** 提交按钮 */
const submitForm = () => {
categoryFormRef.value.validate(async (valid: boolean) => {
if (valid) {
buttonLoading.value = true;
if (form.value.id) {
await updateCategory(form.value).finally(() => (buttonLoading.value = false));
} else {
await addCategory(form.value).finally(() => (buttonLoading.value = false));
}
proxy?.$modal.msgSuccess('操作成功');
dialog.visible = false;
await getList();
}
});
};
/** 删除按钮操作 */
const handleDelete = async (row: CategoryVO) => {
await proxy?.$modal.confirm('是否确认删除流程分类编号为"' + row.id + '"的数据项?');
loading.value = true;
await delCategory(row.id).finally(() => (loading.value = false));
await getList();
proxy?.$modal.msgSuccess('删除成功');
};
onMounted(() => {
getList();
});
</script>

View File

@ -0,0 +1,381 @@
<template>
<div class="p-2">
<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" label-width="68px">
<el-form-item label="请假天数" prop="startLeaveDays">
<el-input v-model="queryParams.startLeaveDays" placeholder="请输入请假天数" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item prop="endLeaveDays"> </el-form-item>
<el-form-item prop="endLeaveDays">
<el-input v-model="queryParams.endLeaveDays" 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="never">
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button v-hasPermi="['demo:leave:add']" type="primary" plain icon="Plus" @click="handleAdd">新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button v-hasPermi="['demo:leave:export']" type="warning" plain icon="Download" @click="handleExport">导出</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @query-table="getList"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" :data="leaveList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column v-if="false" label="主键" align="center" prop="id" />
<el-table-column label="请假类型" align="center">
<template #default="scope">
<el-tag>{{ options.find((e) => e.value === scope.row.leaveType)?.label }}</el-tag>
</template>
</el-table-column>
<el-table-column label="开始时间" align="center" prop="startDate">
<template #default="scope">
<span>{{ parseTime(scope.row.startDate, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="结束时间" align="center" prop="endDate">
<template #default="scope">
<span>{{ parseTime(scope.row.endDate, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="请假天数" align="center" prop="leaveDays" />
<el-table-column label="请假原因" align="center" prop="remark" />
<el-table-column align="center" prop="businessStatusName" label="流程状态" min-width="70">
<template #default="scope">
<el-tag type="success">{{ scope.row.processInstanceVo.businessStatusName }}</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip
v-if="
scope.row.processInstanceVo.businessStatus === 'draft' ||
scope.row.processInstanceVo.businessStatus === 'cancel' ||
scope.row.processInstanceVo.businessStatus === 'back'
"
content="修改"
placement="top"
>
<el-button v-hasPermi="['demo:leave:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)"></el-button>
</el-tooltip>
<el-tooltip
v-if="
scope.row.processInstanceVo.businessStatus === 'draft' ||
scope.row.processInstanceVo.businessStatus === 'cancel' ||
scope.row.processInstanceVo.businessStatus === 'back'
"
content="删除"
placement="top"
>
<el-button v-hasPermi="['demo:leave:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)"></el-button>
</el-tooltip>
<el-tooltip v-if="scope.row.processInstanceVo.businessStatus === 'waiting'" content="撤销" placement="top">
<el-button link type="primary" icon="Notification" @click="handleCancelProcessApply(scope.row.processInstanceVo.id)"></el-button>
</el-tooltip>
<el-tooltip v-if="scope.row.processInstanceVo.businessStatus === 'waiting'" content="审批记录" placement="top">
<el-button link type="primary" icon="Document" @click="handleApprovalRecord(scope.row.processInstanceVo.id)"></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" />
</el-card>
<!-- 添加或修改请假对话框 -->
<el-dialog v-model="dialog.visible" :title="dialog.title" width="800px" append-to-body>
<el-form ref="leaveFormRef" v-loading="loading" :model="form" :rules="rules" label-width="80px">
<el-form-item label="请假类型" prop="leaveType">
<el-select v-model="form.leaveType" placeholder="请选择请假类型" style="width: 100%">
<el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="请假时间">
<el-date-picker
v-model="leaveTime"
type="daterange"
range-separator="To"
start-placeholder="开始时间"
end-placeholder="结束时间"
@change="changeLeaveTime()"
/>
</el-form-item>
<el-form-item label="请假天数" prop="leaveDays">
<el-input v-model="form.leaveDays" disabled type="number" placeholder="请输入请假天数" />
</el-form-item>
<el-form-item label="请假原因" prop="remark">
<el-input v-model="form.remark" type="textarea" :rows="3" placeholder="请输入请假原因" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button :loading="buttonLoading" type="info" @click="submitForm('draft')">暂 存</el-button>
<el-button :loading="buttonLoading" type="primary" @click="submitForm('submit')">提 交</el-button>
<el-button @click="cancel">取 消</el-button>
</div>
</template>
</el-dialog>
<!-- 提交组件 -->
<submitVerify ref="submitVerifyRef" :task-variables="taskVariables" @submit-callback="submitCallback" />
<!-- 审批记录 -->
<approvalRecord ref="approvalRecordRef" />
</div>
</template>
<script setup name="Leave" lang="ts">
import { addLeave, delLeave, getLeave, listLeave, updateLeave } from '@/api/workflow/leave';
import { cancelProcessApply } from '@/api/workflow/processInstance';
import { LeaveForm, LeaveQuery, LeaveVO } from '@/api/workflow/leave/types';
import { startWorkFlow } from '@/api/workflow/task';
import SubmitVerify from '@/components/Process/submitVerify.vue';
import ApprovalRecord from '@/components/Process/approvalRecord.vue';
import { AxiosResponse } from 'axios';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const leaveList = ref<LeaveVO[]>([]);
const buttonLoading = ref(false);
const loading = ref(true);
const showSearch = ref(true);
const ids = ref<Array<string | number>>([]);
const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const leaveTime = ref<Array<string>>([]);
const options = [
{
value: '1',
label: '事假'
},
{
value: '2',
label: '调休'
},
{
value: '3',
label: '病假'
},
{
value: '4',
label: '婚假'
}
];
//提交组件
const submitVerifyRef = ref<InstanceType<typeof SubmitVerify>>();
//审批记录组件
const approvalRecordRef = ref<InstanceType<typeof ApprovalRecord>>();
const queryFormRef = ref<ElFormInstance>();
const leaveFormRef = ref<ElFormInstance>();
const submitFormData = ref<Record<string, any>>({
businessKey: '',
processKey: '',
variables: {}
});
const taskVariables = ref<Record<string, any>>({});
const dialog = reactive<DialogOption>({
visible: false,
title: ''
});
const initFormData: LeaveForm = {
id: undefined,
leaveType: undefined,
startDate: undefined,
endDate: undefined,
leaveDays: undefined,
remark: undefined
};
const data = reactive<PageData<LeaveForm, LeaveQuery>>({
form: { ...initFormData },
queryParams: {
pageNum: 1,
pageSize: 10,
startLeaveDays: undefined,
endLeaveDays: undefined
},
rules: {
id: [{ required: true, message: '主键不能为空', trigger: 'blur' }],
leaveType: [{ required: true, message: '请假类型不能为空', trigger: 'blur' }],
leaveTime: [{ required: true, message: '请假时间不能为空', trigger: 'blur' }],
leaveDays: [{ required: true, message: '请假天数不能为空', trigger: 'blur' }]
}
});
const { queryParams, form, rules } = toRefs(data);
/** 查询请假列表 */
const getList = async () => {
loading.value = true;
const res = await listLeave(queryParams.value);
leaveList.value = res.rows;
total.value = res.total;
loading.value = false;
};
/** 取消按钮 */
const cancel = () => {
reset();
dialog.visible = false;
};
/** 表单重置 */
const reset = () => {
form.value = { ...initFormData };
leaveTime.value = [];
leaveFormRef.value?.resetFields();
};
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.value.pageNum = 1;
getList();
};
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value?.resetFields();
handleQuery();
};
/** 多选框选中数据 */
const handleSelectionChange = (selection: LeaveVO[]) => {
ids.value = selection.map((item) => item.id);
single.value = selection.length != 1;
multiple.value = !selection.length;
};
/** 新增按钮操作 */
const handleAdd = () => {
dialog.visible = true;
dialog.title = '添加请假申请';
nextTick(() => {
reset();
});
};
const changeLeaveTime = () => {
const startDate = new Date(leaveTime.value[0]).getTime();
const endDate = new Date(leaveTime.value[1]).getTime();
const diffInMilliseconds = endDate - startDate;
form.value.leaveDays = Math.floor(diffInMilliseconds / (1000 * 60 * 60 * 24));
};
/** 修改按钮操作 */
const handleUpdate = (row?: LeaveVO) => {
buttonLoading.value = false;
dialog.visible = true;
dialog.title = '修改请假申请';
nextTick(async () => {
reset();
const _id = row?.id || ids.value[0];
const res = await getLeave(_id);
Object.assign(form.value, res.data);
leaveTime.value = [];
leaveTime.value.push(form.value.startDate);
leaveTime.value.push(form.value.endDate);
});
};
/** 提交按钮 */
const submitForm = (status: string) => {
if (leaveTime.value.length === 0) {
proxy?.$modal.msgError('请假时间不能为空');
return;
}
leaveFormRef.value?.validate(async (valid: boolean) => {
form.value.startDate = leaveTime.value[0];
form.value.endDate = leaveTime.value[1];
if (valid) {
buttonLoading.value = true;
let res: AxiosResponse<LeaveVO>;
if (form.value.id) {
res = await updateLeave(form.value);
} else {
res = await addLeave(form.value);
}
form.value = res.data;
if (status === 'draft') {
buttonLoading.value = false;
proxy?.$modal.msgSuccess('暂存成功');
dialog.visible = false;
await getList();
} else {
await handleStartWorkFlow(res.data);
}
}
});
};
/** 删除按钮操作 */
const handleDelete = async (row?: LeaveVO) => {
const _ids = row?.id || ids.value;
await proxy?.$modal.confirm('是否确认删除请假编号为"' + _ids + '"的数据项?').finally(() => (loading.value = false));
await delLeave(_ids);
proxy?.$modal.msgSuccess('删除成功');
await getList();
};
/** 导出按钮操作 */
const handleExport = () => {
proxy?.download(
'demo/leave/export',
{
...queryParams.value
},
`leave_${new Date().getTime()}.xlsx`
);
};
//提交申请
const handleStartWorkFlow = async (data: LeaveVO) => {
submitFormData.value.processKey = 'leave7';
submitFormData.value.businessKey = data.id;
//流程变量
taskVariables.value = {
entity: data,
leaveDays: data.leaveDays,
userList: [1, 2],
userList2: [1, 2]
};
submitFormData.value.variables = taskVariables.value;
const resp = await startWorkFlow(submitFormData.value);
if (submitVerifyRef.value) {
buttonLoading.value = false;
submitVerifyRef.value.openDialog(resp.data.taskId);
}
};
//审批记录
const handleApprovalRecord = (id: string) => {
if (approvalRecordRef.value) {
approvalRecordRef.value.init(id);
}
};
//提交回调
const submitCallback = async () => {
dialog.visible = false;
handleQuery();
};
/** 撤销按钮操作 */
const handleCancelProcessApply = async (id: string) => {
await proxy?.$modal.confirm('是否确认撤销当前单据?');
loading.value = true;
await cancelProcessApply(id).finally(() => (loading.value = false));
await getList();
proxy?.$modal.msgSuccess('撤销成功');
};
onMounted(() => {
getList();
});
</script>

View File

@ -0,0 +1,65 @@
<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 '@/components/BpmnDesign';
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('是否确认保存?');
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);
}
});
};
/**
* 对外暴露子组件方法
*/
defineExpose({
open
});
</script>
<style lang="scss" scoped>
.design {
:deep(.el-dialog .el-dialog__body) {
max-height: 100% !important;
min-height: calc(100vh - 50px);
}
}
</style>

View File

@ -0,0 +1,360 @@
<template>
<div class="p-2">
<el-row :gutter="20">
<!-- 流程分类树 -->
<el-col :lg="4" :xs="24" style="">
<el-card shadow="hover">
<el-input v-model="categoryName" placeholder="请输入流程分类名" prefix-icon="Search" clearable />
<el-tree
ref="categoryTreeRef"
class="mt-2"
node-key="id"
:data="categoryOptions"
:props="{ label: 'categoryName', 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="mb-[10px]">
<el-card shadow="hover">
<el-form v-show="showSearch" ref="queryFormRef" :model="queryParams" :inline="true" label-width="80px">
<el-form-item label="模型名称" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入模型名称" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="模型KEY" prop="key">
<el-input v-model="queryParams.key" placeholder="请输入模型KEY" 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>
</el-card>
</div>
</transition>
<el-card shadow="hover">
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd">新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="success" plain icon="Edit" :disabled="multiple" @click="handleUpdate()">修改</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()">删除</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @query-table="getList"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" :data="modelList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column fixed align="center" type="index" label="序号" width="50"></el-table-column>
<el-table-column fixed align="center" prop="name" label="模型名称"></el-table-column>
<el-table-column align="center" prop="key" label="模型KEY"></el-table-column>
<el-table-column align="center" prop="version" label="版本号" width="90">
<template #default="scope"> v{{ scope.row.version }}.0</template>
</el-table-column>
<el-table-column align="center" prop="metaInfo" label="备注说明" min-width="130"></el-table-column>
<el-table-column align="center" prop="createTime" label="创建时间" width="160"></el-table-column>
<el-table-column align="center" prop="lastUpdateTime" label="更新时间" width="160"></el-table-column>
<el-table-column fixed="right" label="操作" align="center" width="180" class-name="small-padding fixed-width">
<template #default="scope">
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button link type="primary" size="small" icon="Pointer" @click="clickDesign(scope.row.id)">设计流程</el-button>
</el-col>
<el-col :span="1.5">
<el-button link type="primary" size="small" icon="Download" @click="clickExportZip(scope.row)">导出</el-button>
</el-col>
</el-row>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button link type="primary" size="small" icon="ScaleToOriginal" @click="clickDeploy(scope.row.id, scope.row.key)">
流程部署
</el-button>
</el-col>
<el-col :span="1.5">
<el-button link type="primary" size="small" icon="Delete" @click="handleDelete(scope.row)">删除</el-button>
</el-col>
</el-row>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total > 0"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
:total="total"
@pagination="getList"
/>
</el-card>
</el-col>
</el-row>
<!-- 设计流程开始 -->
<Design ref="designRef" @close-call-back="handleQuery"></Design>
<!-- 设计流程结束 -->
<!-- 添加模型对话框 -->
<el-dialog v-model="dialog.visible" :title="dialog.title" width="650px" append-to-body :close-on-click-modal="false">
<el-form ref="formRef" :model="form" :rules="rules" label-width="100px">
<el-form-item label="模型名称:" prop="name">
<el-input v-model="form.name" :disabled="ids && ids.length > 0" maxlength="20" show-word-limit />
</el-form-item>
<el-form-item label="模型KEY" prop="key">
<el-input v-model="form.key" :disabled="ids && ids.length > 0" maxlength="20" show-word-limit />
</el-form-item>
<el-form-item label="流程分类" prop="categoryCode">
<el-tree-select
v-model="form.categoryCode"
:data="categoryOptions"
:props="{ value: 'categoryCode', label: 'categoryName', children: 'children' }"
value-key="categoryCode"
placeholder="请选择流程分类"
check-strictly
/>
</el-form-item>
<el-form-item label="备注:" prop="description">
<el-input v-model="form.description" type="textarea" maxlength="200" show-word-limit></el-input>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script lang="ts" setup name="Model">
import Design from './design.vue';
import { listModel, addModel, delModel, modelDeploy, getInfo, update } from '@/api/workflow/model';
import { ModelQuery, ModelForm, ModelVO } from '@/api/workflow/model/types';
import { listCategory } from '@/api/workflow/category';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const formRef = ref<ElFormInstance>();
const queryFormRef = ref<ElFormInstance>();
const categoryTreeRef = ref<ElTreeInstance>();
const designRef = ref<InstanceType<typeof Design>>();
type CategoryOption = {
categoryCode: string;
categoryName: string;
children?: CategoryOption[];
};
const buttonLoading = ref(false);
const loading = ref(true);
const ids = ref<string[]>([]);
const single = ref(true);
const multiple = ref(true);
const showSearch = ref(true);
const total = ref(0);
const modelList = ref<ModelVO[]>([]);
const categoryOptions = ref<CategoryOption[]>([]);
const categoryName = ref('');
const modelId = ref<string>('');
const dialog = reactive<DialogOption>({
visible: false,
title: ''
});
const initFormData: ModelForm = {
id: '',
name: '',
key: '',
categoryCode: '',
xml: '',
svg: '',
description: ''
};
const data = reactive<PageData<ModelForm, ModelQuery>>({
form: { ...initFormData },
queryParams: {
pageNum: 1,
pageSize: 10,
name: '',
key: '',
categoryCode: ''
},
rules: {
name: [{ required: true, message: '模型不能为空', trigger: 'blur' }],
key: [{ required: true, message: '模型KEY不能为空', trigger: 'blur' }],
categoryCode: [{ required: true, message: '流程分类不能为空', trigger: 'blur' }]
}
});
const { queryParams, form, rules } = toRefs(data);
onMounted(() => {
getList();
getTreeselect();
});
/** 节点单击事件 */
const handleNodeClick = (data: ModelForm) => {
queryParams.value.categoryCode = data.categoryCode;
if (data.categoryCode === 'ALL') {
queryParams.value.categoryCode = '';
}
handleQuery();
};
/** 通过条件过滤节点 */
const filterNode = (value: string, data: any) => {
if (!value) return true;
return data.categoryName.indexOf(value) !== -1;
};
/** 根据名称筛选部门树 */
watchEffect(
() => {
categoryTreeRef.value?.filter(categoryName.value);
},
{
flush: 'post' // watchEffect会在DOM挂载或者更新之前就会触发此属性控制在DOM元素更新后运行
}
);
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.value.pageNum = 1;
getList();
};
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value?.resetFields();
queryParams.value.categoryCode = '';
queryParams.value.pageNum = 1;
queryParams.value.pageSize = 10;
handleQuery();
};
// 多选框选中数据
const handleSelectionChange = (selection: ModelVO[]) => {
ids.value = selection.map((item: ModelVO) => item.id);
single.value = selection.length !== 1;
multiple.value = !selection.length;
};
//分页
const getList = async () => {
loading.value = true;
const resp = await listModel(queryParams.value);
modelList.value = resp.rows;
total.value = resp.total;
loading.value = false;
};
/** 删除按钮操作 */
const handleDelete = async (row?: ModelVO) => {
const id = row?.id || ids.value;
await proxy?.$modal.confirm('是否确认删除模型id为【' + id + '】的数据项?');
loading.value = true;
await delModel(id).finally(() => (loading.value = false));
await getList();
proxy?.$modal.msgSuccess('删除成功');
};
// 流程部署
const clickDeploy = async (id: string, key: string) => {
await proxy?.$modal.confirm('是否部署模型key为【' + key + '】流程?');
loading.value = true;
await modelDeploy(id).finally(() => (loading.value = false));
await getList();
proxy?.$modal.msgSuccess('部署成功');
};
const handleAdd = () => {
ids.value = [];
getTreeselect();
form.value = { ...initFormData };
dialog.visible = true;
dialog.title = '新增模型';
};
const handleUpdate = () => {
dialog.title = '修改模型';
nextTick(async () => {
await getTreeselect();
const _id = ids.value[0];
const res = await getInfo(_id);
Object.assign(form.value, res.data);
dialog.visible = true;
});
};
/** 提交按钮 */
const submitForm = () => {
formRef.value.validate(async (valid: boolean) => {
if (valid) {
buttonLoading.value = true;
if (ids.value && ids.value.length > 0) {
form.value.id = ids.value[0];
await update(form.value);
proxy?.$modal.msgSuccess('新增成功');
} else {
initXml(form.value.key, form.value.name);
form.value.xml = xml.value;
await addModel(form.value);
proxy?.$modal.msgSuccess('新增成功');
}
dialog.visible = false;
await getList();
}
});
};
/** 取消按钮 */
const cancel = () => {
reset();
dialog.visible = false;
};
/** 表单重置 */
const reset = () => {
form.value = { ...initFormData };
formRef.value.resetFields();
};
// 打开设计流程
const clickDesign = async (id: string) => {
await designRef.value.open(id);
};
// 导出流程模型
const clickExportZip = (data: any) => {
proxy?.$download.zip('/workflow/model/export/zip/' + data.id, data.name + '-' + data.key);
};
/** 查询流程分类下拉树结构 */
const getTreeselect = async () => {
const res = await listCategory();
categoryOptions.value = [];
const data: CategoryOption = { categoryCode: 'ALL', categoryName: '顶级节点', children: [] };
data.children = proxy?.handleTree<CategoryOption>(res.data, 'id', 'parentId');
categoryOptions.value.push(data);
};
const xml = ref<string>('');
const initXml = async (key: string, name: string) => {
xml.value = `<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:bioc="http://bpmn.io/schema/bpmn/biocolor/1.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:flowable="http://flowable.org/bpmn" targetNamespace="http://www.flowable.org/processdef">
<process id="${key}" name="${name}">
<startEvent id="startNode1" name="开始" />
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_flow">
<bpmndi:BPMNPlane id="BPMNPlane_flow" bpmnElement="T-2d89e7a3-ba79-4abd-9f64-ea59621c258c">
<bpmndi:BPMNShape id="BPMNShape_startNode1" bpmnElement="startNode1" bioc:stroke="">
<omgdc:Bounds x="240" y="200" width="30" height="30" />
<bpmndi:BPMNLabel>
<omgdc:Bounds x="242" y="237" width="23" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>`;
return xml;
};
</script>

View File

@ -0,0 +1,57 @@
<template>
<el-dialog v-model="data.visible" title="预览" width="70%" append-to-body>
<div v-if="data.type === 'png'" style="align: center">
<el-image v-if="data.type === 'png'" :src="data.url[0]">
<div>流程图加载中 <i class="el-icon-loading"></i></div>
</el-image>
</div>
<div v-if="data.type === 'xml'" class="xml-data">
<div v-for="(xml, index) in data.url" :key="index">
<pre class="font">{{ xml }}</pre>
</div>
</div>
<template #footer>
<span v-if="data.type === 'xml'" class="dialog-footer"> </span>
</template>
</el-dialog>
</template>
<script setup lang="ts">
const data = reactive({
visible: false,
url: new Array<string>(),
type: ''
});
//打开
const openDialog = (url: string[], type: string) => {
data.visible = true;
data.url = url;
data.type = type;
};
/**
* 对外暴露子组件方法
*/
defineExpose({
openDialog
});
</script>
<style>
.xml-data {
background-color: #2b2b2b;
border-radius: 5px;
color: #c6c6c6;
word-break: break-all;
overflow-y: scroll;
overflow-x: hidden;
box-sizing: border-box;
padding: 8px 0px;
height: 500px;
width: inherit;
line-height: 1px;
overflow: auto;
}
.font {
font-family: '幼圆';
font-weight: 500;
}
</style>

View File

@ -0,0 +1,403 @@
<template>
<div class="p-2">
<el-row :gutter="20">
<!-- 流程分类树 -->
<el-col :lg="4" :xs="24" style="">
<el-card shadow="hover">
<el-input v-model="categoryName" placeholder="请输入流程分类名" prefix-icon="Search" clearable />
<el-tree
ref="categoryTreeRef"
class="mt-2"
node-key="id"
:data="categoryOptions"
:props="{ label: 'categoryName', 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="mb-[10px]">
<el-card shadow="hover">
<el-form v-show="showSearch" ref="queryFormRef" :model="queryParams" :inline="true" label-width="120px">
<el-form-item label="流程定义名称" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入流程定义名称" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="流程定义KEY" prop="key">
<el-input v-model="queryParams.key" placeholder="请输入流程定义KEY" 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>
</el-card>
</div>
</transition>
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-button type="primary" icon="UploadFilled" @click="uploadDialog.visible = true">部署流程文件</el-button>
</el-card>
</div>
</transition>
<el-card shadow="hover">
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5"> </el-col>
<right-toolbar v-model:showSearch="showSearch" @query-table="getList"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" :data="processDefinitionList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column fixed align="center" type="index" label="序号" width="50"></el-table-column>
<el-table-column fixed align="center" prop="name" label="流程定义名称"></el-table-column>
<el-table-column align="center" prop="key" label="标识Key"></el-table-column>
<el-table-column align="center" prop="version" label="版本号" width="90">
<template #default="scope"> v{{ scope.row.version }}.0</template>
</el-table-column>
<el-table-column align="center" prop="resourceName" label="流程XML" min-width="80" :show-overflow-tooltip="true">
<template #default="scope">
<el-link type="primary" @click="clickPreviewXML(scope.row.id)">{{ scope.row.resourceName }}</el-link>
</template>
</el-table-column>
<el-table-column align="center" prop="diagramResourceName" label="流程图片" min-width="80" :show-overflow-tooltip="true">
<template #default="scope">
<el-link type="primary" @click="clickPreviewImg(scope.row.id)">{{ scope.row.diagramResourceName }}</el-link>
</template>
</el-table-column>
<el-table-column align="center" prop="suspensionState" label="状态" min-width="70">
<template #default="scope">
<el-tag v-if="scope.row.suspensionState == 1" type="success">激活</el-tag>
<el-tag v-else type="danger">挂起</el-tag>
</template>
</el-table-column>
<el-table-column align="center" prop="deploymentTime" label="部署时间" :show-overflow-tooltip="true"></el-table-column>
<el-table-column fixed="right" label="操作" align="center" width="200" class-name="small-padding fixed-width">
<template #default="scope">
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
link
type="primary"
size="small"
:icon="scope.row.suspensionState === 1 ? 'Lock' : 'Unlock'"
@click="handleProcessDefState(scope.row)"
>
{{ scope.row.suspensionState === 1 ? '挂起流程' : '激活流程' }}
</el-button>
</el-col>
<el-col :span="1.5">
<el-button link type="primary" size="small" icon="Delete" @click="handleDelete(scope.row)">删除</el-button>
</el-col>
</el-row>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button link type="primary" size="small" icon="Sort" @click="handleConvertToModel(scope.row)"> 转换模型 </el-button>
</el-col>
<el-col :span="1.5">
<el-button link type="primary" size="small" icon="Document" @click="getProcessDefinitionHitoryList(scope.row.id, scope.row.key)">
历史版本
</el-button>
</el-col>
</el-row>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total > 0"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
:total="total"
@pagination="getList"
/>
</el-card>
</el-col>
</el-row>
<!-- 预览图片或xml -->
<process-preview ref="previewRef" />
<!-- 部署文件 -->
<el-dialog v-if="uploadDialog.visible" v-model="uploadDialog.visible" :title="uploadDialog.title" width="30%">
<div v-loading="uploadDialogLoading">
<el-upload class="upload-demo" drag accept="application/zip,application/xml,.bpmn" :http-request="handerDeployProcessFile">
<el-icon class="UploadFilled"><upload-filled /></el-icon>
<div class="el-upload__text"><em>点击上传选择BPMN流程文件</em></div>
<div class="el-upload__text">仅支持 .zip.bpmn20.xmlbpmn 格式文件</div>
<div class="el-upload__text">PS:如若部署请部署从本项目模型管理导出的数据</div>
</el-upload>
</div>
</el-dialog>
<!-- 历史版本 -->
<el-dialog v-if="processDefinitionDialog.visible" v-model="processDefinitionDialog.visible" :title="processDefinitionDialog.title" width="70%">
<el-table v-loading="loading" :data="processDefinitionHistoryList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column fixed align="center" type="index" label="序号" width="50"></el-table-column>
<el-table-column fixed align="center" prop="name" label="流程定义名称"></el-table-column>
<el-table-column align="center" prop="key" label="标识Key"></el-table-column>
<el-table-column align="center" prop="version" label="版本号" width="90">
<template #default="scope"> v{{ scope.row.version }}.0</template>
</el-table-column>
<el-table-column align="center" prop="resourceName" label="流程XML" min-width="80" :show-overflow-tooltip="true">
<template #default="scope">
<el-link type="primary" @click="clickPreviewXML(scope.row.id)">{{ scope.row.resourceName }}</el-link>
</template>
</el-table-column>
<el-table-column align="center" prop="diagramResourceName" label="流程图片" min-width="80" :show-overflow-tooltip="true">
<template #default="scope">
<el-link type="primary" @click="clickPreviewImg(scope.row.id)">{{ scope.row.diagramResourceName }}</el-link>
</template>
</el-table-column>
<el-table-column align="center" prop="suspensionState" label="状态" min-width="70">
<template #default="scope">
<el-tag v-if="scope.row.suspensionState == 1" type="success">激活</el-tag>
<el-tag v-else type="danger">挂起</el-tag>
</template>
</el-table-column>
<el-table-column align="center" prop="deploymentTime" label="部署时间" :show-overflow-tooltip="true"></el-table-column>
<el-table-column fixed="right" label="操作" align="center" width="200" class-name="small-padding fixed-width">
<template #default="scope">
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
link
type="primary"
size="small"
:icon="scope.row.suspensionState === 1 ? 'Lock' : 'Unlock'"
@click="handleProcessDefState(scope.row)"
>
{{ scope.row.suspensionState === 1 ? '挂起流程' : '激活流程' }}
</el-button>
</el-col>
<el-col :span="1.5">
<el-button link type="primary" icon="Delete" size="small" @click="handleDelete(scope.row)">删除</el-button>
</el-col>
</el-row>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button link type="primary" icon="Sort" size="small" @click="handleConvertToModel(scope.row)"> 转换模型 </el-button>
</el-col>
</el-row>
</template>
</el-table-column>
</el-table>
</el-dialog>
</div>
</template>
<script lang="ts" setup name="processDefinition">
import {
listProcessDefinition,
processDefinitionImage,
processDefinitionXml,
deleteProcessDefinition,
updateProcessDefState,
convertToModel,
deployProcessFile,
getProcessDefinitionListByKey
} from '@/api/workflow/processDefinition';
import ProcessPreview from './components/processPreview.vue';
import { listCategory } from '@/api/workflow/category';
import { CategoryVO } from '@/api/workflow/category/types';
import { ProcessDefinitionQuery, ProcessDefinitionVO } from '@/api/workflow/processDefinition/types';
import { UploadRequestOptions } from 'element-plus';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const previewRef = ref<InstanceType<typeof ProcessPreview>>();
const queryFormRef = ref<ElFormInstance>();
const categoryTreeRef = ref<ElTreeInstance>();
type CategoryOption = {
categoryCode: string;
categoryName: string;
children?: CategoryOption[];
};
const loading = ref(true);
const ids = ref<Array<string | number>>([]);
const single = ref(true);
const multiple = ref(true);
const showSearch = ref(true);
const total = ref(0);
const uploadDialogLoading = ref(false);
const processDefinitionList = ref<ProcessDefinitionVO[]>([]);
const processDefinitionHistoryList = ref<ProcessDefinitionVO[]>([]);
const url = ref<string[]>([]);
const categoryOptions = ref<CategoryOption[]>([]);
const categoryName = ref('');
const uploadDialog = reactive<DialogOption>({
visible: false,
title: '部署流程文件'
});
const processDefinitionDialog = reactive<DialogOption>({
visible: false,
title: '历史版本'
});
// 查询参数
const queryParams = ref<ProcessDefinitionQuery>({
pageNum: 1,
pageSize: 10,
name: undefined,
key: undefined,
categoryCode: undefined
});
onMounted(() => {
getList();
getTreeselect();
});
/** 节点单击事件 */
const handleNodeClick = (data: CategoryVO) => {
queryParams.value.categoryCode = data.categoryCode;
if (data.categoryCode === 'ALL') {
queryParams.value.categoryCode = '';
}
handleQuery();
};
/** 通过条件过滤节点 */
const filterNode = (value: string, data: any) => {
if (!value) return true;
return data.categoryName.indexOf(value) !== -1;
};
/** 根据名称筛选部门树 */
watchEffect(
() => {
categoryTreeRef.value.filter(categoryName.value);
},
{
flush: 'post' // watchEffect会在DOM挂载或者更新之前就会触发此属性控制在DOM元素更新后运行
}
);
/** 查询流程分类下拉树结构 */
const getTreeselect = async () => {
const res = await listCategory();
categoryOptions.value = [];
const data: CategoryOption = { categoryCode: 'ALL', categoryName: '顶级节点', children: [] };
data.children = proxy?.handleTree<CategoryOption>(res.data, 'id', 'parentId');
categoryOptions.value.push(data);
};
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.value.pageNum = 1;
getList();
};
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value?.resetFields();
queryParams.value.categoryCode = '';
queryParams.value.pageNum = 1;
queryParams.value.pageSize = 10;
handleQuery();
};
// 多选框选中数据
const handleSelectionChange = (selection: any) => {
ids.value = selection.map((item: any) => item.id);
single.value = selection.length !== 1;
multiple.value = !selection.length;
};
//分页
const getList = async () => {
loading.value = true;
const resp = await listProcessDefinition(queryParams.value);
processDefinitionList.value = resp.rows;
total.value = resp.total;
loading.value = false;
};
//获取历史流程定义
const getProcessDefinitionHitoryList = async (id: string, key: string) => {
processDefinitionDialog.visible = true;
loading.value = true;
const resp = await getProcessDefinitionListByKey(key);
if (resp.data && resp.data.length > 0) {
processDefinitionHistoryList.value = resp.data.filter((item: any) => item.id !== id);
}
loading.value = false;
};
//预览图片
const clickPreviewImg = async (id: string) => {
loading.value = true;
const resp = await processDefinitionImage(id);
if (previewRef.value) {
url.value = [];
url.value.push('data:image/png;base64,' + resp.data);
loading.value = false;
previewRef.value.openDialog(url.value, 'png');
}
};
//预览xml
const clickPreviewXML = async (id: string) => {
loading.value = true;
const resp = await processDefinitionXml(id);
if (previewRef.value) {
url.value = [];
url.value = resp.data.xml;
loading.value = false;
previewRef.value.openDialog(url.value, 'xml');
}
};
/** 删除按钮操作 */
const handleDelete = async (row: ProcessDefinitionVO) => {
await proxy?.$modal.confirm('是否确认删除流程定义key为【' + row.key + '】的数据项?');
loading.value = true;
await deleteProcessDefinition(row.deploymentId, row.id).finally(() => (loading.value = false));
await getList();
proxy?.$modal.msgSuccess('删除成功');
};
/** 挂起/激活 */
const handleProcessDefState = async (row: ProcessDefinitionVO) => {
let msg: string;
if (row.suspensionState === 1) {
msg = `暂停后,此流程下的所有任务都不允许往后流转,您确定挂起【${row.name || row.key}】吗?`;
} else {
msg = `启动后,此流程下的所有任务都允许往后流转,您确定激活【${row.name || row.key}】吗?`;
}
await proxy?.$modal.confirm(msg);
loading.value = true;
await updateProcessDefState(row.id).finally(() => (loading.value = false));
await getList();
proxy?.$modal.msgSuccess('操作成功');
};
/** 流程定义转换为模型 */
const handleConvertToModel = async (row: ProcessDefinitionVO) => {
await proxy?.$modal.confirm('是否确认转换流程定义key为【' + row.key + '】的数据项?');
await convertToModel(row.id).finally(() => (loading.value = false));
getList();
proxy?.$modal.msgSuccess('操作成功');
};
//部署文件
const handerDeployProcessFile = (data: UploadRequestOptions): XMLHttpRequest => {
let formData = new FormData();
if (queryParams.value.categoryCode === 'ALL') {
proxy?.$modal.msgError('顶级节点不可作为分类!');
return;
}
if (!queryParams.value.categoryCode) {
proxy?.$modal.msgError('请选择左侧要上传的分类!');
return;
}
uploadDialogLoading.value = true
formData.append('file', data.file);
formData.append('categoryCode', queryParams.value.categoryCode);
deployProcessFile(formData).then(() => {
uploadDialog.visible = false;
proxy?.$modal.msgSuccess('部署成功');
uploadDialogLoading.value = false
handleQuery();
});
};
</script>

View File

@ -0,0 +1,363 @@
<template>
<div class="p-2">
<el-row :gutter="20">
<!-- 流程分类树 -->
<el-col :lg="4" :xs="24" style="">
<el-card shadow="hover">
<el-input v-model="categoryName" placeholder="请输入流程分类名" prefix-icon="Search" clearable />
<el-tree
ref="categoryTreeRef"
class="mt-2"
node-key="id"
:data="categoryOptions"
:props="{ label: 'categoryName', 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">
<div class="mb-[10px]">
<el-card shadow="hover" class="text-center">
<el-radio-group v-model="tab" @change="changeTab(tab)">
<el-radio-button label="running">运行中</el-radio-button>
<el-radio-button label="finish">已完成</el-radio-button>
</el-radio-group>
</el-card>
</div>
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form v-show="showSearch" ref="queryFormRef" :model="queryParams" :inline="true" label-width="120px">
<el-form-item label="流程定义名称" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入流程定义名称" @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="流程定义KEY" prop="key">
<el-input v-model="queryParams.key" placeholder="请输入流程定义KEY" @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>
</el-card>
</div>
</transition>
<el-card shadow="hover">
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete">删除</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @query-table="handleQuery"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" :data="processInstanceList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column fixed align="center" type="index" label="序号" width="50"></el-table-column>
<el-table-column v-if="false" fixed align="center" prop="id" label="id"></el-table-column>
<el-table-column fixed align="center" prop="processDefinitionName" label="流程定义名称"></el-table-column>
<el-table-column fixed align="center" prop="processDefinitionKey" label="流程定义KEY"></el-table-column>
<el-table-column align="center" prop="processDefinitionVersion" label="版本号" width="90">
<template #default="scope"> v{{ scope.row.processDefinitionVersion }}.0</template>
</el-table-column>
<el-table-column v-if="tab === 'running'" align="center" prop="isSuspended" label="状态" min-width="70">
<template #default="scope">
<el-tag v-if="!scope.row.isSuspended" type="success">激活</el-tag>
<el-tag v-else type="danger">挂起</el-tag>
</template>
</el-table-column>
<el-table-column align="center" prop="businessStatusName" label="流程状态" min-width="70">
<template #default="scope">
<el-tag type="success">{{ scope.row.businessStatusName }}</el-tag>
</template>
</el-table-column>
<el-table-column align="center" prop="startTime" label="启动时间" width="160"></el-table-column>
<el-table-column v-if="tab === 'finish'" align="center" prop="endTime" label="结束时间" width="160"></el-table-column>
<el-table-column label="操作" align="center" width="160" class-name="small-padding fixed-width">
<template #default="scope">
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button link type="primary" size="small" icon="Document" @click="handleApprovalRecord(scope.row)">审批记录</el-button>
</el-col>
<el-col :span="1.5">
<el-button link type="primary" size="small" icon="Delete" @click="handleDelete(scope.row)">删除</el-button>
</el-col>
</el-row>
<el-row v-if="tab === 'running'" :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
link
type="primary"
size="small"
icon="Sort"
@click="getProcessDefinitionHitoryList(scope.row.processDefinitionId, scope.row.processDefinitionKey)"
>切换版本</el-button
>
</el-col>
<el-col :span="1.5">
<el-popover :ref="`popoverRef${scope.$index}`" trigger="click" placement="left" :width="300">
<el-input v-model="deleteReason" resize="none" :rows="3" type="textarea" placeholder="请输入作废原因" />
<div style="text-align: right; margin: 5px 0px 0px 0px">
<el-button size="small" text @click="cancelPopover(scope.$index)">取消</el-button>
<el-button size="small" type="primary" @click="handleInvalid(scope.row)">确认</el-button>
</div>
<template #reference>
<el-button link type="primary" size="small" icon="CircleClose">作废</el-button>
</template>
</el-popover>
</el-col>
</el-row>
</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-col>
</el-row>
<el-dialog v-if="processDefinitionDialog.visible" v-model="processDefinitionDialog.visible" :title="processDefinitionDialog.title" width="70%">
<el-table v-loading="loading" :data="processDefinitionHistoryList">
<el-table-column fixed align="center" type="index" label="序号" width="50"></el-table-column>
<el-table-column fixed align="center" prop="name" label="流程定义名称"></el-table-column>
<el-table-column align="center" prop="key" label="标识Key"></el-table-column>
<el-table-column align="center" prop="version" label="版本号" width="90">
<template #default="scope"> v{{ scope.row.version }}.0</template>
</el-table-column>
<el-table-column align="center" prop="suspensionState" label="状态" min-width="70">
<template #default="scope">
<el-tag v-if="scope.row.suspensionState == 1" type="success">激活</el-tag>
<el-tag v-else type="danger">挂起</el-tag>
</template>
</el-table-column>
<el-table-column align="center" prop="deploymentTime" label="部署时间" :show-overflow-tooltip="true"></el-table-column>
<el-table-column fixed="right" label="操作" align="center" width="200" class-name="small-padding fixed-width">
<template #default="scope">
<el-button link type="primary" size="small" icon="Sort" @click="handleChange(scope.row.id)">切换</el-button>
</template>
</el-table-column>
</el-table>
</el-dialog>
<!-- 审批记录 -->
<approvalRecord ref="approvalRecordRef" />
</div>
</template>
<script lang="ts" setup>
import {
getProcessInstanceRunningByPage,
getProcessInstanceFinishByPage,
deleteRuntimeProcessAndHisInst,
deleteFinishProcessAndHisInst,
deleteRuntimeProcessInst
} from '@/api/workflow/processInstance';
import { getProcessDefinitionListByKey, migrationProcessDefinition } from '@/api/workflow/processDefinition';
import ApprovalRecord from '@/components/Process/approvalRecord.vue';
import { listCategory } from '@/api/workflow/category';
import { CategoryVO } from '@/api/workflow/category/types';
import { ProcessInstanceQuery, ProcessInstanceVO } from '@/api/workflow/processInstance/types';
//审批记录组件
const approvalRecordRef = ref<InstanceType<typeof ApprovalRecord>>();
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const queryFormRef = ref<ElFormInstance>();
const categoryTreeRef = ref<ElTreeInstance>();
// 遮罩层
const loading = ref(true);
// 选中数组
const ids = ref<Array<any>>([]);
// 非单个禁用
const single = ref(true);
// 非多个禁用
const multiple = ref(true);
// 显示搜索条件
const showSearch = ref(true);
// 总条数
const total = ref(0);
// 流程定义id
const processDefinitionId = ref<string>('');
// 模型定义表格数据
const processInstanceList = ref<ProcessInstanceVO[]>([]);
const processDefinitionHistoryList = ref<Array<any>>([]);
const categoryOptions = ref<CategoryOption[]>([]);
const categoryName = ref('');
const processDefinitionDialog = reactive<DialogOption>({
visible: false,
title: '流程定义'
});
type CategoryOption = {
categoryCode: string;
categoryName: string;
children?: CategoryOption[];
};
const tab = ref('running');
// 作废原因
const deleteReason = ref('');
// 查询参数
const queryParams = ref<ProcessInstanceQuery>({
pageNum: 1,
pageSize: 10,
name: undefined,
key: undefined,
categoryCode: undefined
});
onMounted(() => {
getProcessInstanceRunningList();
getTreeselect();
});
/** 节点单击事件 */
const handleNodeClick = (data: CategoryVO) => {
queryParams.value.categoryCode = data.categoryCode;
if (data.categoryCode === 'ALL') {
queryParams.value.categoryCode = '';
}
handleQuery();
};
/** 通过条件过滤节点 */
const filterNode = (value: string, data: any) => {
if (!value) return true;
return data.categoryName.indexOf(value) !== -1;
};
/** 根据名称筛选部门树 */
watchEffect(
() => {
categoryTreeRef.value.filter(categoryName.value);
},
{
flush: 'post' // watchEffect会在DOM挂载或者更新之前就会触发此属性控制在DOM元素更新后运行
}
);
/** 查询流程分类下拉树结构 */
const getTreeselect = async () => {
const res = await listCategory();
categoryOptions.value = [];
const data: CategoryOption = { categoryCode: 'ALL', categoryName: '顶级节点', children: [] };
data.children = proxy?.handleTree<CategoryOption>(res.data, 'id', 'parentId');
categoryOptions.value.push(data);
};
//审批记录
const handleApprovalRecord = (row: any) => {
if (approvalRecordRef.value) {
approvalRecordRef.value.init(row.id);
}
};
/** 搜索按钮操作 */
const handleQuery = () => {
if ('running' === tab.value) {
getProcessInstanceRunningList();
} else {
getProcessInstanceFinishList();
}
};
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value?.resetFields();
queryParams.value.categoryCode = '';
queryParams.value.pageNum = 1;
queryParams.value.pageSize = 10;
handleQuery();
};
// 多选框选中数据
const handleSelectionChange = (selection: ProcessInstanceVO[]) => {
ids.value = selection.map((item: any) => item.id);
single.value = selection.length !== 1;
multiple.value = !selection.length;
};
//分页
const getProcessInstanceRunningList = () => {
loading.value = true;
getProcessInstanceRunningByPage(queryParams.value).then((resp) => {
processInstanceList.value = resp.rows;
total.value = resp.total;
loading.value = false;
});
};
//分页
const getProcessInstanceFinishList = () => {
loading.value = true;
getProcessInstanceFinishByPage(queryParams.value).then((resp) => {
processInstanceList.value = resp.rows;
total.value = resp.total;
loading.value = false;
});
};
/** 删除按钮操作 */
const handleDelete = async (row: any) => {
const id = row.id || ids.value;
await proxy?.$modal.confirm('是否确认删除id为【' + id + '】的数据项?');
loading.value = true;
if ('running' === tab.value) {
await deleteRuntimeProcessAndHisInst(id).finally(() => (loading.value = false));
getProcessInstanceRunningList();
} else {
await deleteFinishProcessAndHisInst(id).finally(() => (loading.value = false));
getProcessInstanceFinishList();
}
proxy?.$modal.msgSuccess('删除成功');
};
const changeTab = async (data: string) => {
queryParams.value.pageNum = 1;
if ('running' === data) {
getProcessInstanceRunningList();
} else {
getProcessInstanceFinishList();
}
};
/** 作废按钮操作 */
const handleInvalid = async (row: ProcessInstanceVO) => {
await proxy?.$modal.confirm('是否确认作废业务id为【' + row.businessKey + '】的数据项?');
loading.value = true;
if ('running' === tab.value) {
let param = {
processInstanceId: row.id,
deleteReason: deleteReason.value
};
await deleteRuntimeProcessInst(param).finally(() => (loading.value = false));
getProcessInstanceRunningList();
proxy?.$modal.msgSuccess('操作成功');
}
};
const cancelPopover = async (index: any) => {
(proxy?.$refs[`popoverRef${index}`] as any).hide(); //关闭弹窗
};
//获取流程定义
const getProcessDefinitionHitoryList = (id: string, key: string) => {
processDefinitionDialog.visible = true;
processDefinitionId.value = id;
loading.value = true;
getProcessDefinitionListByKey(key).then((resp) => {
if (resp.data && resp.data.length > 0) {
processDefinitionHistoryList.value = resp.data.filter((item: any) => item.id !== id);
}
loading.value = false;
});
};
//切换流程版本
const handleChange = async (id: string) => {
await proxy?.$modal.confirm('是否确认切换?');
loading.value = true;
migrationProcessDefinition(processDefinitionId.value, id).then((resp) => {
proxy?.$modal.msgSuccess('操作成功');
getProcessInstanceRunningList();
processDefinitionDialog.visible = false;
loading.value = false;
});
};
</script>

View File

@ -0,0 +1,237 @@
<template>
<div class="p-2">
<div class="mb-[10px]">
<el-card shadow="hover" class="text-center">
<el-radio-group v-model="tab" @change="changeTab(tab)">
<el-radio-button label="waiting">待办任务</el-radio-button>
<el-radio-button label="finish">已办任务</el-radio-button>
</el-radio-group>
</el-card>
</div>
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form v-show="showSearch" ref="queryFormRef" :model="queryParams" :inline="true" label-width="68px">
<el-form-item label="任务名称" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入任务名称" @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="流程定义名称" label-width="100" prop="processDefinitionName">
<el-input v-model="queryParams.processDefinitionName" placeholder="请输入流程定义名称" @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="流程定义KEY" label-width="100" prop="processDefinitionKey">
<el-input v-model="queryParams.processDefinitionKey" placeholder="请输入流程定义KEY" @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>
</el-card>
</div>
</transition>
<el-card shadow="hover">
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="Edit" @click="handleUpdate">修改办理人</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @query-table="handleQuery"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" :data="taskList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column fixed align="center" type="index" label="序号" width="50"></el-table-column>
<el-table-column fixed align="center" prop="processDefinitionName" label="流程定义名称"></el-table-column>
<el-table-column fixed align="center" prop="processDefinitionKey" label="流程定义KEY"></el-table-column>
<el-table-column fixed align="center" prop="name" label="任务名称"></el-table-column>
<el-table-column fixed align="center" prop="assigneeName" label="办理人">
<template v-if="tab === 'waiting'" #default="scope">
<template v-if="scope.row.participantVo && scope.row.assignee === null">
<el-tag v-for="(item, index) in scope.row.participantVo.candidateName" :key="index" type="success">
{{ item }}
</el-tag>
</template>
<template v-else>
<el-tag type="success">
{{ scope.row.assigneeName }}
</el-tag>
</template>
</template>
<template v-else-if="tab === 'finish'" #default="scope">
<el-tag type="success">
{{ scope.row.assigneeName }}
</el-tag>
</template>
</el-table-column>
<el-table-column align="center" prop="businessStatusName" label="流程状态" min-width="70">
<template #default="scope">
<el-tag v-if="tab === 'waiting'" type="success">{{ scope.row.businessStatusName }}</el-tag>
<el-tag v-else type="success">已完成</el-tag>
</template>
</el-table-column>
<el-table-column align="center" prop="createTime" label="创建时间" width="160"></el-table-column>
<el-table-column label="操作" align="center" width="160" class-name="small-padding fixed-width">
<template #default="scope">
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button link type="primary" size="small" icon="Document" @click="handleApprovalRecord(scope.row)">审批记录</el-button>
</el-col>
<el-col v-if="scope.row.multiInstance" :span="1.5">
<el-button link type="primary" size="small" icon="CirclePlus" @click="addMultiInstanceUser(scope.row)">加签</el-button>
</el-col>
<el-col v-if="scope.row.multiInstance" :span="1.5">
<el-button link type="primary" size="small" icon="Remove" @click="deleteMultiInstanceUser(scope.row)">减签</el-button>
</el-col>
</el-row>
</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>
<!-- 审批记录 -->
<approvalRecord ref="approvalRecordRef" />
<!-- 提交组件 -->
<submitVerify ref="submitVerifyRef" :task-id="taskId" @submit-callback="handleQuery" />
<!-- 加签组件 -->
<multiInstanceUser ref="multiInstanceUserRef" :title="title" @submit-callback="handleQuery" />
<!-- 加签组件 -->
<SysUser ref="sysUserRef" :multiple="true" @submit-callback="submitCallback" />
</div>
</template>
<script lang="ts" setup>
import { getAllTaskWaitByPage, getAllTaskFinishByPage, updateAssignee } from '@/api/workflow/task';
import ApprovalRecord from '@/components/Process/approvalRecord.vue';
import SubmitVerify from '@/components/Process/submitVerify.vue';
import MultiInstanceUser from '@/components/Process/multiInstance-user.vue';
import SysUser from '@/components/Process/sys-user.vue';
import { TaskQuery, TaskVO } from '@/api/workflow/task/types';
//提交组件
const submitVerifyRef = ref<InstanceType<typeof SubmitVerify>>();
//审批记录组件
const approvalRecordRef = ref<InstanceType<typeof ApprovalRecord>>();
//加签组件
const multiInstanceUserRef = ref<InstanceType<typeof MultiInstanceUser>>();
//选人组件
const sysUserRef = ref<InstanceType<typeof SysUser>>();
const queryFormRef = ref<ElFormInstance>();
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
// 遮罩层
const loading = ref(true);
// 选中数组
const ids = ref<Array<any>>([]);
// 非单个禁用
const single = ref(true);
// 非多个禁用
const multiple = ref(true);
// 显示搜索条件
const showSearch = ref(true);
// 总条数
const total = ref(0);
// 模型定义表格数据
const taskList = ref([]);
// 任务id
const taskId = ref('');
const title = ref('');
// 查询参数
const queryParams = ref<TaskQuery>({
pageNum: 1,
pageSize: 10,
name: undefined,
processDefinitionName: undefined,
processDefinitionKey: undefined
});
const tab = ref('waiting');
onMounted(() => {
getWaitingList();
});
//审批记录
const handleApprovalRecord = (row: TaskVO) => {
if (approvalRecordRef.value) {
approvalRecordRef.value.init(row.processInstanceId);
}
};
//加签
const addMultiInstanceUser = (row: TaskVO) => {
if (multiInstanceUserRef.value) {
title.value = '加签人员';
multiInstanceUserRef.value.getAddMultiInstanceList(row.id, []);
}
};
//减签
const deleteMultiInstanceUser = (row: TaskVO) => {
if (multiInstanceUserRef.value) {
title.value = '减签人员';
multiInstanceUserRef.value.getDeleteMultiInstanceList(row.id);
}
};
/** 搜索按钮操作 */
const handleQuery = () => {
if ('waiting' === tab.value) {
getWaitingList();
} else {
getFinishList();
}
};
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value?.resetFields();
queryParams.value.pageNum = 1;
queryParams.value.pageSize = 10;
handleQuery();
};
// 多选框选中数据
const handleSelectionChange = (selection: any) => {
ids.value = selection.map((item: any) => item.id);
single.value = selection.length !== 1;
multiple.value = !selection.length;
};
const changeTab = async (data: string) => {
queryParams.value.pageNum = 1;
if ('waiting' === data) {
getWaitingList();
} else {
getFinishList();
}
};
//分页
const getWaitingList = () => {
loading.value = true;
getAllTaskWaitByPage(queryParams.value).then((resp) => {
taskList.value = resp.rows;
total.value = resp.total;
loading.value = false;
});
};
const getFinishList = () => {
loading.value = true;
getAllTaskFinishByPage(queryParams.value).then((resp) => {
taskList.value = resp.rows;
total.value = resp.total;
loading.value = false;
});
};
const handleUpdate = () => {
if (sysUserRef.value) {
sysUserRef.value.getUserList([]);
}
};
//修改办理人
const submitCallback = (data) => {
if (data && data.value.length > 0) {
updateAssignee(ids.value, data.value[0].userId).then((resp) => {
sysUserRef.value.close();
proxy?.$modal.msgSuccess('操作成功');
handleQuery();
});
}
};
</script>

View File

@ -0,0 +1,256 @@
<template>
<div class="p-2">
<el-row :gutter="20">
<!-- 流程分类树 -->
<el-col :lg="4" :xs="24" style="">
<el-card shadow="hover">
<el-input v-model="categoryName" placeholder="请输入流程分类名" prefix-icon="Search" clearable />
<el-tree
ref="categoryTreeRef"
class="mt-2"
node-key="id"
:data="categoryOptions"
:props="{ label: 'categoryName', 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="mb-[10px]">
<el-card shadow="hover">
<el-form v-show="showSearch" ref="queryFormRef" :model="queryParams" :inline="true" label-width="120px">
<el-form-item label="流程定义名称" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入流程定义名称" @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>
</el-card>
</div>
</transition>
<el-card shadow="hover">
<template #header>
<el-row :gutter="10" class="mb8">
<right-toolbar v-model:showSearch="showSearch" @query-table="handleQuery"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" :data="processInstanceList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column fixed align="center" type="index" label="序号" width="50"></el-table-column>
<el-table-column v-if="false" fixed align="center" prop="id" label="id"></el-table-column>
<el-table-column fixed align="center" prop="processDefinitionName" label="流程定义名称"></el-table-column>
<el-table-column fixed align="center" prop="processDefinitionKey" label="流程定义KEY"></el-table-column>
<el-table-column align="center" prop="processDefinitionVersion" label="版本号" width="90">
<template #default="scope"> v{{ scope.row.processDefinitionVersion }}.0</template>
</el-table-column>
<el-table-column v-if="tab === 'running'" align="center" prop="isSuspended" label="状态" min-width="70">
<template #default="scope">
<el-tag v-if="!scope.row.isSuspended" type="success">激活</el-tag>
<el-tag v-else type="danger">挂起</el-tag>
</template>
</el-table-column>
<el-table-column align="center" prop="businessStatusName" label="流程状态" min-width="70">
<template #default="scope">
<el-tag type="success">{{ scope.row.businessStatusName }}</el-tag>
</template>
</el-table-column>
<el-table-column align="center" prop="startTime" label="启动时间" width="160"></el-table-column>
<el-table-column v-if="tab === 'finish'" align="center" prop="endTime" label="结束时间" width="160"></el-table-column>
<el-table-column label="操作" align="center" width="160" class-name="small-padding fixed-width">
<template #default="scope">
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button link type="primary" size="small" icon="Document" @click="handleApprovalRecord(scope.row.id)">审批记录</el-button>
</el-col>
<el-col
v-if="scope.row.businessStatus === 'draft' || scope.row.businessStatus === 'cancel' || scope.row.businessStatus === 'back'"
:span="1.5"
>
<el-button link type="primary" size="small" icon="Delete" @click="handleDelete(scope.row)">删除</el-button>
</el-col>
<el-col v-if="scope.row.businessStatus === 'waiting'" :span="1.5">
<el-button link type="primary" size="small" icon="Notification" @click="handleCancelProcessApply(scope.row.id)">撤销</el-button>
</el-col>
<el-col
v-if="scope.row.businessStatus === 'draft' || scope.row.businessStatus === 'cancel' || scope.row.businessStatus === 'back'"
:span="1.5"
>
<el-button link type="primary" size="small" icon="Edit" @click="submitVerifyOpen(scope.row.taskVoList[0].id)">提交</el-button>
</el-col>
</el-row>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total > 0"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
:total="total"
@pagination="getList"
/>
</el-card>
</el-col>
</el-row>
<!-- 审批记录 -->
<approvalRecord ref="approvalRecordRef" />
<!-- 提交组件 -->
<submitVerify ref="submitVerifyRef" @submit-callback="getList" />
</div>
</template>
<script lang="ts" setup>
import { getCurrentSubmitByPage, deleteRuntimeProcessAndHisInst, cancelProcessApply } from '@/api/workflow/processInstance';
import ApprovalRecord from '@/components/Process/approvalRecord.vue';
import SubmitVerify from '@/components/Process/submitVerify.vue';
import { listCategory } from '@/api/workflow/category';
import { CategoryVO } from '@/api/workflow/category/types';
import { ProcessInstanceQuery, ProcessInstanceVO } from '@/api/workflow/processInstance/types';
//提交组件
const submitVerifyRef = ref<InstanceType<typeof SubmitVerify>>();
//审批记录组件
const approvalRecordRef = ref<InstanceType<typeof ApprovalRecord>>();
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const queryFormRef = ref<ElFormInstance>();
const categoryTreeRef = ref<ElTreeInstance>();
// 遮罩层
const loading = ref(true);
// 选中数组
const ids = ref<Array<any>>([]);
// 非单个禁用
const single = ref(true);
// 非多个禁用
const multiple = ref(true);
// 显示搜索条件
const showSearch = ref(true);
// 总条数
const total = ref(0);
// 模型定义表格数据
const processInstanceList = ref<ProcessInstanceVO[]>([]);
const categoryOptions = ref<CategoryOption[]>([]);
const categoryName = ref('');
interface CategoryOption {
categoryCode: string;
categoryName: string;
children?: CategoryOption[];
}
const tab = ref('running');
// 查询参数
const queryParams = ref<ProcessInstanceQuery>({
pageNum: 1,
pageSize: 10,
name: undefined,
categoryCode: undefined
});
onMounted(() => {
getList();
getTreeselect();
});
/** 节点单击事件 */
const handleNodeClick = (data: CategoryVO) => {
queryParams.value.categoryCode = data.categoryCode;
if (data.categoryCode === 'ALL') {
queryParams.value.categoryCode = '';
}
handleQuery();
};
/** 通过条件过滤节点 */
const filterNode = (value: string, data: any) => {
if (!value) return true;
return data.categoryName.indexOf(value) !== -1;
};
/** 根据名称筛选部门树 */
watchEffect(
() => {
categoryTreeRef.value.filter(categoryName.value);
},
{
flush: 'post' // watchEffect会在DOM挂载或者更新之前就会触发此属性控制在DOM元素更新后运行
}
);
/** 查询流程分类下拉树结构 */
const getTreeselect = async () => {
const res = await listCategory();
categoryOptions.value = [];
const data: CategoryOption = { categoryCode: 'ALL', categoryName: '顶级节点', children: [] };
data.children = proxy?.handleTree<CategoryOption>(res.data, 'id', 'parentId');
categoryOptions.value.push(data);
};
//审批记录
const handleApprovalRecord = (processInstanceId: string) => {
if (approvalRecordRef.value) {
approvalRecordRef.value.init(processInstanceId);
}
};
/** 搜索按钮操作 */
const handleQuery = () => {
getList();
};
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value?.resetFields();
queryParams.value.categoryCode = '';
queryParams.value.pageNum = 1;
queryParams.value.pageSize = 10;
handleQuery();
};
// 多选框选中数据
const handleSelectionChange = (selection: ProcessInstanceVO[]) => {
ids.value = selection.map((item: any) => item.id);
single.value = selection.length !== 1;
multiple.value = !selection.length;
};
//分页
const getList = () => {
loading.value = true;
getCurrentSubmitByPage(queryParams.value).then((resp) => {
processInstanceList.value = resp.rows;
total.value = resp.total;
loading.value = false;
});
};
/** 删除按钮操作 */
const handleDelete = async (row: ProcessInstanceVO) => {
const id = row.id || ids.value;
await proxy?.$modal.confirm('是否确认删除id为【' + id + '】的数据项?');
loading.value = true;
if ('running' === tab.value) {
await deleteRuntimeProcessAndHisInst(id).finally(() => (loading.value = false));
getList();
}
proxy?.$modal.msgSuccess('删除成功');
};
/** 撤销按钮操作 */
const handleCancelProcessApply = async (processInstanceId: string) => {
await proxy?.$modal.confirm('是否确认撤销当前单据?');
loading.value = true;
if ('running' === tab.value) {
await cancelProcessApply(processInstanceId).finally(() => (loading.value = false));
getList();
}
proxy?.$modal.msgSuccess('撤销成功');
};
//提交
const submitVerifyOpen = async (id: string) => {
if (submitVerifyRef.value) {
submitVerifyRef.value.openDialog(id);
}
};
</script>

View File

@ -0,0 +1,141 @@
<template>
<div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form v-show="showSearch" ref="queryFormRef" :model="queryParams" :inline="true" label-width="68px">
<el-form-item label="任务名称" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入任务名称" @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="流程定义名称" label-width="100" prop="processDefinitionName">
<el-input v-model="queryParams.processDefinitionName" placeholder="请输入流程定义名称" @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="流程定义KEY" label-width="100" prop="processDefinitionKey">
<el-input v-model="queryParams.processDefinitionKey" placeholder="请输入流程定义KEY" @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>
</el-card>
</div>
</transition>
<el-card shadow="hover">
<template #header>
<el-row :gutter="10" class="mb8">
<right-toolbar v-model:showSearch="showSearch" @query-table="handleQuery"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" :data="taskList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column fixed align="center" type="index" label="序号" width="50"></el-table-column>
<el-table-column fixed align="center" prop="processDefinitionName" label="流程定义名称"></el-table-column>
<el-table-column fixed align="center" prop="processDefinitionKey" label="流程定义KEY"></el-table-column>
<el-table-column fixed align="center" prop="name" label="任务名称"></el-table-column>
<el-table-column fixed align="center" prop="assigneeName" label="办理人">
<template #default="scope">
<template v-if="scope.row.participantVo && scope.row.assignee === null">
<el-tag v-for="(item, index) in scope.row.participantVo.candidateName" :key="index" type="success">
{{ item }}
</el-tag>
</template>
<template v-else>
<el-tag type="success">
{{ scope.row.assigneeName }}
</el-tag>
</template>
</template>
</el-table-column>
<el-table-column align="center" prop="businessStatusName" label="流程状态" min-width="70">
<template #default="scope">
<el-tag type="success">{{ scope.row.businessStatusName }}</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="160" class-name="small-padding fixed-width">
<template #default="scope">
<el-button link type="primary" size="small" icon="Document" @click="handleApprovalRecord(scope.row)">审批记录</el-button>
</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>
<!-- 审批记录 -->
<approvalRecord ref="approvalRecordRef" />
</div>
</template>
<script lang="ts" setup>
import { getTaskCopyByPage} from '@/api/workflow/task';
import ApprovalRecord from '@/components/Process/approvalRecord.vue';
import { TaskQuery, TaskVO } from '@/api/workflow/task/types';
//审批记录组件
const approvalRecordRef = ref<InstanceType<typeof ApprovalRecord>>();
const queryFormRef = ref<ElFormInstance>();
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
// 遮罩层
const loading = ref(true);
// 选中数组
const ids = ref<Array<any>>([]);
// 非单个禁用
const single = ref(true);
// 非多个禁用
const multiple = ref(true);
// 显示搜索条件
const showSearch = ref(true);
// 总条数
const total = ref(0);
// 模型定义表格数据
const taskList = ref([]);
// 查询参数
const queryParams = ref<TaskQuery>({
pageNum: 1,
pageSize: 10,
name: undefined,
processDefinitionName: undefined,
processDefinitionKey: undefined
});
onMounted(() => {
getTaskCopyList();
});
//审批记录
const handleApprovalRecord = (row: TaskVO) => {
if (approvalRecordRef.value) {
approvalRecordRef.value.init(row.processInstanceId);
}
};
/** 搜索按钮操作 */
const handleQuery = () => {
getTaskCopyList();
};
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value?.resetFields();
queryParams.value.pageNum = 1;
queryParams.value.pageSize = 10;
handleQuery();
};
// 多选框选中数据
const handleSelectionChange = (selection: any) => {
ids.value = selection.map((item: any) => item.id);
single.value = selection.length !== 1;
multiple.value = !selection.length;
};
//分页
const getTaskCopyList = () => {
loading.value = true;
getTaskCopyByPage(queryParams.value).then((resp) => {
taskList.value = resp.rows;
total.value = resp.total;
loading.value = false;
});
};
</script>

View File

@ -0,0 +1,128 @@
<template>
<div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form v-show="showSearch" ref="queryFormRef" :model="queryParams" :inline="true" label-width="68px">
<el-form-item label="任务名称" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入任务名称" @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="流程定义名称" label-width="100" prop="processDefinitionName">
<el-input v-model="queryParams.processDefinitionName" placeholder="请输入流程定义名称" @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="流程定义KEY" label-width="100" prop="processDefinitionKey">
<el-input v-model="queryParams.processDefinitionKey" placeholder="请输入流程定义KEY" @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>
</el-card>
</div>
</transition>
<el-card shadow="hover">
<template #header>
<el-row :gutter="10" class="mb8">
<right-toolbar v-model:showSearch="showSearch" @query-table="handleQuery"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" :data="taskList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column fixed align="center" type="index" label="序号" width="50"></el-table-column>
<el-table-column fixed align="center" prop="processDefinitionName" label="流程定义名称"></el-table-column>
<el-table-column fixed align="center" prop="processDefinitionKey" label="流程定义KEY"></el-table-column>
<el-table-column fixed align="center" prop="name" label="任务名称"></el-table-column>
<el-table-column fixed align="center" prop="assigneeName" label="办理人">
<template #default="scope">
<el-tag type="success">
{{ scope.row.assigneeName }}
</el-tag>
</template>
</el-table-column>
<el-table-column align="center" prop="createTime" label="创建时间" width="160"></el-table-column>
<el-table-column label="操作" align="center" width="160" class-name="small-padding fixed-width">
<template #default="scope">
<el-button link type="primary" size="small" icon="Document" @click="handleApprovalRecord(scope.row)">审批记录</el-button>
</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>
<!-- 审批记录 -->
<approvalRecord ref="approvalRecordRef" />
</div>
</template>
<script lang="ts" setup>
import { getTaskFinishByPage } from '@/api/workflow/task';
import ApprovalRecord from '@/components/Process/approvalRecord.vue';
import { TaskQuery, TaskVO } from '@/api/workflow/task/types';
//审批记录组件
const approvalRecordRef = ref<InstanceType<typeof ApprovalRecord>>();
const queryFormRef = ref<ElFormInstance>();
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
// 遮罩层
const loading = ref(true);
// 选中数组
const ids = ref<Array<any>>([]);
// 非单个禁用
const single = ref(true);
// 非多个禁用
const multiple = ref(true);
// 显示搜索条件
const showSearch = ref(true);
// 总条数
const total = ref(0);
// 模型定义表格数据
const taskList = ref([]);
// 查询参数
const queryParams = ref<TaskQuery>({
pageNum: 1,
pageSize: 10,
name: undefined,
processDefinitionName: undefined,
processDefinitionKey: undefined
});
onMounted(() => {
getFinishList();
});
//审批记录
const handleApprovalRecord = (row: TaskVO) => {
if (approvalRecordRef.value) {
approvalRecordRef.value.init(row.processInstanceId);
}
};
/** 搜索按钮操作 */
const handleQuery = () => {
getFinishList();
};
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value?.resetFields();
queryParams.value.pageNum = 1;
queryParams.value.pageSize = 10;
handleQuery();
};
// 多选框选中数据
const handleSelectionChange = (selection: any) => {
ids.value = selection.map((item: any) => item.id);
single.value = selection.length !== 1;
multiple.value = !selection.length;
};
const getFinishList = () => {
loading.value = true;
getTaskFinishByPage(queryParams.value).then((resp) => {
taskList.value = resp.rows;
total.value = resp.total;
loading.value = false;
});
};
</script>

View File

@ -0,0 +1,181 @@
<template>
<div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form v-show="showSearch" ref="queryFormRef" :model="queryParams" :inline="true" label-width="68px">
<el-form-item label="任务名称" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入任务名称" @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="流程定义名称" label-width="100" prop="processDefinitionName">
<el-input v-model="queryParams.processDefinitionName" placeholder="请输入流程定义名称" @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="流程定义KEY" label-width="100" prop="processDefinitionKey">
<el-input v-model="queryParams.processDefinitionKey" placeholder="请输入流程定义KEY" @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>
</el-card>
</div>
</transition>
<el-card shadow="hover">
<template #header>
<el-row :gutter="10" class="mb8">
<right-toolbar v-model:showSearch="showSearch" @query-table="handleQuery"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" :data="taskList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column fixed align="center" type="index" label="序号" width="50"></el-table-column>
<el-table-column fixed align="center" prop="processDefinitionName" label="流程定义名称"></el-table-column>
<el-table-column fixed align="center" prop="processDefinitionKey" label="流程定义KEY"></el-table-column>
<el-table-column fixed align="center" prop="name" label="任务名称"></el-table-column>
<el-table-column fixed align="center" prop="assigneeName" label="办理人">
<template #default="scope">
<template v-if="scope.row.participantVo && scope.row.assignee === null">
<el-tag v-for="(item, index) in scope.row.participantVo.candidateName" :key="index" type="success">
{{ item }}
</el-tag>
</template>
<template v-else>
<el-tag type="success">
{{ scope.row.assigneeName }}
</el-tag>
</template>
</template>
</el-table-column>
<el-table-column align="center" prop="businessStatusName" label="流程状态" min-width="70">
<template #default="scope">
<el-tag type="success">{{ scope.row.businessStatusName }}</el-tag>
</template>
</el-table-column>
<el-table-column align="center" prop="createTime" label="创建时间" width="160"></el-table-column>
<el-table-column label="操作" align="center" width="160" class-name="small-padding fixed-width">
<template #default="scope">
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button link type="primary" size="small" icon="Document" @click="handleApprovalRecord(scope.row)">审批记录</el-button>
</el-col>
<el-col v-if="scope.row.participantVo && (scope.row.participantVo.claim === null || scope.row.participantVo.claim === true)" :span="1.5">
<el-button link type="primary" size="small" icon="Edit" @click="submitVerifyOpen(scope.row.id)">办理</el-button>
</el-col>
<el-col v-if="scope.row.participantVo && scope.row.participantVo.claim === true" :span="1.5">
<el-button link type="primary" size="small" icon="Document" @click="handleReturnTask(scope.row.id)">归还</el-button>
</el-col>
<el-col v-if="scope.row.participantVo && scope.row.participantVo.claim === false" :span="1.5">
<el-button link type="primary" size="small" icon="Document" @click="handleClaimTask(scope.row.id)">认领</el-button>
</el-col>
</el-row>
</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>
<!-- 审批记录 -->
<approvalRecord ref="approvalRecordRef" />
<!-- 提交组件 -->
<submitVerify ref="submitVerifyRef" @submit-callback="handleQuery" />
</div>
</template>
<script lang="ts" setup>
import { getTaskWaitByPage, claim, returnTask } from '@/api/workflow/task';
import ApprovalRecord from '@/components/Process/approvalRecord.vue';
import SubmitVerify from '@/components/Process/submitVerify.vue';
import { TaskQuery, TaskVO } from '@/api/workflow/task/types';
//提交组件
const submitVerifyRef = ref<InstanceType<typeof SubmitVerify>>();
//审批记录组件
const approvalRecordRef = ref<InstanceType<typeof ApprovalRecord>>();
const queryFormRef = ref<ElFormInstance>();
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
// 遮罩层
const loading = ref(true);
// 选中数组
const ids = ref<Array<any>>([]);
// 非单个禁用
const single = ref(true);
// 非多个禁用
const multiple = ref(true);
// 显示搜索条件
const showSearch = ref(true);
// 总条数
const total = ref(0);
// 模型定义表格数据
const taskList = ref([]);
// 查询参数
const queryParams = ref<TaskQuery>({
pageNum: 1,
pageSize: 10,
name: undefined,
processDefinitionName: undefined,
processDefinitionKey: undefined
});
onMounted(() => {
getWaitingList();
});
//审批记录
const handleApprovalRecord = (row: TaskVO) => {
if (approvalRecordRef.value) {
approvalRecordRef.value.init(row.processInstanceId);
}
};
/** 搜索按钮操作 */
const handleQuery = () => {
getWaitingList();
};
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value?.resetFields();
queryParams.value.pageNum = 1;
queryParams.value.pageSize = 10;
handleQuery();
};
// 多选框选中数据
const handleSelectionChange = (selection: any) => {
ids.value = selection.map((item: any) => item.id);
single.value = selection.length !== 1;
multiple.value = !selection.length;
};
//分页
const getWaitingList = () => {
loading.value = true;
getTaskWaitByPage(queryParams.value).then((resp) => {
taskList.value = resp.rows;
total.value = resp.total;
loading.value = false;
});
};
//提交
const submitVerifyOpen = async (id: string) => {
if (submitVerifyRef.value) {
submitVerifyRef.value.openDialog(id);
}
};
/** 认领任务 */
const handleClaimTask = async (taskId: string) => {
loading.value = true;
await claim(taskId).finally(() => (loading.value = false));
getWaitingList();
proxy?.$modal.msgSuccess('操作成功');
};
/** 归还任务 */
const handleReturnTask = async (taskId: string) => {
loading.value = true;
await returnTask(taskId).finally(() => (loading.value = false));
getWaitingList();
proxy?.$modal.msgSuccess('操作成功');
};
</script>