feat(sj_1.0.0): 完成组列表查询组件化和工作流列表

This commit is contained in:
opensnail 2024-04-22 17:12:45 +08:00
parent 6928cf0041
commit 373bf55150
14 changed files with 387 additions and 1 deletions

View File

@ -0,0 +1,49 @@
<script setup lang="ts">
import { onMounted, ref, watch } from 'vue';
import { $t } from '@/locales';
import { translateOptions2 } from '@/utils/common';
import { fetchGetAllGroupNameList } from '@/service/api';
const emit = defineEmits<Emits>();
interface Emits {
(e: 'update:value', value: string): void;
}
/** 组列表 */
const groupNameList = ref<string[]>([]);
/** 组列表 */
const groupNameRef = ref<string>('');
async function getGroupNameList() {
const res = await fetchGetAllGroupNameList();
groupNameList.value = res.data as string[];
}
// function handleGroupNameUpdate(groupName: string) {
// emit('update:value', groupName);
// }
onMounted(() => {
getGroupNameList();
});
watch(
() => groupNameRef.value,
() => {
emit('update:value', groupNameRef.value);
}
);
</script>
<template>
<NSelect
v-model:value="groupNameRef"
:placeholder="$t('page.retryTask.form.groupName')"
:options="translateOptions2(groupNameList)"
clearable
filterable
/>
</template>
<style scoped></style>

View File

@ -182,6 +182,7 @@ const local: App.I18n.Schema = {
retry: 'Retry task', retry: 'Retry task',
retry_task: 'Retry task', retry_task: 'Retry task',
retry_scene: 'Retry scene', retry_scene: 'Retry scene',
workflow: 'Workflow',
'manage_user-detail': 'User Detail', 'manage_user-detail': 'User Detail',
manage_role: 'Role Manage', manage_role: 'Role Manage',
manage_menu: 'Menu Manage', manage_menu: 'Menu Manage',
@ -697,6 +698,24 @@ const local: App.I18n.Schema = {
lru: 'LRU', lru: 'LRU',
round: '轮询' round: '轮询'
} }
},
workflow: {
title: 'Workflow List',
workflowName: 'Workflow name',
groupName: 'Group name',
nextTriggerAt: 'Trigger time',
workflowStatus: 'State',
triggerType: 'Trigger type',
triggerInterval: 'Interval duration',
executorTimeout: 'Overtime time',
updateDt: 'Update time',
form: {
workflowName: 'Please enter Workflow name',
groupName: 'Please enter Group name',
workflowStatus: 'Please enter State'
},
addWorkflow: 'Add Workflow',
editWorkflow: 'Add Workflow'
} }
}, },
form: { form: {

View File

@ -183,6 +183,7 @@ const local: App.I18n.Schema = {
retry: '重试任务', retry: '重试任务',
retry_task: '重试任务', retry_task: '重试任务',
retry_scene: '重试场景', retry_scene: '重试场景',
workflow: '工作流',
'manage_user-detail': '用户详情', 'manage_user-detail': '用户详情',
manage_role: '角色管理', manage_role: '角色管理',
manage_menu: '菜单管理', manage_menu: '菜单管理',
@ -693,6 +694,24 @@ const local: App.I18n.Schema = {
lru: 'LRU', lru: 'LRU',
round: '轮询' round: '轮询'
} }
},
workflow: {
title: '工作流列表',
workflowName: '工作流名称',
groupName: '组名称',
nextTriggerAt: '触发时间',
workflowStatus: '状态',
triggerType: '触发类型',
triggerInterval: '间隔时长',
executorTimeout: '超时时间',
updateDt: '更新时间',
form: {
workflowName: '请输入工作流名称',
groupName: '请输入组名称',
workflowStatus: '请输入状态'
},
addWorkflow: '新增工作流',
editWorkflow: '编辑工作流'
} }
}, },
form: { form: {

View File

@ -43,4 +43,5 @@ export const views: Record<LastLevelRouteKey, RouteComponent | (() => Promise<Ro
retry_scene: () => import("@/views/retry/scene/index.vue"), retry_scene: () => import("@/views/retry/scene/index.vue"),
retry_task: () => import("@/views/retry/task/index.vue"), retry_task: () => import("@/views/retry/task/index.vue"),
"user-center": () => import("@/views/user-center/index.vue"), "user-center": () => import("@/views/user-center/index.vue"),
workflow: () => import("@/views/workflow/index.vue"),
}; };

View File

@ -419,5 +419,14 @@ export const generatedRoutes: GeneratedRoute[] = [
i18nKey: 'route.user-center', i18nKey: 'route.user-center',
hideInMenu: true hideInMenu: true
} }
},
{
name: 'workflow',
path: '/workflow',
component: 'layout.base$view.workflow',
meta: {
title: 'workflow',
i18nKey: 'route.workflow'
}
} }
]; ];

