Browse Source

feat(tasks): 增加作业详情页.

pull/482/head
cKey 4 years ago
parent
commit
ee1aa235b2
  1. 2
      apps/vue/src/locales/lang/en/routes/basic.ts
  2. 2
      apps/vue/src/locales/lang/zh-CN/routes/basic.ts
  3. 35
      apps/vue/src/router/routes/basic.ts
  4. 3
      apps/vue/src/router/routes/index.ts
  5. 2
      apps/vue/src/views/platform/dataDic/components/DataItemTable.vue
  6. 102
      apps/vue/src/views/platform/dataDic/components/DataTree.vue
  7. 10
      apps/vue/src/views/platform/menu/hooks/useMenuFormContext.ts
  8. 120
      apps/vue/src/views/task-management/background-jobs/components/BackgroundJobInfoDetail.vue
  9. 9
      apps/vue/src/views/task-management/background-jobs/components/BackgroundJobInfoTable.vue
  10. 88
      apps/vue/src/views/task-management/background-jobs/datas/DescriptionData.ts
  11. 56
      apps/vue/src/views/task-management/background-jobs/datas/TableData.ts
  12. 2
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN/Abp/TaskManagement/Localization/Resources/en.json
  13. 2
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN/Abp/TaskManagement/Localization/Resources/zh-Hans.json
  14. 2
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/LINGYUN/Abp/TaskManagement/EntityFrameworkCore/EfCoreBackgroundJobLogRepository.cs

2
apps/vue/src/locales/lang/en/routes/basic.ts

@ -1,4 +1,6 @@
export default {
login: 'Login',
errorLogList: 'Error Log',
accountSetting: 'Account Setting',
accountCenter: 'Account Center'
};

2
apps/vue/src/locales/lang/zh-CN/routes/basic.ts

@ -1,4 +1,6 @@
export default {
login: '登录',
errorLogList: '错误日志列表',
accountSetting: '个人设置',
accountCenter: '个人中心'
};

35
apps/vue/src/router/routes/basic.ts

@ -76,3 +76,38 @@ export const ERROR_LOG_ROUTE: AppRouteRecordRaw = {
},
],
};
export const ACCOUNT_CENTER_ROUTE: AppRouteRecordRaw = {
path: '/account',
name: 'Account',
component: LAYOUT,
redirect: '/account/center',
meta: {
title: 'Account',
hideMenu: true,
ignoreAuth: true,
},
children: [
{
path: 'settings',
name: 'ASettings',
component: () => import('/@/views/account/setting/index.vue'),
meta: {
title: t('routes.basic.accountSetting'),
hideMenu: true,
ignoreAuth: true,
},
},
{
path: 'center',
name: 'ACenter',
component: () => import('/@/views/account/center/index.vue'),
meta: {
title: t('routes.basic.accountCenter'),
hideMenu: true,
ignoreAuth: true,
},
},
],
};

3
apps/vue/src/router/routes/index.ts

