feat-wip(projects): 对接流程定义功能ing

This commit is contained in:
AN
2025-06-11 23:32:59 +08:00
parent 4f6c14f358
commit 7b2c857f6d
4 changed files with 170 additions and 79 deletions

View File

@ -54,3 +54,11 @@ export function fetchActiveDefinition(id: CommonType.IdType, active: boolean) {
} }
}); });
} }
/** 发布流程定义 */
export function fetchPublishDefinition(id: CommonType.IdType) {
return request<boolean>({
url: `/workflow/definition/publish/${id}`,
method: 'put'
});
}

View File

@ -90,6 +90,8 @@ declare namespace Api {
flowName: string; flowName: string;
/** 流程类别 */ /** 流程类别 */
category: string; category: string;
/** 流程分类名称 */
categoryName: string;
/** 流程版本 */ /** 流程版本 */
version: string; version: string;
/** 是否发布0未发布 1已发布 9失效 */ /** 是否发布0未发布 1已发布 9失效 */

View File

@ -1,13 +1,16 @@
<script setup lang="tsx"> <script setup lang="tsx">
import { computed, ref } from 'vue'; import { computed, ref, watch } from 'vue';
import { NDivider, NSwitch, NTag } from 'naive-ui'; import { NDivider, NSwitch, NTag } from 'naive-ui';
import { useBoolean, useLoading } from '@sa/hooks'; import { useBoolean, useLoading } from '@sa/hooks';
import { type TableDataWithIndex } from '@sa/hooks';
import { workflowPublishStatusRecord } from '@/constants/workflow'; import { workflowPublishStatusRecord } from '@/constants/workflow';
import { import {
fetchActiveDefinition, fetchActiveDefinition,
fetchBatchDeleteDefinition, fetchBatchDeleteDefinition,
fetchGetCategoryTree, fetchGetCategoryTree,
fetchGetUnPublishDefinitionList fetchGetDefinitionList,
fetchGetUnPublishDefinitionList,
fetchPublishDefinition
} from '@/service/api/workflow'; } from '@/service/api/workflow';
import { useAppStore } from '@/store/modules/app'; import { useAppStore } from '@/store/modules/app';
import { useAuth } from '@/hooks/business/auth'; import { useAuth } from '@/hooks/business/auth';
@ -23,12 +26,28 @@ defineOptions({
name: 'DefinitionList' name: 'DefinitionList'
}); });
interface IsPublishOption {
label: string;
value: boolean;
}
const appStore = useAppStore(); const appStore = useAppStore();
const { download } = useDownload(); const { download } = useDownload();
const { hasAuth } = useAuth(); const { hasAuth } = useAuth();
const { bool: importVisible, setTrue: showImportModal } = useBoolean(); const { bool: importVisible, setTrue: showImportModal } = useBoolean();
const isPublish = ref<boolean>(true);
const isPublishOptions = ref<IsPublishOption[]>([
{
label: '已发布',
value: true
},
{
label: '未发布',
value: false
}
]);
const { const {
columns, columns,
columnChecks, columnChecks,
@ -38,9 +57,10 @@ const {
loading, loading,
mobilePagination, mobilePagination,
searchParams, searchParams,
resetSearchParams resetSearchParams,
updateApiFn
} = useTable({ } = useTable({
apiFn: fetchGetUnPublishDefinitionList, apiFn: fetchGetDefinitionList,
apiParams: { apiParams: {
pageNum: 1, pageNum: 1,
pageSize: 10, pageSize: 10,
@ -70,7 +90,7 @@ const {
minWidth: 120 minWidth: 120
}, },
{ {
key: 'category', key: 'categoryName',
title: '流程分类', title: '流程分类',
align: 'center', align: 'center',
minWidth: 120 minWidth: 120
@ -79,7 +99,10 @@ const {
key: 'version', key: 'version',
title: '版本号', title: '版本号',
align: 'center', align: 'center',
minWidth: 120 minWidth: 120,
render(row) {
return <NTag type="info">v{row.version}.0</NTag>;
}
}, },
{ {
key: 'activityStatus', key: 'activityStatus',
@ -136,13 +159,11 @@ const {
if (row.isPublish === null) { if (row.isPublish === null) {
return null; return null;
} }
const tagMap: Record<Api.Workflow.WorkflowPublishStatus, NaiveUI.ThemeColor> = { const tagMap: Record<Api.Workflow.WorkflowPublishStatus, NaiveUI.ThemeColor> = {
0: 'warning', 0: 'warning',
1: 'success', 1: 'success',
9: 'error' 9: 'error'
}; };
return <NTag type={tagMap[row.isPublish]}>{workflowPublishStatusRecord[row.isPublish]}</NTag>; return <NTag type={tagMap[row.isPublish]}>{workflowPublishStatusRecord[row.isPublish]}</NTag>;
} }
}, },
@ -150,51 +171,76 @@ const {
key: 'operate', key: 'operate',
title: $t('common.operate'), title: $t('common.operate'),
align: 'center', align: 'center',
width: 130, width: 150,
fixed: 'right',
render: row => { render: row => {
const divider = () => { const Divider = <NDivider vertical />;
if (!hasAuth('workflow:definition:edit') || !hasAuth('workflow:definition:remove')) {
return null;
}
return <NDivider vertical />;
};
const editBtn = () => { const buttons = {
if (!hasAuth('workflow:definition:edit')) { edit: (
return null;
}
return (
<ButtonIcon <ButtonIcon
text text
type="primary" type="primary"
icon="material-symbols:drive-file-rename-outline-outline" icon="material-symbols:drive-file-rename-outline-outline"
tooltipContent={$t('common.edit')} tooltipContent={$t('common.edit')}
onClick={() => edit(row.id!)} onClick={() => edit(row.id)}
/> />
); ),
}; delete: (
const deleteBtn = () => {
if (!hasAuth('workflow:definition:remove')) {
return null;
}
return (
<ButtonIcon <ButtonIcon
text text
type="error" type="error"
icon="material-symbols:delete-outline" icon="material-symbols:delete-outline"
tooltipContent={$t('common.delete')} tooltipContent={$t('common.delete')}
popconfirmContent={$t('common.confirmDelete')} popconfirmContent={$t('common.confirmDelete')}
onPositiveClick={() => handleDelete(row.id!)} onPositiveClick={() => handleDelete(row.id)}
/> />
); ),
design: <ButtonIcon text type="primary" icon="material-symbols:design-services" tooltipContent="流程设计" />,
preview: (
<ButtonIcon text type="primary" icon="material-symbols:visibility-outline" tooltipContent="查看流程" />
),
publish: (
<ButtonIcon
text
type="primary"
icon="material-symbols:publish"
tooltipContent="发布流程"
onClick={() => handlePublish(row.id)}
/>
),
copy: <ButtonIcon text type="primary" icon="material-symbols:content-copy" tooltipContent="复制流程" />,
export: (
<ButtonIcon
text
type="primary"
icon="material-symbols:file-export"
tooltipContent="导出流程"
onClick={() => handleExport(row)}
/>
)
}; };
return ( return (
<div class="flex-center gap-8px"> <div class="flex-col">
{editBtn()} <div class="h-[24px] flex-center gap-4px">
{divider()} {buttons.edit}
{deleteBtn()} {Divider}
{buttons.delete}
{Divider}
{buttons.copy}
</div>
<div class="h-[24px] flex-center gap-4px">
{buttons.export}
{Divider}
{isPublish.value ? buttons.preview : buttons.design}
{!isPublish.value && (
<>
{Divider}
{buttons.publish}
</>
)}
</div>
</div> </div>
); );
} }
@ -202,6 +248,13 @@ const {
] ]
}); });
// 监听运行状态变化
watch(isPublish, async () => {
const newApiFn = isPublish.value ? fetchGetDefinitionList : fetchGetUnPublishDefinitionList;
updateApiFn(newApiFn);
await getDataByPage();
});
const { drawerVisible, operateType, editingData, handleAdd, handleEdit, checkedRowKeys, onBatchDeleted, onDeleted } = const { drawerVisible, operateType, editingData, handleAdd, handleEdit, checkedRowKeys, onBatchDeleted, onDeleted } =
useTableOperate(data, getData); useTableOperate(data, getData);
@ -223,14 +276,20 @@ function edit(id: CommonType.IdType) {
handleEdit('id', id); handleEdit('id', id);
} }
function handleExport() {
download('/workflow/definition/export', searchParams, `流程定义_${new Date().getTime()}.xlsx`);
}
function handleDeploy() { function handleDeploy() {
showImportModal(); showImportModal();
} }
async function handlePublish(id: CommonType.IdType) {
const { error } = await fetchPublishDefinition(id);
if (error) return;
window.$message?.success('发布成功');
getDataByPage();
}
function handleExport(row: TableDataWithIndex<Api.Workflow.Definition>) {
download(`/workflow/definition/exportDef/${row.id}`, {}, `${row.flowCode}.json`);
}
const { loading: categoryLoading, startLoading: startCategoryLoading, endLoading: endCategoryLoading } = useLoading(); const { loading: categoryLoading, startLoading: startCategoryLoading, endLoading: endCategoryLoading } = useLoading();
const categoryPattern = ref<string>(); const categoryPattern = ref<string>();
const categoryData = ref<Api.Common.CommonTreeRecord>([]); const categoryData = ref<Api.Common.CommonTreeRecord>([]);
@ -300,18 +359,27 @@ const selectable = computed(() => {
</template> </template>
<div class="h-full flex-col-stretch gap-12px overflow-hidden lt-sm:overflow-auto"> <div class="h-full flex-col-stretch gap-12px overflow-hidden lt-sm:overflow-auto">
<DefinitionSearch v-model:model="searchParams" @reset="resetSearchParams" @search="getDataByPage" /> <DefinitionSearch v-model:model="searchParams" @reset="resetSearchParams" @search="getDataByPage" />
<NCard title="流程定义列表" :bordered="false" size="small" class="sm:flex-1-hidden card-wrapper"> <NCard :bordered="false" size="small" class="sm:flex-1-hidden card-wrapper">
<template #header>
<NSpace>
<NRadioGroup v-model:value="isPublish" on-up size="small">
<NRadioButton
v-for="(status, index) in isPublishOptions"
:key="index"
:value="status.value"
:label="status.label"
/>
</NRadioGroup>
</NSpace>
</template>
<template #header-extra> <template #header-extra>
<TableHeaderOperation <TableHeaderOperation
v-model:columns="columnChecks" v-model:columns="columnChecks"
:disabled-delete="checkedRowKeys.length === 0" :disabled-delete="checkedRowKeys.length === 0"
:loading="loading" :loading="loading"
:show-add="hasAuth('workflow:definition:add')"
:show-delete="hasAuth('workflow:definition:remove')" :show-delete="hasAuth('workflow:definition:remove')"
:show-export="hasAuth('workflow:definition:export')"
@add="handleAdd" @add="handleAdd"
@delete="handleBatchDelete" @delete="handleBatchDelete"
@export="handleExport"
@refresh="getData" @refresh="getData"
> >
<template #prefix> <template #prefix>

View File

@ -151,13 +151,12 @@ const operateColumns = ref<NaiveUI.TableColumn<Api.Workflow.ProcessInstance>[]>(
fixed: 'right', fixed: 'right',
width: 155, width: 155,
render: row => { render: row => {
const showAll = runningStatus.value;
const id = row.id; const id = row.id;
return ( const showAll = runningStatus.value;
<div class="flex-center gap-1px"> const Divider = <NDivider vertical />;
{showAll && [
const cancelBtn = (
<ButtonIcon <ButtonIcon
key="cancel"
text text
type="error" type="error"
showPopconfirmIcon={false} showPopconfirmIcon={false}
@ -167,29 +166,43 @@ const operateColumns = ref<NaiveUI.TableColumn<Api.Workflow.ProcessInstance>[]>(
<NInput v-model:value={cancelModel.comment} size="large" type="textarea" placeholder="请输入作废原因" /> <NInput v-model:value={cancelModel.comment} size="large" type="textarea" placeholder="请输入作废原因" />
} }
onPositiveClick={() => handleCancel(id)} onPositiveClick={() => handleCancel(id)}
/>, />
<NDivider key="div1" vertical />, );
const deleteBtn = (
<ButtonIcon <ButtonIcon
key="delete"
text text
type="error" type="error"
icon="material-symbols:delete-outline" icon="material-symbols:delete-outline"
tooltipContent={$t('common.delete')} tooltipContent={$t('common.delete')}
popconfirmContent={$t('common.confirmDelete')} popconfirmContent={$t('common.confirmDelete')}
onPositiveClick={() => handleDelete(id)} onPositiveClick={() => handleDelete(id)}
/>, />
<NDivider key="div2" vertical /> );
]}
const previewBtn = (
<ButtonIcon text type="info" icon="material-symbols:visibility-outline" tooltipContent="流程预览" /> <ButtonIcon text type="info" icon="material-symbols:visibility-outline" tooltipContent="流程预览" />
<NDivider vertical /> );
const variableBtn = (
<ButtonIcon <ButtonIcon
key=""
text text
type="info" type="info"
icon="material-symbols:variable-insert" icon="material-symbols:variable-insert"
tooltipContent="流程变量" tooltipContent="流程变量"
onClick={() => handleShowVariable(id)} onClick={() => handleShowVariable(id)}
/> />
);
return (
<div class="flex-center gap-1px">
{showAll && cancelBtn}
{showAll && Divider}
{showAll && deleteBtn}
{showAll && Divider}
{previewBtn}
{Divider}
{variableBtn}
</div> </div>
); );
} }