View File

@ -183,7 +183,8 @@ const routeMap: RouteMap = {
"retry": "/retry", "retry": "/retry",
"retry_scene": "/retry/scene", "retry_scene": "/retry/scene",
"retry_task": "/retry/task", "retry_task": "/retry/task",
"user-center": "/user-center" "user-center": "/user-center",
"workflow": "/workflow"
}; };
/** /**

View File

@ -8,3 +8,4 @@ export * from './notify';
export * from './group-config'; export * from './group-config';
export * from './retry-task'; export * from './retry-task';
export * from './retry'; export * from './retry';
export * from './workflow';

View File

@ -0,0 +1,10 @@
import { request } from '../request';
/** get namespace list */
export function fetchGetWorkflowPageList(params?: Api.Workflow.WorkflowSearchParams) {
return request<Api.Workflow.WorkflowList>({
url: '/workflow/page/list',
method: 'get',
params
});
}

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

@ -749,4 +749,40 @@ declare namespace Api {
/** 1: 一致性Hash 2: 随机 3: LRU 4: 轮询 */ /** 1: 一致性Hash 2: 随机 3: LRU 4: 轮询 */
type RouteKey = 1 | 2 | 3 | 4; type RouteKey = 1 | 2 | 3 | 4;
} }
/**
* namespace Workflow
*
* backend api module: "workflow"
*/
namespace Workflow {
import EnableStatusNumber = Api.Common.EnableStatusNumber;
type CommonSearchParams = Pick<Common.PaginatingCommonParams, 'page' | 'size'>;
/** workflow */
type Workflow = Common.CommonRecord<{
/** 工作流名称 */
workflowName: string;
/** 组名称 */
groupName: string;
/** 触发时间 */
nextTriggerAt: string;
/** 状态 */
workflowStatus: EnableStatusNumber;
/** 触发类型 */
triggerType: number;
/** 间隔时长 */
triggerInterval: string;
/** 超时时间 */
executorTimeout: number;
}>;
/** workflow search params */
type WorkflowSearchParams = CommonType.RecordNullable<
Pick<Api.Workflow.Workflow, 'workflowName' | 'groupName' | 'workflowStatus'> & CommonSearchParams
>;
/** workflow list */
type WorkflowList = Common.PaginatingQueryRecord<Workflow>;
}
} }

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

