Browse Source

feat(vben5): add tasks package

pull/1175/head
colin 10 months ago
parent
commit
1be7bf4b9e
  1. 1
      apps/vben5/apps/app-antd/package.json
  2. 6
      apps/vben5/apps/app-antd/src/locales/langs/en-US/abp.json
  3. 6
      apps/vben5/apps/app-antd/src/locales/langs/zh-CN/abp.json
  4. 19
      apps/vben5/apps/app-antd/src/router/routes/modules/abp.ts
  5. 15
      apps/vben5/apps/app-antd/src/views/tasks/job-infos/index.vue
  6. 38
      apps/vben5/packages/@abp/tasks/package.json
  7. 2
      apps/vben5/packages/@abp/tasks/src/api/index.ts
  8. 169
      apps/vben5/packages/@abp/tasks/src/api/useJobInfosApi.ts
  9. 46
      apps/vben5/packages/@abp/tasks/src/api/useJobLogsApi.ts
  10. 1
      apps/vben5/packages/@abp/tasks/src/components/index.ts
  11. 303
      apps/vben5/packages/@abp/tasks/src/components/job-infos/JobInfoDetailDrawer.vue
  12. 349
      apps/vben5/packages/@abp/tasks/src/components/job-infos/JobInfoDrawer.vue
  13. 736
      apps/vben5/packages/@abp/tasks/src/components/job-infos/JobInfoTable.vue
  14. 1
      apps/vben5/packages/@abp/tasks/src/constants/index.ts
  15. 24
      apps/vben5/packages/@abp/tasks/src/constants/permissions.ts
  16. 1
      apps/vben5/packages/@abp/tasks/src/hooks/index.ts
  17. 75
      apps/vben5/packages/@abp/tasks/src/hooks/useJobEnumsMap.ts
  18. 3
      apps/vben5/packages/@abp/tasks/src/index.ts
  19. 2
      apps/vben5/packages/@abp/tasks/src/types/index.ts
  20. 139
      apps/vben5/packages/@abp/tasks/src/types/job-infos.ts
  21. 23
      apps/vben5/packages/@abp/tasks/src/types/job-logs.ts
  22. 6
      apps/vben5/packages/@abp/tasks/tsconfig.json
  23. 20
      apps/vben5/packages/@abp/ui/src/components/properties/PropertyModal.vue
  24. 45
      apps/vben5/packages/@abp/ui/src/components/properties/PropertyTable.vue

1
apps/vben5/apps/app-antd/package.json

@ -41,6 +41,7 @@
"@abp/request": "workspace:*",
"@abp/saas": "workspace:*",
"@abp/settings": "workspace:*",
"@abp/tasks": "workspace:*",
"@abp/ui": "workspace:*",
"@vben/access": "workspace:*",
"@vben/common-ui": "workspace:*",

6
apps/vben5/apps/app-antd/src/locales/langs/en-US/abp.json

@ -107,5 +107,11 @@
"demo": {
"title": "Demo",
"books": "Books"
},
"tasks": {
"title": "Task Management",
"jobInfo": {
"title": "Job Manage"
}
}
}

6
apps/vben5/apps/app-antd/src/locales/langs/zh-CN/abp.json

@ -107,5 +107,11 @@
"demo": {
"title": "演示",
"books": "书籍列表"
},
"tasks": {
"title": "后台作业",
"jobInfo": {
"title": "作业管理"
}
}
}

19
apps/vben5/apps/app-antd/src/router/routes/modules/abp.ts

@ -431,6 +431,25 @@ const routes: RouteRecordRaw[] = [
},
],
},
{
name: 'TaskManagement',
path: '/task-management',
meta: {
title: $t('abp.tasks.title'),
icon: 'eos-icons:background-tasks',
},
children: [
{
meta: {
title: $t('abp.tasks.jobInfo.title'),
icon: 'eos-icons:job',
},
name: 'TaskManagementJobInfos',
path: '/task-management/background-jobs',
component: () => import('#/views/tasks/job-infos/index.vue'),
},
],
},
{
name: 'AbpDemo',
path: '/abp/demos',

15
apps/vben5/apps/app-antd/src/views/tasks/job-infos/index.vue

@ -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>

38
apps/vben5/packages/@abp/tasks/package.json

@ -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:"
}
}

2
apps/vben5/packages/@abp/tasks/src/api/index.ts

@ -0,0 +1,2 @@
export * from './useJobInfosApi';
export * from './useJobLogsApi';

169
apps/vben5/packages/@abp/tasks/src/api/useJobInfosApi.ts

@ -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,
};
}

46
apps/vben5/packages/@abp/tasks/src/api/useJobLogsApi.ts

@ -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,
};
}

1
apps/vben5/packages/@abp/tasks/src/components/index.ts

@ -0,0 +1 @@
export { default as JobInfoTable } from './job-infos/JobInfoTable.vue';

303
apps/vben5/packages/@abp/tasks/src/components/job-infos/JobInfoDetailDrawer.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>

