feat-wip(projects): 流程定义完成

This commit is contained in:
AN
2025-06-13 00:20:45 +08:00
parent f52fa40326
commit 997f4a2d61
10 changed files with 138 additions and 16 deletions

View File

@ -36,3 +36,6 @@ UPDATE `sys_menu` SET `path` = 'https://gitee.com/xlsea/ruoyi-plus-soybean', `co
-- plus-ui 需要禁用的页面 -- plus-ui 需要禁用的页面
UPDATE `sys_menu` SET `status` = '1' WHERE `menu_id` IN ( '116', '130', '131', '132', '11700', '11701' ); UPDATE `sys_menu` SET `status` = '1' WHERE `menu_id` IN ( '116', '130', '131', '132', '11700', '11701' );
-- 工作流菜单
UPDATE `sys_menu` SET `component` = 'workflow/processDefinition/index', WHERE `menu_id` = 11620;

View File

@ -236,6 +236,7 @@ const local: App.I18n.Schema = {
exception_404: '404', exception_404: '404',
exception_500: '500', exception_500: '500',
'workflow_process-definition': 'Process Definition', 'workflow_process-definition': 'Process Definition',
'workflow_process-definition_design': 'Process Definition Design',
'workflow_process-instance': 'Process Instance', 'workflow_process-instance': 'Process Instance',
workflow_leave: 'Leave Apply' workflow_leave: 'Leave Apply'
}, },

View File

@ -236,6 +236,7 @@ const local: App.I18n.Schema = {
exception_404: '404', exception_404: '404',
exception_500: '500', exception_500: '500',
'workflow_process-definition': '流程定义', 'workflow_process-definition': '流程定义',
'workflow_process-definition_design': '流程设计',
'workflow_process-instance': '流程实例', 'workflow_process-instance': '流程实例',
workflow_leave: '请假申请' workflow_leave: '请假申请'
}, },

View File

@ -45,6 +45,7 @@ export const views: Record<LastLevelRouteKey, RouteComponent | (() => Promise<Ro
tool_gen: () => import("@/views/tool/gen/index.vue"), tool_gen: () => import("@/views/tool/gen/index.vue"),
workflow_category: () => import("@/views/workflow/category/index.vue"), workflow_category: () => import("@/views/workflow/category/index.vue"),
workflow_leave: () => import("@/views/workflow/leave/index.vue"), workflow_leave: () => import("@/views/workflow/leave/index.vue"),
"workflow_process-definition": () => import("@/views/workflow/process-definition/index.vue"), "workflow_process-definition": () => import("@/views/workflow/process-definition/definition/index.vue"),
"workflow_process-definition_design": () => import("@/views/workflow/process-definition/design/index.vue"),
"workflow_process-instance": () => import("@/views/workflow/process-instance/index.vue"), "workflow_process-instance": () => import("@/views/workflow/process-instance/index.vue"),
}; };

View File

@ -361,13 +361,14 @@ export const generatedRoutes: GeneratedRoute[] = [
}, },
{ {
name: 'workflow_process-definition', name: 'workflow_process-definition',
path: '/workflow/process-definition', path: '/workflow/process-definition/definition',
component: 'view.workflow_process-definition', component: 'view.workflow_process-definition',
meta: { meta: {
title: 'workflow_process-definition', title: 'workflow_process-definition',
i18nKey: 'route.workflow_process-definition' i18nKey: 'route.workflow_process-definition'
} }
}, },
{ {
name: 'workflow_process-instance', name: 'workflow_process-instance',
path: '/workflow/process-instance', path: '/workflow/process-instance',

View File

@ -202,7 +202,8 @@ const routeMap: RouteMap = {
"workflow": "/workflow", "workflow": "/workflow",
"workflow_category": "/workflow/category", "workflow_category": "/workflow/category",
"workflow_leave": "/workflow/leave", "workflow_leave": "/workflow/leave",
"workflow_process-definition": "/workflow/process-definition", "workflow_process-definition": "/workflow/process-definition/definition",
"workflow_process-definition_design": "/workflow/process-definition/design",
"workflow_process-instance": "/workflow/process-instance" "workflow_process-instance": "/workflow/process-instance"
}; };

View File

@ -62,3 +62,11 @@ export function fetchPublishDefinition(id: CommonType.IdType) {
method: 'put' method: 'put'
}); });
} }
/** 复制流程定义 */
export function fetchCopyDefinition(id: CommonType.IdType) {
return request<boolean>({
url: `/workflow/definition/copy/${id}`,
method: 'post'
});
}

View File

@ -56,7 +56,8 @@ declare module "@elegant-router/types" {
"workflow": "/workflow"; "workflow": "/workflow";
"workflow_category": "/workflow/category"; "workflow_category": "/workflow/category";
"workflow_leave": "/workflow/leave"; "workflow_leave": "/workflow/leave";
"workflow_process-definition": "/workflow/process-definition"; "workflow_process-definition": "/workflow/process-definition/definition";
"workflow_process-definition_design": "/workflow/process-definition/design";
"workflow_process-instance": "/workflow/process-instance"; "workflow_process-instance": "/workflow/process-instance";
}; };
@ -154,6 +155,7 @@ declare module "@elegant-router/types" {
| "workflow_category" | "workflow_category"
| "workflow_leave" | "workflow_leave"
| "workflow_process-definition" | "workflow_process-definition"
| "workflow_process-definition_design"
| "workflow_process-instance" | "workflow_process-instance"
>; >;

View File

@ -7,6 +7,7 @@ import { workflowPublishStatusRecord } from '@/constants/workflow';
import { import {
fetchActiveDefinition, fetchActiveDefinition,
fetchBatchDeleteDefinition, fetchBatchDeleteDefinition,
fetchCopyDefinition,
fetchGetCategoryTree, fetchGetCategoryTree,
fetchGetDefinitionList, fetchGetDefinitionList,
fetchGetUnPublishDefinitionList, fetchGetUnPublishDefinitionList,
@ -16,11 +17,12 @@ import { useAppStore } from '@/store/modules/app';
import { useAuth } from '@/hooks/business/auth'; import { useAuth } from '@/hooks/business/auth';
import { useDownload } from '@/hooks/business/download'; import { useDownload } from '@/hooks/business/download';
import { useTable, useTableOperate } from '@/hooks/common/table'; import { useTable, useTableOperate } from '@/hooks/common/table';
import { useRouterPush } from '@/hooks/common/router';
import { $t } from '@/locales'; import { $t } from '@/locales';
import ButtonIcon from '@/components/custom/button-icon.vue'; import ButtonIcon from '@/components/custom/button-icon.vue';
import DefinitionOperateDrawer from './modules/definition-operate-drawer.vue'; import DefinitionOperateDrawer from '../modules/definition-operate-drawer.vue';
import DefinitionSearch from './modules/definition-search.vue'; import DefinitionSearch from '../modules/definition-search.vue';
import DefinitionImportModal from './modules/definition-import-modal.vue'; import DefinitionImportModal from '../modules/definition-import-modal.vue';
defineOptions({ defineOptions({
name: 'DefinitionList' name: 'DefinitionList'
@ -34,9 +36,9 @@ interface IsPublishOption {
const appStore = useAppStore(); const appStore = useAppStore();
const { download } = useDownload(); const { download } = useDownload();
const { hasAuth } = useAuth(); const { hasAuth } = useAuth();
const { routerPushByKey } = useRouterPush();
const { bool: importVisible, setTrue: showImportModal } = useBoolean(); const { bool: importVisible, setTrue: showImportModal } = useBoolean();
const isPublish = ref<boolean>(true); const isPublish = ref<boolean>(true);
const isPublishOptions = ref<IsPublishOption[]>([ const isPublishOptions = ref<IsPublishOption[]>([
{ {
@ -196,9 +198,23 @@ const {
onPositiveClick={() => handleDelete(row.id)} onPositiveClick={() => handleDelete(row.id)}
/> />
), ),
design: <ButtonIcon text type="primary" icon="material-symbols:design-services" tooltipContent="流程设计" />, design: (
<ButtonIcon
text
type="primary"
icon="material-symbols:design-services"
tooltipContent="流程设计"
onClick={() => handleDesign(row.id)}
/>
),
preview: ( preview: (
<ButtonIcon text type="primary" icon="material-symbols:visibility-outline" tooltipContent="查看流程" /> <ButtonIcon
text
type="primary"
icon="material-symbols:visibility-outline"
tooltipContent="查看流程"
onClick={() => handlePreview(row.id)}
/>
), ),
publish: ( publish: (
<ButtonIcon <ButtonIcon
@ -206,10 +222,20 @@ const {
type="primary" type="primary"
icon="material-symbols:publish" icon="material-symbols:publish"
tooltipContent="发布流程" tooltipContent="发布流程"
onClick={() => handlePublish(row.id)} popconfirmContent={`确定要发布 ${row.flowName} 吗?`}
onPositiveClick={() => handlePublish(row.id)}
/>
),
copy: (
<ButtonIcon
text
type="primary"
icon="material-symbols:content-copy"
tooltipContent="复制流程"
popconfirmContent={`确定要复制 ${row.flowName} 吗?`}
onPositiveClick={() => handleCopy(row.id)}
/> />
), ),
copy: <ButtonIcon text type="primary" icon="material-symbols:content-copy" tooltipContent="复制流程" />,
export: ( export: (
<ButtonIcon <ButtonIcon
text text
@ -287,6 +313,36 @@ async function handlePublish(id: CommonType.IdType) {
getDataByPage(); getDataByPage();
} }
async function handleCopy(id: CommonType.IdType) {
const { error } = await fetchCopyDefinition(id);
if (error) return;
window.$message?.success('复制成功');
//
if (isPublish.value) {
isPublish.value = false;
} else {
getDataByPage();
}
}
function handleDesign(id: CommonType.IdType) {
routerPushByKey('workflow_process-definition_design', {
query: {
definitionId: id.toString(),
disabled: 'false'
}
});
}
function handlePreview(id: CommonType.IdType) {
routerPushByKey('workflow_process-definition_design', {
query: {
definitionId: id.toString(),
disabled: 'true'
}
});
}
function handleExport(row: TableDataWithIndex<Api.Workflow.Definition>) { function handleExport(row: TableDataWithIndex<Api.Workflow.Definition>) {
download(`/workflow/definition/exportDef/${row.id}`, {}, `${row.flowCode}.json`); download(`/workflow/definition/exportDef/${row.id}`, {}, `${row.flowCode}.json`);
} }
@ -325,7 +381,7 @@ const selectable = computed(() => {
</script> </script>
<template> <template>
<TableSiderLayout :sider-title="$t('page.system.dept.title')"> <TableSiderLayout sider-title="流程分类">
<template #header-extra> <template #header-extra>
<NButton size="small" text class="h-18px" @click.stop="() => handleResetTreeData()"> <NButton size="small" text class="h-18px" @click.stop="() => handleResetTreeData()">
<template #icon> <template #icon>
@ -352,7 +408,7 @@ const selectable = computed(() => {
@update:selected-keys="handleClickTree" @update:selected-keys="handleClickTree"
> >
<template #empty> <template #empty>
<NEmpty :description="$t('page.system.dept.empty')" class="h-full min-h-200px justify-center" /> <NEmpty description="暂无流程分类" class="h-full min-h-200px justify-center" />
</template> </template>
</NTree> </NTree>
</NSpin> </NSpin>

View File

@ -0,0 +1,48 @@
<script setup lang="ts">
import { useRoute } from 'vue-router';
import { useEventListener } from '@vueuse/core';
import { stringify } from 'qs';
import { getToken } from '@/store/modules/auth/shared';
import { getTabIdByRoute } from '@/store/modules/tab/shared';
import { useTabStore } from '@/store/modules/tab';
import { useRouterPush } from '@/hooks/common/router';
import { getServiceBaseURL } from '@/utils/service';
const isHttpProxy = import.meta.env.DEV && import.meta.env.VITE_HTTP_PROXY === 'Y';
const { baseURL } = getServiceBaseURL(import.meta.env, isHttpProxy);
const { removeTab } = useTabStore();
const { routerPushByKey } = useRouterPush();
const route = useRoute();
const disabled = route.query.disabled === 'true';
const urlParams = {
Authorization: `Bearer ${getToken()}`,
id: route.query.definitionId,
clientid: import.meta.env.VITE_APP_CLIENT_ID,
disabled
};
const iframeUrl = `${baseURL}/warm-flow-ui/index.html?${stringify(urlParams)}`;
function messageHandler(event: MessageEvent) {
switch (event.data.method) {
case 'close': {
const tabId = getTabIdByRoute(route);
removeTab(tabId);
routerPushByKey('workflow_process-definition');
break;
}
default: {
break;
}
}
}
// iframe监听组件内设计器保存事件
useEventListener('message', messageHandler);
</script>
<template>
<iframe :src="iframeUrl" class="size-full"></iframe>
</template>