@ -866,6 +866,24 @@ declare namespace App {
round: string; round: string;
}; };
}; };
workflow: {
title: string;
workflowName: string;
groupName: string;
nextTriggerAt: string;
workflowStatus: string;
triggerType: string;
triggerInterval: string;
executorTimeout: string;
updateDt: string;
form: {
workflowName: string;
groupName: string;
workflowStatus: string;
};
addWorkflow: string;
editWorkflow: string;
};
}; };
form: { form: {
required: string; required: string;

View File

@ -96,6 +96,7 @@ declare module 'vue' {
RouterView: typeof import('vue-router')['RouterView'] RouterView: typeof import('vue-router')['RouterView']
Search: typeof import('../components/common/search-form.vue')['default'] Search: typeof import('../components/common/search-form.vue')['default']
SearchForm: typeof import('./../components/common/search-form.vue')['default'] SearchForm: typeof import('./../components/common/search-form.vue')['default']
SelectGroup: typeof import('./../components/common/select-group.vue')['default']
SoybeanAvatar: typeof import('./../components/custom/soybean-avatar.vue')['default'] SoybeanAvatar: typeof import('./../components/custom/soybean-avatar.vue')['default']
SvgIcon: typeof import('./../components/custom/svg-icon.vue')['default'] SvgIcon: typeof import('./../components/custom/svg-icon.vue')['default']
SystemLogo: typeof import('./../components/common/system-logo.vue')['default'] SystemLogo: typeof import('./../components/common/system-logo.vue')['default']

View File

@ -58,6 +58,7 @@ declare module "@elegant-router/types" {
"retry_scene": "/retry/scene"; "retry_scene": "/retry/scene";
"retry_task": "/retry/task"; "retry_task": "/retry/task";
"user-center": "/user-center"; "user-center": "/user-center";
"workflow": "/workflow";
}; };
/** /**
@ -108,6 +109,7 @@ declare module "@elegant-router/types" {
| "pods" | "pods"
| "retry" | "retry"
| "user-center" | "user-center"
| "workflow"
>; >;
/** /**
@ -153,6 +155,7 @@ declare module "@elegant-router/types" {
| "retry_scene" | "retry_scene"
| "retry_task" | "retry_task"
| "user-center" | "user-center"
| "workflow"
>; >;
/** /**

View File

@ -0,0 +1,172 @@
<script setup lang="tsx">
import { NButton, NPopconfirm } from 'naive-ui';
import { fetchGetWorkflowPageList } from '@/service/api';
import { $t } from '@/locales';
import { useAppStore } from '@/store/modules/app';
import { useTable, useTableOperate } from '@/hooks/common/table';
import WorkflowSearch from './modules/workflow-search.vue';
const appStore = useAppStore();
const { columns, columnChecks, data, getData, loading, mobilePagination, searchParams, resetSearchParams } = useTable({
apiFn: fetchGetWorkflowPageList,
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
workflowName: null,
groupName: null,
workflowStatus: null
},
columns: () => [
{
type: 'selection',
align: 'center',
width: 48
},
{
key: 'index',
title: $t('common.index'),
align: 'center',
width: 64
},
{
key: 'workflowName',
title: $t('page.workflow.workflowName'),
align: 'left',
minWidth: 120
},
{
key: 'groupName',
title: $t('page.workflow.groupName'),
align: 'left',
minWidth: 120
},
{
key: 'nextTriggerAt',
title: $t('page.workflow.nextTriggerAt'),
align: 'left',
minWidth: 120
},
{
key: 'workflowStatus',
title: $t('page.workflow.workflowStatus'),
align: 'left',
minWidth: 120
},
{
key: 'triggerType',
title: $t('page.workflow.triggerType'),
align: 'left',
minWidth: 120
},
{
key: 'triggerInterval',
title: $t('page.workflow.triggerInterval'),
align: 'left',
minWidth: 120
},
{
key: 'executorTimeout',
title: $t('page.workflow.executorTimeout'),
align: 'left',
minWidth: 120
},
{
key: 'updateDt',
title: $t('page.workflow.updateDt'),
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 {
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">
<WorkflowSearch v-model:model="searchParams" @reset="resetSearchParams" @search="getData" />
<NCard
:title="$t('page.workflow.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"
/>
</NCard>
</div>
</template>
<style scoped></style>

View File

@ -0,0 +1,47 @@
<script setup lang="ts">
import { $t } from '@/locales';
import { translateOptions } from '@/utils/common';
import { retryTaskStatusTypeOptions } from '@/constants/business';
import SelectGroup from '@/components/common/select-group.vue';
defineOptions({
name: 'WorkflowSearch'
});
interface Emits {
(e: 'reset'): void;
(e: 'search'): void;
}
const emit = defineEmits<Emits>();
const model = defineModel<Api.Workflow.WorkflowSearchParams>('model', { required: true });
function reset() {
emit('reset');
}
function search() {
emit('search');
}
</script>
<template>
<SearchForm :model="model" @search="search" @reset="reset">
<NFormItemGi span="24 s:12 m:6" :label="$t('page.workflow.groupName')" path="groupName" class="pr-24px">
<SelectGroup v-model:value="model.groupName" />
</NFormItemGi>
<NFormItemGi span="24 s:12 m:6" :label="$t('page.workflow.workflowName')" path="workflowName" class="pr-24px">
<NInput v-model:value="model.workflowName" :placeholder="$t('page.workflow.form.workflowName')" />
</NFormItemGi>
<NFormItemGi span="24 s:12 m:6" :label="$t('page.workflow.workflowStatus')" path="workflowStatus" class="pr-24px">
<NSelect
v-model:value="model.workflowStatus"
:placeholder="$t('page.workflow.form.workflowStatus')"
:options="translateOptions(retryTaskStatusTypeOptions)"
/>
</NFormItemGi>
</SearchForm>
</template>
<style scoped></style>