feat:流程-流程实例的版本问题

1.流程变更后,新旧流程按版本执行,按各种版本的流程定义和流程配置继续流转
2.可以切换流程实例的版本
This commit is contained in:
lvjunzhao
2025-02-25 18:42:40 +08:00
parent 2850e8abf5
commit 2d240ccf0c
6 changed files with 399 additions and 23 deletions

View File

@ -4,6 +4,7 @@ import {
WorkflowPageParams,
WorkflowPageResult,
ChangeResultModel,
WorkflowPageModel,
} from './model/index';
import { defHttp } from '/@/utils/http/axios';
@ -22,6 +23,7 @@ enum Api {
HistoryList = '/workflow/schema-history/list',
HistorySetCurrent = '/workflow/schema-history/set-current',
PreviewProcess = '/workflow/execute/preview',
UpdateVersion = '/workflow/schema/updateVersion',
}
/**
* @description: 导出流程设计
@ -51,7 +53,19 @@ export async function getDesignPage(params: WorkflowPageParams, mode: ErrorMessa
},
);
}
/**
* @description: 查询Workflow全量
*/
export async function getDesignList(mode: ErrorMessageMode = 'modal') {
return defHttp.get<Array<WorkflowPageModel>>(
{
url: Api.List,
},
{
errorMessageMode: mode,
},
);
}
/**
* @description: 获取Workflow信息
*/
@ -191,3 +205,20 @@ export async function getPreviewProcess(schemaId: string, mode: ErrorMessageMode
},
);
}
/**
* updateVersion 更新流程
*/
export async function updateProcessVersionApi(
updateVersionDto: Recordable,
mode: ErrorMessageMode = 'modal',
) {
return defHttp.post<boolean>(
{
url: Api.UpdateVersion,
params: updateVersionDto,
},
{
errorMessageMode: mode,
},
);
}

View File

@ -0,0 +1,3 @@
import { withInstall } from '/@/utils';
import updateProcessVersionModal from './src/UpdateProcessVersionModal.vue';
export const UpdateProcessVersionModal = withInstall(updateProcessVersionModal); //流程版本变更

View File

