Files
geg-gas-web/src/views/workflow/design/index.vue
lvjunzhao 2d240ccf0c feat:流程-流程实例的版本问题
1.流程变更后,新旧流程按版本执行,按各种版本的流程定义和流程配置继续流转
2.可以切换流程实例的版本
2025-02-25 18:42:40 +08:00

393 lines
11 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<PageWrapper dense contentFullHeight fixedHeight contentClass="flex">
<div
class="w-1/4 xl:w-1/5 overflow-hidden bg-white h-full"
:style="{ 'border-right': '1px solid #e5e7eb' }"
>
<BasicTree
search
:title="t('流程分类')"
:clickRowToExpand="true"
:treeData="data.treeData"
:fieldNames="{ key: 'id', title: 'name' }"
@select="handleSelect"
/>
</div>
<BasicTable
@register="registerTable"
class="w-3/4 xl:w-4/5"
@selection-change="selectionChange"
>
<template #toolbar>
<a-button type="primary" v-auth="'design:add'" @click="handleCreate">{{
t('新增')
}}</a-button>
<a-button v-auth="'design:preview'" @click.stop="preview">{{ t('预览流程') }}</a-button>
<a-button v-auth="'design:disable'" @click="forbidden">{{ enabledTitle }}</a-button>
<History :schemaId="schemaId"
><a-button v-auth="'design:history'">{{ t('历史记录') }}</a-button></History
>
<ImportFlow :coverType="false">
<a-tooltip placement="topLeft">
<template #title>
<span>新增导入会保留原id流程新增新流程并不会验证code和name是否相同如果旧流程还绑定中导入新流程也是绑定状态的话会导致page查询注入workflow出错</span>
</template>
<a-button v-auth="'design:importFlow'">{{ t('新增导入流程') }}</a-button>
</a-tooltip>
</ImportFlow>
<ImportFlow :coverType="true">
<a-tooltip placement="topLeft">
<template #title>
<span>覆盖导入同样支持新增流程导入会有保存验证保存出错请确认报错信息</span>
</template>
<a-button v-auth="'design:importFlow'">{{ t('覆盖导入流程') }}</a-button>
</a-tooltip>
</ImportFlow>
<a-button v-auth="'design:exportFlow'" @click.stop="handleDownByData">{{
t('导出流程')
}}</a-button>
<a-button v-auth="'design:classifyMgt'" @click="handleCategory">{{
t('分类管理')
}}</a-button>
<a-button @click="handleUpdateVersion">{{
t('流程版本变更')
}}</a-button>
</template>
<template #action="{ record }">
<TableAction
:actions="[
{
icon: 'clarity:note-edit-line',
auth: 'design:edit',
onClick: handleEdit.bind(null, record),
},
{
icon: 'ant-design:delete-outlined',
auth: 'design:delete',
color: 'error',
popConfirm: {
title: t('是否确认删除'),
confirm: handleDelete.bind(null, record),
},
},
]"
/>
</template>
</BasicTable>
<CategoryModal
title="流程"
:dicId="FlowCategory.ID"
@register="registerCategoryModal"
@success="getCategoryTree"
/>
<Preview v-if="data.previewVisible" :xml="data.xml" @close="() => {data.previewVisible = false}" />
<WorkflowDesignModal
v-if="data.visibleBpmnDesign"
:editData="data.editData"
@close="handleClose"
/>
</PageWrapper>
</template>
<script lang="ts" setup>
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';
import { useMessage } from '/@/hooks/web/useMessage';
import { useI18n } from '/@/hooks/web/useI18n';
import { getDicDetailList } from '/@/api/system/dic';
import {
deleteDesign,
getDesignPage,
exportDesign,
enabledDesign,
disabledDesign,
getPreviewProcess,
} from '/@/api/workflow/design';
import { getBpmnJsonAndXmlById } from './bpmn/config/info';
import { FlowCategory } from '/@/enums/workflowEnum';
import { useModal } from '/@/components/Modal';
import { downloadByData } from '/@/utils/file/download';
import { BasicTable, useTable, TableAction, FormSchema, BasicColumn } from '/@/components/Table';
import History from './History.vue';
import ImportFlow from './ImportFlow.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({
loader: () => import('./bpmn/index.vue'),
loadingComponent: LoadingBox,
});
const configColumns: BasicColumn[] = [
{
title: t('编码'),
dataIndex: 'code',
align: 'left',
},
{
title: t('名称'),
dataIndex: 'name',
align: 'left',
},
{
title: t('分类'),
dataIndex: 'categoryName',
width: 80,
align: 'left',
},
{
title: t('流程状态'),
dataIndex: 'enabledMark',
width: 120,
align: 'left',
customRender: ({ record }) => {
const status = record.enabledMark;
const enable = ~~status === 1;
const color = enable ? 'green' : 'red';
const text = enable ? t('启用') : t('停用');
return h(Tag, { color: color }, () => text);
},
},
{
title: t('备注'),
dataIndex: 'remark',
width: 120,
align: 'left',
},
];
const searchFormSchema: FormSchema[] = [
{
field: 'keyword',
label: t('关键字'),
component: 'Input',
colProps: { span: 8 },
componentProps: {
placeholder: t('请输入关键字'),
},
},
];
const { notification } = useMessage();
const [registerTable, { reload, getSelectRows, clearSelectedRowKeys }] = useTable({
title: t('流程模板列表'),
api: getDesignPage,
rowKey: 'id',
columns: configColumns,
formConfig: {
rowProps: {
gutter: 16,
},
schemas: searchFormSchema,
showResetButton: false,
},
rowSelection: {
type: 'radio',
},
beforeFetch: (params) => {
//发送请求默认新增 左边树结构所选机构id
return { ...params, category: data.classifyId, isAuth: false };
},
useSearchForm: true,
showTableSetting: true,
striped: false,
pagination: {
pageSize: 18,
},
actionColumn: {
width: 80,
title: t('操作'),
dataIndex: 'action',
slots: { customRender: 'action' },
},
tableSetting: {
size: false,
setting: false,
},
});
let data: {
visibleBpmnDesign: boolean;
treeData: Array<TreeItem>;
classifyId: string;
previewVisible: boolean;
xml: string;
editData: {
id: string;
json: { processConfig: any; childNodeConfig: Array<any> };
xml: string;
};
} = reactive({
visibleBpmnDesign: false,
treeData: [],
classifyId: '',
previewVisible: false,
xml: '',
editData: { id: '', json: { processConfig: null, childNodeConfig: [] }, xml: '' },
});
const [registerCategoryModal, { openModal: openCategoryModal }] = useModal();
const enabledTitle = ref(t('启用'));
const schemaId = computed(() => {
const selectRows = getSelectRows();
if (selectRows.length > 0) {
return selectRows[0].id;
} else {
return '';
}
});
onMounted(() => {
getCategoryTree();
});
function handleCategory() {
openCategoryModal(true, { title: t('流程分类管理') });
}
function selectionChange({ keys, rows }) {
if (keys?.length > 0) {
enabledTitle.value = rows[0].enabledMark === 1 ? t('禁用') : t('启用');
} else {
enabledTitle.value = t('启用');
}
}
function handleCreate() {
data.visibleBpmnDesign = true;
}
function handleClose() {
data.visibleBpmnDesign = false;
data.editData.id = '';
data.editData.xml = '';
data.editData.json = { processConfig: null, childNodeConfig: [] };
reload();
}
async function handleEdit(record: Recordable) {
let res = await getBpmnJsonAndXmlById(record.id);
data.editData.id = record.id;
data.editData.xml = res.xml;
data.editData.json = res.json;
data.visibleBpmnDesign = true;
}
function handleDelete(record: Recordable) {
deleteDesign([record.id]).then((_) => {
notification.success({
message: t('提示'),
description: t('删除成功'),
}); //提示消息
reload();
});
}
function handleSelect(selectIds: Array<string>) {
clearSelectedRowKeys();
data.classifyId = selectIds[0];
reload();
}
async function getCategoryTree() {
let res = (await getDicDetailList({
itemId: FlowCategory.ID,
})) as unknown as TreeItem[];
data.treeData = res.map((ele) => {
ele.icon = 'ant-design:tags-outlined';
return ele;
});
}
async function handleDownByData() {
let row = checkSelectSingleRow();
if (row) {
try {
let res: any = await exportDesign(row.id);
downloadByData(res, row.name + '.json');
} 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();
if (row) {
try {
if (row.enabledMark == 1) {
let res = await disabledDesign(row.id);
if (res) {
notification.success({
message: t('禁用流程'),
description: t('禁用成功!'),
});
reload();
}
} else {
let res = await enabledDesign(row.id);
if (res) {
notification.success({
message: t('启动流程'),
description: t('启动成功!'),
});
reload();
}
}
clearSelectedRowKeys();
} catch (error) {}
}
}
function checkSelectSingleRow() {
const selectRows = getSelectRows();
if (selectRows.length > 0) {
return selectRows[0];
} else {
notification.open({
type: 'error',
message: t('流程'),
description: t('请选择一个流程进行'),
});
return false;
}
}
async function preview() {
let row = checkSelectSingleRow();
if (row) {
try {
if (row.id) {
let res = await getPreviewProcess(row.id);
if (res) {
data.xml = res;
data.previewVisible = true;
}
}
} catch (error) {}
}
}
</script>