24 changed files with 2019 additions and 11 deletions
@ -0,0 +1,15 @@ |
|||
<script lang="ts" setup> |
|||
import { Page } from '@vben/common-ui'; |
|||
|
|||
import { JobInfoTable } from '@abp/tasks'; |
|||
|
|||
defineOptions({ |
|||
name: 'TaskManagementJobInfos', |
|||
}); |
|||
</script> |
|||
|
|||
<template> |
|||
<Page> |
|||
<JobInfoTable /> |
|||
</Page> |
|||
</template> |
|||
@ -0,0 +1,38 @@ |
|||
{ |
|||
"name": "@abp/tasks", |
|||
"version": "9.0.4", |
|||
"homepage": "https://github.com/colinin/abp-next-admin", |
|||
"bugs": "https://github.com/colinin/abp-next-admin/issues", |
|||
"repository": { |
|||
"type": "git", |
|||
"url": "git+https://github.com/colinin/abp-next-admin.git", |
|||
"directory": "packages/@abp/tasks" |
|||
}, |
|||
"license": "MIT", |
|||
"type": "module", |
|||
"sideEffects": [ |
|||
"**/*.css" |
|||
], |
|||
"exports": { |
|||
".": { |
|||
"types": "./src/index.ts", |
|||
"default": "./src/index.ts" |
|||
} |
|||
}, |
|||
"dependencies": { |
|||
"@abp/components": "workspace:*", |
|||
"@abp/core": "workspace:*", |
|||
"@abp/request": "workspace:*", |
|||
"@abp/ui": "workspace:*", |
|||
"@ant-design/icons-vue": "catalog:", |
|||
"@vben/access": "workspace:*", |
|||
"@vben/common-ui": "workspace:*", |
|||
"@vben/hooks": "workspace:*", |
|||
"@vben/icons": "workspace:*", |
|||
"@vben/layouts": "workspace:*", |
|||
"@vben/locales": "workspace:*", |
|||
"ant-design-vue": "catalog:", |
|||
"vue": "catalog:*", |
|||
"vxe-table": "catalog:" |
|||
} |
|||
} |
|||
@ -0,0 +1,2 @@ |
|||
export * from './useJobInfosApi'; |
|||
export * from './useJobLogsApi'; |
|||
@ -0,0 +1,169 @@ |
|||
import type { ListResultDto, PagedResultDto } from '@abp/core'; |
|||
|
|||
import type { |
|||
BackgroundJobDefinitionDto, |
|||
BackgroundJobInfoBatchInput, |
|||
BackgroundJobInfoCreateDto, |
|||
BackgroundJobInfoDto, |
|||
BackgroundJobInfoGetListInput, |
|||
BackgroundJobInfoUpdateDto, |
|||
} from '../types/job-infos'; |
|||
|
|||
import { useRequest } from '@abp/request'; |
|||
|
|||
export function useJobInfosApi() { |
|||
const { cancel, request } = useRequest(); |
|||
|
|||
function createApi( |
|||
input: BackgroundJobInfoCreateDto, |
|||
): Promise<BackgroundJobInfoDto> { |
|||
return request<BackgroundJobInfoDto>( |
|||
'/api/task-management/background-jobs', |
|||
{ |
|||
data: input, |
|||
method: 'POST', |
|||
}, |
|||
); |
|||
} |
|||
|
|||
function deleteApi(id: string): Promise<void> { |
|||
return request(`/api/task-management/background-jobs/${id}`, { |
|||
method: 'DELETE', |
|||
}); |
|||
} |
|||
|
|||
function bulkDeleteApi(input: BackgroundJobInfoBatchInput): Promise<void> { |
|||
return request(`/api/task-management/background-jobs/bulk-delete`, { |
|||
data: input, |
|||
method: 'DELETE', |
|||
}); |
|||
} |
|||
|
|||
function getApi(id: string): Promise<BackgroundJobInfoDto> { |
|||
return request<BackgroundJobInfoDto>( |
|||
`/api/task-management/background-jobs/${id}`, |
|||
{ |
|||
method: 'GET', |
|||
}, |
|||
); |
|||
} |
|||
|
|||
function getPagedListApi( |
|||
input?: BackgroundJobInfoGetListInput, |
|||
): Promise<PagedResultDto<BackgroundJobInfoDto>> { |
|||
return request<PagedResultDto<BackgroundJobInfoDto>>( |
|||
`/api/task-management/background-jobs`, |
|||
{ |
|||
method: 'GET', |
|||
params: input, |
|||
}, |
|||
); |
|||
} |
|||
|
|||
function pauseApi(id: string): Promise<void> { |
|||
return request(`/api/task-management/background-jobs/${id}/pause`, { |
|||
method: 'PUT', |
|||
}); |
|||
} |
|||
|
|||
function bulkPauseApi(input: BackgroundJobInfoBatchInput): Promise<void> { |
|||
return request(`/api/task-management/background-jobs/bulk-pause`, { |
|||
data: input, |
|||
method: 'PUT', |
|||
}); |
|||
} |
|||
|
|||
function resumeApi(id: string): Promise<void> { |
|||
return request(`/api/task-management/background-jobs/${id}/resume`, { |
|||
method: 'PUT', |
|||
}); |
|||
} |
|||
|
|||
function bulkResumeApi(input: BackgroundJobInfoBatchInput): Promise<void> { |
|||
return request(`/api/task-management/background-jobs/bulk-resume`, { |
|||
data: input, |
|||
method: 'PUT', |
|||
}); |
|||
} |
|||
|
|||
function triggerApi(id: string): Promise<void> { |
|||
return request(`/api/task-management/background-jobs/${id}/trigger`, { |
|||
method: 'PUT', |
|||
}); |
|||
} |
|||
|
|||
function bulkTriggerApi(input: BackgroundJobInfoBatchInput): Promise<void> { |
|||
return request(`/api/task-management/background-jobs/bulk-trigger`, { |
|||
data: input, |
|||
method: 'PUT', |
|||
}); |
|||
} |
|||
|
|||
function stopApi(id: string): Promise<void> { |
|||
return request(`/api/task-management/background-jobs/${id}/stop`, { |
|||
method: 'PUT', |
|||
}); |
|||
} |
|||
|
|||
function bulkStopApi(input: BackgroundJobInfoBatchInput): Promise<void> { |
|||
return request(`/api/task-management/background-jobs/bulk-stop`, { |
|||
data: input, |
|||
method: 'PUT', |
|||
}); |
|||
} |
|||
|
|||
function startApi(id: string): Promise<void> { |
|||
return request(`/api/task-management/background-jobs/${id}/start`, { |
|||
method: 'PUT', |
|||
}); |
|||
} |
|||
|
|||
function bulkStartApi(input: BackgroundJobInfoBatchInput): Promise<void> { |
|||
return request(`/api/task-management/background-jobs/bulk-start`, { |
|||
data: input, |
|||
method: 'PUT', |
|||
}); |
|||
} |
|||
|
|||
function updateApi( |
|||
id: string, |
|||
input: BackgroundJobInfoUpdateDto, |
|||
): Promise<BackgroundJobInfoDto> { |
|||
return request(`/api/task-management/background-jobs/${id}`, { |
|||
data: input, |
|||
method: 'PUT', |
|||
}); |
|||
} |
|||
|
|||
function getDefinitionsApi(): Promise< |
|||
ListResultDto<BackgroundJobDefinitionDto> |
|||
> { |
|||
return request<ListResultDto<BackgroundJobDefinitionDto>>( |
|||
`/api/task-management/background-jobs/definitions`, |
|||
{ |
|||
method: 'GET', |
|||
}, |
|||
); |
|||
} |
|||
|
|||
return { |
|||
bulkDeleteApi, |
|||
bulkPauseApi, |
|||
bulkResumeApi, |
|||
bulkStartApi, |
|||
bulkStopApi, |
|||
bulkTriggerApi, |
|||
cancel, |
|||
createApi, |
|||
deleteApi, |
|||
getApi, |
|||
getDefinitionsApi, |
|||
getPagedListApi, |
|||
pauseApi, |
|||
resumeApi, |
|||
startApi, |
|||
stopApi, |
|||
triggerApi, |
|||
updateApi, |
|||
}; |
|||
} |
|||
@ -0,0 +1,46 @@ |
|||
import type { PagedResultDto } from '@abp/core'; |
|||
|
|||
import type { |
|||
BackgroundJobLogDto, |
|||
BackgroundJobLogGetListInput, |
|||
} from '../types'; |
|||
|
|||
import { useRequest } from '@abp/request'; |
|||
|
|||
export function useJobLogsApi() { |
|||
const { cancel, request } = useRequest(); |
|||
|
|||
function getApi(id: string): Promise<BackgroundJobLogDto> { |
|||
return request<BackgroundJobLogDto>( |
|||
`/api/task-management/background-jobs/logs/${id}`, |
|||
{ |
|||
method: 'GET', |
|||
}, |
|||
); |
|||
} |
|||
|
|||
function deleteApi(id: string): Promise<void> { |
|||
return request(`/api/task-management/background-jobs/logs/${id}`, { |
|||
method: 'DELETE', |
|||
}); |
|||
} |
|||
|
|||
function getPagedListApi( |
|||
input?: BackgroundJobLogGetListInput, |
|||
): Promise<PagedResultDto<BackgroundJobLogDto>> { |
|||
return request<PagedResultDto<BackgroundJobLogDto>>( |
|||
`/api/task-management/background-jobs/logs`, |
|||
{ |
|||
method: 'GET', |
|||
params: input, |
|||
}, |
|||
); |
|||
} |
|||
|
|||
return { |
|||
cancel, |
|||
deleteApi, |
|||
getApi, |
|||
getPagedListApi, |
|||
}; |
|||
} |
|||
@ -0,0 +1 @@ |
|||
export { default as JobInfoTable } from './job-infos/JobInfoTable.vue'; |
|||
@ -0,0 +1,303 @@ |
|||
<script setup lang="ts"> |
|||
import type { BackgroundJobInfoDto, BackgroundJobLogDto } from '../../types'; |
|||
|
|||
import { ref } from 'vue'; |
|||
|
|||
import { useVbenDrawer } from '@vben/common-ui'; |
|||
import { createIconifyIcon } from '@vben/icons'; |
|||
import { $t } from '@vben/locales'; |
|||
|
|||
import { formatToDateTime } from '@abp/core'; |
|||
import { PropertyTable } from '@abp/ui'; |
|||
import { |
|||
Button, |
|||
Checkbox, |
|||
Descriptions, |
|||
DescriptionsItem, |
|||
Empty, |
|||
List, |
|||
ListItem, |
|||
ListItemMeta, |
|||
message, |
|||
Popconfirm, |
|||
TabPane, |
|||
Tabs, |
|||
Tag, |
|||
} from 'ant-design-vue'; |
|||
|
|||
import { useJobInfosApi } from '../../api/useJobInfosApi'; |
|||
import { useJobLogsApi } from '../../api/useJobLogsApi'; |
|||
import { useJobEnumsMap } from '../../hooks/useJobEnumsMap'; |
|||
import { JobType } from '../../types'; |
|||
|
|||
const DeleteOutlined = createIconifyIcon('ant-design:delete-outlined'); |
|||
const SuccessIcon = createIconifyIcon('grommet-icons:status-good'); |
|||
const FailedIcon = createIconifyIcon('grommet-icons:status-warning'); |
|||
|
|||
const { getApi } = useJobInfosApi(); |
|||
const { deleteApi: deleteJobLogApi, getPagedListApi: getJobLogsApi } = |
|||
useJobLogsApi(); |
|||
const { |
|||
jobPriorityColor, |
|||
jobPriorityMap, |
|||
jobSourceMap, |
|||
jobStatusColor, |
|||
jobStatusMap, |
|||
jobTypeMap, |
|||
} = useJobEnumsMap(); |
|||
|
|||
const activeTabKey = ref('basic'); |
|||
const jobInfo = ref<BackgroundJobInfoDto>(); |
|||
const jobLogs = ref<BackgroundJobLogDto[]>([]); |
|||
const paginationInfo = ref({ |
|||
current: 1, |
|||
pageSize: 10, |
|||
show: true, |
|||
totalCount: 0, |
|||
}); |
|||
const [Drawer, drawerApi] = useVbenDrawer({ |
|||
class: 'w-1/2', |
|||
onCancel() { |
|||
drawerApi.close(); |
|||
}, |
|||
onClosed() { |
|||
activeTabKey.value = 'basic'; |
|||
}, |
|||
async onOpenChange(isOpen) { |
|||
if (isOpen) { |
|||
await onGet(); |
|||
} |
|||
}, |
|||
showConfirmButton: false, |
|||
}); |
|||
|
|||
async function onTabChange(activeKey: string) { |
|||
if (activeKey === 'logs') { |
|||
await onGetLogs(1); |
|||
} |
|||
} |
|||
|
|||
function onSizeChange() {} |
|||
|
|||
async function onGet() { |
|||
drawerApi.setState({ loading: true }); |
|||
try { |
|||
jobLogs.value = []; |
|||
const { id } = drawerApi.getData<BackgroundJobInfoDto>(); |
|||
jobInfo.value = await getApi(id); |
|||
} finally { |
|||
drawerApi.setState({ loading: false }); |
|||
} |
|||
} |
|||
|
|||
async function onGetLogs(page?: number) { |
|||
drawerApi.setState({ loading: true }); |
|||
try { |
|||
const pageNumber = page ?? 1; |
|||
paginationInfo.value.current = pageNumber; |
|||
const { id } = drawerApi.getData<BackgroundJobInfoDto>(); |
|||
const jobLogResult = await getJobLogsApi({ |
|||
jobId: id, |
|||
maxResultCount: paginationInfo.value.pageSize, |
|||
skipCount: (pageNumber - 1) * paginationInfo.value.pageSize, |
|||
}); |
|||
jobLogs.value = jobLogResult.items; |
|||
paginationInfo.value.totalCount = jobLogResult.totalCount; |
|||
} finally { |
|||
drawerApi.setState({ loading: false }); |
|||
} |
|||
} |
|||
|
|||
async function onDeleteLog(jobLog: BackgroundJobLogDto) { |
|||
drawerApi.setState({ loading: true }); |
|||
try { |
|||
await deleteJobLogApi(jobLog.id); |
|||
message.success($t('AbpUi.DeletedSuccessfully')); |
|||
await onGetLogs(paginationInfo.value.current); |
|||
} finally { |
|||
drawerApi.setState({ loading: false }); |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<template> |
|||
<Drawer :title="$t('TaskManagement.BackgroundJobDetail')"> |
|||
<Tabs |
|||
v-if="jobInfo" |
|||
v-model:active-key="activeTabKey" |
|||
@change="(key) => onTabChange(key.toString())" |
|||
> |
|||
<TabPane key="basic" :tab="$t('TaskManagement.BasicInfo')"> |
|||
<Descriptions |
|||
:colon="false" |
|||
:column="2" |
|||
bordered |
|||
size="small" |
|||
:label-style="{ minWidth: '120px' }" |
|||
> |
|||
<DescriptionsItem :label="$t('TaskManagement.DisplayName:Status')"> |
|||
<Tag :color="jobStatusColor[jobInfo.status]"> |
|||
{{ jobStatusMap[jobInfo.status] }} |
|||
</Tag> |
|||
</DescriptionsItem> |
|||
<DescriptionsItem :label="$t('TaskManagement.DisplayName:Source')"> |
|||
<span>{{ jobSourceMap[jobInfo.source] }}</span> |
|||
</DescriptionsItem> |
|||
<DescriptionsItem :label="$t('TaskManagement.DisplayName:IsEnabled')"> |
|||
<Checkbox disabled :checked="jobInfo.isEnabled" /> |
|||
</DescriptionsItem> |
|||
<DescriptionsItem :label="$t('TaskManagement.DisplayName:Priority')"> |
|||
<Tag :color="jobPriorityColor[jobInfo.priority]"> |
|||
{{ jobPriorityMap[jobInfo.priority] }} |
|||
</Tag> |
|||
</DescriptionsItem> |
|||
<DescriptionsItem :label="$t('TaskManagement.DisplayName:Group')"> |
|||
<span>{{ jobInfo.group }}</span> |
|||
</DescriptionsItem> |
|||
<DescriptionsItem :label="$t('TaskManagement.DisplayName:Name')"> |
|||
<span>{{ jobInfo.name }}</span> |
|||
</DescriptionsItem> |
|||
<DescriptionsItem |
|||
:label="$t('TaskManagement.DisplayName:Description')" |
|||
:span="2" |
|||
> |
|||
<span>{{ jobInfo.description }}</span> |
|||
</DescriptionsItem> |
|||
<DescriptionsItem |
|||
:label="$t('TaskManagement.DisplayName:Type')" |
|||
:span="2" |
|||
> |
|||
<span>{{ jobInfo.type }}</span> |
|||
</DescriptionsItem> |
|||
<DescriptionsItem |
|||
:label="$t('TaskManagement.DisplayName:CreationTime')" |
|||
> |
|||
<span>{{ formatToDateTime(jobInfo.creationTime) }}</span> |
|||
</DescriptionsItem> |
|||
<DescriptionsItem |
|||
:label="$t('TaskManagement.DisplayName:LockTimeOut')" |
|||
> |
|||
<span>{{ jobInfo.lockTimeOut }}</span> |
|||
</DescriptionsItem> |
|||
<DescriptionsItem :label="$t('TaskManagement.DisplayName:BeginTime')"> |
|||
<span>{{ formatToDateTime(jobInfo.beginTime) }}</span> |
|||
</DescriptionsItem> |
|||
<DescriptionsItem :label="$t('TaskManagement.DisplayName:EndTime')"> |
|||
<span>{{ |
|||
jobInfo.endTime |
|||
? formatToDateTime(jobInfo.endTime) |
|||
: jobInfo.endTime |
|||
}}</span> |
|||
</DescriptionsItem> |
|||
<DescriptionsItem :label="$t('TaskManagement.DisplayName:JobType')"> |
|||
<span>{{ jobTypeMap[jobInfo.jobType] }}</span> |
|||
</DescriptionsItem> |
|||
<DescriptionsItem |
|||
v-if="jobInfo.jobType === JobType.Period" |
|||
:label="$t('TaskManagement.DisplayName:Cron')" |
|||
> |
|||
<span>{{ jobInfo.cron }}</span> |
|||
</DescriptionsItem> |
|||
<DescriptionsItem |
|||
v-else |
|||
:label="$t('TaskManagement.DisplayName:Interval')" |
|||
> |
|||
<span>{{ jobInfo.interval }}</span> |
|||
</DescriptionsItem> |
|||
<DescriptionsItem |
|||
:label="$t('TaskManagement.DisplayName:LastRunTime')" |
|||
> |
|||
<span>{{ |
|||
jobInfo.lastRunTime |
|||
? formatToDateTime(jobInfo.lastRunTime) |
|||
: jobInfo.lastRunTime |
|||
}}</span> |
|||
</DescriptionsItem> |
|||
<DescriptionsItem |
|||
:label="$t('TaskManagement.DisplayName:NextRunTime')" |
|||
> |
|||
<span>{{ |
|||
jobInfo.nextRunTime |
|||
? formatToDateTime(jobInfo.nextRunTime) |
|||
: jobInfo.nextRunTime |
|||
}}</span> |
|||
</DescriptionsItem> |
|||
<DescriptionsItem |
|||
:label="$t('TaskManagement.DisplayName:TriggerCount')" |
|||
> |
|||
<span>{{ jobInfo.triggerCount }}</span> |
|||
</DescriptionsItem> |
|||
<DescriptionsItem :label="$t('TaskManagement.DisplayName:MaxCount')"> |
|||
<span>{{ jobInfo.maxCount }}</span> |
|||
</DescriptionsItem> |
|||
<DescriptionsItem :label="$t('TaskManagement.DisplayName:TryCount')"> |
|||
<span>{{ jobInfo.tryCount }}</span> |
|||
</DescriptionsItem> |
|||
<DescriptionsItem |
|||
:label="$t('TaskManagement.DisplayName:MaxTryCount')" |
|||
> |
|||
<span>{{ jobInfo.maxTryCount }}</span> |
|||
</DescriptionsItem> |
|||
<DescriptionsItem :label="$t('TaskManagement.DisplayName:Result')"> |
|||
<span>{{ jobInfo.result }}</span> |
|||
</DescriptionsItem> |
|||
</Descriptions> |
|||
</TabPane> |
|||
<TabPane key="paramters" :tab="$t('TaskManagement.Paramters')"> |
|||
<PropertyTable :data="jobInfo.args" disabled /> |
|||
</TabPane> |
|||
<TabPane key="logs" :tab="$t('TaskManagement.BackgroundJobLogs')"> |
|||
<List |
|||
item-layout="vertical" |
|||
size="default" |
|||
bordered |
|||
:data-source="jobLogs" |
|||
:pagination="{ |
|||
current: paginationInfo.current, |
|||
pageSize: paginationInfo.pageSize, |
|||
total: paginationInfo.totalCount, |
|||
showSizeChanger: paginationInfo.show, |
|||
onChange: onGetLogs, |
|||
onShowSizeChange: onSizeChange, |
|||
}" |
|||
> |
|||
<template #renderItem="{ item }"> |
|||
<ListItem :key="item.id"> |
|||
<template #extra> |
|||
<Popconfirm |
|||
placement="topLeft" |
|||
:title="$t('AbpUi.AreYouSure')" |
|||
:description="$t('AbpUi.ItemWillBeDeletedMessage')" |
|||
@confirm="onDeleteLog(item)" |
|||
> |
|||
<Button block danger type="link"> |
|||
<template #icon> |
|||
<DeleteOutlined class="inline size-5" /> |
|||
</template> |
|||
</Button> |
|||
</Popconfirm> |
|||
</template> |
|||
<ListItemMeta :description="item.message"> |
|||
<template #avatar> |
|||
<SuccessIcon |
|||
v-if="!item.exception" |
|||
class="size-8" |
|||
color="seagreen" |
|||
/> |
|||
<FailedIcon v-else class="size-8" color="orangered" /> |
|||
</template> |
|||
<template #title> |
|||
<span>{{ item.runTime }}</span> |
|||
</template> |
|||
</ListItemMeta> |
|||
{{ item.exception ?? item.message }} |
|||
</ListItem> |
|||
</template> |
|||
</List> |
|||
</TabPane> |
|||
</Tabs> |
|||
<Empty v-else /> |
|||
</Drawer> |
|||
</template> |
|||
|
|||
<style scoped></style> |
|||
@ -0,0 +1,349 @@ |
|||
<script setup lang="ts"> |
|||
import type { FormInstance } from 'ant-design-vue/lib/form'; |
|||
|
|||
import type { |
|||
BackgroundJobDefinitionDto, |
|||
BackgroundJobInfoDto, |
|||
} from '../../types/job-infos'; |
|||
|
|||
import { reactive, ref, useTemplateRef } from 'vue'; |
|||
|
|||
import { useVbenDrawer } from '@vben/common-ui'; |
|||
import { $t } from '@vben/locales'; |
|||
|
|||
import { formatToDate } from '@abp/core'; |
|||
import { PropertyTable } from '@abp/ui'; |
|||
import { |
|||
Checkbox, |
|||
DatePicker, |
|||
Form, |
|||
FormItem, |
|||
Input, |
|||
InputNumber, |
|||
message, |
|||
Select, |
|||
Tabs, |
|||
Textarea, |
|||
} from 'ant-design-vue'; |
|||
|
|||
import { useJobInfosApi } from '../../api/useJobInfosApi'; |
|||
import { JobPriority, JobType } from '../../types/job-infos'; |
|||
|
|||
defineOptions({ |
|||
name: 'JobInfoDrawer', |
|||
}); |
|||
const emits = defineEmits<{ |
|||
(event: 'change', data: BackgroundJobInfoDto): void; |
|||
}>(); |
|||
const TabPane = Tabs.TabPane; |
|||
interface JobParamter { |
|||
key: string; |
|||
value: any; |
|||
} |
|||
|
|||
const activedTab = ref('basic'); |
|||
const jobTypeOptions = reactive([ |
|||
{ |
|||
label: $t('BackgroundTasks.JobType:Once'), |
|||
value: JobType.Once, |
|||
}, |
|||
{ |
|||
label: $t('BackgroundTasks.JobType:Period'), |
|||
value: JobType.Period, |
|||
}, |
|||
{ |
|||
label: $t('BackgroundTasks.JobType:Persistent'), |
|||
value: JobType.Persistent, |
|||
}, |
|||
]); |
|||
const jobPriorityOptions = reactive([ |
|||
{ |
|||
label: $t('BackgroundTasks.Priority:Low'), |
|||
value: JobPriority.Low, |
|||
}, |
|||
{ |
|||
label: $t('BackgroundTasks.Priority:BelowNormal'), |
|||
value: JobPriority.BelowNormal, |
|||
}, |
|||
{ |
|||
label: $t('BackgroundTasks.Priority:Normal'), |
|||
value: JobPriority.Normal, |
|||
}, |
|||
{ |
|||
label: $t('BackgroundTasks.Priority:AboveNormal'), |
|||
value: JobPriority.AboveNormal, |
|||
}, |
|||
{ |
|||
label: $t('BackgroundTasks.Priority:High'), |
|||
value: JobPriority.High, |
|||
}, |
|||
]); |
|||
const jobDefinetions = ref<BackgroundJobDefinitionDto[]>([]); |
|||
const form = useTemplateRef<FormInstance>('form'); |
|||
const formModel = ref<BackgroundJobInfoDto>({} as BackgroundJobInfoDto); |
|||
const { createApi, getApi, getDefinitionsApi, updateApi } = useJobInfosApi(); |
|||
const [Drawer, drawerApi] = useVbenDrawer({ |
|||
class: 'w-auto', |
|||
onCancel() { |
|||
drawerApi.close(); |
|||
}, |
|||
onConfirm: async () => { |
|||
await onSubmit(); |
|||
}, |
|||
onOpenChange: async (isOpen: boolean) => { |
|||
if (isOpen) { |
|||
try { |
|||
formModel.value = { |
|||
beginTime: formatToDate(new Date()), |
|||
isEnabled: true, |
|||
jobType: JobType.Once, |
|||
priority: JobPriority.Normal, |
|||
} as BackgroundJobInfoDto; |
|||
drawerApi.setState({ loading: true }); |
|||
const dto = drawerApi.getData<BackgroundJobInfoDto>(); |
|||
if (dto?.id) { |
|||
await onGet(dto.id); |
|||
} else { |
|||
await onInitDefaultJobs(); |
|||
drawerApi.setState({ |
|||
title: $t('TaskManagement.BackgroundJobs:AddNew'), |
|||
}); |
|||
} |
|||
} finally { |
|||
drawerApi.setState({ loading: false }); |
|||
} |
|||
} |
|||
}, |
|||
title: $t('BackgroundJobs:Edit'), |
|||
}); |
|||
async function onGet(id: string) { |
|||
const dto = await getApi(id); |
|||
formModel.value = dto; |
|||
drawerApi.setState({ |
|||
title: `${$t('TaskManagement.BackgroundJobs:Edit')} - ${dto.name}`, |
|||
}); |
|||
} |
|||
|
|||
async function onInitDefaultJobs() { |
|||
const { items } = await getDefinitionsApi(); |
|||
jobDefinetions.value = items; |
|||
} |
|||
|
|||
async function onSubmit() { |
|||
await form.value?.validate(); |
|||
try { |
|||
drawerApi.setState({ confirmLoading: true }); |
|||
const api = formModel.value?.id |
|||
? updateApi(formModel.value.id, formModel.value) |
|||
: createApi(formModel.value); |
|||
const dto = await api; |
|||
message.success($t('AbpUi.SavedSuccessfully')); |
|||
emits('change', dto); |
|||
drawerApi.close(); |
|||
} finally { |
|||
drawerApi.setState({ confirmLoading: false }); |
|||
} |
|||
} |
|||
|
|||
function onJobDefineChange(jobName: string) { |
|||
formModel.value.args = {}; |
|||
const jobDefine = jobDefinetions.value.find((x) => x.name === jobName); |
|||
if (jobDefine) { |
|||
const params: Record<string, any> = {}; |
|||
jobDefine.paramters.forEach((param) => { |
|||
params[param.name] = undefined; |
|||
}); |
|||
formModel.value.args = params; |
|||
formModel.value.type = jobDefine.name; |
|||
} |
|||
} |
|||
|
|||
function onEditParam(record: JobParamter) { |
|||
formModel.value.args[record.key] = record.value; |
|||
} |
|||
|
|||
function onDeleteParam(record: JobParamter) { |
|||
delete formModel.value.args[record.key]; |
|||
} |
|||
</script> |
|||
|
|||
<template> |
|||
<Drawer> |
|||
<div style="width: 800px"> |
|||
<Form |
|||
ref="form" |
|||
:model="formModel" |
|||
:label-col="{ span: 4 }" |
|||
:wrapper-col="{ span: 18 }" |
|||
> |
|||
<Tabs v-model="activedTab"> |
|||
<TabPane key="basic" :tab="$t('TaskManagement.BasicInfo')"> |
|||
<FormItem |
|||
v-if="!formModel.id" |
|||
:label="$t('TaskManagement.BackgroundJobs')" |
|||
:help="$t('TaskManagement.BackgroundJobs')" |
|||
> |
|||
<Select |
|||
class="w-full" |
|||
allow-clear |
|||
:options="jobDefinetions" |
|||
:field-names="{ label: 'displayName', value: 'name' }" |
|||
@change="(val) => onJobDefineChange(val!.toString())" |
|||
/> |
|||
</FormItem> |
|||
<FormItem |
|||
name="isEnabled" |
|||
:label="$t('TaskManagement.DisplayName:IsEnabled')" |
|||
> |
|||
<Checkbox v-model:checked="formModel.isEnabled"> |
|||
{{ $t('TaskManagement.DisplayName:IsEnabled') }} |
|||
</Checkbox> |
|||
</FormItem> |
|||
<FormItem |
|||
name="group" |
|||
required |
|||
:label="$t('TaskManagement.DisplayName:Group')" |
|||
> |
|||
<Input |
|||
v-model:value="formModel.group" |
|||
allow-clear |
|||
autocomplete="off" |
|||
/> |
|||
</FormItem> |
|||
<FormItem |
|||
name="name" |
|||
required |
|||
:label="$t('TaskManagement.DisplayName:Name')" |
|||
> |
|||
<Input |
|||
v-model:value="formModel.name" |
|||
allow-clear |
|||
autocomplete="off" |
|||
/> |
|||
</FormItem> |
|||
<FormItem |
|||
name="type" |
|||
required |
|||
:label="$t('TaskManagement.DisplayName:Type')" |
|||
:extra="$t('TaskManagement.Description:Type')" |
|||
> |
|||
<Textarea |
|||
allow-clear |
|||
v-model:value="formModel.type" |
|||
:auto-size="{ minRows: 3 }" |
|||
/> |
|||
</FormItem> |
|||
<FormItem |
|||
name="beginTime" |
|||
required |
|||
:label="$t('TaskManagement.DisplayName:BeginTime')" |
|||
> |
|||
<DatePicker |
|||
class="w-full" |
|||
value-format="YYYY-MM-DD" |
|||
allow-clear |
|||
v-model:value="formModel.beginTime" |
|||
/> |
|||
</FormItem> |
|||
<FormItem |
|||
name="endTime" |
|||
:label="$t('TaskManagement.DisplayName:EndTime')" |
|||
> |
|||
<DatePicker |
|||
class="w-full" |
|||
value-format="YYYY-MM-DD" |
|||
allow-clear |
|||
v-model:value="formModel.endTime" |
|||
/> |
|||
</FormItem> |
|||
<FormItem |
|||
name="jobType" |
|||
:label="$t('TaskManagement.DisplayName:JobType')" |
|||
:extra="$t('TaskManagement.Description:JobType')" |
|||
> |
|||
<Select |
|||
class="w-full" |
|||
allow-clear |
|||
v-model:value="formModel.jobType" |
|||
:options="jobTypeOptions" |
|||
/> |
|||
</FormItem> |
|||
<FormItem |
|||
v-if="formModel.jobType === JobType.Period" |
|||
name="cron" |
|||
required |
|||
:label="$t('TaskManagement.DisplayName:Cron')" |
|||
:extra="$t('TaskManagement.Description:Cron')" |
|||
> |
|||
<Input allow-clear v-model:value="formModel.cron" /> |
|||
</FormItem> |
|||
<FormItem |
|||
name="maxCount" |
|||
:label="$t('TaskManagement.DisplayName:MaxCount')" |
|||
:extra="$t('TaskManagement.Description:MaxCount')" |
|||
> |
|||
<InputNumber |
|||
class="w-full" |
|||
allow-clear |
|||
v-model:value="formModel.maxCount" |
|||
/> |
|||
</FormItem> |
|||
<FormItem |
|||
name="maxTryCount" |
|||
:label="$t('TaskManagement.DisplayName:MaxTryCount')" |
|||
:extra="$t('TaskManagement.Description:MaxTryCount')" |
|||
> |
|||
<InputNumber |
|||
class="w-full" |
|||
allow-clear |
|||
v-model:value="formModel.maxTryCount" |
|||
/> |
|||
</FormItem> |
|||
<FormItem |
|||
name="priority" |
|||
:label="$t('TaskManagement.DisplayName:Priority')" |
|||
:extra="$t('TaskManagement.Description:Priority')" |
|||
> |
|||
<Select |
|||
class="w-full" |
|||
allow-clear |
|||
v-model:value="formModel.priority" |
|||
:options="jobPriorityOptions" |
|||
/> |
|||
</FormItem> |
|||
<FormItem |
|||
name="lockTimeOut" |
|||
:label="$t('TaskManagement.DisplayName:LockTimeOut')" |
|||
:extra="$t('TaskManagement.Description:LockTimeOut')" |
|||
> |
|||
<InputNumber |
|||
class="w-full" |
|||
allow-clear |
|||
v-model:value="formModel.lockTimeOut" |
|||
/> |
|||
</FormItem> |
|||
<FormItem |
|||
name="description" |
|||
:label="$t('TaskManagement.DisplayName:Description')" |
|||
> |
|||
<Textarea |
|||
allow-clear |
|||
v-model:value="formModel.description" |
|||
:auto-size="{ minRows: 3 }" |
|||
/> |
|||
</FormItem> |
|||
</TabPane> |
|||
<TabPane key="paramters" :tab="$t('TaskManagement.Paramters')"> |
|||
<PropertyTable |
|||
:data="formModel.args" |
|||
@change="onEditParam" |
|||
@delete="onDeleteParam" |
|||
/> |
|||
</TabPane> |
|||
</Tabs> |
|||
</Form> |
|||
</div> |
|||
</Drawer> |
|||
</template> |
|||
|
|||
<style scoped></style> |
|||
@ -0,0 +1,736 @@ |
|||
<script setup lang="ts"> |
|||
import type { SortOrder } from '@abp/core'; |
|||
import type { VxeGridListeners, VxeGridProps } from '@abp/ui'; |
|||
import type { MenuInfo } from 'ant-design-vue/es/menu/src/interface'; |
|||
|
|||
import type { VbenFormProps } from '@vben/common-ui'; |
|||
|
|||
import type { BackgroundJobInfoDto } from '../../types/job-infos'; |
|||
|
|||
import { defineAsyncComponent, h, ref } from 'vue'; |
|||
|
|||
import { useAccess } from '@vben/access'; |
|||
import { useVbenDrawer } from '@vben/common-ui'; |
|||
import { createIconifyIcon } from '@vben/icons'; |
|||
import { $t } from '@vben/locales'; |
|||
|
|||
import { formatToDateTime } from '@abp/core'; |
|||
import { useVbenVxeGrid } from '@abp/ui'; |
|||
import { |
|||
DeleteOutlined, |
|||
EditOutlined, |
|||
EllipsisOutlined, |
|||
PlusOutlined, |
|||
} from '@ant-design/icons-vue'; |
|||
import { |
|||
Button, |
|||
Dropdown, |
|||
Menu, |
|||
MenuItem, |
|||
message, |
|||
Modal, |
|||
Tag, |
|||
} from 'ant-design-vue'; |
|||
|
|||
import { useJobInfosApi } from '../../api/useJobInfosApi'; |
|||
import { BackgroundJobsPermissions } from '../../constants/permissions'; |
|||
import { useJobEnumsMap } from '../../hooks/useJobEnumsMap'; |
|||
import { JobSource, JobStatus, JobType } from '../../types/job-infos'; |
|||
|
|||
defineOptions({ |
|||
name: 'GdprTable', |
|||
}); |
|||
|
|||
const PauseIcon = createIconifyIcon('material-symbols:pause-outline'); |
|||
const ResumeIcon = createIconifyIcon('material-symbols:resume-outline'); |
|||
const StartIcon = createIconifyIcon('solar:restart-circle-outline'); |
|||
const StopIcon = createIconifyIcon('solar:stop-circle-outline'); |
|||
const TriggerIcon = createIconifyIcon('grommet-icons:trigger'); |
|||
|
|||
const allowPauseStatus = [ |
|||
JobStatus.Queuing, |
|||
JobStatus.Running, |
|||
JobStatus.FailedRetry, |
|||
]; |
|||
const allowResumeStatus = [JobStatus.Paused, JobStatus.Stopped]; |
|||
const allowTriggerStatus = [ |
|||
JobStatus.Queuing, |
|||
JobStatus.Running, |
|||
JobStatus.Completed, |
|||
JobStatus.FailedRetry, |
|||
]; |
|||
const allowStartStatus = [ |
|||
JobStatus.None, |
|||
JobStatus.Stopped, |
|||
JobStatus.FailedRetry, |
|||
]; |
|||
const allowStopStatus = [JobStatus.Queuing, JobStatus.Running]; |
|||
|
|||
const selectedKeys = ref<string[]>([]); |
|||
|
|||
const { hasAccessByCodes } = useAccess(); |
|||
const { |
|||
bulkDeleteApi, |
|||
bulkStartApi, |
|||
bulkStopApi, |
|||
cancel, |
|||
deleteApi, |
|||
getPagedListApi, |
|||
pauseApi, |
|||
resumeApi, |
|||
startApi, |
|||
stopApi, |
|||
triggerApi, |
|||
} = useJobInfosApi(); |
|||
const { |
|||
jobPriorityColor, |
|||
jobPriorityMap, |
|||
jobSourceMap, |
|||
jobStatusColor, |
|||
jobStatusMap, |
|||
jobTypeMap, |
|||
} = useJobEnumsMap(); |
|||
|
|||
const formOptions: VbenFormProps = { |
|||
// 默认展开 |
|||
collapsed: true, |
|||
collapsedRows: 2, |
|||
fieldMappingTime: [ |
|||
[ |
|||
'time', |
|||
['beginTime', 'endTime'], |
|||
['YYYY-MM-DD HH:mm:ss', 'YYYY-MM-DD HH:mm:ss'], |
|||
], |
|||
[ |
|||
'lastRunTime', |
|||
['beginLastRunTime', 'endLastRunTime'], |
|||
['YYYY-MM-DD HH:mm:ss', 'YYYY-MM-DD HH:mm:ss'], |
|||
], |
|||
], |
|||
schema: [ |
|||
{ |
|||
component: 'Input', |
|||
fieldName: 'group', |
|||
label: $t('TaskManagement.DisplayName:Group'), |
|||
}, |
|||
{ |
|||
component: 'Input', |
|||
fieldName: 'name', |
|||
label: $t('TaskManagement.DisplayName:Name'), |
|||
}, |
|||
{ |
|||
component: 'RangePicker', |
|||
componentProps: { |
|||
showTime: true, |
|||
}, |
|||
fieldName: 'lastRunTime', |
|||
formItemClass: 'col-span-2 items-baseline', |
|||
label: $t('TaskManagement.DisplayName:LastRunTime'), |
|||
}, |
|||
{ |
|||
component: 'Select', |
|||
componentProps: { |
|||
allowClear: true, |
|||
options: [ |
|||
{ |
|||
label: jobStatusMap[JobStatus.None], |
|||
value: JobStatus.None, |
|||
}, |
|||
{ |
|||
label: jobStatusMap[JobStatus.Running], |
|||
value: JobStatus.Running, |
|||
}, |
|||
{ |
|||
label: jobStatusMap[JobStatus.Completed], |
|||
value: JobStatus.Completed, |
|||
}, |
|||
{ |
|||
label: jobStatusMap[JobStatus.FailedRetry], |
|||
value: JobStatus.FailedRetry, |
|||
}, |
|||
{ |
|||
label: jobStatusMap[JobStatus.Paused], |
|||
value: JobStatus.Paused, |
|||
}, |
|||
{ |
|||
label: jobStatusMap[JobStatus.Queuing], |
|||
value: JobStatus.Queuing, |
|||
}, |
|||
{ |
|||
label: jobStatusMap[JobStatus.Stopped], |
|||
value: JobStatus.Stopped, |
|||
}, |
|||
], |
|||
}, |
|||
defaultValue: JobStatus.Running, |
|||
fieldName: 'status', |
|||
label: $t('TaskManagement.DisplayName:Status'), |
|||
}, |
|||
{ |
|||
component: 'Select', |
|||
componentProps: { |
|||
allowClear: true, |
|||
options: [ |
|||
{ |
|||
label: jobSourceMap[JobSource.None], |
|||
value: JobSource.None, |
|||
}, |
|||
{ |
|||
label: jobSourceMap[JobSource.User], |
|||
value: JobSource.User, |
|||
}, |
|||
{ |
|||
label: jobSourceMap[JobSource.System], |
|||
value: JobSource.System, |
|||
}, |
|||
], |
|||
}, |
|||
defaultValue: JobSource.User, |
|||
fieldName: 'source', |
|||
label: $t('TaskManagement.DisplayName:Source'), |
|||
}, |
|||
{ |
|||
component: 'RangePicker', |
|||
componentProps: { |
|||
showTime: true, |
|||
}, |
|||
fieldName: 'time', |
|||
formItemClass: 'col-span-2 items-baseline', |
|||
label: $t('TaskManagement.DisplayName:BeginTime'), |
|||
}, |
|||
{ |
|||
component: 'Select', |
|||
componentProps: { |
|||
allowClear: true, |
|||
options: [ |
|||
{ |
|||
label: jobTypeMap[JobType.Once], |
|||
value: JobType.Once, |
|||
}, |
|||
{ |
|||
label: jobTypeMap[JobType.Period], |
|||
value: JobType.Period, |
|||
}, |
|||
{ |
|||
label: jobTypeMap[JobType.Persistent], |
|||
value: JobType.Persistent, |
|||
}, |
|||
], |
|||
}, |
|||
fieldName: 'jobType', |
|||
label: $t('TaskManagement.DisplayName:JobType'), |
|||
}, |
|||
{ |
|||
component: 'Input', |
|||
componentProps: { |
|||
allowClear: true, |
|||
}, |
|||
fieldName: 'filter', |
|||
formItemClass: 'col-span-2 items-baseline', |
|||
label: $t('AbpUi.Search'), |
|||
}, |
|||
{ |
|||
component: 'Checkbox', |
|||
componentProps: { |
|||
render: () => { |
|||
return h('span', $t('TaskManagement.DisplayName:IsAbandoned')); |
|||
}, |
|||
}, |
|||
fieldName: 'isAbandoned', |
|||
label: $t('TaskManagement.DisplayName:IsAbandoned'), |
|||
}, |
|||
], |
|||
// 控制表单是否显示折叠按钮 |
|||
showCollapseButton: true, |
|||
submitOnChange: true, |
|||
// 按下回车时是否提交表单 |
|||
submitOnEnter: true, |
|||
wrapperClass: 'grid-cols-4', |
|||
}; |
|||
const gridOptions: VxeGridProps<BackgroundJobInfoDto> = { |
|||
columns: [ |
|||
{ |
|||
align: 'center', |
|||
fixed: 'left', |
|||
type: 'checkbox', |
|||
}, |
|||
{ |
|||
align: 'left', |
|||
field: 'group', |
|||
fixed: 'left', |
|||
sortable: true, |
|||
title: $t('TaskManagement.DisplayName:Group'), |
|||
width: 150, |
|||
}, |
|||
{ |
|||
align: 'left', |
|||
field: 'name', |
|||
fixed: 'left', |
|||
slots: { default: 'name' }, |
|||
sortable: true, |
|||
title: $t('TaskManagement.DisplayName:Name'), |
|||
width: 300, |
|||
}, |
|||
{ |
|||
align: 'left', |
|||
field: 'description', |
|||
sortable: true, |
|||
title: $t('TaskManagement.DisplayName:Description'), |
|||
width: 350, |
|||
}, |
|||
{ |
|||
align: 'left', |
|||
field: 'creationTime', |
|||
formatter({ cellValue }) { |
|||
return cellValue ? formatToDateTime(cellValue) : cellValue; |
|||
}, |
|||
sortable: true, |
|||
title: $t('TaskManagement.DisplayName:CreationTime'), |
|||
width: 150, |
|||
}, |
|||
{ |
|||
align: 'left', |
|||
field: 'status', |
|||
slots: { default: 'status' }, |
|||
sortable: true, |
|||
title: $t('TaskManagement.DisplayName:Status'), |
|||
width: 100, |
|||
}, |
|||
{ |
|||
align: 'left', |
|||
field: 'result', |
|||
sortable: true, |
|||
title: $t('TaskManagement.DisplayName:Result'), |
|||
width: 200, |
|||
}, |
|||
{ |
|||
align: 'left', |
|||
field: 'lastRunTime', |
|||
formatter({ cellValue }) { |
|||
return cellValue ? formatToDateTime(cellValue) : cellValue; |
|||
}, |
|||
sortable: true, |
|||
title: $t('TaskManagement.DisplayName:LastRunTime'), |
|||
width: 150, |
|||
}, |
|||
{ |
|||
align: 'left', |
|||
field: 'nextRunTime', |
|||
formatter({ cellValue }) { |
|||
return cellValue ? formatToDateTime(cellValue) : cellValue; |
|||
}, |
|||
sortable: true, |
|||
title: $t('TaskManagement.DisplayName:NextRunTime'), |
|||
width: 150, |
|||
}, |
|||
{ |
|||
align: 'left', |
|||
field: 'jobType', |
|||
slots: { default: 'type' }, |
|||
sortable: true, |
|||
title: $t('TaskManagement.DisplayName:JobType'), |
|||
width: 150, |
|||
}, |
|||
{ |
|||
align: 'left', |
|||
field: 'priority', |
|||
slots: { default: 'priority' }, |
|||
sortable: true, |
|||
title: $t('TaskManagement.DisplayName:Priority'), |
|||
width: 150, |
|||
}, |
|||
{ |
|||
align: 'left', |
|||
field: 'cron', |
|||
sortable: true, |
|||
title: $t('TaskManagement.DisplayName:Cron'), |
|||
width: 150, |
|||
}, |
|||
{ |
|||
align: 'left', |
|||
field: 'triggerCount', |
|||
sortable: true, |
|||
title: $t('TaskManagement.DisplayName:TriggerCount'), |
|||
width: 100, |
|||
}, |
|||
{ |
|||
align: 'left', |
|||
field: 'tryCount', |
|||
sortable: true, |
|||
title: $t('TaskManagement.DisplayName:TryCount'), |
|||
width: 150, |
|||
}, |
|||
{ |
|||
field: 'action', |
|||
fixed: 'right', |
|||
slots: { default: 'action' }, |
|||
title: $t('AbpUi.Actions'), |
|||
width: 220, |
|||
}, |
|||
], |
|||
exportConfig: {}, |
|||
keepSource: true, |
|||
proxyConfig: { |
|||
ajax: { |
|||
query: async ({ page }, formValues) => { |
|||
return await getPagedListApi({ |
|||
maxResultCount: page.pageSize, |
|||
skipCount: (page.currentPage - 1) * page.pageSize, |
|||
...formValues, |
|||
}); |
|||
}, |
|||
}, |
|||
response: { |
|||
total: 'totalCount', |
|||
list: 'items', |
|||
}, |
|||
}, |
|||
toolbarConfig: { |
|||
refresh: true, |
|||
}, |
|||
}; |
|||
const gridEvents: VxeGridListeners<BackgroundJobInfoDto> = { |
|||
checkboxAll: (params) => { |
|||
selectedKeys.value = params.records.map((x) => x.id); |
|||
}, |
|||
checkboxChange: (params) => { |
|||
selectedKeys.value = params.records.map((x) => x.id); |
|||
}, |
|||
sortChange: onSort, |
|||
}; |
|||
const [JobInfoDrawer, jobDrawerApi] = useVbenDrawer({ |
|||
connectedComponent: defineAsyncComponent(() => import('./JobInfoDrawer.vue')), |
|||
}); |
|||
const [JobInfoDetailDrawer, jobDetailDrawerApi] = useVbenDrawer({ |
|||
connectedComponent: defineAsyncComponent( |
|||
() => import('./JobInfoDetailDrawer.vue'), |
|||
), |
|||
}); |
|||
const [Grid, gridApi] = useVbenVxeGrid({ |
|||
formOptions, |
|||
gridEvents, |
|||
gridOptions, |
|||
}); |
|||
|
|||
function onSort(params: { field: string; order: SortOrder }) { |
|||
const sorting = params.order ? `${params.field} ${params.order}` : undefined; |
|||
gridApi.query({ sorting }); |
|||
} |
|||
|
|||
function onShow(row: BackgroundJobInfoDto) { |
|||
jobDetailDrawerApi.setData(row); |
|||
jobDetailDrawerApi.open(); |
|||
} |
|||
|
|||
function onCreate() { |
|||
jobDrawerApi.setData({}); |
|||
jobDrawerApi.open(); |
|||
} |
|||
|
|||
function onEdit(row: BackgroundJobInfoDto) { |
|||
jobDrawerApi.setData(row); |
|||
jobDrawerApi.open(); |
|||
} |
|||
|
|||
function onDelete(row: BackgroundJobInfoDto) { |
|||
Modal.confirm({ |
|||
centered: true, |
|||
content: $t('AbpUi.ItemWillBeDeletedMessage'), |
|||
onCancel: () => { |
|||
cancel(); |
|||
}, |
|||
onOk: async () => { |
|||
gridApi.setLoading(true); |
|||
try { |
|||
await deleteApi(row.id); |
|||
message.success($t('AbpUi.DeletedSuccessfully')); |
|||
await gridApi.query(); |
|||
} finally { |
|||
gridApi.setLoading(false); |
|||
} |
|||
}, |
|||
title: $t('AbpUi.AreYouSure'), |
|||
}); |
|||
} |
|||
|
|||
function onBulkDelete() { |
|||
Modal.confirm({ |
|||
centered: true, |
|||
content: $t('TaskManagement.MultipleSelectJobsWillBeDeletedMessage'), |
|||
async onOk() { |
|||
gridApi.setLoading(true); |
|||
try { |
|||
await bulkDeleteApi({ |
|||
jobIds: selectedKeys.value, |
|||
}); |
|||
message.success($t('AbpUi.SavedSuccessfully')); |
|||
await gridApi.query(); |
|||
} finally { |
|||
gridApi.setLoading(false); |
|||
} |
|||
}, |
|||
title: $t('AbpUi.AreYouSure'), |
|||
}); |
|||
} |
|||
|
|||
function onBulkStop() { |
|||
Modal.confirm({ |
|||
centered: true, |
|||
content: $t('TaskManagement.SelectJobWillBeStopMessage'), |
|||
async onOk() { |
|||
gridApi.setLoading(true); |
|||
try { |
|||
await bulkStopApi({ |
|||
jobIds: selectedKeys.value, |
|||
}); |
|||
message.success($t('AbpUi.SavedSuccessfully')); |
|||
await gridApi.query(); |
|||
} finally { |
|||
gridApi.setLoading(false); |
|||
} |
|||
}, |
|||
title: $t('AbpUi.AreYouSure'), |
|||
}); |
|||
} |
|||
|
|||
function onBulkStart() { |
|||
Modal.confirm({ |
|||
centered: true, |
|||
content: $t('TaskManagement.SelectJobWillBeStartMessage'), |
|||
async onOk() { |
|||
gridApi.setLoading(true); |
|||
try { |
|||
await bulkStartApi({ |
|||
jobIds: selectedKeys.value, |
|||
}); |
|||
message.success($t('AbpUi.SavedSuccessfully')); |
|||
await gridApi.query(); |
|||
} finally { |
|||
gridApi.setLoading(false); |
|||
} |
|||
}, |
|||
title: $t('AbpUi.AreYouSure'), |
|||
}); |
|||
} |
|||
|
|||
function onMenuClick(row: BackgroundJobInfoDto, info: MenuInfo) { |
|||
switch (info.key) { |
|||
case 'pause': { |
|||
onJobStatusChange( |
|||
row, |
|||
pauseApi, |
|||
$t('TaskManagement.SelectJobWillBePauseMessage'), |
|||
); |
|||
break; |
|||
} |
|||
case 'resume': { |
|||
onJobStatusChange( |
|||
row, |
|||
resumeApi, |
|||
$t('TaskManagement.SelectJobWillBeResumeMessage'), |
|||
); |
|||
break; |
|||
} |
|||
case 'start': { |
|||
onJobStatusChange( |
|||
row, |
|||
startApi, |
|||
$t('TaskManagement.SelectJobWillBeStartMessage'), |
|||
); |
|||
break; |
|||
} |
|||
case 'stop': { |
|||
onJobStatusChange( |
|||
row, |
|||
stopApi, |
|||
$t('TaskManagement.SelectJobWillBeStopMessage'), |
|||
); |
|||
break; |
|||
} |
|||
case 'trigger': { |
|||
onJobStatusChange( |
|||
row, |
|||
triggerApi, |
|||
$t('TaskManagement.SelectJobWillBeTriggerMessage'), |
|||
); |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
|
|||
function onJobStatusChange( |
|||
job: BackgroundJobInfoDto, |
|||
api: (jobId: string) => Promise<void>, |
|||
warningMsg: string, |
|||
) { |
|||
Modal.confirm({ |
|||
centered: true, |
|||
content: warningMsg, |
|||
async onOk() { |
|||
gridApi.setLoading(true); |
|||
try { |
|||
await api(job.id); |
|||
message.success($t('AbpUi.SavedSuccessfully')); |
|||
await gridApi.query(); |
|||
} finally { |
|||
gridApi.setLoading(false); |
|||
} |
|||
}, |
|||
title: $t('AbpUi.AreYouSure'), |
|||
}); |
|||
} |
|||
</script> |
|||
|
|||
<template> |
|||
<Grid :table-title="$t('TaskManagement.BackgroundJobs')"> |
|||
<template #toolbar-tools> |
|||
<div class="flex flex-row gap-4"> |
|||
<Button |
|||
v-if="selectedKeys.length > 0" |
|||
:icon="h(StartIcon)" |
|||
ghost |
|||
type="primary" |
|||
v-access:code="[BackgroundJobsPermissions.Start]" |
|||
@click="onBulkStart" |
|||
> |
|||
{{ $t('TaskManagement.BackgroundJobs:Start') }} |
|||
</Button> |
|||
<Button |
|||
v-if="selectedKeys.length > 0" |
|||
:icon="h(StopIcon)" |
|||
danger |
|||
ghost |
|||
type="primary" |
|||
v-access:code="[BackgroundJobsPermissions.Stop]" |
|||
@click="onBulkStop" |
|||
> |
|||
{{ $t('TaskManagement.BackgroundJobs:Pause') }} |
|||
</Button> |
|||
<Button |
|||
:icon="h(PlusOutlined)" |
|||
type="primary" |
|||
v-access:code="[BackgroundJobsPermissions.Create]" |
|||
@click="onCreate" |
|||
> |
|||
{{ $t('TaskManagement.BackgroundJobs:AddNew') }} |
|||
</Button> |
|||
<Button |
|||
v-if="selectedKeys.length > 0" |
|||
:icon="h(DeleteOutlined)" |
|||
danger |
|||
type="primary" |
|||
v-access:code="[BackgroundJobsPermissions.Delete]" |
|||
@click="onBulkDelete" |
|||
> |
|||
{{ $t('AbpUi.Delete') }} |
|||
</Button> |
|||
</div> |
|||
</template> |
|||
<template #name="{ row }"> |
|||
<Button type="link" @click="onShow(row)">{{ row.name }}</Button> |
|||
</template> |
|||
<template #status="{ row }"> |
|||
<Tag :color="jobStatusColor[row.status]"> |
|||
{{ jobStatusMap[row.status] }} |
|||
</Tag> |
|||
</template> |
|||
<template #type="{ row }"> |
|||
<Tag color="blue"> |
|||
{{ jobTypeMap[row.jobType] }} |
|||
</Tag> |
|||
</template> |
|||
<template #priority="{ row }"> |
|||
<Tag :color="jobPriorityColor[row.priority]"> |
|||
{{ jobPriorityMap[row.priority] }} |
|||
</Tag> |
|||
</template> |
|||
<template #action="{ row }"> |
|||
<div class="flex flex-row"> |
|||
<Button |
|||
v-if="hasAccessByCodes([BackgroundJobsPermissions.Update])" |
|||
:icon="h(EditOutlined)" |
|||
block |
|||
type="link" |
|||
@click="onEdit(row)" |
|||
> |
|||
{{ $t('AbpUi.Edit') }} |
|||
</Button> |
|||
<Button |
|||
v-if=" |
|||
hasAccessByCodes([BackgroundJobsPermissions.Delete]) && |
|||
(row.source !== JobSource.System || |
|||
hasAccessByCodes([BackgroundJobsPermissions.ManageSystemJobs])) |
|||
" |
|||
:icon="h(DeleteOutlined)" |
|||
block |
|||
danger |
|||
type="link" |
|||
@click="onDelete(row)" |
|||
> |
|||
{{ $t('AbpUi.Delete') }} |
|||
</Button> |
|||
<Dropdown> |
|||
<template #overlay> |
|||
<Menu @click="(info) => onMenuClick(row, info)"> |
|||
<MenuItem |
|||
v-if=" |
|||
allowPauseStatus.includes(row.status) && |
|||
hasAccessByCodes([BackgroundJobsPermissions.Pause]) |
|||
" |
|||
key="pause" |
|||
:icon="h(PauseIcon)" |
|||
> |
|||
{{ $t('TaskManagement.BackgroundJobs:Pause') }} |
|||
</MenuItem> |
|||
<MenuItem |
|||
v-if=" |
|||
allowResumeStatus.includes(row.status) && |
|||
hasAccessByCodes([BackgroundJobsPermissions.Resume]) |
|||
" |
|||
key="resume" |
|||
:icon="h(ResumeIcon)" |
|||
> |
|||
{{ $t('TaskManagement.BackgroundJobs:Resume') }} |
|||
</MenuItem> |
|||
<MenuItem |
|||
v-if=" |
|||
allowTriggerStatus.includes(row.status) && |
|||
hasAccessByCodes([BackgroundJobsPermissions.Trigger]) |
|||
" |
|||
key="trigger" |
|||
:icon="h(TriggerIcon)" |
|||
> |
|||
{{ $t('TaskManagement.BackgroundJobs:Trigger') }} |
|||
</MenuItem> |
|||
<MenuItem |
|||
v-if=" |
|||
allowStartStatus.includes(row.status) && |
|||
hasAccessByCodes([BackgroundJobsPermissions.Start]) |
|||
" |
|||
key="start" |
|||
:icon="h(StartIcon)" |
|||
> |
|||
{{ $t('TaskManagement.BackgroundJobs:Start') }} |
|||
</MenuItem> |
|||
<MenuItem |
|||
v-if=" |
|||
allowStopStatus.includes(row.status) && |
|||
hasAccessByCodes([BackgroundJobsPermissions.Stop]) |
|||
" |
|||
key="stop" |
|||
:icon="h(StopIcon)" |
|||
> |
|||
{{ $t('TaskManagement.BackgroundJobs:Stop') }} |
|||
</MenuItem> |
|||
</Menu> |
|||
</template> |
|||
<Button :icon="h(EllipsisOutlined)" type="link" /> |
|||
</Dropdown> |
|||
</div> |
|||
</template> |
|||
</Grid> |
|||
<JobInfoDrawer /> |
|||
<JobInfoDetailDrawer /> |
|||
</template> |
|||
|
|||
<style lang="scss" scoped></style> |
|||
@ -0,0 +1 @@ |
|||
export * from './permissions'; |
|||
@ -0,0 +1,24 @@ |
|||
/** 作业管理权限 */ |
|||
export const BackgroundJobsPermissions = { |
|||
/** 新增 */ |
|||
Create: 'TaskManagement.BackgroundJobs.Create', |
|||
Default: 'TaskManagement.BackgroundJobs', |
|||
/** 删除 */ |
|||
Delete: 'TaskManagement.BackgroundJobs.Delete', |
|||
/** 管理触发器 */ |
|||
ManageActions: 'TaskManagement.BackgroundJobs.ManageActions', |
|||
/** 管理系统作业 */ |
|||
ManageSystemJobs: 'TaskManagement.BackgroundJobs.ManageSystemJobs', |
|||
/** 暂停 */ |
|||
Pause: 'TaskManagement.BackgroundJobs.Pause', |
|||
/** 恢复 */ |
|||
Resume: 'TaskManagement.BackgroundJobs.Resume', |
|||
/** 启动 */ |
|||
Start: 'TaskManagement.BackgroundJobs.Start', |
|||
/** 停止 */ |
|||
Stop: 'TaskManagement.BackgroundJobs.Stop', |
|||
/** 触发 */ |
|||
Trigger: 'TaskManagement.BackgroundJobs.Trigger', |
|||
/** 修改 */ |
|||
Update: 'TaskManagement.BackgroundJobs.Update', |
|||
}; |
|||
@ -0,0 +1 @@ |
|||
export * from './useJobEnumsMap'; |
|||
@ -0,0 +1,75 @@ |
|||
import { reactive } from 'vue'; |
|||
|
|||
import { $t } from '@vben/locales'; |
|||
|
|||
import { JobPriority, JobSource, JobStatus, JobType } from '../types/job-infos'; |
|||
|
|||
export function useJobEnumsMap() { |
|||
const jobStatusMap = reactive<{ |
|||
[key: number]: string; |
|||
}>({ |
|||
[JobStatus.Completed]: $t('TaskManagement.DisplayName:Completed'), |
|||
[JobStatus.FailedRetry]: $t('TaskManagement.DisplayName:FailedRetry'), |
|||
[JobStatus.None]: $t('TaskManagement.DisplayName:None'), |
|||
[JobStatus.Paused]: $t('TaskManagement.DisplayName:Paused'), |
|||
[JobStatus.Queuing]: $t('TaskManagement.DisplayName:Queuing'), |
|||
[JobStatus.Running]: $t('TaskManagement.DisplayName:Running'), |
|||
[JobStatus.Stopped]: $t('TaskManagement.DisplayName:Stopped'), |
|||
}); |
|||
const jobStatusColor = reactive<{ |
|||
[key: number]: string; |
|||
}>({ |
|||
[JobStatus.Completed]: '#339933', |
|||
[JobStatus.FailedRetry]: '#FF6600', |
|||
[JobStatus.None]: '', |
|||
[JobStatus.Paused]: '#CC6633', |
|||
[JobStatus.Queuing]: '#008B8B', |
|||
[JobStatus.Running]: '#3399CC', |
|||
[JobStatus.Stopped]: '#F00000', |
|||
}); |
|||
|
|||
const jobTypeMap = reactive<{ |
|||
[key: number]: string; |
|||
}>({ |
|||
[JobType.Once]: $t('TaskManagement.DisplayName:Once'), |
|||
[JobType.Period]: $t('TaskManagement.DisplayName:Period'), |
|||
[JobType.Persistent]: $t('TaskManagement.DisplayName:Persistent'), |
|||
}); |
|||
|
|||
const jobPriorityMap = reactive<{ |
|||
[key: number]: string; |
|||
}>({ |
|||
[JobPriority.AboveNormal]: $t('TaskManagement.DisplayName:AboveNormal'), |
|||
[JobPriority.BelowNormal]: $t('TaskManagement.DisplayName:BelowNormal'), |
|||
[JobPriority.High]: $t('TaskManagement.DisplayName:High'), |
|||
[JobPriority.Low]: $t('TaskManagement.DisplayName:Low'), |
|||
[JobPriority.Normal]: $t('TaskManagement.DisplayName:Normal'), |
|||
}); |
|||
|
|||
const jobPriorityColor = reactive<{ |
|||
[key: number]: string; |
|||
}>({ |
|||
[JobPriority.AboveNormal]: 'orange', |
|||
[JobPriority.BelowNormal]: 'cyan', |
|||
[JobPriority.High]: 'red', |
|||
[JobPriority.Low]: 'purple', |
|||
[JobPriority.Normal]: 'blue', |
|||
}); |
|||
|
|||
const jobSourceMap = reactive<{ |
|||
[key: number]: string; |
|||
}>({ |
|||
[JobSource.None]: $t('TaskManagement.DisplayName:None'), |
|||
[JobSource.System]: $t('TaskManagement.DisplayName:System'), |
|||
[JobSource.User]: $t('TaskManagement.DisplayName:User'), |
|||
}); |
|||
|
|||
return { |
|||
jobPriorityColor, |
|||
jobPriorityMap, |
|||
jobSourceMap, |
|||
jobStatusColor, |
|||
jobStatusMap, |
|||
jobTypeMap, |
|||
}; |
|||
} |
|||
@ -0,0 +1,3 @@ |
|||
export * from './api'; |
|||
export * from './components'; |
|||
export * from './types'; |
|||
@ -0,0 +1,2 @@ |
|||
export * from './job-infos'; |
|||
export * from './job-logs'; |
|||
@ -0,0 +1,139 @@ |
|||
import type { |
|||
ExtensibleAuditedEntityDto, |
|||
IHasConcurrencyStamp, |
|||
PagedAndSortedResultRequestDto, |
|||
} from '@abp/core'; |
|||
|
|||
enum JobStatus { |
|||
Completed = 0, |
|||
FailedRetry = 15, |
|||
None = -1, |
|||
Paused = 20, |
|||
Queuing = 5, |
|||
Running = 10, |
|||
Stopped = 30, |
|||
} |
|||
|
|||
enum JobType { |
|||
Once = 0, |
|||
Period = 1, |
|||
Persistent = 2, |
|||
} |
|||
|
|||
enum JobPriority { |
|||
AboveNormal = 20, |
|||
BelowNormal = 10, |
|||
High = 25, |
|||
Low = 5, |
|||
Normal = 15, |
|||
} |
|||
|
|||
enum JobSource { |
|||
None = -1, |
|||
System = 10, |
|||
User = 0, |
|||
} |
|||
|
|||
interface BackgroundJobInfoDto |
|||
extends ExtensibleAuditedEntityDto<string>, |
|||
IHasConcurrencyStamp { |
|||
args: Record<string, any>; |
|||
beginTime: string; |
|||
cron?: string; |
|||
description?: string; |
|||
endTime?: string; |
|||
group: string; |
|||
interval: number; |
|||
isAbandoned: boolean; |
|||
isEnabled: boolean; |
|||
jobType: JobType; |
|||
lastRunTime?: string; |
|||
lockTimeOut: number; |
|||
maxCount: number; |
|||
maxTryCount: number; |
|||
name: string; |
|||
nextRunTime?: string; |
|||
priority: JobPriority; |
|||
result?: string; |
|||
source: JobSource; |
|||
status: JobStatus; |
|||
triggerCount: number; |
|||
tryCount: number; |
|||
type: string; |
|||
} |
|||
|
|||
interface BackgroundJobInfoCreateOrUpdateDto { |
|||
args: Record<string, any>; |
|||
cron?: string; |
|||
description?: string; |
|||
interval: number; |
|||
isEnabled: boolean; |
|||
jobType: JobType; |
|||
lockTimeOut: number; |
|||
maxCount: number; |
|||
maxTryCount: number; |
|||
priority: JobPriority; |
|||
} |
|||
|
|||
interface BackgroundJobInfoCreateDto |
|||
extends BackgroundJobInfoCreateOrUpdateDto { |
|||
beginTime: string; |
|||
endTime?: string; |
|||
group: string; |
|||
name: string; |
|||
nodeName?: string; |
|||
source: JobSource; |
|||
type: string; |
|||
} |
|||
|
|||
interface BackgroundJobInfoUpdateDto |
|||
extends BackgroundJobInfoCreateOrUpdateDto, |
|||
IHasConcurrencyStamp {} |
|||
|
|||
interface BackgroundJobInfoGetListInput extends PagedAndSortedResultRequestDto { |
|||
beginCreationTime?: Date; |
|||
beginLastRunTime?: Date; |
|||
beginTime?: Date; |
|||
endCreationTime?: Date; |
|||
endLastRunTime?: Date; |
|||
endTime?: Date; |
|||
filter?: string; |
|||
group?: string; |
|||
isAbandoned?: boolean; |
|||
jobType?: JobType; |
|||
name?: string; |
|||
priority?: JobPriority; |
|||
source?: JobSource; |
|||
status?: JobStatus; |
|||
type?: string; |
|||
} |
|||
|
|||
interface BackgroundJobInfoBatchInput { |
|||
jobIds: string[]; |
|||
} |
|||
|
|||
interface BackgroundJobParamterDto { |
|||
description?: string; |
|||
displayName: string; |
|||
name: string; |
|||
required: boolean; |
|||
} |
|||
|
|||
interface BackgroundJobDefinitionDto { |
|||
description?: string; |
|||
displayName: string; |
|||
name: string; |
|||
paramters: BackgroundJobParamterDto[]; |
|||
} |
|||
|
|||
export { JobPriority, JobSource, JobStatus, JobType }; |
|||
|
|||
export type { |
|||
BackgroundJobDefinitionDto, |
|||
BackgroundJobInfoBatchInput, |
|||
BackgroundJobInfoCreateDto, |
|||
BackgroundJobInfoDto, |
|||
BackgroundJobInfoGetListInput, |
|||
BackgroundJobInfoUpdateDto, |
|||
BackgroundJobParamterDto, |
|||
}; |
|||
@ -0,0 +1,23 @@ |
|||
import type { EntityDto, PagedAndSortedResultRequestDto } from '@abp/core'; |
|||
|
|||
interface BackgroundJobLogDto extends EntityDto<string> { |
|||
exception?: string; |
|||
jobGroup: string; |
|||
jobName: string; |
|||
jobType: string; |
|||
message: string; |
|||
runTime: string; |
|||
} |
|||
|
|||
interface BackgroundJobLogGetListInput extends PagedAndSortedResultRequestDto { |
|||
beginRunTime?: string; |
|||
endRunTime?: string; |
|||
filter?: string; |
|||
group?: string; |
|||
hasExceptions?: boolean; |
|||
jobId?: string; |
|||
name?: string; |
|||
type?: string; |
|||
} |
|||
|
|||
export type { BackgroundJobLogDto, BackgroundJobLogGetListInput }; |
|||
@ -0,0 +1,6 @@ |
|||
{ |
|||
"$schema": "https://json.schemastore.org/tsconfig", |
|||
"extends": "@vben/tsconfig/web.json", |
|||
"include": ["src"], |
|||
"exclude": ["node_modules"] |
|||
} |
|||
Loading…
Reference in new issue