@ -1,6 +1,6 @@
import type { AppRouteRecordRaw, AppRouteModule } from '/@/router/types';
import { PAGE_NOT_FOUND_ROUTE, REDIRECT_ROUTE } from '/@/router/routes/basic';
import { PAGE_NOT_FOUND_ROUTE, REDIRECT_ROUTE, ACCOUNT_CENTER_ROUTE } from '/@/router/routes/basic';
import { mainOutRoutes } from './mainOut';
import { PageEnum } from '/@/enums/pageEnum';
@ -40,6 +40,7 @@ export const LoginRoute: AppRouteRecordRaw = {
export const basicRoutes = [
LoginRoute,
RootRoute,
ACCOUNT_CENTER_ROUTE,
...mainOutRoutes,
REDIRECT_ROUTE,
PAGE_NOT_FOUND_ROUTE,

2
apps/vue/src/views/platform/dataDic/components/DataItemTable.vue

@ -104,7 +104,7 @@
});
},
handleAppendItem() {
this.openModal(true, {} as DataItem, true);
this.openModal(true, {}, true);
},
handleEdit(record: Recordable) {
// ,

102
apps/vue/src/views/platform/dataDic/components/DataTree.vue

@ -23,7 +23,7 @@
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue';
import { defineComponent, ref, onMounted } from 'vue';
import { useLocalization } from '/@/hooks/abp/useLocalization';
import { Modal, Card } from 'ant-design-vue';
@ -47,7 +47,7 @@
DataItemModal,
},
emits: ['change', 'append-item'],
setup() {
setup(_, { emit }) {
const { L } = useLocalization('AppPlatform');
const title = L('DisplayName:DataDictionary');
const treeData = ref<Data[]>();
@ -62,62 +62,45 @@
const formTitle = ref('');
const getDataId = ref('');
return {
L,
formItems,
formTitle,
title,
getDataId,
treeData,
replaceFields,
registerDataModal,
openDataModal,
closeDataModal,
registerItemModal,
openItemModal,
};
},
mounted() {
this.onLoadAllDataDic();
},
methods: {
getContentMenus(node: any): ContextMenuItem[] {
onMounted(onLoadAllDataDic);
function getContentMenus(node: any): ContextMenuItem[] {
return [
{
label: this.L('Data:Edit'),
label: L('Data:Edit'),
handler: () => {
get(node.eventKey).then((res) => {
this.formTitle = this.L('Data:Edit');
this.openDataModal(true, res, true);
formTitle.value = L('Data:Edit');
openDataModal(true, res, true);
});
},
icon: 'ant-design:edit-outlined',
},
{
label: this.L('Data:AddNew'),
label: L('Data:AddNew'),
handler: () => {
this.handleNewData(node.eventKey);
handleNewData(node.eventKey);
},
icon: 'ant-design:plus-outlined',
},
{
label: this.L('Data:AppendItem'),
label: L('Data:AppendItem'),
handler: () => {
this.getDataId = node.eventKey;
this.openItemModal(true, {}, true);
getDataId.value = node.eventKey;
openItemModal(true, {}, true);
},
icon: 'ant-design:plus-square-outlined',
},
{
label: this.L('Data:Delete'),
label: L('Data:Delete'),
handler: () => {
Modal.warning({
title: this.t('AbpUi.AreYouSure'),
content: this.t('AbpUi.ItemWillBeDeletedMessage'),
title: L('AbpUi.AreYouSure'),
content: L('AbpUi.ItemWillBeDeletedMessage'),
okCancel: true,
onOk: () => {
remove(node.eventKey).then(() => {
this.onLoadAllDataDic();
onLoadAllDataDic();
});
},
});
@ -125,30 +108,34 @@
icon: 'ant-design:delete-outlined',
},
];
},
onLoadAllDataDic() {
}
function onLoadAllDataDic() {
getAll().then((res) => {
this.treeData = listToTree(res.items, {
treeData.value = listToTree(res.items, {
pid: 'parentId',
});
});
},
handleNodeChange(selectKeys: String[]) {
}
function handleNodeChange(selectKeys: String[]) {
if (selectKeys.length > 0) {
this.$emit('change', selectKeys[0]);
emit('change', selectKeys[0]);
}
},
handleNewData(parentId: string | null) {
this.formTitle = this.L('Data:AddNew');
this.openDataModal(
}
function handleNewData(parentId: string | null) {
formTitle.value = L('Data:AddNew');
openDataModal(
true,
{
parentId: parentId,
} as Data,
true,
);
},
handleSaveChanges(val) {
}
function handleSaveChanges(val) {
const api: Promise<Data> = val.id
? update(val.id, {
name: val.name,
@ -162,9 +149,28 @@
parentId: val.parentId,
});
return api.then(() => {
this.onLoadAllDataDic();
onLoadAllDataDic();
});
},
}
return {
L,
formItems,
formTitle,
title,
getDataId,
treeData,
replaceFields,
registerDataModal,
openDataModal,
closeDataModal,
registerItemModal,
openItemModal,
getContentMenus,
handleNewData,
handleNodeChange,
handleSaveChanges,
};
},
});
</script>

10
apps/vue/src/views/platform/menu/hooks/useMenuFormContext.ts

@ -23,7 +23,8 @@ export function useMenuFormContext({ menuModel, formElRef }: UseMenuFormContext)
const { L } = useLocalization('AppPlatform');
function getMetaFormSchemas(meta: DataItem[]): TabFormSchema[] {
return meta.map((item) => {
return meta.sort((pre, next) => pre.name.localeCompare(next.name))
.map((item) => {
const schema: TabFormSchema = {
tab: L('DisplayName:Meta'),
field: 'meta.'.concat(item.name),
@ -31,6 +32,11 @@ export function useMenuFormContext({ menuModel, formElRef }: UseMenuFormContext)
colProps: { span: 24 },
required: !item.allowBeNull,
component: 'Input',
componentProps: {
style: {
width: '100%',
},
}
};
switch (item.valueType) {
case ValueType.Boolean:
@ -46,7 +52,7 @@ export function useMenuFormContext({ menuModel, formElRef }: UseMenuFormContext)
onChange: (e: ChangeEvent) => {
model[field] = e.target.checked.toString();
},
});
}, () => item.displayName);
};
break;
case ValueType.Date:

120
apps/vue/src/views/task-management/background-jobs/components/BackgroundJobInfoDetail.vue

@ -0,0 +1,120 @@
<template>
<PageWrapper>
<template #title>
<ArrowLeftOutlined @click="handleBack" />
作业详情
</template>
<Skeleton :loading="jobInfo === undefined">
<Card class="mt-4" :title="L('BasicInfo')">
<Description @register="registerDescription" :data="jobInfo" />
</Card>
<Card class="mt-4" :title="L('BackgroundJobLogs')">
<List
ref="logListElRef"
item-layout="vertical"
size="default"
bordered
:loading="fetchingLog"
:pagination="{
pageSize: fetchLogCount,
total: maxLogCount,
showSizeChanger: true,
onChange: fetchJobLogs,
onShowSizeChange: handleSizeChange,
}"
:data-source="jobLogs"
>
<template #renderItem="{ item }">
<ListItem :key="item.id">
<ListItemMeta :description="item.message">
<template #avatar>
<Icon v-if="!item.exception" :size="40" icon="grommet-icons:status-good" color="seagreen" />
<Icon v-else :size="40" icon="grommet-icons:status-warning" color="orangered" />
</template>
<template #title>
<span>{{ item.runTime }}</span>
</template>
</ListItemMeta>
{{ item.exception ?? item.message }}
</ListItem>
</template>
</List>
</Card>
</Skeleton>
</PageWrapper>
</template>
<script lang="ts" setup>
import { onMounted, ref } from 'vue';
import { Card, List, Skeleton } from 'ant-design-vue';
import { ArrowLeftOutlined } from '@ant-design/icons-vue';
import { Icon } from '/@/components/Icon';
import { PageWrapper } from '/@/components/Page';
import { Description, useDescription } from '/@/components/Description';
import { useRoute, useRouter } from 'vue-router';
import { useLocalization } from '/@/hooks/abp/useLocalization';
import { formatPagedRequest } from '/@/utils/http/abp/helper';
import { getById } from '/@/api/task-management/backgroundJobInfo';
import { getList as getJobLogs } from '/@/api/task-management/backgroundJobLog';
import { BackgroundJobInfo } from '/@/api/task-management/model/backgroundJobInfoModel';
import { BackgroundJobLog } from '/@/api/task-management/model/backgroundJobLogModel';
import { getDescriptionSchemas } from '../datas/DescriptionData';
const ListItem = List.Item;
const ListItemMeta = List.Item.Meta;
const { L } = useLocalization('TaskManagement');
const { back } = useRouter();
const route = useRoute();
const maxLogCount = ref(0);
const fetchLogCount = ref(10);
const fetchingLog = ref(false);
const logListElRef = ref<any>();
const jobInfo = ref<BackgroundJobInfo>();
const jobLogs = ref<BackgroundJobLog[]>([]);
const [registerDescription] = useDescription({
bordered: true,
column: 3,
schema: getDescriptionSchemas(),
});
onMounted(fetchJob);
function fetchJob() {
getById(String(route.params.id)).then((res) => {
jobInfo.value = res;
jobLogs.value = [];
fetchJobLogs(1);
});
}
function fetchJobLogs(page: number) {
const request = {
skipCount: page,
maxResultCount: fetchLogCount.value,
};
formatPagedRequest(request);
fetchingLog.value = true;
getJobLogs({
jobId: String(route.params.id),
sorting: '',
skipCount: request.skipCount,
maxResultCount: request.maxResultCount,
}).then((res) => {
jobLogs.value = res.items;
maxLogCount.value = res.totalCount;
}).finally(() => {
fetchingLog.value = false;
});
}
function handleSizeChange(current: number, size: number) {
fetchLogCount.value = size;
fetchJobLogs(current);
}
function handleBack() {
back();
}
</script>

9
apps/vue/src/views/task-management/background-jobs/components/BackgroundJobInfoTable.vue

@ -26,6 +26,9 @@
<template #enable="{ record }">
<Switch :checked="record.isEnabled" disabled />
</template>
<template #name="{ record }">
<a href="javascript:(0);" @click="handleDetail(record)">{{ record.name }}</a>
</template>
<template #status="{ record }">
<Tooltip v-if="record.isAbandoned" color="orange" :title="L('Description:IsAbandoned')">
<Tag :color="JobStatusColor[record.status]">{{ JobStatusMap[record.status] }}</Tag>
@ -88,6 +91,7 @@
import { Switch, Modal, Tag, Tooltip, message } from 'ant-design-vue';
import { useLocalization } from '/@/hooks/abp/useLocalization';
import { usePermission } from '/@/hooks/web/usePermission';
import { useGo } from '/@/hooks/web/usePage';
import { useModal } from '/@/components/Modal';
import { BasicTable, TableAction, useTable } from '/@/components/Table';
import { formatPagedRequest } from '/@/utils/http/abp/helper';
@ -98,6 +102,7 @@
import { JobStatusMap, JobStatusColor, JobTypeMap, JobPriorityMap, JobPriorityColor } from '../datas/typing';
import BackgroundJobInfoModal from './BackgroundJobInfoModal.vue';
const go = useGo();
const { L } = useLocalization('TaskManagement');
const { hasPermission } = usePermission();
const [registerModal, { openModal }] = useModal();
@ -149,6 +154,10 @@
openModal(true, record);
}
function handleDetail(record) {
go(`/task-management/background-jobs/${record.id}`);
}
function handlePause(record) {
pause(record.id).then(() => {
message.success(L('Successful'));

88
apps/vue/src/views/task-management/background-jobs/datas/DescriptionData.ts

@ -0,0 +1,88 @@
import { DescItem } from "/@/components/Description";
import { useLocalization } from '/@/hooks/abp/useLocalization';
const { L } = useLocalization('TaskManagement');
export function getDescriptionSchemas() : DescItem[] {
return [
{
label: L('DisplayName:Group'),
field: 'group',
},
{
label: L('DisplayName:Name'),
field: 'name',
},
{
label: L('DisplayName:Type'),
field: 'type',
},
{
label: L('DisplayName:CreationTime'),
field: 'creationTime',
},
{
label: L('DisplayName:BeginTime'),
field: 'beginTime',
},
{
label: L('DisplayName:EndTime'),
field: 'endTime',
},
{
label: L('DisplayName:LockTimeOut'),
field: 'lockTimeOut',
span: 1,
},
{
label: L('DisplayName:Description'),
field: 'description',
span: 2,
},
{
label: L('DisplayName:LastRunTime'),
field: 'lastRunTime',
span: 1.5,
},
{
label: L('DisplayName:NextRunTime'),
field: 'nextRunTime',
span: 1.5,
},
{
label: L('DisplayName:Status'),
field: 'status',
span: 1,
},
{
label: L('DisplayName:JobType'),
field: 'jobType',
span: 1,
},
{
label: L('DisplayName:Priority'),
field: 'priority',
span: 1,
},
{
label: L('DisplayName:TriggerCount'),
field: 'triggerCount',
span: 0.75,
},
{
label: L('DisplayName:MaxCount'),
field: 'maxCount',
span: 0.75,
},
{
label: L('DisplayName:TryCount'),
field: 'tryCount',
span: 0.75,
},
{
label: L('DisplayName:MaxTryCount'),
field: 'maxTryCount',
span: 0.75,
},
];
}

56
apps/vue/src/views/task-management/background-jobs/datas/TableData.ts

@ -27,6 +27,9 @@ export function getDataColumns(): BasicColumn[] {
width: 300,
sorter: true,
fixed: 'left',
slots: {
customRender: 'name',
}
},
{
title: L('DisplayName:Description'),
@ -125,3 +128,56 @@ export function getDataColumns(): BasicColumn[] {
},
];
}
export function getLogDataColumns(): BasicColumn[] {
return [
{
title: 'id',
dataIndex: 'id',
width: 1,
ifShow: false,
},
{
title: L('DisplayName:Group'),
dataIndex: 'jobGroup',
align: 'left',
width: 150,
sorter: true,
},
{
title: L('DisplayName:Name'),
dataIndex: 'jobName',
align: 'left',
width: 150,
sorter: true,
},
{
title: L('DisplayName:Type'),
dataIndex: 'jobType',
align: 'left',
width: 150,
sorter: true,
},
{
title: L('DisplayName:LastRunTime'),
dataIndex: 'runTime',
align: 'left',
width: 150,
sorter: true,
},
{
title: L('DisplayName:Result'),
dataIndex: 'message',
align: 'left',
width: 150,
sorter: true,
},
{
title: L('DisplayName:Exception'),
dataIndex: 'exception',
align: 'left',
width: 150,
sorter: true,
},
];
}

2
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN/Abp/TaskManagement/Localization/Resources/en.json

@ -67,6 +67,8 @@
"DisplayName:AboveNormal": "Above Normal",
"DisplayName:High": "High",
"BackgroundJobs": "Jobs",
"BackgroundJobDetail": "Job Detail",
"BackgroundJobLogs": "Job Logs",
"BackgroundJobs:AddNew": "New Job",
"BackgroundJobs:Edit": "Edit Job",
"BackgroundJobs:AddNewArg": "New Args",

2
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN/Abp/TaskManagement/Localization/Resources/zh-Hans.json

@ -67,6 +67,8 @@
"DisplayName:AboveNormal": "高于正常",
"DisplayName:High": "高",
"BackgroundJobs": "作业列表",
"BackgroundJobDetail": "作业详情",
"BackgroundJobLogs": "作业日志",
"BackgroundJobs:AddNew": "新建作业",
"BackgroundJobs:Edit": "编辑作业",
"BackgroundJobs:AddNewArg": "添加参数",

2
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/LINGYUN/Abp/TaskManagement/EntityFrameworkCore/EfCoreBackgroundJobLogRepository.cs

@ -56,7 +56,7 @@ public class EfCoreBackgroundJobLogRepository :
.WhereIf(filter.HasExceptions.HasValue, x => !string.IsNullOrWhiteSpace(x.Exception))
.WhereIf(filter.BeginRunTime.HasValue, x => x.RunTime.CompareTo(filter.BeginRunTime.Value) >= 0)
.WhereIf(filter.EndRunTime.HasValue, x => x.RunTime.CompareTo(filter.EndRunTime.Value) <= 0)
.OrderBy(sorting ?? nameof(BackgroundJobInfo.Name))
.OrderBy(sorting ?? $"{nameof(BackgroundJobLog.RunTime)} DESC")
.PageBy(skipCount, maxResultCount)
.ToListAsync(GetCancellationToken(cancellationToken));
}

Loading…
Cancel
Save