@ -0,0 +1,185 @@
<template>
<BasicModal @register="registerUpdateProcessVersionModal" v-bind="$attrs" wrapClassName="workflow-modal" @ok="handleSubmit" @cancel="cancel">
<a-table
:pagination="false"
:dataSource="data.dataSource"
:row-selection="rowSelection"
:columns="configColumns">
<template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'activityFlag'"
>{{ record.activityFlag == 1 ? t('当前版本') : '' }}
</template>
<template v-if="column.dataIndex === 'operation'">
<div class="flex">
<a-button
size="small"
type="primary"
class="mr-2"
@click="preview(record.xmlContent)"
>{{ t('预览') }}
</a-button>
</div>
</template>
</template>
</a-table>
<Preview v-if="data.previewVisible" :xml="data.xml" @close="() => {data.previewVisible = false}" />
</BasicModal>
</template>
<script setup lang="ts">
import { BasicModal, useModal, useModalInner } from '/@/components/Modal';
import {
updateProcessVersionApi,
getHistory,
} from '/@/api/workflow/design';
import { ChangeResultModel, HistoryModel } from '/@/api/workflow/model';
import { ref, reactive, onMounted, } from 'vue';
import Preview from '/@/views/workflow/design/Preview.vue';
import { useI18n } from '/@/hooks/web/useI18n';
const { t } = useI18n();
const props = withDefaults(
defineProps<{
processDefinitionKey: string,
processIds: Array<string>,
schemaId: string,
}>(),
{
processDefinitionKey: '',
processIds: () => [],
schemaId: '',
},
);
onMounted(() => {
getHistoryList();
});
const configColumns = [
{
title: t('序号'),
align: 'center',
customRender: ({ index }) => `${index + 1}`, // 显示每一行的序号
width: 80,
},
{
title: t('状态'),
dataIndex: 'activityFlag',
sorter: {
multiple: 4,
},
},
{
title: t('版本'),
dataIndex: 'version',
},
{
title: t('创建人'),
dataIndex: 'createUserName',
},
{
title: t('备注'),
dataIndex: 'remark',
width: 180,
},
{
title: t('创建时间'),
dataIndex: 'createDate',
},
{
title: t('操作'),
dataIndex: 'operation',
width: 120,
align: 'center',
},
];
const data: {
visible: boolean;
previewVisible: boolean;
xml: string;
dataSource: Array<HistoryModel>;
reusltVisible: boolean;
resultDataSource: ChangeResultModel[];
} = reactive({
visible: false,
previewVisible: false,
xml: '',
dataSource: [],
reusltVisible: false,
resultDataSource: [],
});
const emits = defineEmits(['success', 'cancel']);
const [registerUpdateProcessVersionModal, { setModalProps, closeModal }] = useModalInner(async () => {
setModalProps({
title: '流程版本变更',
confirmLoading: false,
draggable: false,
showOkBtn: false,
showCancelBtn: false,
destroyOnClose: true,
width: 800,
fixedHeight: true,
});
});
const getHistoryList = async () => {
try {
let res = await getHistory(props.schemaId);
data.dataSource = res;
}
catch (error) {}
}
function cancel() {
emits('cancel')
}
const selectedRowSignleKey = ref('');
// 右边保留流程图 单选配置
const rowSelection = {
type: 'radio', // 设置为单选
getRecordKey: record => record.definitionId,
onChange: (selectedRowKeysValue, selectedRows) => {
selectedRowSignleKey.value = selectedRowKeysValue; // 更新选中的行keys
},
};
const handleSubmit = async () => {
const values = {
processDefinitionKey: props.processDefinitionKey,
definitionIdNew: selectedRowSignleKey.value[0],
processIds: props.processIds,
};
await updateProcessVersionApi(values);
closeModal();
emits('success');
};
function preview(xml: string) {
if (xml) {
data.xml = xml;
data.previewVisible = true;
}
}
</script>
<style lang="less">
.workflow-modal {
.ant-modal {
width: 60%!important;
max-width: 60%!important;
top: 0;
padding-bottom: 0;
}
.ant-modal-content {
display: flex;
flex-direction: column;
height: calc(100vh);
}
.ant-modal-body {
flex: 1;
}
}
</style>

View File

