feat(任务中心): 任务中心部分操作接口联调&跳转交互

This commit is contained in:
baiqi 2024-10-18 11:52:06 +08:00 committed by 刘瑞斌
parent e996402bc3
commit 3b437fe180
10 changed files with 175 additions and 25 deletions

View File

@ -28,6 +28,7 @@ import {
import type { CommonList, TableQueryParams } from '@/models/common'; import type { CommonList, TableQueryParams } from '@/models/common';
import type { TimingTaskCenterApiCaseItem } from '@/models/projectManagement/taskCenter'; import type { TimingTaskCenterApiCaseItem } from '@/models/projectManagement/taskCenter';
import type { import type {
TaskCenterResourcePoolItem,
TaskCenterResourcePoolStatus, TaskCenterResourcePoolStatus,
TaskCenterStatisticsItem, TaskCenterStatisticsItem,
TaskCenterSystemTaskItem, TaskCenterSystemTaskItem,
@ -58,7 +59,7 @@ export function getProjectExecuteTaskStatistics(data: string[]) {
// 项目任务-获取资源池列表 // 项目任务-获取资源池列表
export function getProjectTaskCenterResourcePools() { export function getProjectTaskCenterResourcePools() {
return MSR.get<string[]>({ url: projectTaskCenterResourcePoolsUrl }); return MSR.get<TaskCenterResourcePoolItem[]>({ url: projectTaskCenterResourcePoolsUrl });
} }
// 项目任务-停止任务 // 项目任务-停止任务
@ -98,7 +99,7 @@ export function getSystemExecuteTaskStatistics(data: string[]) {
// 系统任务-获取资源池列表 // 系统任务-获取资源池列表
export function getSystemTaskCenterResourcePools() { export function getSystemTaskCenterResourcePools() {
return MSR.get<string[]>({ url: systemTaskCenterResourcePoolsUrl }); return MSR.get<TaskCenterResourcePoolItem[]>({ url: systemTaskCenterResourcePoolsUrl });
} }
// 系统任务-停止任务 // 系统任务-停止任务
@ -138,7 +139,7 @@ export function getOrganizationExecuteTaskStatistics(data: string[]) {
// 组织任务-获取资源池列表 // 组织任务-获取资源池列表
export function getOrgTaskCenterResourcePools() { export function getOrgTaskCenterResourcePools() {
return MSR.get<string[]>({ url: organizationTaskCenterResourcePoolsUrl }); return MSR.get<TaskCenterResourcePoolItem[]>({ url: organizationTaskCenterResourcePoolsUrl });
} }
// 组织任务-停止任务 // 组织任务-停止任务

View File

@ -14,6 +14,10 @@
:placeholder="props.placeholder" :placeholder="props.placeholder"
:loading="props.loading" :loading="props.loading"
:value-key="props.valueKey" :value-key="props.valueKey"
:field-names="{
label: props.labelKey,
value: props.valueKey,
}"
:path-mode="false" :path-mode="false"
@change="handleMsCascaderChange" @change="handleMsCascaderChange"
@clear="clearValues" @clear="clearValues"
@ -56,7 +60,11 @@
:virtual-list-props="props.virtualListProps" :virtual-list-props="props.virtualListProps"
:loading="props.loading" :loading="props.loading"
:value-key="props.valueKey" :value-key="props.valueKey"
:path-mode="false" :field-names="{
label: props.labelKey,
value: props.valueKey,
}"
:path-mode="props.pathMode"
@change="(val) => emit('change', val)" @change="(val) => emit('change', val)"
> >
<template v-if="props.prefix" #prefix> <template v-if="props.prefix" #prefix>
@ -215,7 +223,7 @@
// TODO: arco-design cascader label/ path-mode // TODO: arco-design cascader label/ path-mode
function getInputLabel(data: CascaderOption) { function getInputLabel(data: CascaderOption) {
const isTagCount = data[props.labelKey].includes('+'); const isTagCount = (data[props.labelKey] || data.label).includes('+');
if (isTagCount) { if (isTagCount) {
return data.label; return data.label;
} }
@ -226,7 +234,7 @@
} }
function getInputLabelTooltip(data: CascaderOption) { function getInputLabelTooltip(data: CascaderOption) {
const isTagCount = data[props.labelKey].includes('+'); const isTagCount = (data[props.labelKey] || data.label).includes('+');
const label = getInputLabel(data); const label = getInputLabel(data);
if (isTagCount && Array.isArray(innerValue.value)) { if (isTagCount && Array.isArray(innerValue.value)) {
return Object.values(selectedLabelObj).join(''); return Object.values(selectedLabelObj).join('');

View File

@ -1,5 +1,5 @@
<template> <template>
<div> <div v-if="props.pagination?.total > 1">
<a-tooltip <a-tooltip
:content="activeDetailIsFirst ? t('ms.prevNextButton.noPrev') : t('ms.prevNextButton.prev')" :content="activeDetailIsFirst ? t('ms.prevNextButton.noPrev') : t('ms.prevNextButton.prev')"
:mouse-enter-delay="300" :mouse-enter-delay="300"

View File

@ -3,6 +3,14 @@
v-model:model-value="cron" v-model:model-value="cron"
:disabled="props.disabled" :disabled="props.disabled"
:class="props.class" :class="props.class"
:loading="loading"
:fallback-option="
(val) => ({
label: `${val}`,
value: val,
})
"
:placeholder="t('ms.cron.select.placeholder')"
allow-create allow-create
@change="emit('change', $event)" @change="emit('change', $event)"
> >
@ -40,6 +48,9 @@
const cron = defineModel<string>('modelValue', { const cron = defineModel<string>('modelValue', {
required: true, required: true,
}); });
const loading = defineModel<boolean>('loading', {
required: false,
});
const syncFrequencyOptions = [ const syncFrequencyOptions = [
{ label: t('ms.cron.select.timeTaskHour'), value: '0 0 0/1 * * ?' }, { label: t('ms.cron.select.timeTaskHour'), value: '0 0 0/1 * * ?' },

View File

@ -3,4 +3,5 @@ export default {
'ms.cron.select.timeTaskSixHour': '(Every 6 hours)', 'ms.cron.select.timeTaskSixHour': '(Every 6 hours)',
'ms.cron.select.timeTaskTwelveHour': '(Every 12 hours)', 'ms.cron.select.timeTaskTwelveHour': '(Every 12 hours)',
'ms.cron.select.timeTaskDay': '(Every day)', 'ms.cron.select.timeTaskDay': '(Every day)',
'ms.cron.select.placeholder': 'Expressions can be entered directly',
}; };

View File

@ -3,4 +3,5 @@ export default {
'ms.cron.select.timeTaskSixHour': '(每 6 小时)', 'ms.cron.select.timeTaskSixHour': '(每 6 小时)',
'ms.cron.select.timeTaskTwelveHour': '(每 12 小时)', 'ms.cron.select.timeTaskTwelveHour': '(每 12 小时)',
'ms.cron.select.timeTaskDay': '(每天)', 'ms.cron.select.timeTaskDay': '(每天)',
'ms.cron.select.placeholder': '可直接输入表达式',
}; };

View File

@ -8,6 +8,7 @@ export interface TaskCenterSystemTaskItem {
projectId: string; // 项目ID projectId: string; // 项目ID
organizationId: string; // 组织ID organizationId: string; // 组织ID
id: string; id: string;
reportId: string;
taskName: string; taskName: string;
resourceId: string; // 资源ID resourceId: string; // 资源ID
num: number; num: number;
@ -27,6 +28,7 @@ export interface TaskCenterTaskDetailParams extends TableQueryParams {
export interface TaskCenterTaskItem { export interface TaskCenterTaskItem {
id: string; id: string;
reportId: string;
num: number; num: number;
taskName: string; taskName: string;
status: string; // 执行状态 status: string; // 执行状态
@ -49,6 +51,7 @@ export interface TaskCenterTaskItem {
export interface TaskCenterTaskDetailItem { export interface TaskCenterTaskDetailItem {
id: string; id: string;
reportId: string;
taskId: string; // 任务ID taskId: string; // 任务ID
resourceId: string; resourceId: string;
resourceName: string; resourceName: string;
@ -85,3 +88,9 @@ export interface TaskCenterResourcePoolStatus {
id: string; id: string;
status: boolean; // 状态, true: 正常, false: 异常 status: boolean; // 状态, true: 正常, false: 异常
} }
export interface TaskCenterResourcePoolItem {
id: string;
name: string;
children: TaskCenterResourcePoolItem[];
}

View File

@ -15,10 +15,11 @@
:options="resourcePoolOptions" :options="resourcePoolOptions"
:placeholder="t('common.pleaseSelect')" :placeholder="t('common.pleaseSelect')"
option-size="small" option-size="small"
label-key="value" label-key="name"
value-key="key" value-key="id"
class="w-[240px]" class="w-[240px]"
:prefix="t('ms.taskCenter.resourcePool')" :prefix="t('ms.taskCenter.resourcePool')"
label-path-mode
@change="searchTask" @change="searchTask"
> >
</MsCascader> </MsCascader>
@ -141,7 +142,7 @@
{ {
title: t('ms.taskCenter.taskID'), title: t('ms.taskCenter.taskID'),
dataIndex: 'num', dataIndex: 'num',
width: 180, width: 100,
columnSelectorDisabled: true, columnSelectorDisabled: true,
showTooltip: true, showTooltip: true,
fixed: 'left', fixed: 'left',
@ -447,10 +448,7 @@
async function initResourcePools() { async function initResourcePools() {
try { try {
const res = await currentResourcePoolRequest(); const res = await currentResourcePoolRequest();
resourcePoolOptions.value = res.map((item) => ({ resourcePoolOptions.value = res;
key: item,
value: item,
}));
} catch (error) { } catch (error) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log(error); console.log(error);

View File

@ -130,6 +130,32 @@
</template> </template>
</ms-base-table> </ms-base-table>
<batchTaskReportDrawer v-model:visible="taskReportDrawerVisible" type="case" :module-type="reportModuleType" /> <batchTaskReportDrawer v-model:visible="taskReportDrawerVisible" type="case" :module-type="reportModuleType" />
<CaseReportDrawer
v-model:visible="showCaseDetailDrawer"
:report-id="activeDetailId"
:active-report-index="activeReportIndex"
:table-data="propsRes.data"
:page-change="propsEvent.pageChange"
:pagination="{
current: 1,
pageSize: 10,
total: 1,
}"
:share-time="shareTime"
/>
<ReportDetailDrawer
v-model:visible="showDetailDrawer"
:report-id="activeDetailId"
:active-report-index="activeReportIndex"
:table-data="propsRes.data"
:page-change="propsEvent.pageChange"
:pagination="{
current: 1,
pageSize: 10,
total: 1,
}"
:share-time="shareTime"
/>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
@ -145,7 +171,10 @@
import batchTaskReportDrawer from './batchTaskReportDrawer.vue'; import batchTaskReportDrawer from './batchTaskReportDrawer.vue';
import execStatus from './execStatus.vue'; import execStatus from './execStatus.vue';
import executionStatus from './executionStatus.vue'; import executionStatus from './executionStatus.vue';
import CaseReportDrawer from '@/views/api-test/report/component/caseReportDrawer.vue';
import ReportDetailDrawer from '@/views/api-test/report/component/reportDetailDrawer.vue';
import { getShareTime } from '@/api/modules/api-test/report';
import { import {
getOrganizationExecuteTaskList, getOrganizationExecuteTaskList,
getOrganizationExecuteTaskStatistics, getOrganizationExecuteTaskStatistics,
@ -164,15 +193,16 @@
import useModal from '@/hooks/useModal'; import useModal from '@/hooks/useModal';
import useOpenNewPage from '@/hooks/useOpenNewPage'; import useOpenNewPage from '@/hooks/useOpenNewPage';
import useTableStore from '@/hooks/useTableStore'; import useTableStore from '@/hooks/useTableStore';
import useAppStore from '@/store/modules/app';
import { characterLimit } from '@/utils'; import { characterLimit } from '@/utils';
import { hasAnyPermission } from '@/utils/permission'; import { hasAnyPermission } from '@/utils/permission';
import { TaskCenterTaskItem } from '@/models/taskCenter'; import { TaskCenterTaskItem } from '@/models/taskCenter';
import { ReportEnum } from '@/enums/reportEnum'; import { ReportEnum } from '@/enums/reportEnum';
import { ApiTestRouteEnum, TestPlanRouteEnum } from '@/enums/routeEnum'; import { TestPlanRouteEnum } from '@/enums/routeEnum';
import { TableKeyEnum } from '@/enums/tableEnum'; import { TableKeyEnum } from '@/enums/tableEnum';
import { FilterSlotNameEnum } from '@/enums/tableFilterEnum'; import { FilterSlotNameEnum } from '@/enums/tableFilterEnum';
import { ExecuteResultEnum, ExecuteStatusEnum, ExecuteTaskType, ExecuteTriggerMode } from '@/enums/taskCenter'; import { ExecuteResultEnum, ExecuteStatusEnum, ExecuteTaskType } from '@/enums/taskCenter';
import { executeFinishedRateMap, executeMethodMap, executeResultMap, executeStatusMap } from './config'; import { executeFinishedRateMap, executeMethodMap, executeResultMap, executeStatusMap } from './config';
@ -187,6 +217,7 @@
const { openModal } = useModal(); const { openModal } = useModal();
const { openNewPage } = useOpenNewPage(); const { openNewPage } = useOpenNewPage();
const tableStore = useTableStore(); const tableStore = useTableStore();
const appStore = useAppStore();
const keyword = ref(''); const keyword = ref('');
const tableSelected = ref<string[]>([]); const tableSelected = ref<string[]>([]);
@ -553,6 +584,45 @@
console.log('rerunTask', record); console.log('rerunTask', record);
} }
/**
* 报告详情 showReportDetail
*/
const activeDetailId = ref<string>('');
const activeReportIndex = ref<number>(0);
const showDetailDrawer = ref<boolean>(false);
const showCaseDetailDrawer = ref<boolean>(false);
function showReportDetail(record: TaskCenterTaskItem) {
activeDetailId.value = record.id;
if (record.taskType === 'API_SCENARIO') {
showDetailDrawer.value = true;
} else {
showCaseDetailDrawer.value = true;
}
}
const shareTime = ref<string>('');
async function getTime() {
try {
const res = await getShareTime(appStore.currentProjectId);
const match = res.match(/^(\d+)([MYHD])$/);
if (match) {
const value = parseInt(match[1], 10);
const type = match[2];
const translations: Record<string, string> = {
M: t('msTimeSelector.month'),
Y: t('msTimeSelector.year'),
H: t('msTimeSelector.hour'),
D: t('msTimeSelector.day'),
};
shareTime.value = value + (translations[type] || translations.D);
}
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
}
}
const taskReportDrawerVisible = ref(false); const taskReportDrawerVisible = ref(false);
const reportModuleType = ref(); const reportModuleType = ref();
function checkReport(record: TaskCenterTaskItem) { function checkReport(record: TaskCenterTaskItem) {
@ -561,8 +631,8 @@
? ReportEnum.API_REPORT ? ReportEnum.API_REPORT
: ReportEnum.API_SCENARIO_REPORT; : ReportEnum.API_SCENARIO_REPORT;
taskReportDrawerVisible.value = true; taskReportDrawerVisible.value = true;
} else if (record.taskType === ExecuteTaskType.API_SCENARIO) { } else if (['API_CASE', 'API_SCENARIO'].includes(record.taskType)) {
openNewPage(ApiTestRouteEnum.API_TEST_REPORT); showReportDetail(record);
} else if (record.taskType === ExecuteTaskType.TEST_PLAN) { } else if (record.taskType === ExecuteTaskType.TEST_PLAN) {
openNewPage(TestPlanRouteEnum.TEST_PLAN_REPORT); openNewPage(TestPlanRouteEnum.TEST_PLAN_REPORT);
} }
@ -570,6 +640,7 @@
onMounted(async () => { onMounted(async () => {
searchTask(); searchTask();
getTime();
}); });
watch( watch(

View File

@ -21,7 +21,7 @@
@batch-action="handleTableBatch" @batch-action="handleTableBatch"
> >
<template #num="{ record }"> <template #num="{ record }">
<a-button type="text" class="max-w-full justify-start px-0" @click="openTask(record.id)"> <a-button type="text" class="max-w-full justify-start px-0" @click="checkDetail(record)">
<a-tooltip :content="record.id"> <a-tooltip :content="record.id">
<div class="one-line-text"> <div class="one-line-text">
{{ record.num }} {{ record.num }}
@ -39,8 +39,20 @@
<template #resourceType="{ record }"> <template #resourceType="{ record }">
{{ t(scheduleTaskTypeMap[record.resourceType]) }} {{ t(scheduleTaskTypeMap[record.resourceType]) }}
</template> </template>
<template #runRule="{ record }">
<MsCronSelect
v-model:model-value="record.value"
v-model:loading="record.runRuleLoading"
@change="(val) => handleRunRuleChange(val, record)"
/>
</template>
<template #action="{ record }"> <template #action="{ record }">
<MsButton v-permission="['SYSTEM_USER:READ+DELETE']" class="!mr-[12px]" @click="deleteTask(record)"> <MsButton
v-if="['API_IMPORT', 'TEST_PLAN', 'API_SCENARIO'].includes(record.resourceType)"
v-permission="['SYSTEM_USER:READ+DELETE']"
class="!mr-[12px]"
@click="deleteTask(record)"
>
{{ t('common.delete') }} {{ t('common.delete') }}
</MsButton> </MsButton>
<MsButton v-permission="['SYSTEM_USER:READ+DELETE']" class="!mr-0" @click="checkDetail(record)"> <MsButton v-permission="['SYSTEM_USER:READ+DELETE']" class="!mr-0" @click="checkDetail(record)">
@ -55,6 +67,7 @@
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import MsButton from '@/components/pure/ms-button/index.vue'; import MsButton from '@/components/pure/ms-button/index.vue';
import MsCronSelect from '@/components/pure/ms-cron-select/index.vue';
import MsIcon from '@/components/pure/ms-icon-font/index.vue'; import MsIcon from '@/components/pure/ms-icon-font/index.vue';
import MsBaseTable from '@/components/pure/ms-table/base-table.vue'; import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
import type { BatchActionParams, BatchActionQueryParams, MsTableColumn } from '@/components/pure/ms-table/type'; import type { BatchActionParams, BatchActionQueryParams, MsTableColumn } from '@/components/pure/ms-table/type';
@ -69,7 +82,7 @@
import { characterLimit } from '@/utils'; import { characterLimit } from '@/utils';
import { hasAnyPermission } from '@/utils/permission'; import { hasAnyPermission } from '@/utils/permission';
import { ApiTestRouteEnum } from '@/enums/routeEnum'; import { ApiTestRouteEnum, TestPlanRouteEnum } from '@/enums/routeEnum';
import { TableKeyEnum } from '@/enums/tableEnum'; import { TableKeyEnum } from '@/enums/tableEnum';
import { scheduleTaskTypeMap } from './config'; import { scheduleTaskTypeMap } from './config';
@ -115,8 +128,9 @@
}, },
{ {
title: 'ms.taskCenter.runRule', title: 'ms.taskCenter.runRule',
slotName: 'runRule',
dataIndex: 'value', dataIndex: 'value',
width: 120, width: 220,
}, },
{ {
title: 'ms.taskCenter.operationUser', title: 'ms.taskCenter.operationUser',
@ -219,6 +233,7 @@
(item) => { (item) => {
return { return {
...item, ...item,
runRuleLoading: false,
operationTime: item.operationTime ? dayjs(item.operationTime).format('YYYY-MM-DD HH:mm:ss') : '-', operationTime: item.operationTime ? dayjs(item.operationTime).format('YYYY-MM-DD HH:mm:ss') : '-',
lastFinishTime: item.lastFinishTime ? dayjs(item.lastFinishTime).format('YYYY-MM-DD HH:mm:ss') : '-', lastFinishTime: item.lastFinishTime ? dayjs(item.lastFinishTime).format('YYYY-MM-DD HH:mm:ss') : '-',
nextExecuteTime: item.nextExecuteTime ? dayjs(item.nextExecuteTime).format('YYYY-MM-DD HH:mm:ss') : '-', nextExecuteTime: item.nextExecuteTime ? dayjs(item.nextExecuteTime).format('YYYY-MM-DD HH:mm:ss') : '-',
@ -282,9 +297,28 @@
} }
function checkDetail(record: any) { function checkDetail(record: any) {
openNewPage(ApiTestRouteEnum.API_TEST_MANAGEMENT, { switch (record.resourceType) {
taskDrawer: true, case 'API_IMPORT':
}); openNewPage(ApiTestRouteEnum.API_TEST_MANAGEMENT, {
taskDrawer: true,
});
break;
case 'TEST_PLAN':
openNewPage(TestPlanRouteEnum.TEST_PLAN_REPORT_DETAIL, {
reportId: record.reportId,
});
break;
case 'API_SCENARIO':
openNewPage(ApiTestRouteEnum.API_TEST_REPORT, {
reportId: record.reportId,
});
break;
default:
openNewPage(ApiTestRouteEnum.API_TEST_MANAGEMENT, {
taskDrawer: true,
});
break;
}
} }
async function handleBeforeEnableChange(record: any) { async function handleBeforeEnableChange(record: any) {
@ -316,6 +350,22 @@
} }
} }
async function handleRunRuleChange(
val: string | number | boolean | Record<string, any> | (string | number | boolean | Record<string, any>)[],
record: any
) {
try {
record.runRuleLoading = true;
// await runRuleChange();
Message.success(t('common.updateSuccess'));
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
} finally {
record.runRuleLoading = false;
}
}
onMounted(() => { onMounted(() => {
loadList(); loadList();
}); });