提交代码

dev_xd
姜玉琦 2024-08-19 23:37:38 +08:00
parent 3f0d87a28d
commit 8045c5535b
32 changed files with 3926 additions and 3 deletions

View File

@ -1,8 +1,8 @@
{
"name": "ruoyi",
"name": "yanzhu",
"version": "3.6.2",
"description": "若依管理系统",
"author": "若依",
"description": "总包单位管理系统",
"author": "研筑",
"license": "MIT",
"scripts": {
"dev": "vite",
@ -19,6 +19,9 @@
"@vueup/vue-quill": "1.1.0",
"@vueuse/core": "9.5.0",
"axios": "0.27.2",
"bpmn-js": "^8.10.0",
"bpmn-js-task-resize": "^1.2.0",
"bpmn-js-token-simulation": "^0.10.0",
"echarts": "5.4.0",
"element-plus": "2.2.27",
"file-saver": "2.0.5",

View File

@ -0,0 +1,55 @@
import request from '@/utils/request';
import { AxiosPromise } from 'axios';
import { CategoryForm, CategoryQuery, CategoryVO } from '@/api/workflow/category/types';
// 查询流程分类列表
export function listCategory(query: CategoryQuery): AxiosPromise<CategoryVO[]> {
return request({
url: '/workflow/category/list',
method: 'get',
params: query
});
}
// 查询流程分类列表
export function listAllCategory(query?: CategoryQuery): AxiosPromise<CategoryVO[]> {
return request({
url: '/workflow/category/listAll',
method: 'get',
params: query
});
}
// 查询流程分类详细
export function getCategory(categoryId?: string | number): AxiosPromise<CategoryVO> {
return request({
url: '/workflow/category/' + categoryId,
method: 'get'
});
}
// 新增流程分类
export function addCategory(data: CategoryForm) {
return request({
url: '/workflow/category',
method: 'post',
data: data
});
}
// 修改流程分类
export function updateCategory(data: CategoryForm) {
return request({
url: '/workflow/category',
method: 'put',
data: data
});
}
// 删除流程分类
export function delCategory(categoryIds?: string | number | Array<string | number>) {
return request({
url: '/workflow/category/' + categoryIds,
method: 'delete'
});
}

View File

@ -0,0 +1,24 @@
/**
*
*/
export interface CategoryQuery extends PageQuery {
categoryName?: string;
code?: string;
}
/**
*
*/
export interface CategoryVO extends BaseEntity {
categoryId: string | number;
categoryName: string;
code: string;
remark: string;
}
export interface CategoryForm {
categoryId?: string | number;
categoryName?: string;
code?: string;
remark?: string;
}

View File

@ -0,0 +1,45 @@
import request from '@/utils/request';
import { AxiosPromise } from 'axios';
import { DeployVO, ProcessQuery } from '@/api/workflow/deploy/types';
// 查询流程部署列表
export function listDeploy(query: ProcessQuery): AxiosPromise<DeployVO[]> {
return request({
url: '/workflow/deploy/list',
method: 'get',
params: query
});
}
export function listPublish(query: ProcessQuery): AxiosPromise<DeployVO[]> {
return request({
url: '/workflow/deploy/publishList',
method: 'get',
params: query
});
}
// 获取流程模型流程图
export function getBpmnXml(definitionId?: string) {
return request({
url: '/workflow/deploy/bpmnXml/' + definitionId,
method: 'get'
});
}
// 修改流程状态
export function changeState(params?: any) {
return request({
url: '/workflow/deploy/changeState',
method: 'put',
params: params
});
}
// 删除流程部署
export function delDeploy(deployIds?: string | string[]) {
return request({
url: '/workflow/deploy/' + deployIds,
method: 'delete'
});
}

View File

@ -0,0 +1,19 @@
export interface DeployVO {
definitionId: string;
processName: string;
processKey: string;
category: string;
version: number;
formId: string | number;
formName: string;
deploymentId: string;
suspended: boolean;
deploymentTime: string;
}
export interface ProcessQuery extends PageQuery {
processKey?: string;
processName?: string;
category?: string;
state?: string;
}

View File

@ -0,0 +1,46 @@
import request from '@/utils/request';
import { FormForm, FormQuery, FormVO } from './types';
import { AxiosPromise } from 'axios';
// 查询流程表单列表
export function listForm(query?: FormQuery): AxiosPromise<FormVO[]> {
return request({
url: '/workflow/form/list',
method: 'get',
params: query
});
}
// 查询流程表单详细
export function getForm(formId: string | number): AxiosPromise<FormVO> {
return request({
url: '/workflow/form/' + formId,
method: 'get'
});
}
// 新增流程表单
export function addForm(data: FormForm) {
return request({
url: '/workflow/form',
method: 'post',
data: data
});
}
// 修改流程表单
export function updateForm(data: FormForm) {
return request({
url: '/workflow/form',
method: 'put',
data: data
});
}
// 删除流程表单
export function delForm(formId?: string | number | (string | number)[]) {
return request({
url: '/workflow/form/' + formId,
method: 'delete'
});
}

View File

@ -0,0 +1,17 @@
export interface FormVO extends BaseEntity {
formId: number | string;
formName: string;
content: string;
remark: string;
}
export interface FormForm {
formId: number | string | undefined;
formName: string;
content?: string;
remark: string;
}
export interface FormQuery extends PageQuery {
formName?: string;
}

View File

@ -0,0 +1,19 @@
import request from '@/utils/request';
import { AxiosPromise } from 'axios';
import { UserVO, DeptVO } from '@/api/workflow/identity/types';
// 查询流程模型信息
export function selectUser(query?: any): AxiosPromise<UserVO[]> {
return request({
url: '/workflow/identity/selectUser',
method: 'get',
params: query
});
}
export function deptTreeSelect(): AxiosPromise<DeptVO[]> {
return request({
url: '/workflow/identity/deptTree',
method: 'get'
});
}

View File

@ -0,0 +1,11 @@
export interface UserVO {
userId: string | number;
username: string;
nickname: string;
deptId: number;
}
export interface DeptVO {
deptId: string | number;
deptName: string;
}

View File

@ -0,0 +1,81 @@
import request from '@/utils/request';
import {ListenerFieldForm, ListenerForm, ListenerQuery, ListenerVO} from './types';
import {AxiosPromise} from 'axios';
// 分页查询流程监听器
export function queryListenerPage(query?: ListenerQuery): AxiosPromise<ListenerVO[]> {
return request({
url: '/workflow/listener/queryPage',
method: 'get',
params: query
});
}
// 列表查询流程监听器
export function queryListenerList(query?: ListenerQuery): AxiosPromise<ListenerVO[]> {
return request({
url: '/workflow/listener/queryList',
method: 'get',
params: query
});
}
// 详细查询流程监听器
export function getListener(formId: string | number): AxiosPromise<ListenerVO> {
return request({
url: '/workflow/listener/query/' + formId,
method: 'get'
});
}
// 新增流程监听器
export function addListener(data: ListenerForm) {
return request({
url: '/workflow/listener/insert',
method: 'post',
data: data
});
}
// 修改流程监听器
export function updateListener(data: ListenerForm) {
return request({
url: '/workflow/listener/update',
method: 'post',
data: data
});
}
// 删除流程监听器
export function delListener(listenerId?: string | number | (string | number)[]) {
return request({
url: '/workflow/listener/delete/' + listenerId,
method: 'post'
});
}
// 新增流程监听器字段
export function insertListenerFieldAPI(data: ListenerFieldForm) {
return request({
url: '/workflow/listener/insertField',
method: 'post',
data: data
});
}
// 修改流程监听器字段
export function updateListenerFieldAPI(data: ListenerFieldForm) {
return request({
url: '/workflow/listener/updateField',
method: 'post',
data: data
});
}
// 删除流程监听器字段
export function deleteListenerFieldAPI(fieldIds?: string | number | (string | number)[]) {
return request({
url: '/workflow/listener/deleteField/' + fieldIds,
method: 'post'
});
}

View File

@ -0,0 +1,40 @@
export interface ListenerVO {
id: number | string;
listenerType: string;
name: string;
eventType: string;
valueType: string;
value: string;
fields: ListenerFieldVO[];
}
export interface ListenerForm {
id: number | string | undefined;
listenerType: string;
name: string;
eventType: string;
valueType: string;
value: string;
}
export interface ListenerQuery extends PageQuery {
listenerType?: string;
eventType?: string;
name?: string;
}
export interface ListenerFieldVO {
id: number | string;
listenerId: number | string;
fieldName: string;
fieldType: string;
fieldValue: string;
}
export interface ListenerFieldForm {
id: number | string | undefined;
listenerId: number | string | undefined;
fieldName: string | undefined;
fieldType: string | undefined;
fieldValue: string | undefined;
}

View File

@ -0,0 +1,86 @@
import request from '@/utils/request';
import { AxiosPromise } from 'axios';
import { ModelQuery, ModelVO } from './types';
// 查询流程模型信息
export function listModel(query?: ModelQuery): AxiosPromise<ModelVO[]> {
return request({
url: '/workflow/model/list',
method: 'get',
params: query
});
}
// 查询流程模型信息
export function historyModel(query?: ModelQuery): AxiosPromise<ModelVO[]> {
return request({
url: '/workflow/model/historyList',
method: 'get',
params: query
});
}
export function getModel(modelId?: string) {
return request({
url: '/workflow/model/' + modelId,
method: 'get'
});
}
// 新增模型信息
export function addModel(data?: any) {
return request({
url: '/workflow/model',
method: 'post',
data: data
});
}
// 修改模型信息
export function updateModel(data?: any) {
return request({
url: '/workflow/model',
method: 'put',
data: data
});
}
// 保存流程模型
export function saveModel(data?: any) {
return request({
url: '/workflow/model/save',
method: 'post',
data: data
});
}
export function latestModel(params?: any) {
return request({
url: '/workflow/model/latest',
method: 'post',
params: params
});
}
export function delModel(modelIds?: string | string[]) {
return request({
url: '/workflow/model/' + modelIds,
method: 'delete'
});
}
export function deployModel(params?: any) {
return request({
url: '/workflow/model/deploy',
method: 'post',
params: params
});
}
// 获取流程模型流程图
export function getBpmnXml(modelId?: string) {
return request({
url: '/workflow/model/bpmnXml/' + modelId,
method: 'get'
});
}

View File

@ -0,0 +1,38 @@
export interface ModelVO extends BaseEntity {
modelId: string;
modelKey: string;
modelName: string;
category: string;
version: number;
formType: number;
formId: number | string;
description: string;
createTime: string;
bpmnXml: string;
content: string;
}
export interface ModelForm {
modelId: string | undefined;
modelKey: string;
modelName: string;
category: string;
description: string;
formType: number | undefined;
formId: number | string | undefined;
bpmnXml: string;
newVersion: boolean;
}
export interface ModelQuery extends PageQuery {
modelKey?: string;
modelName?: string;
}
export interface DesignerForm {
modelId: string;
form: {
processName: string;
processKey: string;
};
}

View File

@ -0,0 +1,108 @@
import request from '@/utils/request';
import { AxiosPromise } from 'axios';
import { ProcessQuery, ProcessVO } from '@/api/workflow/work/types';
// 查询流程列表
export function listProcess(query?: ProcessQuery): AxiosPromise<ProcessVO[]> {
return request({
url: '/workflow/process/list',
method: 'get',
params: query
});
}
// 查询流程列表
export function getProcessForm(query?: any) {
return request({
url: '/workflow/process/getProcessForm',
method: 'get',
params: query
});
}
// 部署流程实例
export function startProcess(processDefId?: string, data?: string) {
return request({
url: '/workflow/process/start/' + processDefId,
method: 'post',
data: data
});
}
// 删除流程实例
export function delProcess(ids?: string) {
return request({
url: '/workflow/process/instance/' + ids,
method: 'delete'
});
}
// 获取流程图
export function getBpmnXml(processDefId?: string) {
return request({
url: '/workflow/process/bpmnXml/' + processDefId,
method: 'get'
});
}
export function detailProcess(query?: any) {
return request({
url: '/workflow/process/detail',
method: 'get',
params: query
});
}
// 我的发起的流程
export function listOwnProcess(query?: any) {
return request({
url: '/workflow/process/ownList',
method: 'get',
params: query
});
}
// 我待办的流程
export function listTodoProcess(query?: any) {
return request({
url: '/workflow/process/todoList',
method: 'get',
params: query
});
}
// 我待签的流程
export function listClaimProcess(query?: any) {
return request({
url: '/workflow/process/claimList',
method: 'get',
params: query
});
}
// 我已办的流程
export function listFinishedProcess(query?: any) {
return request({
url: '/workflow/process/finishedList',
method: 'get',
params: query
});
}
// 查询流程抄送列表
export function listCopyProcess(query?: any) {
return request({
url: '/workflow/process/copyList',
method: 'get',
params: query
});
}
// 取消申请
export function stopProcess(data?: any) {
return request({
url: '/workflow/task/stopProcess',
method: 'post',
data: data
});
}

View File

@ -0,0 +1,73 @@
import request from '@/utils/request';
// 完成任务
export function complete(data?: any) {
return request({
url: '/workflow/task/complete',
method: 'post',
data: data
});
}
// 委派任务
export function delegate(data?: any) {
return request({
url: '/workflow/task/delegate',
method: 'post',
data: data
});
}
// 转办任务
export function transfer(data?: any) {
return request({
url: '/workflow/task/transfer',
method: 'post',
data: data
});
}
// 退回任务
export function returnTask(data?: any) {
return request({
url: '/workflow/task/return',
method: 'post',
data: data
});
}
// 拒绝任务
export function rejectTask(data?: any) {
return request({
url: '/workflow/task/reject',
method: 'post',
data: data
});
}
// 签收任务
export function claimTask(data?: any) {
return request({
url: '/workflow/task/claim',
method: 'post',
data: data
});
}
// 可退回任务列表
export function returnList(data?: any) {
return request({
url: '/workflow/task/returnList',
method: 'post',
data: data
});
}
// 撤回任务
export function revokeProcess(data?: any) {
return request({
url: '/workflow/task/revokeProcess',
method: 'post',
data: data
});
}

View File

@ -0,0 +1,32 @@
export interface ProcessVO extends BaseEntity {
category: string;
definitionId: string;
deploymentId: string;
deploymentTime: string;
formId: string | number;
formName: string;
processKey: string;
processName: string;
taskId: string;
suspended: boolean;
version: number;
procInsId?: string;
}
export interface ProcessQuery extends PageQuery {
processKey?: string;
processName?: string;
category?: string;
state?: string;
}
export interface TaskForm {
comment: string;
procInsId: string;
taskId: string;
userId: string;
copyUserIds: string;
nextUserIds: string;
vars: string;
targetKey: string;
}

View File

@ -0,0 +1,210 @@
<template>
<div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div class="search" v-show="showSearch">
<el-form :model="queryParams" ref="queryFormRef" :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="code">
<el-input v-model="queryParams.code" 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 type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['workflow:category:add']"></el-button>
</el-col>
<el-col :span="1.5">
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['workflow:category:edit']">
修改
</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['workflow:category:remove']">
删除
</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="warning" plain icon="Download" @click="handleExport()" v-hasPermi="['workflow:category:export']"></el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" :data="categoryList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="分类编号" align="center" prop="categoryId" v-if="true" />
<el-table-column label="分类名称" align="center" prop="categoryName" />
<el-table-column label="分类编码" align="center" prop="code" />
<el-table-column label="备注" align="center" prop="remark" />
<el-table-column label="操作" align="center" width="150" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip content="修改" placement="top">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['workflow:category:edit']"></el-button>
</el-tooltip>
<el-tooltip content="删除" placement="top">
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['workflow:category:remove']"></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
</el-card>
<!-- 添加或修改参数配置对话框 -->
<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
<el-form ref="categoryFormRef" :model="form" :rules="rules" label-width="80px">
<el-form-item label="分类名称" prop="categoryName">
<el-input v-model="form.categoryName" placeholder="请输入分类名称" />
</el-form-item>
<el-form-item label="分类编码" prop="code">
<el-input v-model="form.code" placeholder="请输入分类编码" />
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button 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 { CategoryForm, CategoryQuery, CategoryVO } from "@/api/workflow/category/types";
import { ComponentInternalInstance } from "vue";
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const categoryList = ref<CategoryVO[]>([]);
const loading = ref(true);
const showSearch = ref(true);
const ids = ref<Array<number | string>>([]);
const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const queryFormRef = ref(ElForm);
const categoryFormRef = ref(ElForm);
const dialog = reactive<DialogOption>({
visible: false,
title: ''
});
const initFormData: CategoryForm = {
categoryId: undefined,
categoryName: '',
code: '',
remark: ''
}
const data = reactive<PageData<CategoryForm, CategoryQuery>>({
form: {...initFormData},
queryParams: {
pageNum: 1,
pageSize: 10,
categoryName: '',
code: ''
},
rules: {
categoryName: [{ required: true, message: "分类名称不能为空", trigger: "blur" }],
code: [{ required: true, message: "分类编码不能为空", trigger: "blur" }]
}
});
const { queryParams, form, rules } = toRefs(data);
/** 查询参数列表 */
const getList = async () => {
loading.value = true;
const res = await listCategory(queryParams.value);
categoryList.value = res.rows;
total.value = res.total;
loading.value = false;
}
/** 取消按钮 */
const cancel = () => {
reset();
dialog.visible = false;
}
/** 表单重置 */
const reset = () => {
form.value = {...initFormData};
categoryFormRef.value.resetFields();
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.value.pageNum = 1;
getList();
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields();
handleQuery();
}
/** 多选框选中数据 */
const handleSelectionChange = (selection: CategoryVO[]) => {
ids.value = selection.map(item => item.categoryId);
single.value = selection.length != 1;
multiple.value = !selection.length;
}
/** 新增按钮操作 */
const handleAdd = () => {
dialog.visible = true;
dialog.title = "添加参数";
nextTick(() => {
reset();
})
}
/** 修改按钮操作 */
const handleUpdate = (row?: CategoryVO) => {
dialog.visible = true;
dialog.title = "修改参数";
const categoryId = row?.categoryId || ids.value[0];
nextTick(async () => {
reset();
const res = await getCategory(categoryId);
form.value = res.data;
})
}
/** 提交按钮 */
const submitForm = () => {
categoryFormRef.value.validate(async (valid: boolean) => {
if (valid) {
form.value.categoryId ? await updateCategory(form.value) : await addCategory(form.value);
proxy?.$modal.msgSuccess("操作成功");
dialog.visible = false;
getList();
}
});
}
/** 删除按钮操作 */
const handleDelete = async (row?: CategoryVO) => {
const categoryIds = row?.categoryId || ids.value;
await proxy?.$modal.confirm('是否确认删除参数编号为"' + categoryIds + '"的数据项?');
await delCategory(categoryIds);
getList();
proxy?.$modal.msgSuccess("删除成功");
}
/** 导出按钮操作 */
const handleExport = () => {
proxy?.download("workflow/category/export", {
...queryParams.value
}, `category_${new Date().getTime()}.xlsx`);
}
onMounted(() => {
getList();
})
</script>

View File

@ -0,0 +1,235 @@
<template>
<div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div class="search" v-show="showSearch">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
<el-form-item label="流程标识" prop="processKey">
<el-input v-model="queryParams.processKey" placeholder="请输入流程标识" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="流程名称" prop="processName">
<el-input v-model="queryParams.processName" placeholder="请输入流程名称" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="流程分类" prop="category">
<el-select v-model="queryParams.category" clearable placeholder="流程分类">
<el-option v-for="item in categoryOptions" :key="item.categoryId" :label="item.categoryName" :value="item.code" />
</el-select>
</el-form-item>
<el-form-item label="状态" prop="state">
<el-select v-model="queryParams.state" clearable placeholder="请选择状态">
<el-option :key="1" label="激活" value="active" />
<el-option :key="2" label="挂起" value="suspended" />
</el-select>
</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 type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['workflow:deploy:remove']">
删除
</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" fit :data="deployList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="流程标识" align="center" prop="processKey" :show-overflow-tooltip="true" />
<el-table-column label="流程名称" align="center" prop="processName" :show-overflow-tooltip="true" />
<el-table-column label="流程分类" align="center" prop="categoryName" :formatter="categoryFormat" />
<el-table-column label="流程版本" align="center">
<template #default="scope">
<el-tag size="small">v{{ scope.row.version }}</el-tag>
</template>
</el-table-column>
<el-table-column label="状态" align="center">
<template #default="scope">
<el-tag type="success" v-if="!scope.row.suspended"></el-tag>
<el-tag type="warning" v-if="scope.row.suspended"></el-tag>
</template>
</el-table-column>
<el-table-column label="部署时间" align="center" prop="deploymentTime" width="180" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip content="版本管理" placement="top">
<el-button link type="primary" icon="PriceTag" @click="handlePublish(scope.row)" v-hasPermi="['workflow:deploy:list']"></el-button>
</el-tooltip>
<el-tooltip content="删除" placement="top">
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['workflow:deploy:remove']"></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
</el-card>
<!-- &lt;!&ndash; 流程图对话框 &ndash;&gt;-->
<!-- <el-dialog :title="processDialog.title" v-model="processDialog.visible" width="500px" append-to-body>-->
<!-- <process-viewer :key="`designer-${processView.index}`" :xml="processView.xmlData" :style="{height: '400px'}" />-->
<!-- </el-dialog>-->
<!-- 版本管理 -->
<el-dialog title="版本管理" v-model="publishDialog.visible" width="700px" append-to-body>
<el-table v-loading="publishLoading" :data="publishList">
<el-table-column label="流程标识" align="center" prop="processKey" :show-overflow-tooltip="true" />
<el-table-column label="流程名称" align="center" :show-overflow-tooltip="true">
<template #default="scope">
<el-button type="text" @click="handleProcessView(scope.row)">
<span>{{ scope.row.processName }}</span>
</el-button>
</template>
</el-table-column>
<el-table-column label="流程版本" align="center">
<template #default="scope">
<el-tag size="small">v{{ scope.row.version }}</el-tag>
</template>
</el-table-column>
<el-table-column label="状态" align="center">
<template #default="scope">
<el-tag type="success" v-if="!scope.row.suspended"></el-tag>
<el-tag type="warning" v-if="scope.row.suspended"></el-tag>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip content="挂起" placement="top">
<el-button link type="primary" icon="VideoPause" @click="handleChangeState(scope.row, 'suspended')" v-hasPermi="['workflow:deploy:status']"></el-button>
</el-tooltip>
<el-tooltip content="激活" placement="top">
<el-button link type="primary" icon="VideoPlay" @click="handleChangeState(scope.row, 'active')" v-hasPermi="['workflow:deploy:status']"></el-button>
</el-tooltip>
<el-tooltip content="删除" placement="top">
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['workflow:deploy:remove']"></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<pagination v-show="publishTotal > 0" :total="publishTotal" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getPublishList" />
</el-dialog>
</div>
</template>
<script setup name="Deploy" lang="ts">
import { listAllCategory } from '@/api/workflow/category'
import { listDeploy, listPublish, getBpmnXml, changeState, delDeploy } from '@/api/workflow/deploy'
import { ComponentInternalInstance } from "vue";
import { CategoryVO } from "@/api/workflow/category/types";
import {DeployVO, ProcessQuery} from "@/api/workflow/deploy/types";
import { ElForm } from 'element-plus';
import {ModelVO} from "@/api/workflow/model/types";
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const categoryOptions = ref<CategoryVO[]>([]);
const deployList = ref<DeployVO[]>([]);
const loading = ref(true);
const showSearch = ref(true)
const ids = ref<Array<string>>([]);
const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const publishList = ref<DeployVO[]>([]);
const publishLoading = ref(true);
const publishTotal = ref(0);
const queryFormRef = ref(ElForm);
const processDialog = reactive<DialogOption>({
visible: false,
title: ''
});
const publishDialog = reactive<DialogOption>({
visible: false,
title: ''
});
const data = reactive<PageData<{}, ProcessQuery>>({
form: {},
queryParams: {
pageNum: 1,
pageSize: 10,
processKey: '',
processName: '',
category: ''
},
rules: {}
});
const { queryParams } = toRefs(data);
/** 查询流程分类列表 */
const getCategoryList = async () => {
const res = await listAllCategory();
categoryOptions.value = res.data;
};
const getList = async () => {
loading.value = true;
const res = await listDeploy(queryParams.value);
deployList.value = res.rows;
total.value = res.total;
loading.value = false;
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.value.pageNum = 1;
getList();
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields();
handleQuery();
}
/** 多选框选中数据 */
const handleSelectionChange = (selection: DeployVO[]) => {
ids.value = selection.map(item => item.deploymentId);
single.value = selection.length != 1;
multiple.value = !selection.length;
}
/** 删除按钮操作 */
const handleDelete = async (row?: DeployVO) => {
const deploymentIds = row?.deploymentId || ids.value;
await proxy?.$modal.confirm('是否确认删除参数编号为"' + deploymentIds + '"的数据项?');
await delDeploy(deploymentIds);
getList();
proxy?.$modal.msgSuccess("删除成功");
}
const getPublishList = async () => {
publishLoading.value = true;
const res = await listPublish(queryParams.value);
publishList.value = res.rows;
publishTotal.value = res.total;
publishLoading.value = false;
}
const handlePublish = (row: DeployVO) => {
queryParams.value.processKey = row.processKey;
publishDialog.visible = true;
getPublishList();
}
const handleProcessView = (row: DeployVO) => {
}
const handleChangeState = async (row: DeployVO, state: string) => {
const params = {
definitionId: row.definitionId,
state: state
}
await changeState(params);
proxy?.$modal.msgSuccess("操作成功");
getPublishList();
}
const categoryFormat = (row: ModelVO) => {
return categoryOptions.value.find(k => k.code === row.category)?.categoryName ?? '';
}
onMounted(() => {
getCategoryList();
getList();
})
</script>

View File

@ -0,0 +1,260 @@
<template>
<div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div class="search" v-show="showSearch">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="70">
<el-form-item label="表单名称" prop="formName">
<el-input v-model="queryParams.formName" 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 type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['workflow:form:add']"></el-button>
</el-col>
<el-col :span="1.5">
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['workflow:form:edit']"></el-button>
</el-col>
<el-col :span="1.5">
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['workflow:form:remove']"></el-button>
</el-col>
<el-col :span="1.5">
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['workflow:form:export']"></el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" :data="formList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="表单主键" align="center" prop="formId" v-if="false" />
<el-table-column label="表单名称" align="center" prop="formName" />
<el-table-column label="备注" align="center" prop="remark" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip content="详情" placement="top">
<el-button link type="primary" icon="View" @click="handleDetail(scope.row)" v-hasPermi="['workflow:form:query']"></el-button>
</el-tooltip>
<el-tooltip content="修改" placement="top">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['workflow:form:edit']"></el-button>
</el-tooltip>
<el-tooltip content="删除" placement="top">
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['workflow:form:remove']"></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
</el-card>
<!-- 流程表单设计器对话框 -->
<el-dialog :title="designer.title" v-model="designer.visible" fullscreen>
<div id="form-designer">
<v-form-designer ref="vfDesignerRef" :resetFormJson="true" :designer-config="designerConfig">
<!-- 自定义按钮插槽 -->
<template #customToolButtons>
<el-button link type="primary" icon="Finished" @click="dialog.visible = true">保存</el-button>
</template>
</v-form-designer>
</div>
</el-dialog>
<!-- 预览表单对话框 -->
<el-dialog :title="render.title" v-model="render.visible" width="60%" append-to-body>
<v-form-render :form-json="{}" :form-data="{}" ref="vfRenderRef" />
</el-dialog>
<!-- 添加或修改流程表单对话框 -->
<el-dialog :title="dialog.title" v-model="dialog.visible" width="600px" append-to-body>
<el-form ref="formFormRef" :model="form" :rules="rules" label-width="80px">
<el-form-item label="表单名称" prop="formName">
<el-input v-model="form.formName" placeholder="请输入表单名称" />
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" placeholder="请输入备注" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup name="Form" lang="ts">
import { listForm, addForm, updateForm, getForm, delForm } from "@/api/workflow/form";
import { FormForm, FormQuery, FormVO } from "@/api/workflow/form/types";
import { ComponentInternalInstance } from "vue";
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const formList = ref<FormVO[]>([]);
const loading = ref(true);
const showSearch = ref(true);
const ids = ref<Array<number | string>>([]);
const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const vfDesignerRef = ref(null);
const vfRenderRef = ref(null);
const formFormRef = ref(ElForm);
const queryFormRef = ref(ElForm);
const designerConfig = reactive({
externalLink: true,
toolbarMaxWidth: 510,
// languageMenu: true,
//externalLink: false,
//formTemplates: false,
//eventCollapse: false,
//clearDesignerButton: false,
//previewFormButton: false,
})
const designer = reactive<DialogOption>({
visible: false,
title: ''
})
const render = reactive<DialogOption>({
visible: false,
title: ''
})
const dialog = reactive<DialogOption>({
visible: false,
title: ''
});
const initFormData: FormForm = {
formId: undefined,
formName: '',
content: '',
remark: ''
}
const data = reactive<PageData<FormForm, FormQuery>>({
form: {...initFormData},
queryParams: {
pageNum: 1,
pageSize: 10,
formName: ''
},
rules: {
formName: [{ required: true, message: "表单名称不能为空", trigger: "blur" }]
}
});
const { queryParams, form, rules } = toRefs<PageData<FormForm, FormQuery>>(data);
/** 查询岗位列表 */
const getList = async () => {
loading.value = true;
const res = await listForm(queryParams.value);
formList.value = res.rows;
total.value = res.total;
loading.value = false;
}
/** 取消按钮 */
const cancel = () => {
reset();
dialog.visible = false;
}
/** 表单重置 */
const reset = () => {
form.value = {...initFormData};
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.value.pageNum = 1;
getList();
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields();
handleQuery();
}
/** 多选框选中数据 */
const handleSelectionChange = (selection: FormVO[]) => {
ids.value = selection.map(item => item.formId);
single.value = selection.length != 1;
multiple.value = !selection.length;
}
/** 新增表单操作 */
const handleAdd = () => {
designer.visible = true;
nextTick(() => {
reset();
vfDesignerRef.value.clearDesigner();
})
}
/** 修改表单操作 */
const handleUpdate = (row?: FormVO) => {
designer.visible = true;
nextTick(async () => {
const formId = row?.formId || ids.value[0];
const res = await getForm(formId);
form.value = res.data;
vfDesignerRef.value.setFormJson(form.value.content);
})
}
/** 查看表单操作 */
const handleDetail = (row: FormVO) => {
render.visible = true;
render.title = '查看表单详情';
nextTick(async () => {
vfRenderRef.value.setFormJson(row.content || {formConfig: {}, widgetList: []});
});
}
/** 提交表单操作 */
const submitForm = () => {
const formJson = vfDesignerRef.value.getFormJson();
form.value.content = JSON.stringify(formJson);
nextTick(async () => {
form.value.formId ? await updateForm(form.value) : await addForm(form.value);
proxy?.$modal.msgSuccess("操作成功");
dialog.visible = false;
designer.visible = false;
getList();
})
}
/** 删除按钮操作 */
const handleDelete = async (row?: FormVO) => {
const formIds = row?.formId || ids.value;
await proxy?.$modal.confirm('是否确认删除表单编号为"' + formIds + '"的数据项?');
await delForm(formIds);
getList();
proxy?.$modal.msgSuccess("删除成功");
}
/** 导出按钮操作 */
const handleExport = () => {
proxy?.download("workflow/form/export", {
...queryParams.value
}, `form_${new Date().getTime()}.xlsx`);
}
onMounted(() => {
getList();
});
</script>
<style lang="scss" scoped>
#form-designer {
.main-container {
margin: 0;
}
label {
font-weight: normal;
}
:deep(.external-link) {
display: flex;
align-items: center;
}
}
</style>

View File

@ -0,0 +1,479 @@
<template>
<div class="p-2 app-container">
<!-- 查询条件 -->
<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" :inline="true" :model="queryParams" label-width="120">
<el-form-item label="监听类型" prop="listenerType">
<el-select v-model="queryParams.listenerType" clearable placeholder="请选择监听类型" @change="handleQuery">
<el-option v-for="dict in sys_listener_type" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
<el-form-item label="名称" prop="name">
<el-input v-model="queryParams.name" clearable placeholder="请输入名称" @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="事件类型" prop="eventType">
<el-select v-model="queryParams.eventType" clearable placeholder="请选择事件类型" @change="handleQuery">
<el-option
v-for="dict in sys_task_listener_event_type"
v-if="queryParams.listenerType === 'task'"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
<el-option
v-for="dict in sys_execution_listener_event_type"
v-if="queryParams.listenerType === 'execution'"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
<el-option
v-for="dict in sys_task_listener_event_type"
v-if="!queryParams.listenerType"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
<el-option
v-for="dict in sys_execution_listener_event_type"
v-if="!queryParams.listenerType"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button icon="Search" type="primary" @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:listener:add']" icon="Plus" plain type="primary" @click="handleAdd"></el-button>
</el-col>
<el-col :span="1.5">
<el-button v-hasPermi="['workflow:listener:edit']" :disabled="single" icon="Edit" plain type="success" @click="handleUpdate()"
>修改</el-button
>
</el-col>
<el-col :span="1.5">
<el-button v-hasPermi="['workflow:listener:remove']" :disabled="multiple" icon="Delete" plain type="danger" @click="handleDelete()"
>删除</el-button
>
</el-col>
<el-col :span="1.5">
<el-button v-hasPermi="['workflow:listener:export']" icon="Download" plain type="warning" @click="handleExport"></el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
</template>
<!-- 分页列表 -->
<el-table
v-loading="tableLoading"
:data="listenerList"
@selection-change="handleSelectionChange"
v-fixTableHeight
highlight-current-row
show-overflow-tooltip
>
<el-table-column align="center" type="selection" width="55" />
<el-table-column type="expand" width="50px">
<template #default="props">
<el-table :data="props.row.fields" width="60%">
<el-table-column v-if="false" label="主键" prop="id" />
<el-table-column label="字段名称" prop="fieldName" />
<el-table-column label="字段类型" prop="fieldType">
<template #default="scope">
<dict-tag :options="sys_listener_field_type" :value="scope.row.fieldType" />
</template>
</el-table-column>
<el-table-column label="字段值" prop="fieldValue" />
<el-table-column align="center" class-name="small-padding fixed-width" label="操作">
<template #default="scope">
<el-tooltip content="修改" placement="top">
<el-button
v-hasPermi="['workflow:listener:edit']"
icon="Edit"
link
type="primary"
@click="handleFieldUpdate(scope.row)"
></el-button>
</el-tooltip>
<el-tooltip content="删除" placement="top">
<el-button
v-hasPermi="['workflow:listener:remove']"
icon="Delete"
link
type="danger"
@click="handleFieldDelete(scope.row)"
></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
</template>
</el-table-column>
<el-table-column v-if="false" align="center" label="主键" prop="id" />
<el-table-column align="center" label="监听类型" prop="listenerType">
<template #default="scope">
<dict-tag :options="sys_listener_type" :value="scope.row.listenerType" />
</template>
</el-table-column>
<el-table-column align="center" label="名称" prop="name" />
<el-table-column align="center" label="值类型" prop="valueType">
<template #default="scope">
<dict-tag :options="sys_listener_value_type" :value="scope.row.valueType" />
</template>
</el-table-column>
<el-table-column align="center" label="值" prop="value" />
<el-table-column align="center" label="事件类型" prop="eventType">
<template #default="scope">
<dict-tag v-if="scope.row.listenerType === 'task'" :options="sys_task_listener_event_type" :value="scope.row.eventType" />
<dict-tag v-if="scope.row.listenerType === 'execution'" :options="sys_execution_listener_event_type" :value="scope.row.eventType" />
</template>
</el-table-column>
<el-table-column align="center" class-name="small-padding fixed-width" label="操作">
<template #default="scope">
<el-tooltip content="添加字段" placement="top">
<el-button v-hasPermi="['workflow:listener:edit']" icon="Plus" link type="primary" @click="addField(scope.row)"></el-button>
</el-tooltip>
<el-tooltip content="修改" placement="top">
<el-button v-hasPermi="['workflow:listener:edit']" icon="Edit" link type="primary" @click="handleUpdate(scope.row)"></el-button>
</el-tooltip>
<el-tooltip content="删除" placement="top">
<el-button v-hasPermi="['workflow:listener:remove']" icon="Delete" link type="danger" @click="handleDelete(scope.row)"></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" v-model:limit="queryParams.pageSize" v-model:page="queryParams.pageNum" :total="total" @pagination="getList" />
</el-card>
<!-- 添加或修改流程表单对话框 -->
<el-dialog v-model="dialog.visible" :close-on-click-modal="false" :title="dialog.title" append-to-body width="600px">
<el-form ref="listenerFormRef" v-loading="formLoading" :model="form" :rules="rules" label-width="120px">
<el-form-item label="监听类型" prop="listenerType">
<el-radio-group v-model="form.listenerType">
<el-radio v-for="dict in sys_listener_type" :key="dict.value" :label="dict.value">{{ dict.label }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="名称" prop="name">
<el-input v-model="form.name" placeholder="请输入名称" />
</el-form-item>
<el-form-item label="值类型" prop="valueType">
<el-radio-group v-model="form.valueType">
<el-radio v-for="dict in sys_listener_value_type" :key="dict.value" :label="dict.value">{{ dict.label }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="值" prop="value">
<el-input v-model="form.value" placeholder="请输入值" />
</el-form-item>
<el-form-item label="事件类型" prop="eventType">
<el-select v-model="form.eventType" clearable placeholder="请选择事件类型" style="width: 100%">
<el-option
v-for="dict in sys_task_listener_event_type"
v-if="form.listenerType === 'task'"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
<el-option
v-for="dict in sys_execution_listener_event_type"
v-if="form.listenerType === 'execution'"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</template>
</el-dialog>
<!-- 监听器字段对话框 -->
<el-dialog v-model="fieldDialog.visible" :close-on-click-modal="false" :title="fieldDialog.title" append-to-body width="600px">
<el-form ref="listenerFieldFormRef" v-loading="fieldFormLoading" :model="initFieldFormData" :rules="filedRules" label-width="120px">
<el-form-item label="名称" prop="fieldName">
<el-input v-model="filedForm.fieldName" placeholder="请输入名称" />
</el-form-item>
<el-form-item label="字段类型" prop="fieldType">
<el-radio-group v-model="filedForm.fieldType">
<el-radio v-for="dict in sys_listener_field_type" :key="dict.value" :label="dict.value">{{ dict.label }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="值" prop="fieldValue">
<el-input v-model="filedForm.fieldValue" placeholder="请输入值" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submitFieldForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script lang="ts" name="Form" setup>
onMounted(() => {
getList();
});
import {
addListener,
deleteListenerFieldAPI,
delListener,
getListener,
insertListenerFieldAPI,
queryListenerPage,
updateListener,
updateListenerFieldAPI
} from "@/api/workflow/listener";
import {
ListenerFieldForm,
ListenerFieldVO,
ListenerForm,
ListenerQuery,
ListenerVO
} from "@/api/workflow/listener/types";
import {ComponentInternalInstance} from "vue";
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const {
sys_listener_type,
sys_listener_value_type,
sys_task_listener_event_type,
sys_execution_listener_event_type,
sys_listener_field_type
} = toRefs<any>(proxy?.useDict("sys_listener_type", "sys_listener_value_type", "sys_task_listener_event_type", "sys_execution_listener_event_type", "sys_listener_field_type"));
const listenerList = ref<ListenerVO[]>([]);
const showSearch = ref(true);
const ids = ref<Array<number | string>>([]);
const names = ref<Array<string>>([]);
const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const tableLoading = ref(false);
const formLoading = ref(false);
const fieldFormLoading = ref(false);
const listenerFormRef = ref(ElForm);
const listenerFieldFormRef = ref(ElForm);
const queryFormRef = ref(ElForm);
const listenerId = ref<number | string>('');
const dialog = reactive<DialogOption>({
visible: false,
title: ''
});
const fieldDialog = reactive<DialogOption>({
visible: false,
title: ''
});
const initFormData: ListenerForm = {
id: undefined,
valueType: '',
eventType: '',
listenerType: '',
name: '',
value: ''
}
const data = reactive<PageData<ListenerForm, ListenerQuery>>({
form: {...initFormData},
queryParams: {
pageNum: 1,
pageSize: 10,
name: '',
listenerType: '',
eventType: ''
},
rules: {
listenerType: [{ required: true, message: "监听类型不能为空", trigger: "blur" }],
name: [{ required: true, message: "名称不能为空", trigger: "blur" }],
valueType: [{ required: true, message: "值类型不能为空", trigger: "blur" }],
value: [{ required: true, message: "值不能为空", trigger: "blur" }]
}
});
const { queryParams, form, rules } = toRefs<PageData<ListenerForm, ListenerQuery>>(data);
const initFieldFormData: ListenerFieldForm = {
id: undefined,
listenerId: undefined,
fieldName: undefined,
fieldType: undefined,
fieldValue: undefined
}
const filedData = reactive({
filedForm: {...initFieldFormData},
filedRules: {
fieldName: [{ required: true, message: "请输入字段名称", trigger: "blur" }],
fieldType: [{ required: true, message: "请选择字段类型", trigger: "blur" }]
}
})
const { filedForm, filedRules } = toRefs(filedData);
/** 查询列表 */
const getList = async () => {
tableLoading.value = true;
const res = await queryListenerPage(queryParams.value);
listenerList.value = res.rows;
total.value = res.total;
tableLoading.value = false;
}
/** 取消按钮 */
const cancel = () => {
reset();
dialog.visible = false;
fieldDialog.visible = false;
}
/** 表单重置 */
const reset = () => {
form.value = {...initFormData};
filedForm.value = {...initFieldFormData};
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.value.pageNum = 1;
getList();
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields();
handleQuery();
}
/** 多选框选中数据 */
const handleSelectionChange = (selection: ListenerVO[]) => {
ids.value = selection.map(item => item.id);
names.value = selection.map(item => item.name);
single.value = selection.length != 1;
multiple.value = !selection.length;
}
/** 新增表单操作 */
const handleAdd = () => {
dialog.visible = true;
nextTick(() => {
reset();
})
}
/** 修改表单操作 */
const handleUpdate = (row?: ListenerVO) => {
dialog.visible = true;
const id = row?.id || ids.value[0];
nextTick(async () => {
formLoading.value = true;
reset();
const res = await getListener(id);
formLoading.value = false;
form.value = res.data;
})
}
/** 提交表单操作 */
const submitForm = () => {
listenerFormRef.value.validate(async (valid: boolean) => {
if (valid) {
dialog.visible = true;
formLoading.value = true;
try {
form.value.id ? await updateListener(form.value) : await addListener(form.value);
} finally {
formLoading.value = false;
}
dialog.visible = false;
proxy?.$modal.msgSuccess("操作成功");
getList();
}
});
}
/** 删除按钮操作 */
const handleDelete = async (row?: ListenerVO) => {
const listenerIds = row?.id || ids.value;
const listenerNames = row?.name || ids.value;
await proxy?.$modal.confirm('是否确认删除【' + listenerNames + '】的数据项?');
tableLoading.value = true;
try {
await delListener(listenerIds);
} finally {
tableLoading.value = false;
}
getList();
proxy?.$modal.msgSuccess("删除成功");
}
/** 添加字段操作 */
const addField = (row: ListenerVO) => {
fieldDialog.visible = true;
// listenerField.value = row.fields;
listenerId.value = row.id;
nextTick(() => {
reset();
filedForm.value.listenerId = row.id;
})
}
/** 修改字段操作 */
const handleFieldUpdate = (row: ListenerFieldVO) => {
fieldDialog.visible = true;
filedForm.value.id = row.id;
filedForm.value.listenerId = row.listenerId;
filedForm.value.fieldName = row.fieldName;
filedForm.value.fieldType = row.fieldType;
filedForm.value.fieldValue = row.fieldValue;
}
/** 提交字段表单操作 */
const submitFieldForm = () => {
listenerFieldFormRef.value.validate(async (valid: boolean) => {
if (valid) {
fieldDialog.visible = true;
fieldFormLoading.value = true;
try {
filedForm.value.id ? await updateListenerFieldAPI(filedForm.value) : await insertListenerFieldAPI(filedForm.value);
} finally {
fieldFormLoading.value = false;
}
listenerId.value = '';
fieldDialog.visible = false;
proxy?.$modal.msgSuccess("操作成功");
getList();
}
});
}
/** 删除字段按钮操作 */
const handleFieldDelete = async (row?: ListenerFieldVO) => {
const fieldIds = row?.id || ids.value;
const fieldNames = row?.fieldName || ids.value;
await proxy?.$modal.confirm('是否确认删除【' + fieldNames + '】的数据项?');
tableLoading.value = true;
try {
await deleteListenerFieldAPI(fieldIds);
} finally {
tableLoading.value = false;
}
getList();
proxy?.$modal.msgSuccess("删除成功");
}
/** 导出按钮操作 */
const handleExport = () => {
proxy?.download("workflow/form/export", {
...queryParams.value
}, `form_${new Date().getTime()}.xlsx`);
}
</script>

View File

@ -0,0 +1,436 @@
<template>
<div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div class="search" v-show="showSearch">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="70">
<el-form-item label="模型标识" prop="modelKey">
<el-input v-model="queryParams.modelKey" placeholder="请输入模型标识" clearable style="width: 200px" @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="模型名称" prop="modelName">
<el-input v-model="queryParams.modelName" placeholder="请输入模型名称" clearable style="width: 200px" @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="流程分类" prop="category">
<el-select v-model="queryParams.category" clearable placeholder="请选择" @change="handleQuery">
<el-option
v-for="item in categoryOptions"
:key="item.categoryId"
:label="item.categoryName"
:value="item.code">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="流程分类" prop="category">
<el-select v-model="queryParams.category" clearable placeholder="请选择" @change="handleQuery">
<el-option
v-for="item in categoryOptions"
:key="item.categoryId"
:label="item.categoryName"
:value="item.code">
</el-option>
</el-select>
</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 type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['workflow:model:add']"></el-button>
</el-col>
<el-col :span="1.5">
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete" v-hasPermi="['workflow:model:remove']"></el-button>
</el-col>
<el-col :span="1.5">
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['workflow:model:export']"></el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="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 label="模型标识" align="center" prop="modelKey" :show-overflow-tooltip="true" />
<el-table-column label="模型名称" align="center" :show-overflow-tooltip="true">
<template #default="scope">
<el-button type="text" @click="handleProcessView(scope.row)">
<span>{{ scope.row.modelName }}</span>
</el-button>
</template>
</el-table-column>
<el-table-column label="模型名称" align="center" prop="modelName" :show-overflow-tooltip="true" />
<el-table-column label="流程分类" align="center" prop="categoryName" :formatter="categoryFormat" />
<el-table-column label="模型版本" align="center">
<template #default="scope">
<el-tag size="small">v{{ scope.row.version }}</el-tag>
</template>
</el-table-column>
<el-table-column label="描述" align="center" prop="description" :show-overflow-tooltip="true" />
<el-table-column label="创建时间" align="center" prop="createTime" width="180" />
<el-table-column label="操作" width="180" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip content="修改" placement="top">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['workflow:model:edit']"></el-button>
</el-tooltip>
<el-tooltip content="删除" placement="top">
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['workflow:model:remove']"></el-button>
</el-tooltip>
<el-tooltip content="设计" placement="top">
<el-button link type="primary" icon="Brush" @click="handleDesigner(scope.row)" v-hasPermi="['workflow:model:designer']"></el-button>
</el-tooltip>
<el-tooltip content="部署" placement="top">
<el-button link type="primary" icon="Promotion" @click="handleDeploy(scope.row)" v-hasPermi="['workflow:model:deploy']"></el-button>
</el-tooltip>
<el-tooltip content="历史" placement="top">
<el-button link type="primary" icon="Discount" @click="handleHistory(scope.row)" v-hasPermi="['workflow:model:list']"></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
</el-card>
<!-- 添加或修改模型信息对话框 -->
<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
<el-form ref="modelFormRef" :model="form" :rules="rules" label-width="80px">
<el-form-item label="模型标识" prop="modelKey">
<el-input v-model="form.modelKey" clearable disabled placeholder="请输入模型标识" />
</el-form-item>
<el-form-item label="模型名称" prop="modelName">
<el-input v-model="form.modelName" clearable :disabled="form.modelId !== undefined" placeholder="请输入模型名称" />
</el-form-item>
<el-form-item label="流程分类" prop="category">
<el-select v-model="form.category" placeholder="请选择" clearable style="width:100%">
<el-option v-for="item in categoryOptions" :key="item.categoryId" :label="item.categoryName" :value="item.code" />
</el-select>
</el-form-item>
<el-form-item label="描述" prop="description">
<el-input v-model="form.description" type="textarea" placeholder="请输入内容" maxlength="200" show-word-limit />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</template>
</el-dialog>
<el-dialog :title="designer.title" v-model="designer.visible" append-to-body fullscreen>
<ProcessDesigner
:key="`designer-${reloadIndex}`"
ref="modelDesignerRef"
v-loading="designerLoading"
:designer-form="designerForm"
:bpmn-xml="bpmnXml"
@save="onSaveDesigner"
/>
</el-dialog>
<el-dialog :title="history.title" v-model="history.visible" append-to-body>
<el-table v-loading="historyLoading" :data="historyList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="模型标识" align="center" prop="modelKey" :show-overflow-tooltip="true" />
<el-table-column label="模型名称" align="center" prop="modelName" :show-overflow-tooltip="true" />
<el-table-column label="流程分类" align="center" prop="categoryName" :formatter="categoryFormat" />
<el-table-column label="模型版本" align="center">
<template #default="scope">
<el-tag>v{{ scope.row.version }}</el-tag>
</template>
</el-table-column>
<el-table-column label="描述" align="center" prop="description" :show-overflow-tooltip="true" />
<el-table-column label="创建时间" align="center" prop="createTime" width="180" />
<el-table-column label="操作" width="180" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip content="部署" placement="top">
<el-button link type="primary" icon="Promotion" @click="handleDeploy(scope.row)" v-hasPermi="['workflow:model:deploy']"></el-button>
</el-tooltip>
<el-tooltip content="设为最新" placement="top">
<el-button link type="primary" icon="Star" @click="handleLatest(scope.row)" v-hasPermi="['workflow:model:save']"></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
</el-dialog>
<!-- 流程图 -->
<el-dialog :title="processDialog.title" v-model="processDialog.visible" width="70%">
<process-viewer :key="`designer-${reloadIndex}`" :xml="processXml" :style="{height: '650px'}" />
</el-dialog>
</div>
</template>
<script setup name="Model" lang="ts">
import { getBpmnXml, listModel, historyModel, latestModel, addModel, updateModel, saveModel, delModel, deployModel, getModel } from "@/api/workflow/model";
import { listAllCategory } from "@/api/workflow/category";
import { ModelForm, ModelQuery, ModelVO, DesignerForm } from "@/api/workflow/model/types";
import { CategoryVO } from "@/api/workflow/category/types";
import ProcessDesigner from "@/components/ProcessDesigner";
import { ComponentInternalInstance } from "vue";
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const modelList = ref<ModelVO[]>([]);
const loading = ref(true);
const showSearch = ref(true);
const ids = ref<Array<string>>([]);
const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const categoryOptions = ref<CategoryVO[]>([]);
const designerLoading = ref(true);
const bpmnXml = ref<string>('');
const reloadIndex = ref(0);
const processXml = ref<string>("");
const historyList = ref<ModelVO[]>([]);
const historyLoading = ref(true);
const historyTotal = ref(0);
const modelFormRef = ref(ElForm);
const queryFormRef = ref(ElForm);
const modelDesignerRef = ref(null)
const dialog = reactive<DialogOption>({
visible: false,
title: ''
});
const processDialog = reactive<DialogOption>({
visible: false,
title: '流程图'
});
const designer = reactive<DialogOption>({
visible: false,
title: ''
});
const history = reactive<DialogOption>({
visible: false,
title: ''
});
const initFormData: ModelForm = {
modelId: undefined,
modelKey: `Process_${new Date().getTime()}`,
modelName: `业务流程_${new Date().getTime()}`,
category: '',
description: '',
formType: undefined,
formId: undefined,
bpmnXml: '',
newVersion: false
}
const data = reactive<PageData<ModelForm, ModelQuery>>({
form: {...initFormData},
queryParams: {
pageNum: 1,
pageSize: 10,
modelKey: '',
modelName: '',
category: ''
},
rules: {
modelKey: [{ required: true, message: "岗位名称不能为空", trigger: "blur" }],
modelName: [{ required: true, message: "岗位编码不能为空", trigger: "blur" }],
}
});
const designerForm = reactive<DesignerForm>({
modelId: '',
form: {
processName: '',
processKey: ''
}
});
const { queryParams, form, rules } = toRefs<PageData<ModelForm, ModelQuery>>(data);
const router = useRouter();
/** 查询模型列表 */
const getList = async () => {
loading.value = true;
const res = await listModel(queryParams.value);
modelList.value = res.rows;
total.value = res.total;
loading.value = false;
}
/** 取消按钮 */
const cancel = () => {
reset();
dialog.visible = false;
}
/** 表单重置 */
const reset = () => {
form.value = {...initFormData};
modelFormRef.value.resetFields();
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.value.pageNum = 1;
getList();
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields();
handleQuery();
}
/** 多选框选中数据 */
const handleSelectionChange = (selection: ModelVO[]) => {
ids.value = selection.map(item => item.modelId);
single.value = selection.length != 1;
multiple.value = !selection.length;
}
/** 新增按钮操作 */
const handleAdd = () => {
dialog.visible = true;
dialog.title = "添加模型";
nextTick(() => {
reset();
})
}
/** 修改按钮操作 */
const handleUpdate = (row?: ModelVO) => {
dialog.visible = true;
dialog.title = "修改模型";
nextTick(async () => {
reset();
const modelId = row?.modelId || ids.value[0];
const res = await getModel(modelId);
form.value = res.data;
});
};
/** 删除按钮操作 */
const handleDelete = async (row?: ModelVO) => {
const modelIds = row?.modelId || ids.value;
await proxy?.$modal.confirm('是否确认删除参数编号为"' + modelIds + '"的数据项?');
await delModel(modelIds);
getList();
proxy?.$modal.msgSuccess("删除成功");
}
/** 导出按钮操作 */
const handleExport = () => {
proxy?.download("workflow/model/export", {
...queryParams.value
}, `model_${new Date().getTime()}.xlsx`);
};
/** 查看流程图 */
const handleProcessView = async (row: ModelVO) => {
reloadIndex.value++;
// xml
const res = await getBpmnXml(row.modelId);
processXml.value = res.data;
processDialog.visible = true;
}
/** 设计按钮操作 */
const handleDesigner = async (row: ModelVO) => {
reloadIndex.value++;
designerForm.modelId = row.modelId;
const res = await getBpmnXml(row.modelId);
bpmnXml.value = res.data || '';
designerLoading.value = false;
designer.title = "流程设计 - " + row.modelName;
designer.visible = true;
}
const handleDeploy = (row?: ModelVO) => {
loading.value = true;
nextTick(async () => {
await deployModel({ modelId: row?.modelId });
proxy?.$modal.msgSuccess("操作成功");
router.push({
name: 'Deploy',
path: '/workflow/deploy'
});
loading.value = false;
});
}
const handleLatest = async (row: ModelVO) => {
await proxy?.$modal.confirm('是否将此模型保存为新版本?');
historyLoading.value = true;
await latestModel({modelId: row.modelId});
history.visible = false;
getList();
proxy?.$modal.msgSuccess("操作成功");
historyLoading.value = false;
}
/** 查询历史列表 */
const getHistoryList = async () => {
historyLoading.value = true;
const res = await historyModel(queryParams.value);
historyList.value = res.rows;
historyTotal.value = res.total;
historyLoading.value = false;
}
const handleHistory = (row?: ModelVO) => {
history.visible = true;
history.title = "模型历史";
queryParams.value.modelKey = row?.modelKey;
getHistoryList();
}
/** 提交表单操作 */
const submitForm = () => {
modelFormRef.value.validate(async (valid: boolean) => {
if (valid) {
form.value.modelId ? await updateModel(form.value) : await addModel(form.value);
proxy?.$modal.msgSuccess("操作成功");
dialog.visible = false;
getList();
}
})
}
/** 查询流程分类列表 */
const getCategoryList = async () => {
const res = await listAllCategory();
categoryOptions.value = res.data;
}
const onSaveDesigner = async (str: string) => {
bpmnXml.value = str;
let dataBody = {
modelId: designerForm.modelId,
bpmnXml: str
}
proxy?.$modal.confirm('是否将此模型保存为新版本?').then(() => {
confirmSave(dataBody, true)
}).catch(action => {
if (action === 'cancel') {
confirmSave(dataBody, false)
}
})
}
const confirmSave = async (body: any, newVersion: boolean) => {
designerLoading.value = true;
await saveModel(Object.assign(body, { newVersion: newVersion }));
getList();
proxy?.$modal.msgSuccess("保存成功");
designerLoading.value = false;
designer.visible = false;
}
const categoryFormat = (row: ModelVO) => {
return categoryOptions.value.find(k => k.code === row.category)?.categoryName ?? '';
}
onMounted(() => {
getCategoryList()
getList();
});
</script>
<style lang="scss" scoped>
.el-dialog__body {
max-height: calc(100vh) !important;
overflow-y: auto;
overflow-x: hidden;
}
</style>

View File

@ -0,0 +1,142 @@
<template>
<div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div class="search" v-show="showSearch">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="70">
<el-form-item label="流程名称" prop="processName">
<el-input v-model="queryParams.processName" placeholder="请输入流程名称" clearable style="width: 200px" @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="流程分类" prop="category">
<el-select v-model="queryParams.category" clearable placeholder="请选择">
<el-option v-for="item in categoryOptions" :key="item.categoryId" :label="item.categoryName" :value="item.code" />
</el-select>
</el-form-item>
<el-form-item label="接收时间" style="width: 308px;">
<el-date-picker
v-model="dateRange"
value-format="YYYY-MM-DD"
type="daterange"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
></el-date-picker>
</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 type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['workflow:process:claimExport']"></el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" :data="claimList">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="任务编号" align="center" prop="taskId" :show-overflow-tooltip="true" />
<el-table-column label="流程名称" align="center" prop="procDefName" />
<el-table-column label="任务节点" align="center" prop="taskName" />
<el-table-column label="流程版本" align="center">
<template #default="scope">
<el-tag>v{{scope.row.procDefVersion}}</el-tag>
</template>
</el-table-column>
<el-table-column label="流程发起人" align="center" prop="startUserName" />
<el-table-column label="接收时间" align="center" prop="createTime" width="180" />
<el-table-column label="操作" width="180" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip content="签收" placement="top">
<el-button link type="primary" icon="EditPen" @click="handleClaim(scope.row)" v-hasPermi="['workflow:process:claim']"></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
</el-card>
</div>
</template>
<script setup name="Claim" lang="ts">
import { listClaimProcess } from "@/api/workflow/work/process";
import { claimTask } from "@/api/workflow/work/task";
import { ComponentInternalInstance } from "vue";
import { ProcessQuery, ProcessVO } from "@/api/workflow/work/types";
import { DateModelType } from "element-plus";
import { listAllCategory } from "@/api/workflow/category";
import { CategoryVO } from "@/api/workflow/category/types";
import { ModelVO } from "@/api/workflow/model/types";
const router = useRouter();
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const claimList = ref<ProcessVO[]>([]);
const loading = ref(true);
const showSearch = ref(true);
const ids = ref<Array<number | string>>([]);
const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const dateRange = ref<[DateModelType, DateModelType]>(['','']);
const categoryOptions = ref<CategoryVO[]>([]);
const queryFormRef = ref(ElForm);
const queryParams = ref<ProcessQuery>({
pageNum: 1,
pageSize: 10,
processName: ''
});
/** 查询流程分类列表 */
const getCategoryList = async () => {
const res = await listAllCategory();
categoryOptions.value = res.data;
}
/** 查询待办列表 */
const getList = async () => {
loading.value = true;
const res = await listClaimProcess(proxy?.addDateRange(queryParams.value, dateRange.value));
claimList.value = res.rows;
total.value = res.total;
loading.value = false;
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.value.pageNum = 1;
getList();
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields();
handleQuery();
}
/** 签收按钮操作 */
const handleClaim = async (row: ProcessVO) => {
const res = await claimTask({ taskId: row.taskId })
proxy?.$modal.msgSuccess(res.msg);
router.push({ path: '/work/todo' })
}
/** 导出按钮操作 */
const handleExport = () => {
proxy?.download("workflow/process/claimExport", {
...queryParams.value
}, `claim_${new Date().getTime()}.xlsx`);
}
const categoryFormat = (row: ModelVO) => {
return categoryOptions.value.find(k => k.code === row.category)?.categoryName ?? '';
}
onMounted(() => {
getCategoryList();
getList();
});
</script>

View File

@ -0,0 +1,133 @@
<template>
<div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div class="search" v-show="showSearch">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="70">
<el-form-item label="流程名称" prop="processName">
<el-input v-model="queryParams.processName" placeholder="请输入流程名称" clearable style="width: 200px" @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="流程分类" prop="category">
<el-select v-model="queryParams.category" clearable placeholder="请选择">
<el-option v-for="item in categoryOptions" :key="item.categoryId" :label="item.categoryName" :value="item.code" />
</el-select>
</el-form-item>
<el-form-item label="发起人" prop="originatorName">
<el-input v-model="queryParams.originatorName" placeholder="请输入发起人" clearable style="width: 200px" @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 type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['workflow:process:copyExport']"></el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" :data="copyList">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="抄送编号" align="center" prop="copyId" />
<el-table-column label="标题" align="center" prop="title" :show-overflow-tooltip="true" />
<el-table-column label="流程名称" align="center" prop="processName" :show-overflow-tooltip="true" />
<el-table-column label="流程分类" align="center" prop="category" :formatter="categoryFormat" />
<el-table-column label="发起人" align="center" prop="originatorName" />
<el-table-column label="创建时间" align="center" prop="createTime">
<template #default="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" width="180" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip content="详情" placement="top">
<el-button link type="primary" icon="View" @click="handleDetails(scope.row)" v-hasPermi="['workflow:process:query']"></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
</el-card>
</div>
</template>
<script setup name="Copy" lang="ts">
import { listCopyProcess } from "@/api/workflow/work/process"
import {ProcessQuery, ProcessVO} from "@/api/workflow/work/types";
import { ComponentInternalInstance } from "vue";
import { CategoryVO } from "@/api/workflow/category/types";
import { listAllCategory } from "@/api/workflow/category";
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const copyList = ref<ProcessVO[]>([]);
const loading = ref(true);
const showSearch = ref(true);
const ids = ref<Array<number | string>>([]);
const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const categoryOptions = ref<CategoryVO[]>([]);
const queryFormRef = ref(ElForm);
const queryParams = ref<ProcessQuery>({
pageNum: 1,
pageSize: 10,
processName: "",
originatorName: "",
category: ''
});
/** 查询流程分类列表 */
const getCategoryList = async () => {
const res = await listAllCategory();
categoryOptions.value = res.data;
}
/** 查询待办列表 */
const getList = async () => {
loading.value = true;
const res = await listCopyProcess(queryParams.value);
copyList.value = res.rows;
total.value = res.total;
loading.value = false;
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.value.pageNum = 1;
getList();
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields();
handleQuery();
}
/** 流程详情 */
const handleDetails = (row: ProcessVO) => {
// router.push({
// path: '/workflow/process/detail/' + row.procInsId,
// query: {
// processed: false
// }
// })
}
/** 导出按钮操作 */
const handleExport = () => {
proxy?.download("workflow/process/copyExport", {
...queryParams.value
}, `copy_process_${new Date().getTime()}.xlsx`);
}
const categoryFormat = (row: ProcessVO) => {
return categoryOptions.value.find(find => find.code === row.category)?.categoryName ?? '';
}
onMounted(() => {
getCategoryList();
getList();
});
</script>

View File

@ -0,0 +1,544 @@
<template>
<div class="app-container">
<el-tabs tab-position="top" :model-value="processed === true ? 'approval' : 'form'">
<el-tab-pane label="任务办理" name="approval" v-if="processed === true">
<el-card class="box-card" shadow="hover" v-if="taskFormOpen">
<template #header>
<span>填写表单</span>
</template>
<div class="cu-content">
<v-form-render :form-json="{}" :form-data="{}" ref="vfRenderRef" />
</div>
</el-card>
<el-card class="box-card" shadow="hover">
<template #header>
<span>审批流程</span>
</template>
<el-row>
<el-col :span="20" :offset="2">
<el-form ref="taskFormRef" :model="taskForm" :rules="rules" label-width="120px">
<el-form-item label="审批意见" prop="comment">
<el-input type="textarea" :rows="5" v-model="taskForm.comment" placeholder="请输入 审批意见" />
</el-form-item>
<el-form-item label="抄送人" prop="copyUserIds">
<el-tag :key="index" v-for="(item, index) in copyUser" closable :disable-transitions="false" @close="handleClose('copy', item)">
{{ item.nickName }}
</el-tag>
<el-button class="button-new-tag" type="primary" icon="el-icon-plus" circle @click="onSelectCopyUsers" />
</el-form-item>
<el-form-item label="指定审批人" prop="copyUserIds">
<el-tag :key="index" v-for="(item, index) in nextUser" closable :disable-transitions="false" @close="handleClose('next', item)">
{{ item.nickName }}
</el-tag>
<el-button class="button-new-tag" type="primary" icon="el-icon-plus" circle @click="onSelectNextUsers" />
</el-form-item>
</el-form>
</el-col>
</el-row>
<el-row :gutter="10" type="flex" justify="center">
<el-col :span="1.5">
<el-button icon="CircleCheck" type="success" @click="handleComplete"></el-button>
</el-col>
<el-col :span="1.5">
<el-button icon="ChatLineSquare" type="primary" @click="handleDelegate"></el-button>
</el-col>
<el-col :span="1.5">
<el-button icon="Switch" type="success" @click="handleTransfer"></el-button>
</el-col>
<el-col :span="1.5">
<el-button icon="RefreshLeft" type="warning" @click="handleReturn">退</el-button>
</el-col>
<el-col :span="1.5">
<el-button icon="CircleClose" type="danger" @click="handleReject"></el-button>
</el-col>
</el-row>
</el-card>
</el-tab-pane>
<el-tab-pane label="表单信息" name="form">
<div v-if="formVisible">
<el-card class="box-card" shadow="never" v-for="(item, index) in processFormList" :key="index">
<template #header>
<span>{{ item.title }}</span>
</template>
<!--流程处理表单模块-->
<div class="cu-content">
<v-form-render :form-json="item.formModel" :form-data="item.formData" ref="vFormRenderRef" />
</div>
</el-card>
</div>
</el-tab-pane>
<el-tab-pane label="流转记录" name="record">
<el-card class="box-card" shadow="never">
<el-col :span="20" :offset="2">
<div class="block">
<el-timeline>
<el-timeline-item v-for="(item, index) in historyProcNodeList" :key="index" :type="tagType(item.endTime)">
<p style="font-weight: 700">{{ item.activityName }}</p>
<el-card v-if="item.activityType === 'startEvent'" class="box-card" shadow="hover">
{{ item.assigneeName }} {{ item.createTime }} 发起流程
</el-card>
<el-card v-if="item.activityType === 'userTask'" class="box-card" shadow="hover">
<el-descriptions :column="5" :labelStyle="{'font-weight': 'bold'}">
<el-descriptions-item label="实际办理">{{ item.assigneeName || '-'}}</el-descriptions-item>
<el-descriptions-item label="候选办理">{{ item.candidate || '-'}}</el-descriptions-item>
<el-descriptions-item label="接收时间">{{ item.createTime || '-'}}</el-descriptions-item>
<el-descriptions-item label="办结时间">{{ item.endTime || '-' }}</el-descriptions-item>
<el-descriptions-item label="耗时">{{ item.duration || '-'}}</el-descriptions-item>
</el-descriptions>
<div v-if="item.commentList && item.commentList.length > 0">
<div v-for="(comment, index) in item.commentList" :key="index">
<el-divider content-position="left">
<el-tag :type="approveTypeTag(comment.type)">{{ commentType(comment.type) }}</el-tag>
<el-tag type="info" effect="plain">{{ comment.time }}</el-tag>
</el-divider>
<span>{{ comment.fullMessage }}</span>
</div>
</div>
</el-card>
<el-card v-if="item.activityType === 'endEvent'" class="box-card" shadow="hover">
{{ item.createTime }} 结束流程
</el-card>
</el-timeline-item>
</el-timeline>
</div>
</el-col>
</el-card>
</el-tab-pane>
<el-tab-pane label="流程跟踪" name="track">
<el-card class="box-card" shadow="never">
<process-viewer
:key="`designer-${loadIndex}`"
:style="'height:' + height"
:xml="processXml"
:finishedInfo="finishedInfo"
:allCommentList="historyProcNodeList"
/>
</el-card>
</el-tab-pane>
</el-tabs>
<!--退回流程-->
<el-dialog :title="returnDialog.title" v-model="returnDialog.visible" width="40%" append-to-body>
<el-radio-group v-model="returnTaskKey">
<el-radio-button v-for="item in returnTaskList" :key="item.id" :label="item.id">
{{ item.name }}
</el-radio-button>
</el-radio-group>
<template #footer>
<el-button @click="returnDialog.visible = false"> </el-button>
<el-button type="primary" @click="submitReturn"> </el-button>
</template>
</el-dialog>
<el-dialog :title="userSelectDialog.title" v-model="userSelectDialog.visible" width="60%" append-to-body>
<el-row type="flex" :gutter="20">
<!--部门数据-->
<el-col :span="5">
<el-card shadow="never" style="height: 100%">
<template #header>
<span>部门列表</span>
</template>
<div class="head-container">
<el-input v-model="deptName" placeholder="请输入部门名称" prefix-icon="Search" clearable />
<el-tree
:data="deptOptions"
:props="{ label: 'label', children: 'children' }"
:expand-on-click-node="false"
:filter-node-method="filterNode"
ref="deptTreeRef"
default-expand-all
@node-click="handleNodeClick"
/>
</div>
</el-card>
</el-col>
<el-col :span="18">
<el-table
ref="userTable"
:key="userSelectType"
height="500"
v-loading="userLoading"
:data="userList"
highlight-current-row
@current-change="changeCurrentUser"
@selection-change="handleSelectionChange"
>
<el-table-column v-if="userSelectType === 'copy' || userSelectType === 'next'" width="55" type="selection" />
<el-table-column v-else width="30">
<template #default="scope">
<el-radio :label="scope.row.userId" v-model="currentUserId">{{''}}</el-radio>
</template>
</el-table-column>
<el-table-column label="用户名称" align="center" prop="userName" />
<el-table-column label="用户昵称" align="center" prop="nickName" />
<el-table-column label="手机" align="center" prop="phonenumber" />
</el-table>
<pagination :total="userTotal" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
</el-col>
</el-row>
<template #footer>
<el-button @click="userSelectDialog.visible = false"> </el-button>
<el-button type="primary" @click="submitUserData"> </el-button>
</template>
</el-dialog>
</div>
</template>
<script setup name="Detail" lang="ts">
import { detailProcess } from "@/api/workflow/work/process";
import { complete, delegate, transfer, rejectTask, returnList, returnTask } from "@/api/workflow/work/task";
import { deptTreeSelect, selectUser } from "@/api/workflow/identity";
import { TaskForm } from "@/api/workflow/work/types";
import { UserVO, DeptVO } from "@/api/workflow/identity/types";
import { ComponentInternalInstance } from "vue";
import {useRoute} from "vue-router";
const route = useRoute();
const router = useRouter();
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const userList = ref<UserVO[]>([]);
const processed = ref(false);
const taskFormOpen = ref(false)
const userMultipleSelection = ref([]);
const userSelectType = ref();
const currentUserId = ref();
const userLoading = ref(false);
const userTotal = ref(0);
const loadIndex = ref(0);
const height = ref(document.documentElement.clientHeight - 205 + 'px;');
const processXml = ref('');
const taskFormVisible = ref(false);
const processFormList = ref([]);
const taskFormData = ref([]);
const historyProcNodeList = ref<any>();
const formVisible = ref(false);
const finishedInfo = ref({});
const deptName = ref('');
const deptOptions = ref<DeptVO[]>([]);
const returnTaskList = ref();
const returnTaskKey = ref();
const copyUser = ref([]);
const nextUser = ref([]);
const taskFormRef = ref(ElForm);
const vFormRenderRef = ref(null);
const deptTreeRef = ref(null);
const returnDialog = reactive<DialogOption>({
visible: false,
title: '退回流程'
});
const userSelectDialog = reactive<DialogOption>({
visible: false,
title: ''
});
const taskForm = reactive<TaskForm>({
comment: '',
procInsId: '',
taskId: '',
userId: '',
copyUserIds: '',
nextUserIds: '',
vars: '',
targetKey: ''
});
const rules = ref({
comment: [{ required: true, message: '请输入审批意见', trigger: 'blur' }]
});
const queryParams = ref({
pageNum: 1,
pageSize: 10
});
const tagType = (val: any) => {
if (val) {
return "success";
} else {
return "info";
}
}
const commentType = (val: string) => {
switch (val) {
case '1': return '通过'
case '2': return '退回'
case '3': return '驳回'
case '4': return '委派'
case '5': return '转办'
case '6': return '终止'
case '7': return '撤回'
}
}
const approveTypeTag = (val: string) => {
switch (val) {
case '1': return 'success'
case '2': return 'warning'
case '3': return 'danger'
case '4': return 'primary'
case '5': return 'success'
case '6': return 'danger'
case '7': return 'info'
}
}
const initData = () => {
taskForm.procInsId = route.params && route.params.procInsId as string;
taskForm.taskId = route.query && route.query.taskId as string;
processed.value = route.query && (route.query.processed || false) === "true";
//
getProcessDetails(taskForm.procInsId, taskForm.taskId);
loadIndex.value++;
};
/** 通过条件过滤节点 */
const filterNode = (value: string, data: any) => {
if (!value) return true
return data.label.indexOf(value) !== -1
}
// /** */
// watchEffect(
// () => {deptTreeRef.value.filter(deptName.value);},
// {
// flush: 'post' // watchEffectDOMDOM
// }
// );
//
const handleNodeClick = (data: any) => {
getList(data.id);
}
/** 查询部门下拉树结构 */
const getTreeSelect = async () => {
const res = await deptTreeSelect();
deptOptions.value = res.data;
};
/** 查询用户列表 */
const getList = async (deptId?: number) => {
userLoading.value = true;
const res = await selectUser({deptId: deptId});
userLoading.value = false;
userList.value = res.rows;
userTotal.value = res.total;
}
const getProcessDetails = async (procInsId: string, taskId: string) => {
const params = {procInsId: procInsId, taskId: taskId}
const res = await detailProcess(params);
const data = res.data;
processXml.value = data.bpmnXml;
processFormList.value = data.processFormList;
taskFormVisible.value = data.existTaskForm;
if (taskFormVisible.value) {
taskFormData.value = data.taskFormData;
}
historyProcNodeList.value = data.historyProcNodeList;
finishedInfo.value = data.flowViewer;
formVisible.value = true;
nextTick(() => {
processFormList.value.forEach((item: any, index: any) => {
if (item.disabled) {
vFormRenderRef.value[index].disableForm();
}
})
})
}
const onSelectCopyUsers = () => {
userMultipleSelection.value = copyUser;
onSelectUsers('添加抄送人', 'copy')
}
const onSelectNextUsers = () => {
userMultipleSelection.value = nextUser;
onSelectUsers('指定审批人', 'next')
}
const onSelectUsers = (title: string, type: string) => {
userSelectType.value = type;
userSelectDialog.title = title;
userSelectDialog.visible = true;
getTreeSelect();
getList()
}
/** 通过任务 */
const handleComplete = () => {
//
taskFormRef.value.validate(async (valid: boolean) => {
if (valid) {
const res = await complete(taskForm)
proxy?.$modal.msgSuccess(res.msg);
goBack();
}
});
}
/** 委派任务 */
const handleDelegate = () => {
userSelectType.value = 'delegate';
userSelectDialog.title = '委派任务'
userSelectDialog.visible = true;
getTreeSelect();
}
/** 转办任务 */
const handleTransfer = () => {
userSelectType.value = 'transfer';
userSelectDialog.title = '转办任务';
userSelectDialog.visible = true;
getTreeSelect();
}
/** 退回任务 */
const handleReturn = async () => {
//
taskFormRef.value.validate(async (valid: boolean) => {
if (valid) {
const res = await returnList(taskForm);
returnTaskList.value = res.data;
returnDialog.visible = true;
}
});
}
/** 拒绝任务 */
const handleReject = async () => {
await proxy?.$modal.confirm('拒绝审批单流程会终止,是否继续?');
await rejectTask(taskForm);
proxy?.$modal.msgSuccess("操作成功");
goBack();
}
/** 返回页面 */
const goBack = () => {
//
proxy?.$tab.closePage(route);
router.back()
}
//
const handleClose = (type: any, tag: any) => {
let userObj = userMultipleSelection.value.find(item => item.userId === tag.id);
userMultipleSelection.value.splice(userMultipleSelection.value.indexOf(userObj), 1);
if (type === 'copy') {
copyUser.value = userMultipleSelection.value;
// ID
if (copyUser.value && copyUser.value.length > 0) {
const val = copyUser.value.map(item => item.id);
taskForm.copyUserIds = val instanceof Array ? val.join(',') : val;
} else {
taskForm.copyUserIds = '';
}
} else if (type === 'next') {
nextUser.value = userMultipleSelection.value;
// ID
if (nextUser.value && nextUser.value.length > 0) {
const val = nextUser.value.map(item => item.id);
taskForm.nextUserIds = val instanceof Array ? val.join(',') : val;
} else {
taskForm.nextUserIds = '';
}
}
}
const changeCurrentUser = (val: any) => {
// currentUserId = val.userId
}
const handleSelectionChange = () => {
}
const submitReturn = () => {
//
taskFormRef.value.validate(async (valid: boolean) => {
if (valid) {
if (!returnTaskKey) {
proxy?.$modal.msgError("请选择退回节点!");
}
taskForm.targetKey = returnTaskKey.value;
const res = await returnTask(taskForm);
proxy?.$modal.msgSuccess(res.msg);
goBack()
}
});
console.log("taskForm => ", taskForm.targetKey);
}
const submitUserData = () => {
let type = userSelectType.value;
if (type === 'copy' || type === 'next') {
if (!userMultipleSelection || userMultipleSelection.value.length <= 0) {
proxy?.$modal.msgError("请选择用户");
return false;
}
let userIds = userMultipleSelection.value.map(k => k.userId);
if (type === 'copy') {
// ID
copyUser.value = userMultipleSelection.value;
taskForm.copyUserIds = userIds instanceof Array ? userIds.join(',') : userIds;
} else if (type === 'next') {
// ID
nextUser.value = userMultipleSelection.value;
taskForm.nextUserIds = userIds instanceof Array ? userIds.join(',') : userIds;
}
userSelectDialog.visible = false;
} else {
if (!taskForm.comment) {
proxy?.$modal.msgError("请输入审批意见");
return false;
}
if (!currentUserId.value) {
proxy?.$modal.msgError("请选择用户");
return false;
}
taskForm.userId = currentUserId.value;
if (type === 'delegate') {
delegate(taskForm).then(res => {
proxy?.$modal.msgSuccess(res.msg);
goBack();
});
}
if (type === 'transfer') {
transfer(taskForm).then(res => {
proxy?.$modal.msgSuccess(res.msg);
goBack();
});
}
}
}
onMounted(() => {
initData();
});
</script>
<style lang="scss" scoped>
.clearfix:before,
.clearfix:after {
display: table;
content: "";
}
.clearfix:after {
clear: both
}
.box-card {
width: 100%;
margin-bottom: 20px;
}
.el-tag + .el-tag {
margin-left: 10px;
}
.el-row {
margin-bottom: 20px;
&:last-child {
margin-bottom: 0;
}
}
.el-col {
border-radius: 4px;
}
.button-new-tag {
margin-left: 10px;
}
</style>

View File

@ -0,0 +1,152 @@
<template>
<div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div class="search" v-show="showSearch">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="70">
<el-form-item label="流程名称" prop="processName">
<el-input v-model="queryParams.processName" placeholder="请输入流程名称" clearable style="width: 200px" @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="流程分类" prop="category">
<el-select v-model="queryParams.category" clearable placeholder="请选择">
<el-option v-for="item in categoryOptions" :key="item.categoryId" :label="item.categoryName" :value="item.code" />
</el-select>
</el-form-item>
<el-form-item label="审批时间" style="width: 308px;">
<el-date-picker
v-model="dateRange"
value-format="YYYY-MM-DD"
type="daterange"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
></el-date-picker>
</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 type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['workflow:process:finishedExport']"></el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" :data="finishedList">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="任务编号" align="center" prop="taskId" :show-overflow-tooltip="true" />
<el-table-column label="流程名称" align="center" prop="procDefName" :show-overflow-tooltip="true" />
<el-table-column label="流程分类" align="center" prop="category" :formatter="categoryFormat" />
<el-table-column label="任务节点" align="center" prop="taskName" />
<el-table-column label="流程发起人" align="center" prop="startUserName" />
<el-table-column label="接收时间" align="center" prop="createTime" width="180" />
<el-table-column label="审批时间" align="center" prop="finishTime" width="180" />
<el-table-column label="耗时" align="center" prop="duration" width="180" />
<el-table-column label="操作" width="180" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip content="详情" placement="top">
<el-button link type="primary" icon="View" @click="handleDetails(scope.row)" v-hasPermi="['workflow:process:query']"></el-button>
</el-tooltip>
<el-tooltip content="撤回" placement="top">
<el-button link type="primary" icon="View" @click="handleRevoke(scope.row)" v-hasPermi="['workflow:process:revoke']"></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
</el-card>
</div>
</template>
<script setup name="Finished" lang="ts">
import { listFinishedProcess } from "@/api/workflow/work/process";
import { revokeProcess } from "@/api/workflow/work/task";
import {ProcessQuery, ProcessVO} from "@/api/workflow/work/types";
import {DateModelType} from "element-plus";
import { ComponentInternalInstance } from "vue";
import { listAllCategory } from "@/api/workflow/category";
import { CategoryVO } from "@/api/workflow/category/types";
const router = useRouter();
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const finishedList = ref<ProcessVO[]>([]);
const loading = ref(true);
const showSearch = ref(true);
const ids = ref<Array<number | string>>([]);
const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const dateRange = ref<[DateModelType, DateModelType]>(['','']);
const categoryOptions = ref<CategoryVO[]>([]);
const queryFormRef = ref(ElForm);
const queryParams = ref<ProcessQuery>({
pageNum: 1,
pageSize: 10,
processName: '',
category: ''
});
/** 查询流程分类列表 */
const getCategoryList = async () => {
const res = await listAllCategory();
categoryOptions.value = res.data;
}
/** 查询待办列表 */
const getList = async () => {
loading.value = true;
const res = await listFinishedProcess(proxy?.addDateRange(queryParams.value, dateRange.value));
finishedList.value = res.rows;
total.value = res.total;
loading.value = false;
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.value.pageNum = 1;
getList();
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields();
handleQuery();
}
/** 流程流转记录 */
const handleDetails = (row: any) => {
router.push({
path: '/workflow/process/detail/' + row.procInsId,
query: {
processed: false
}
})
}
/** 撤回任务 */
const handleRevoke = async (row: any) => {
const params = {
procInsId: row.procInsId,
taskId: row.taskId
};
const res = await revokeProcess(params);
proxy?.$modal.msgSuccess(res.msg);
getList();
}
/** 导出按钮操作 */
const handleExport = () => {
proxy?.download("workflow/process/finishedExport", {
...queryParams.value
}, `finished_${new Date().getTime()}.xlsx`);
}
const categoryFormat = (row: ProcessVO) => {
return categoryOptions.value.find(find => find.code === row.category)?.categoryName ?? '';
}
onMounted(() => {
getCategoryList();
getList();
});
</script>

View File

@ -0,0 +1,163 @@
<template>
<div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div class="search" v-show="showSearch">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="70">
<el-form-item label="流程标识" prop="processKey">
<el-input v-model="queryParams.processKey" placeholder="请输入流程标识" clearable style="width: 200px" @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="流程名称" prop="processName">
<el-input v-model="queryParams.processName" placeholder="请输入流程名称" clearable style="width: 200px" @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="流程分类" prop="category">
<el-select v-model="queryParams.category" clearable placeholder="请选择">
<el-option v-for="item in categoryOptions" :key="item.categoryId" :label="item.categoryName" :value="item.code" />
</el-select>
</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 type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['workflow:process:startExport']"></el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" :data="processList">
<el-table-column label="流程标识" align="center" prop="processKey" :show-overflow-tooltip="true" />
<el-table-column label="流程名称" align="center" :show-overflow-tooltip="true">
<template #default="scope">
<el-button type="text" @click="handleProcessView(scope.row)">
<span>{{ scope.row.processName }}</span>
</el-button>
</template>
</el-table-column>
<el-table-column label="流程分类" align="center" prop="categoryName" :formatter="categoryFormat" />
<el-table-column label="流程版本" align="center">
<template #default="scope">
<el-tag>v{{ scope.row.version }}</el-tag>
</template>
</el-table-column>
<el-table-column label="状态" align="center">
<template #default="scope">
<el-tag type="success" v-if="!scope.row.suspended"></el-tag>
<el-tag type="warning" v-if="scope.row.suspended"></el-tag>
</template>
</el-table-column>
<el-table-column label="部署时间" align="center" prop="deploymentTime" width="180" />
<el-table-column label="操作" width="180" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip content="发起" placement="top">
<el-button link type="primary" icon="VideoPlay" @click="handleStart(scope.row)" v-hasPermi="['workflow:process:start']"></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
</el-card>
<!-- 流程图 -->
<el-dialog :title="processDialog.title" v-model="processDialog.visible" width="70%">
<process-viewer :key="`designer-${reloadIndex}`" :xml="processXml" :style="{height: '650px'}" />
</el-dialog>
</div>
</template>
<script setup name="WorkProcess" lang="ts">
import { listProcess, getBpmnXml } from "@/api/workflow/work/process";
import { listAllCategory } from "@/api/workflow/category";
import { ProcessQuery, ProcessVO } from "@/api/workflow/work/types";
import { CategoryVO } from "@/api/workflow/category/types";
import { ComponentInternalInstance } from "vue";
const router = useRouter();
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const processList = ref<ProcessVO[]>([]);
const loading = ref(true);
const showSearch = ref(true);
const total = ref(0);
const reloadIndex = ref(0);
const processXml = ref("");
const categoryOptions = ref<CategoryVO[]>([]);
const queryFormRef = ref(ElForm);
const processDialog = reactive<DialogOption>({
visible: false,
title: '流程图'
});
const queryParams = ref<ProcessQuery>({
pageNum: 1,
pageSize: 10,
processKey: '',
processName: '',
category: ''
});
/** 查询流程分类列表 */
const getCategoryList = async () => {
const res = await listAllCategory();
categoryOptions.value = res.data;
}
/** 查询流程列表 */
const getList = async () => {
loading.value = true;
const res = await listProcess(queryParams.value);
processList.value = res.rows;
total.value = res.total;
loading.value = false;
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.value.pageNum = 1;
getList();
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields();
handleQuery();
}
/** 查看流程图 */
const handleProcessView = async (row: any) => {
reloadIndex.value++;
// xml
const res = await getBpmnXml(row.definitionId);
processXml.value = res.data;
processDialog.visible = true;
}
/** 发起流程 */
const handleStart = (row: ProcessVO) => {
router.push({
path: '/workflow/process/start/' + row.deploymentId,
query: {
definitionId: row.definitionId,
}
})
};
/** 导出按钮操作 */
const handleExport = () => {
proxy?.download("workflow/process/startExport", {
...queryParams.value
}, `start_process_${new Date().getTime()}.xlsx`);
}
const categoryFormat = (row: ProcessVO) => {
return categoryOptions.value.find(k => k.code === row.category)?.categoryName ?? '';
}
onMounted(() => {
getCategoryList();
getList();
});
</script>

View File

@ -0,0 +1,202 @@
<template>
<div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div class="search" v-show="showSearch">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="70">
<el-form-item label="流程标识" prop="processKey">
<el-input v-model="queryParams.processKey" placeholder="请输入流程标识" clearable style="width: 200px" @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="流程名称" prop="processName">
<el-input v-model="queryParams.processName" placeholder="请输入流程名称" clearable style="width: 200px" @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="流程分类" prop="category">
<el-select v-model="queryParams.category" clearable placeholder="流程分类">
<el-option v-for="item in categoryOptions" :key="item.categoryId" :label="item.categoryName" :value="item.code" />
</el-select>
</el-form-item>
<el-form-item label="提交时间" style="width: 308px;">
<el-date-picker
v-model="dateRange"
value-format="YYYY-MM-DD"
type="daterange"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
></el-date-picker>
</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 type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['workflow:process:remove']">
删除
</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['workflow:process:ownExport']"></el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" :data="ownProcessList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="流程编号" align="center" prop="procInsId" :show-overflow-tooltip="true" />
<el-table-column label="流程名称" align="center" prop="procDefName" :show-overflow-tooltip="true" />
<el-table-column label="流程类别" align="center" prop="category" :formatter="categoryFormat" />
<el-table-column label="流程版本" align="center" width="80px">
<template #default="scope">
<el-tag>v{{ scope.row.procDefVersion }}</el-tag>
</template>
</el-table-column>
<el-table-column label="当前节点" align="center" prop="taskName" />
<el-table-column label="提交时间" align="center" prop="createTime" width="180" />
<el-table-column label="流程状态" align="center" width="100">
<template #default="scope">
<dict-tag :options="wf_process_status" :value="scope.row.processStatus" />
</template>
</el-table-column>
<el-table-column label="耗时" align="center" prop="duration" width="180" />
<el-table-column label="操作" width="180" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip content="详情" placement="top">
<el-button link type="primary" icon="View" @click="handleDetails(scope.row)" v-hasPermi="['workflow:process:query']"></el-button>
</el-tooltip>
<el-tooltip content="取消" placement="top">
<el-button link type="primary" icon="CircleClose" @click="handleStop(scope.row)" v-hasPermi="['workflow:process:cancel']"></el-button>
</el-tooltip>
<el-tooltip content="再次发起" placement="top">
<el-button link type="primary" icon="RefreshRight" @click="handleAgain(scope.row)" v-hasPermi="['workflow:process:start']"></el-button>
</el-tooltip>
<el-tooltip content="删除" placement="top">
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['workflow:process:remove']"></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
</el-card>
</div>
</template>
<script setup name="Own" lang="ts">
import { listOwnProcess, stopProcess, delProcess } from "@/api/workflow/work/process";
import { listAllCategory } from "@/api/workflow/category";
import { ProcessQuery, ProcessVO } from "@/api/workflow/work/types";
import { CategoryVO } from "@/api/workflow/category/types";
import { DateModelType } from "element-plus";
import { ComponentInternalInstance } from "vue";
const router = useRouter();
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { wf_process_status } = toRefs<any>(proxy?.useDict("wf_process_status"));
const categoryOptions = ref<CategoryVO[]>([]);
const ownProcessList = ref<ProcessVO[]>([]);
const loading = ref(true);
const showSearch = ref(true);
const ids = ref<Array<number | string>>([]);
const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const dateRange = ref<[DateModelType, DateModelType]>(['','']);
const queryFormRef = ref(ElForm);
const queryParams = ref<ProcessQuery>({
pageNum: 1,
pageSize: 10,
processKey: '',
processName: '',
category: ''
});
/** 查询流程分类列表 */
const getCategoryList = async () => {
const res = await listAllCategory();
categoryOptions.value = res.data;
};
/** 查询我的流程列表 */
const getList = async () => {
loading.value = true;
const res = await listOwnProcess(proxy?.addDateRange(queryParams.value, dateRange.value));
ownProcessList.value = res.rows;
total.value = res.total;
loading.value = false;
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.value.pageNum = 1;
getList();
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields();
handleQuery();
}
/** 多选框选中数据 */
const handleSelectionChange = (selection: ProcessVO[]) => {
ids.value = selection.map(item => item.procInsId);
single.value = selection.length != 1;
multiple.value = !selection.length;
}
/** 流程详情 */
const handleDetails = (row: ProcessVO) => {
router.push({
path: '/workflow/process/detail/' + row.procInsId,
query: {
processed: false
}
})
}
/** 取消流程申请 */
const handleStop = async (row: ProcessVO) => {
await stopProcess( { procInsId: row.procInsId });
proxy?.$modal.msgSuccess("操作成功");
getList();
}
/** 再次发起流程 */
const handleAgain = (row: ProcessVO) => {
// router.push({
// path: '/workflow/process/start/' + row.deployId,
// query: {
// definitionId: row.procDefId,
// procInsId: row.procInsId
// }
// })
}
/** 删除按钮操作 */
const handleDelete = async (row?: ProcessVO) => {
const procInsIds = row?.procInsId || ids.value;
await proxy?.$modal.confirm('是否确认删除流程定义编号为"' + procInsIds + '"的数据项?');
await delProcess(procInsIds);
getList();
proxy?.$modal.msgSuccess("删除成功");
}
/** 导出按钮操作 */
const handleExport = () => {
proxy?.download("workflow/process/ownExport", {
...queryParams.value
}, `own_process_${new Date().getTime()}.xlsx`);
}
const categoryFormat = (row: ProcessVO) => {
return categoryOptions.value.find(k => k.code === row.category)?.categoryName ?? '';
}
onMounted(() => {
getCategoryList();
getList();
});
</script>

View File

@ -0,0 +1,76 @@
<template>
<div class="app-container">
<el-card class="box-card">
<template #header>
<span>发起流程</span>
</template>
<div class="form-conf" v-if="dialog.visible">
<v-form-render :form-json="formModel" :form-data="formData" ref="vfRenderRef"></v-form-render>
<div class="cu-submit">
<el-button type="primary" @click="submit"></el-button>
<el-button @click="reset"></el-button>
</div>
</div>
</el-card>
</div>
</template>
<script setup name="WorkStart" lang="ts">
import { getProcessForm, startProcess } from '@/api/workflow/work/process';
import { ComponentInternalInstance } from "vue";
const route = useRoute();
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const vfRenderRef = ref(null);
const deployId = ref();
const definitionId = ref();
const formModel = ref({});
const formData = ref({});
const dialog = reactive<DialogOption>({
visible: false,
title: ''
});
const initData = async () => {
deployId.value = route.params && route.params.deployId;
definitionId.value = route.query && route.query.definitionId;
const res = await getProcessForm({ definitionId: definitionId.value, deployId: deployId.value });
formModel.value = res.data.formModel;
dialog.visible = true;
nextTick(async () => {
vfRenderRef.value.setFormJson(formModel.value || {formConfig: {}, widgetList: []});
});
}
const submit = async () => {
const data = await vfRenderRef.value.getFormData();
if (definitionId.value) {
const res = await startProcess(definitionId.value, JSON.stringify(data));
proxy?.$modal.msgSuccess(res.msg);
// const obj = { path: "/work/own" };
// proxy?.$tab.closeOpenPage(obj);
proxy?.$tab.closePage();
proxy?.$router.back();
}
}
const reset = () => {
vfRenderRef.value.resetForm();
}
onMounted(() => {
initData();
});
</script>
<style lang="scss" scoped>
.form-conf {
margin: 15px auto;
width: 80%;
padding: 15px;
}
</style>

View File

@ -0,0 +1,145 @@
<template>
<div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div class="search" v-show="showSearch">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="70">
<el-form-item label="流程名称" prop="processName">
<el-input v-model="queryParams.processName" placeholder="请输入流程名称" clearable style="width: 200px" @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="流程分类" prop="category">
<el-select v-model="queryParams.category" clearable placeholder="请选择">
<el-option v-for="item in categoryOptions" :key="item.categoryId" :label="item.categoryName" :value="item.code" />
</el-select>
</el-form-item>
<el-form-item label="接收时间" style="width: 308px;">
<el-date-picker
v-model="dateRange"
value-format="YYYY-MM-DD"
type="daterange"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
></el-date-picker>
</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 type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['workflow:process:todoExport']"></el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" :data="todoList">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="任务编号" align="center" prop="taskId" :show-overflow-tooltip="true" />
<el-table-column label="流程名称" align="center" prop="procDefName" />
<el-table-column label="任务节点" align="center" prop="taskName" />
<el-table-column label="流程分类" align="center" prop="category" :formatter="categoryFormat" />
<el-table-column label="流程版本" align="center">
<template #default="scope">
<el-tag>v{{scope.row.procDefVersion}}</el-tag>
</template>
</el-table-column>
<el-table-column label="流程发起人" align="center" prop="startUserName" />
<el-table-column label="接收时间" align="center" prop="createTime" width="180" />
<el-table-column label="操作" width="180" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip content="办理" placement="top">
<el-button link type="primary" icon="EditPen" @click="handleProcess(scope.row)" v-hasPermi="['workflow:process:approval']"></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
</el-card>
</div>
</template>
<script setup name="Todo" lang="ts">
import { listTodoProcess } from '@/api/workflow/work/process';
import { ProcessVO, ProcessQuery } from "@/api/workflow/work/types";
import {DateModelType} from "element-plus";
import { ComponentInternalInstance } from "vue";
import { listAllCategory } from "@/api/workflow/category";
import { CategoryVO } from "@/api/workflow/category/types";
import { ModelVO } from "@/api/workflow/model/types";
const router = useRouter();
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const todoList = ref<ProcessVO[]>([]);
const loading = ref(true);
const showSearch = ref(true);
const ids = ref<Array<number | string>>([]);
const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const dateRange = ref<[DateModelType, DateModelType]>(['','']);
const categoryOptions = ref<CategoryVO[]>([]);
const queryFormRef = ref(ElForm);
const queryParams = ref<ProcessQuery>({
pageNum: 1,
pageSize: 10,
processName: '',
category: ''
});
/** 查询流程分类列表 */
const getCategoryList = async () => {
const res = await listAllCategory();
categoryOptions.value = res.data;
}
/** 查询待办列表 */
const getList = async () => {
loading.value = true;
const res = await listTodoProcess(proxy?.addDateRange(queryParams.value, dateRange.value));
todoList.value = res.rows;
total.value = res.total;
loading.value = false;
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.value.pageNum = 1;
getList();
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields();
handleQuery();
}
/** 跳转到处理页面 */
const handleProcess = (row: ProcessVO) => {
router.push({
path: '/workflow/process/detail/' + row.procInsId,
query: {
taskId: row.taskId,
processed: true
}
})
}
/** 导出按钮操作 */
const handleExport = () => {
proxy?.download("workflow/process/todoExport", {
...queryParams.value
}, `todo_${new Date().getTime()}.xlsx`);
}
const categoryFormat = (row: ModelVO) => {
return categoryOptions.value.find(k => k.code === row.category)?.categoryName ?? '';
}
onMounted(() => {
getCategoryList();
getList();
});
</script>

View File

@ -0,0 +1,28 @@
import compression from 'vite-plugin-compression';
export default function createCompression(env: any) {
const { VITE_BUILD_COMPRESS } = env;
const plugin: any[] = [];
if (VITE_BUILD_COMPRESS) {
const compressList = VITE_BUILD_COMPRESS.split(',');
if (compressList.includes('gzip')) {
// http://doc.ruoyi.vip/ruoyi-vue/other/faq.html#使用gzip解压缩静态文件
plugin.push(
compression({
ext: '.gz',
deleteOriginFile: false
})
);
}
if (compressList.includes('brotli')) {
plugin.push(
compression({
ext: '.br',
algorithm: 'brotliCompress',
deleteOriginFile: false
})
);
}
}
return plugin;
}

View File

@ -0,0 +1,8 @@
import Icons from 'unplugin-icons/vite';
export default () => {
return Icons({
// 自动安装图标库
autoInstall: true
});
};

View File

@ -0,0 +1,13 @@
import UnoCss from 'unocss/vite';
import { presetUno, presetAttributify, presetIcons } from 'unocss';
export default () => {
return UnoCss({
presets: [presetUno(), presetAttributify(), presetIcons()],
// rules: [['search', {}]],
shortcuts: {
'panel-title':
'pb-[5px] font-sans leading-[1.1] font-medium text-base text-[#6379bb] border-b border-b-solid border-[var(--el-border-color-light)] mb-5 mt-0'
}
});
};