349
apps/vben5/packages/@abp/tasks/src/components/job-infos/JobInfoDrawer.vue

@ -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>

736
apps/vben5/packages/@abp/tasks/src/components/job-infos/JobInfoTable.vue

@ -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>

1
apps/vben5/packages/@abp/tasks/src/constants/index.ts

@ -0,0 +1 @@
export * from './permissions';

24
apps/vben5/packages/@abp/tasks/src/constants/permissions.ts

@ -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',
};

1
apps/vben5/packages/@abp/tasks/src/hooks/index.ts

@ -0,0 +1 @@
export * from './useJobEnumsMap';

75
apps/vben5/packages/@abp/tasks/src/hooks/useJobEnumsMap.ts

@ -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,
};
}

3
apps/vben5/packages/@abp/tasks/src/index.ts

@ -0,0 +1,3 @@
export * from './api';
export * from './components';
export * from './types';

2
apps/vben5/packages/@abp/tasks/src/types/index.ts

@ -0,0 +1,2 @@
export * from './job-infos';
export * from './job-logs';

139
apps/vben5/packages/@abp/tasks/src/types/job-infos.ts

@ -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,
};

23
apps/vben5/packages/@abp/tasks/src/types/job-logs.ts

@ -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 };

6
apps/vben5/packages/@abp/tasks/tsconfig.json

@ -0,0 +1,6 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "@vben/tsconfig/web.json",
"include": ["src"],
"exclude": ["node_modules"]
}

20
apps/vben5/packages/@abp/ui/src/components/properties/PropertyModal.vue

@ -13,11 +13,15 @@ const emits = defineEmits<{
}>();
const [Form, formApi] = useVbenForm({
commonConfig: {
formItemClass: 'w-full',
},
handleSubmit: onSubmit,
schema: [
{
component: 'Input',
componentProps: {
allowClear: true,
autocomplete: 'off',
},
fieldName: 'key',
@ -25,9 +29,15 @@ const [Form, formApi] = useVbenForm({
rules: 'required',
},
{
component: 'Input',
component: 'Textarea',
componentProps: {
allowClear: true,
autocomplete: 'off',
autoSize: {
minRows: 2,
},
class: 'w-full',
showCount: true,
},
fieldName: 'value',
label: $t('component.extra_property_dictionary.value'),
@ -46,6 +56,14 @@ const [Modal, modalApi] = useVbenModal({
onConfirm: async () => {
await formApi.validateAndSubmitForm();
},
onOpenChange(isOpen) {
if (isOpen) {
const input = modalApi.getData();
if (input) {
formApi.setValues(input);
}
}
},
title: $t('component.extra_property_dictionary.title'),
});
function onSubmit(input: Record<string, any>) {

45
apps/vben5/packages/@abp/ui/src/components/properties/PropertyTable.vue

@ -25,6 +25,7 @@ const emits = defineEmits<{
(event: 'delete', data: PropertyInfo): void;
}>();
const DeleteOutlined = createIconifyIcon('ant-design:delete-outlined');
const EditOutlined = createIconifyIcon('ant-design:edit-outlined');
const PlusOutlined = createIconifyIcon('ant-design:plus-outlined');
const getDataResource = computed((): PropertyInfo[] => {
@ -77,6 +78,11 @@ function onCreate() {
modalApi.open();
}
function onEdit(prop: Record<string, string>) {
modalApi.setData(prop);
modalApi.open();
}
function onDelete(prop: Record<string, string>) {
emits('delete', {
key: prop.key!,
@ -108,21 +114,40 @@ function onChange(prop: Record<string, string>) {
</Button>
</div>
<div class="justify-center">
<Table :columns="getTableColumns" :data-source="getDataResource">
<Table bordered :columns="getTableColumns" :data-source="getDataResource">
<template #bodyCell="{ record, column }">
<template v-if="column.dataIndex === 'action'">
<Popconfirm
v-if="props.allowDelete"
:title="`${$t('component.extra_property_dictionary.itemWillBeDeleted', [record.key])}`"
@confirm="onDelete(record)"
>
<Button block class="flex items-center gap-2" danger type="link">
<div class="flex flex-row">
<Button
v-if="!props.disabled && props.allowEdit"
block
type="link"
class="flex items-center gap-2"
@click="onEdit(record)"
>
<template #icon>
<DeleteOutlined class="inline" />
<EditOutlined class="inline" />
</template>
{{ $t('component.extra_property_dictionary.actions.delete') }}
{{ $t('component.extra_property_dictionary.actions.update') }}
</Button>
</Popconfirm>
<Popconfirm
v-if="props.allowDelete"
:title="`${$t('component.extra_property_dictionary.itemWillBeDeleted', [record.key])}`"
@confirm="onDelete(record)"
>
<Button
block
class="flex items-center gap-2"
danger
type="link"
>
<template #icon>
<DeleteOutlined class="inline" />
</template>
{{ $t('component.extra_property_dictionary.actions.delete') }}
</Button>
</Popconfirm>
</div>
</template>
</template>
</Table>

Loading…
Cancel
Save