feat(sj_1.0.0): 接入工作流

This commit is contained in:
opensnail 2024-04-23 14:31:09 +08:00
parent 8ac203b885
commit 6adbedd185
17 changed files with 401 additions and 32 deletions

View File

@ -30,7 +30,7 @@ const onChange = (value: string) => {
<template>
<NDropdown v-if="appStore.isMobile" :value="namespaceId" :options="dropOptions" trigger="hover" @select="onChange">
<div>
<ButtonIcon :tooltip-content="$t('icon.namepase')" tooltip-placement="left">
<ButtonIcon :tooltip-content="$t('icon.namespace')" tooltip-placement="left">
<SvgIcon icon="oui:app-spaces" />
</ButtonIcon>
</div>

View File

@ -173,7 +173,7 @@ const local: App.I18n.Schema = {
'function_toggle-auth': 'Toggle Auth',
'function_super-page': 'Super Admin Visible',
pods: 'Online Machine',
namepase: 'Namepase',
namespace: 'namespace',
manage: 'System Manage',
manage_user: 'User Manage',
notify: 'notify',
@ -183,6 +183,8 @@ const local: App.I18n.Schema = {
retry_task: 'Retry task',
retry_scene: 'Retry scene',
workflow: 'Workflow',
workflow_task: 'Workflow Task',
workflow_batch: 'Workflow Batch',
job: 'Schedule Task Management',
job_task: 'Schedule Task List',
'manage_user-detail': 'User Detail',
@ -701,6 +703,22 @@ const local: App.I18n.Schema = {
round: '轮询'
}
},
workflowBatch: {
title: 'Workflow Batch List',
workflowName: 'Workflow name',
groupName: 'Group name',
executionAt: 'Execution time',
taskBatchStatus: 'State',
operationReason: 'Reason for operation',
createDt: 'Creation time',
form: {
workflowName: 'Please enter Workflow name',
taskBatchStatus: 'Please enter State',
groupName: 'Please enter Group name'
},
addWorkflowBatch: 'Add Workflow batch',
editWorkflowBatch: 'Add Workflow batch'
},
workflow: {
title: 'Workflow List',
workflowName: 'Workflow name',
@ -810,7 +828,7 @@ const local: App.I18n.Schema = {
expand: 'Expand Menu',
pin: 'Pin',
unpin: 'Unpin',
namepase: 'Switch Namepase'
namespace: 'Switch namespace'
}
};

View File

@ -173,7 +173,7 @@ const local: App.I18n.Schema = {
'function_toggle-auth': '切换权限',
'function_super-page': '超级管理员可见',
pods: '在线机器',
namepase: '命名空间',
namespace: '命名空间',
group: '组管理',
manage: '系统管理',
manage_user: '用户管理',
@ -184,8 +184,10 @@ const local: App.I18n.Schema = {
retry_task: '重试任务',
retry_scene: '重试场景',
workflow: '工作流',
job: '定时任务管理',
job_task: '定时任务信息',
workflow_task: '任务管理',
workflow_batch: '执行批次',
job: '定时任务',
job_task: '任务管理',
'manage_user-detail': '用户详情',
manage_role: '角色管理',
manage_menu: '菜单管理',
@ -715,6 +717,22 @@ const local: App.I18n.Schema = {
addWorkflow: '新增工作流',
editWorkflow: '编辑工作流'
},
workflowBatch: {
title: '工作流批次列表',
workflowName: '工作流名称',
groupName: '组名称',
executionAt: '执行时间',
taskBatchStatus: '状态',
operationReason: '操作原因',
createDt: '创建时间',
form: {
workflowName: '请输入工作流名称',
taskBatchStatus: '请输入状态',
groupName: '请输入组名称'
},
addWorkflowBatch: '新增工作流批次',
editWorkflowBatch: '编辑工作流批次'
},
jobTask: {
title: '定时任务列表',
groupName: '组名称',
@ -806,7 +824,7 @@ const local: App.I18n.Schema = {
expand: '展开菜单',
pin: '固定',
unpin: '取消固定',
namepase: '切换空间'
namespace: '切换空间'
}
};

View File

@ -37,12 +37,13 @@ export const views: Record<LastLevelRouteKey, RouteComponent | (() => Promise<Ro
manage_user: () => import("@/views/manage/user/index.vue"),
"multi-menu_first_child": () => import("@/views/multi-menu/first_child/index.vue"),
"multi-menu_second_child_home": () => import("@/views/multi-menu/second_child_home/index.vue"),
namepase: () => import("@/views/namepase/index.vue"),
namespace: () => import("@/views/namespace/index.vue"),
notify_recipient: () => import("@/views/notify/recipient/index.vue"),
notify_scene: () => import("@/views/notify/scene/index.vue"),
pods: () => import("@/views/pods/index.vue"),
retry_scene: () => import("@/views/retry/scene/index.vue"),
retry_task: () => import("@/views/retry/task/index.vue"),
"user-center": () => import("@/views/user-center/index.vue"),
workflow: () => import("@/views/workflow/index.vue"),
workflow_batch: () => import("@/views/workflow/batch/index.vue"),
workflow_task: () => import("@/views/workflow/task/index.vue"),
};

View File

@ -45,7 +45,9 @@ export const generatedRoutes: GeneratedRoute[] = [
component: 'layout.base$view.about',
meta: {
title: 'about',
i18nKey: 'route.about'
i18nKey: 'route.about',
order: 1000,
icon: 'material-symbols:help-outline-rounded'
}
},
{
@ -173,7 +175,9 @@ export const generatedRoutes: GeneratedRoute[] = [
component: 'layout.base$view.group',
meta: {
title: 'group',
i18nKey: 'route.group'
i18nKey: 'route.group',
order: 30,
icon: 'material-symbols:group-work-outline'
}
},
{
@ -193,7 +197,9 @@ export const generatedRoutes: GeneratedRoute[] = [
component: 'layout.base',
meta: {
title: 'job',
i18nKey: 'route.job'
i18nKey: 'route.job',
order: 50,
icon:'eos-icons:cronjob'
},
children: [
{
@ -348,12 +354,12 @@ export const generatedRoutes: GeneratedRoute[] = [
]
},
{
name: 'namepase',
path: '/namepase',
component: 'layout.base$view.namepase',
name: 'namespace',
path: '/namespace',
component: 'layout.base$view.namespace',
meta: {
title: 'namepase',
i18nKey: 'route.namepase',
title: 'namespace',
i18nKey: 'route.namespace',
icon: 'oui:app-spaces',
order: 20
}
@ -365,7 +371,8 @@ export const generatedRoutes: GeneratedRoute[] = [
meta: {
title: 'notify',
i18nKey: 'route.notify',
order: 100
order: 100,
icon: 'material-symbols:notifications-active-outline-rounded'
},
children: [
{
@ -406,7 +413,8 @@ export const generatedRoutes: GeneratedRoute[] = [
meta: {
title: 'retry',
i18nKey: 'route.retry',
order: 50
order: 70,
icon: 'carbon:retry-failed'
},
children: [
{
@ -442,10 +450,32 @@ export const generatedRoutes: GeneratedRoute[] = [
{
name: 'workflow',
path: '/workflow',
component: 'layout.base$view.workflow',
component: 'layout.base',
meta: {
title: 'workflow',
i18nKey: 'route.workflow'
}
i18nKey: 'route.workflow',
order: 60,
icon: 'lucide:workflow'
},
children: [
{
name: 'workflow_batch',
path: '/workflow/batch',
component: 'view.workflow_batch',
meta: {
title: 'workflow_batch',
i18nKey: 'route.workflow_batch'
}
},
{
name: 'workflow_task',
path: '/workflow/task',
component: 'view.workflow_task',
meta: {
title: 'workflow_task',
i18nKey: 'route.workflow_task'
}
}
]
}
];

View File

@ -114,14 +114,14 @@ function transformElegantRouteToVueRoute(
}
}
// add redirect to child
if (children?.length && !vueRoute.redirect) {
vueRoute.redirect = {
name: children[0].name
};
}
if (children?.length) {
const childRoutes = children.flatMap(child => transformElegantRouteToVueRoute(child, layouts, views));
@ -177,7 +177,7 @@ const routeMap: RouteMap = {
"multi-menu_second": "/multi-menu/second",
"multi-menu_second_child": "/multi-menu/second/child",
"multi-menu_second_child_home": "/multi-menu/second/child/home",
"namepase": "/namepase",
"namespace": "/namespace",
"notify": "/notify",
"notify_recipient": "/notify/recipient",
"notify_scene": "/notify/scene",
@ -186,7 +186,9 @@ const routeMap: RouteMap = {
"retry_scene": "/retry/scene",
"retry_task": "/retry/task",
"user-center": "/user-center",
"workflow": "/workflow"
"workflow": "/workflow",
"workflow_batch": "/workflow/batch",
"workflow_task": "/workflow/task"
};
/**

View File

@ -1,6 +1,6 @@
import { request } from '../request';
/** get namespace list */
/** get workflow page list */
export function fetchGetWorkflowPageList(params?: Api.Workflow.WorkflowSearchParams) {
return request<Api.Workflow.WorkflowList>({
url: '/workflow/page/list',
@ -8,3 +8,21 @@ export function fetchGetWorkflowPageList(params?: Api.Workflow.WorkflowSearchPar
params
});
}
/** get namespace list */
export function fetchGetWorkflowNameList(params?: Api.WorkflowBatch.WorkflowBatchSearchParams) {
return request<Api.Workflow.Workflow[]>({
url: '/workflow/workflow-name/list',
method: 'get',
params
});
}
/** get workflow batch list */
export function fetchGetWorkflowBatchList(params?: Api.WorkflowBatch.WorkflowBatchSearchParams) {
return request<Api.WorkflowBatch.WorkflowBatchList>({
url: '/workflow/batch/page/list',
method: 'get',
params
});
}

35
src/typings/api.d.ts vendored
View File

@ -874,4 +874,39 @@ declare namespace Api {
/** JobTask list */
type JobTaskList = Common.PaginatingQueryRecord<JobTask>;
}
/**
* namespace WorkflowBatch
*
* backend api module: "workflowBatch"
*/
namespace WorkflowBatch {
type CommonSearchParams = Pick<Common.PaginatingCommonParams, 'page' | 'size'>;
/** workflowBatch */
type WorkflowBatch = Common.CommonRecord<{
/** 工作流名称 */
workflowName: string;
/** 工作流ID */
workflowId?: number;
/** 组名称 */
groupName: string;
/** 执行时间 */
executionAt: string;
/** 状态 */
taskBatchStatus: string;
/** 操作原因 */
operationReason: string;
/** 创建时间 */
createDt: string;
}>;
/** workflowBatch search params */
type WorkflowBatchSearchParams = CommonType.RecordNullable<
Pick<Api.WorkflowBatch.WorkflowBatch, 'workflowId' | 'groupName' | 'taskBatchStatus'> & CommonSearchParams
>;
/** workflowBatch list */
type WorkflowBatchList = Common.PaginatingQueryRecord<WorkflowBatch>;
}
}

18
src/typings/app.d.ts vendored
View File

@ -884,6 +884,22 @@ declare namespace App {
addWorkflow: string;
editWorkflow: string;
};
workflowBatch: {
title: string;
workflowName: string;
groupName: string;
executionAt: string;
taskBatchStatus: string;
operationReason: string;
createDt: string;
form: {
workflowName: string;
taskBatchStatus: string;
groupName: string;
};
addWorkflowBatch: string;
editWorkflowBatch: string;
};
jobTask: {
title: string;
groupName: string;
@ -951,7 +967,7 @@ declare namespace App {
expand: string;
pin: string;
unpin: string;
namepase: string;
namespace: string;
};
};

View File

@ -51,7 +51,7 @@ declare module "@elegant-router/types" {
"multi-menu_second": "/multi-menu/second";
"multi-menu_second_child": "/multi-menu/second/child";
"multi-menu_second_child_home": "/multi-menu/second/child/home";
"namepase": "/namepase";
"namespace": "/namespace";
"notify": "/notify";
"notify_recipient": "/notify/recipient";
"notify_scene": "/notify/scene";
@ -61,6 +61,8 @@ declare module "@elegant-router/types" {
"retry_task": "/retry/task";
"user-center": "/user-center";
"workflow": "/workflow";
"workflow_batch": "/workflow/batch";
"workflow_task": "/workflow/task";
};
/**
@ -107,7 +109,7 @@ declare module "@elegant-router/types" {
| "login"
| "manage"
| "multi-menu"
| "namepase"
| "namespace"
| "notify"
| "pods"
| "retry"
@ -152,14 +154,15 @@ declare module "@elegant-router/types" {
| "manage_user"
| "multi-menu_first_child"
| "multi-menu_second_child_home"
| "namepase"
| "namespace"
| "notify_recipient"
| "notify_scene"
| "pods"
| "retry_scene"
| "retry_task"
| "user-center"
| "workflow"
| "workflow_batch"
| "workflow_task"
>;
/**

View File

@ -0,0 +1,169 @@
<script setup lang="tsx">
import { NButton, NPopconfirm } from 'naive-ui';
import { fetchGetWorkflowBatchList } from '@/service/api';
import { $t } from '@/locales';
import { useAppStore } from '@/store/modules/app';
import { useTable, useTableOperate } from '@/hooks/common/table';
import WorkflowBatchSearch from './modules/workflow-batch-search.vue';
const appStore = useAppStore();
const { columns, columnChecks, data, getData, loading, mobilePagination, searchParams, resetSearchParams } = useTable({
apiFn: fetchGetWorkflowBatchList,
apiParams: {
page: 1,
size: 10,
// if you want to use the searchParams in Form, you need to define the following properties, and the value is null
// the value can not be undefined, otherwise the property in Form will not be reactive
workflowId: null,
groupName: null,
taskBatchStatus: null
},
columns: () => [
{
type: 'selection',
align: 'center',
width: 48
},
{
key: 'index',
title: $t('common.index'),
align: 'center',
width: 64
},
{
key: 'workflowName',
title: $t('page.workflowBatch.workflowName'),
align: 'left',
minWidth: 120
},
{
key: 'groupName',
title: $t('page.workflowBatch.groupName'),
align: 'left',
minWidth: 120
},
{
key: 'executionAt',
title: $t('page.workflowBatch.executionAt'),
align: 'left',
minWidth: 120
},
{
key: 'taskBatchStatus',
title: $t('page.workflowBatch.taskBatchStatus'),
align: 'left',
minWidth: 120
},
{
key: 'operationReason',
title: $t('page.workflowBatch.operationReason'),
align: 'left',
minWidth: 120
},
{
key: 'createDt',
title: $t('page.workflowBatch.createDt'),
align: 'left',
minWidth: 120
},
{
key: 'operate',
title: $t('common.operate'),
align: 'center',
width: 130,
render: row => (
<div class="flex-center gap-8px">
<NButton type="primary" ghost size="small" onClick={() => edit(row.id!)}>
{$t('common.edit')}
</NButton>
<NPopconfirm onPositiveClick={() => handleDelete(row.id!)}>
{{
default: () => $t('common.confirmDelete'),
trigger: () => (
<NButton type="error" ghost size="small">
{$t('common.delete')}
</NButton>
)
}}
</NPopconfirm>
</div>
)
}
]
});
const {
drawerVisible,
operateType,
editingData,
handleAdd,
handleEdit,
checkedRowKeys,
onBatchDeleted,
onDeleted
// closeDrawer
} = useTableOperate(data, getData);
async function handleBatchDelete() {
// request
console.log(checkedRowKeys.value);
onBatchDeleted();
}
function handleDelete(id: string) {
// request
console.log(id);
onDeleted();
}
function edit(id: string) {
handleEdit(id);
}
</script>
<template>
<div class="min-h-500px flex-col-stretch gap-16px overflow-hidden lt-sm:overflow-auto">
<WorkflowBatchSearch v-model:model="searchParams" @reset="resetSearchParams" @search="getData" />
<NCard
:title="$t('page.workflowBatch.title')"
:bordered="false"
size="small"
class="sm:flex-1-hidden card-wrapper"
header-class="view-card-header"
>
<template #header-extra>
<TableHeaderOperation
v-model:columns="columnChecks"
:disabled-delete="checkedRowKeys.length === 0"
:loading="loading"
@add="handleAdd"
@delete="handleBatchDelete"
@refresh="getData"
/>
</template>
<NDataTable
v-model:checked-row-keys="checkedRowKeys"
:columns="columns"
:data="data"
:flex-height="!appStore.isMobile"
:scroll-x="962"
:loading="loading"
remote
:row-key="row => row.id"
:pagination="mobilePagination"
class="sm:h-full"
/>
<WorkflowBatchOperateDrawer
v-model:visible="drawerVisible"
:operate-type="operateType"
:row-data="editingData"
@submitted="getData"
/>
</NCard>
</div>
</template>
<style scoped></style>

View File

@ -0,0 +1,59 @@
<script setup lang="ts">
import { ref } from 'vue';
import { $t } from '@/locales';
import SelectGroup from '@/components/common/select-group.vue';
import { fetchGetWorkflowNameList } from '@/service/api';
defineOptions({
name: 'WorkflowBatchSearch'
});
interface Emits {
(e: 'reset'): void;
(e: 'search'): void;
}
const emit = defineEmits<Emits>();
/** 组列表 */
const workflowList = ref<Api.Workflow.Workflow[]>([]);
const model = defineModel<Api.WorkflowBatch.WorkflowBatchSearchParams>('model', { required: true });
function reset() {
emit('reset');
}
function search() {
emit('search');
}
async function groupNameUpdate(groupName: string) {
const res = await fetchGetWorkflowNameList({ groupName });
workflowList.value = res.data as Api.Workflow.Workflow[];
}
</script>
<template>
<SearchForm :model="model" @search="search" @reset="reset">
<NFormItemGi span="24 s:12 m:6" :label="$t('page.workflowBatch.groupName')" path="userName" class="pr-24px">
<SelectGroup v-model:value="model.groupName" @update:value="groupNameUpdate" />
</NFormItemGi>
<NFormItemGi span="24 s:12 m:6" :label="$t('page.workflowBatch.workflowName')" path="userName" class="pr-24px">
<NSelect
v-model:value="model.workflowId"
:placeholder="$t('page.workflowBatch.form.workflowName')"
value-field="id"
label-field="workflowName"
:options="workflowList"
clearable
filterable
/>
</NFormItemGi>
<NFormItemGi span="24 s:12 m:6" :label="$t('page.workflowBatch.taskBatchStatus')" path="userName" class="pr-24px">
<NInput v-model:value="model.taskBatchStatus" :placeholder="$t('page.workflowBatch.form.taskBatchStatus')" />
</NFormItemGi>
</SearchForm>
</template>
<style scoped></style>