@ -49,6 +49,9 @@
<a-button v-auth="'design:classifyMgt'" @click="handleCategory">{{
t('分类管理')
}}</a-button>
<a-button @click="handleUpdateVersion">{{
t('流程版本变更')
}}</a-button>
</template>
<template #action="{ record }">
@ -79,7 +82,7 @@
@register="registerCategoryModal"
@success="getCategoryTree"
/>
<Preview v-if="data.previewVisible" :xml="data.xml" @close="data.previewVisible = false" />
<Preview v-if="data.previewVisible" :xml="data.xml" @close="() => {data.previewVisible = false}" />
<WorkflowDesignModal
v-if="data.visibleBpmnDesign"
:editData="data.editData"
@ -91,6 +94,7 @@
import { onMounted, reactive, computed, defineAsyncComponent, h, ref } from 'vue';
import { CategoryModal } from '/@/components/CategoryModal';
import { UpdateProcessVersionModal } from '/@/components/UpdateProcessVersion';
import { BasicTree, TreeItem } from '/@/components/Tree';
import { PageWrapper } from '/@/components/Page';
import { LoadingBox } from '/@/components/ModalPanel/index';
@ -114,7 +118,11 @@
import History from './History.vue';
import ImportFlow from './ImportFlow.vue';
import { Tag } from 'ant-design-vue';
import { Row, Tag } from 'ant-design-vue';
import { useRouter } from 'vue-router';
const { currentRoute } = useRouter();
const router = useRouter();
const { t } = useI18n();
const Preview = defineAsyncComponent(() => import('./Preview.vue'));
const WorkflowDesignModal = defineAsyncComponent({
@ -305,6 +313,26 @@
} catch (error) {}
}
}
const versionData = {
definitionKey: '',
schemaId: '',
}
/**
* 更新选中的流程图对应的执行流程的版本
*/
async function handleUpdateVersion() {
let row = checkSelectSingleRow();
if (row) {
router.push({
path: '/task/monitor',
query: {
definitionKey: row.definitionKey,
}
});
}
}
async function forbidden() {
let row = checkSelectSingleRow();

View File

@ -17,13 +17,17 @@
@register="registerTable"
class="w-3/4 xl:w-4/5"
@selection-change="selectionChange"
@row-dbClick="dbClickRow"
>
<template #toolbar>
<LookProcess :taskId="taskId" :processId="processId" @close="reload"
<!-- <LookProcess :taskId="taskId" :processId="processId" @close="reload"
><a-button v-auth="'monitor:view'">{{ t('查看') }}</a-button></LookProcess
>
<a-button v-auth="'monitor:appointedAuditor'" @click="approveUser" v-if="data.type === 0">{{
> -->
<!-- <a-button v-auth="'monitor:appointedAuditor'" @click="approveUser" v-if="data.type === 0">{{
t('指派审核人')
}}</a-button> -->
<a-button @click="handleUpdateVersion">{{
t('流程版本变更')
}}</a-button>
<a-button v-auth="'monitor:pending'" @click="setSuspended" v-if="data.type === 0">{{
suspendedTitle
@ -52,6 +56,8 @@
</template>
</BasicTable>
<!-- 查看 -->
<LookProcess ref="lookProcess" :taskId="taskId" :processId="processId" @close="reload"/>
<!-- 指派审核人 -->
<ApproveProcessMonitorUser
v-if="data.approvedUserVisible"
@ -65,22 +71,54 @@
}
"
/>
<!-- 流程版本变更 -->
<UpdateProcessVersionModal
v-if="data.visibleVersion"
title="流程版本变更"
:processDefinitionKey="versionData.definitionKey"
:schemaId="versionData.schemaId"
:processIds="versionData.processIds"
@register="registerUpdateProcessVersionModal"
@success="() => {
data.visibleVersion = false;
reload();
clearSelectedRowKeys();
}"
@cancel="() => {
data.visibleVersion = false;
reload();
clearSelectedRowKeys();
}"
/>
</PageWrapper>
</template>
<script lang="ts" setup>
import { createVNode, reactive, ref } from 'vue';
import { createVNode, onMounted, reactive, ref } from 'vue';
import { useModal } from '/@/components/Modal';
import { BasicTree } from '/@/components/Tree';
import { PageWrapper } from '/@/components/Page';
import LookProcess from './components/LookProcess.vue';
import ApproveProcessMonitorUser from './components/flow/ApproveProcessMonitorUser.vue';
import { UpdateProcessVersionModal } from '/@/components/UpdateProcessVersion';
import { deleteWorkflow, getProcessMonitorPage, postSetSuspended } from '/@/api/workflow/monitor';
import { getDesignList } from '/@/api/workflow/design';
import { Modal } from 'ant-design-vue';
import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
import { notification, Tag } from 'ant-design-vue';
import { ProcessMonitorStatus } from '/@/enums/workflowEnum';
import { BasicTable, useTable, FormSchema, BasicColumn } from '/@/components/Table';
import { useI18n } from '/@/hooks/web/useI18n';
import { useRouter } from 'vue-router';
import { aW } from '@fullcalendar/core/internal-common';
const { currentRoute } = useRouter();
const router = useRouter();
const definitionKey = ref('');
onMounted(() => {
definitionKey.value = currentRoute.value.query.definitionKey as string;
});
const { t } = useI18n();
const configColumns: BasicColumn[] = [
{
@ -141,6 +179,20 @@
},
];
const searchFormSchema: FormSchema[] = [
{
field: 'definitionKey',
label: '流程',
component: 'ApiSelect',
value: definitionKey,
componentProps: {
mode: 'single',
api: getDesignList,
showSearch: true,
labelField: 'name',
valueField: 'definitionKey',
getPopupContainer: () => document.body,
},
},
{
field: 'keyword',
label: t('关键字'),
@ -172,7 +224,9 @@
icon: 'ant-design:profile-outlined',
},
];
const queryData = ref({});
const selectedKeys = ref([0]);
const lookProcess = ref()
const [registerTable, { reload, getSelectRows, clearSelectedRowKeys }] = useTable({
title: t('流程监控列表'),
api: getProcessMonitorPage,
@ -187,11 +241,17 @@
showResetButton: false,
},
rowSelection: {
type: 'radio',
// type: 'radio',
type: 'group',
},
beforeFetch: (params) => {
//发送请求默认新增 左边树结构所选机构id
return { ...params, type: data.type };
queryData.value = {
...params,
definitionKey: params.definitionKey?params.definitionKey:definitionKey.value,
type: data.type
};
return queryData.value
},
useSearchForm: true,
showTableSetting: true,
@ -207,9 +267,11 @@
let data: {
type: number;
approvedUserVisible: boolean;
visibleVersion: boolean;
} = reactive({
type: 0,
approvedUserVisible: false,
visibleVersion: false,
});
const processId = ref('');
const taskId = ref('');
@ -293,8 +355,15 @@
}
function checkSelectSingleRow(tip?) {
const selectRows: any = getSelectRows();
if (selectRows.length > 0) {
if (selectRows.length == 1) {
return selectRows[0];
} else if (selectRows.length > 1) {
notification.open({
type: 'error',
message: t('流程'),
description: t(tip || '请选择单一一个流程进行'),
});
return false;
} else {
notification.open({
type: 'error',
@ -304,4 +373,59 @@
return false;
}
}
function checkSelectGroupRow(tip?) {
const selectRows: any = getSelectRows();
if (selectRows.length > 0) {
let processDefinitionKey = selectRows[0].processDefinitionKey;
let flagCheck = true;
selectRows.forEach(element => {
if(element.processDefinitionKey != processDefinitionKey) flagCheck = false;
});
if(!flagCheck) {
notification.open({
type: 'error',
message: t('选择了多个流程'),
description: t(tip || '请按单一流程选择后重新批量配置'),
});
return false;
}
return selectRows;
} else {
notification.open({
type: 'error',
message: t('流程'),
description: t(tip || '请选择最少一个流程实例进行'),
});
return false;
}
}
async function dbClickRow(record) {
processId.value = record.processId;
taskId.value = record.taskId;//改版 一个流程中可能有多个taskId 已不返回
await lookProcess.value.look();
}
const [registerUpdateProcessVersionModal, { openModal: openUpdateProcessVersionModal }] = useModal();
const versionData = {
definitionKey: '',
schemaId: '',
processIds: [],
}
/**
* 更新选中的流程图对应的执行流程的版本
*/
async function handleUpdateVersion() {
let selectRows = checkSelectGroupRow();
if (selectRows) {
versionData.definitionKey = selectRows[0].processDefinitionKey;
versionData.schemaId = selectRows[0].schemaId;
versionData.processIds = selectRows.map(selectRows => selectRows.processId);
await nextTick();
openUpdateProcessVersionModal();
}
}
function nextTick () {
data.visibleVersion = true;
}
</script>

View File

@ -19,21 +19,23 @@
import LookTask from './flow/LookTask.vue';
import { LoadingBox } from '/@/components/ModalPanel/index';
import { notification } from 'ant-design-vue';
import { onMounted, ref } from 'vue';
import { onActivated, onMounted, ref } from 'vue';
import { useI18n } from '/@/hooks/web/useI18n';
const { t } = useI18n();
const props = withDefaults(
defineProps<{
processId: string;
taskId: string;
visible?: boolean;
}>(),
{
processId: '',
taskId: '',
visible: false,
const props = defineProps({
processId: {
type: String,
default: ''
},
);
taskId: {
type: String,
default: ''
},
visible: {
type: Boolean,
default: false
},
})
let emits = defineEmits(['close']);
let visible = ref(false);
let showLoading = ref(false);
@ -42,7 +44,7 @@
look();
}
});
async function look() {
function look() {
if (props.processId) {
showLoading.value = false;
visible.value = true;
@ -59,4 +61,7 @@
visible.value = false;
emits('close');
}
defineExpose({
look
});
</script>