feat(任务中心): 重跑&接口用例/场景/测试计划执行历史的执行结果

This commit is contained in:
baiqi 2024-11-14 16:51:30 +08:00 committed by Craftsman
parent 287c342268
commit 8914f05c93
19 changed files with 510 additions and 114 deletions

View File

@ -122,7 +122,6 @@ import {
ApiCaseChangeHistoryParams, ApiCaseChangeHistoryParams,
ApiCaseDependencyParams, ApiCaseDependencyParams,
ApiCaseDetail, ApiCaseDetail,
ApiCaseExecuteHistoryItem,
ApiCaseExecuteHistoryParams, ApiCaseExecuteHistoryParams,
ApiCasePageParams, ApiCasePageParams,
type ApiCaseStatisticsItem, type ApiCaseStatisticsItem,
@ -158,6 +157,7 @@ import {
UpdateScheduleParams, UpdateScheduleParams,
} from '@/models/apiTest/management'; } from '@/models/apiTest/management';
import type { BatchEditMockParams, MockDetail, MockParams, UpdateMockParams } from '@/models/apiTest/mock'; import type { BatchEditMockParams, MockDetail, MockParams, UpdateMockParams } from '@/models/apiTest/mock';
import type { ExecuteHistoryItem } from '@/models/apiTest/scenario';
import type { import type {
AddModuleParams, AddModuleParams,
BatchApiParams, BatchApiParams,
@ -605,7 +605,7 @@ export function batchExecuteCase(data: ApiCaseBatchExecuteParams) {
// 获取接口用例-执行历史 // 获取接口用例-执行历史
export function getApiCaseExecuteHistory(data: ApiCaseExecuteHistoryParams) { export function getApiCaseExecuteHistory(data: ApiCaseExecuteHistoryParams) {
return MSR.post<CommonList<ApiCaseExecuteHistoryItem>>({ url: GetExecuteHistoryUrl, data }); return MSR.post<CommonList<ExecuteHistoryItem>>({ url: GetExecuteHistoryUrl, data });
} }
// 获取接口用例-变更历史 // 获取接口用例-变更历史

View File

@ -19,6 +19,7 @@ import {
organizationStopTaskUrl, organizationStopTaskUrl,
organizationTaskCenterResourcePoolsUrl, organizationTaskCenterResourcePoolsUrl,
organizationTaskOrderUrl, organizationTaskOrderUrl,
organizationTaskRerunUrl,
} from '@/api/requrls/taskCenter'; } from '@/api/requrls/taskCenter';
import type { CommonList, TableQueryParams } from '@/models/common'; import type { CommonList, TableQueryParams } from '@/models/common';
@ -127,3 +128,8 @@ export function organizationBatchTaskReportList(data: TaskCenterBatchParams) {
export function organizationProjectOptions() { export function organizationProjectOptions() {
return MSR.get<ProjectListItem[]>({ url: organizationProjectOptionsUrl }); return MSR.get<ProjectListItem[]>({ url: organizationProjectOptionsUrl });
} }
// 组织任务-重跑
export function organizationTaskRerun(id: string) {
return MSR.get({ url: `${organizationTaskRerunUrl}/${id}` });
}

View File

@ -18,6 +18,7 @@ import {
projectStopTaskUrl, projectStopTaskUrl,
projectTaskCenterResourcePoolsUrl, projectTaskCenterResourcePoolsUrl,
projectTaskOrderUrl, projectTaskOrderUrl,
projectTaskRerunUrl,
scheduleProCenterListUrl, scheduleProCenterListUrl,
} from '@/api/requrls/taskCenter'; } from '@/api/requrls/taskCenter';
@ -127,3 +128,8 @@ export function projectEditCron(cron: string, id: string) {
export function projectBatchTaskReportList(data: TaskCenterBatchParams) { export function projectBatchTaskReportList(data: TaskCenterBatchParams) {
return MSR.post<CommonList<TaskCenterBatchTaskReportItem>>({ url: projectBatchTaskReportUrl, data }); return MSR.post<CommonList<TaskCenterBatchTaskReportItem>>({ url: projectBatchTaskReportUrl, data });
} }
// 项目任务-重跑
export function projectTaskRerun(id: string) {
return MSR.get({ url: `${projectTaskRerunUrl}/${id}` });
}

View File

@ -21,6 +21,7 @@ import {
systemStopTaskUrl, systemStopTaskUrl,
systemTaskCenterResourcePoolsUrl, systemTaskCenterResourcePoolsUrl,
systemTaskOrderUrl, systemTaskOrderUrl,
systemTaskRerunUrl,
} from '@/api/requrls/taskCenter'; } from '@/api/requrls/taskCenter';
import type { CommonList, TableQueryParams } from '@/models/common'; import type { CommonList, TableQueryParams } from '@/models/common';
@ -141,3 +142,8 @@ export function systemOrgOptions() {
export function systemProjectOptions() { export function systemProjectOptions() {
return MSR.get<ProjectListItem[]>({ url: systemProjectOptionsUrl }); return MSR.get<ProjectListItem[]>({ url: systemProjectOptionsUrl });
} }
// 系统任务-重跑
export function systemTaskRerun(id: string) {
return MSR.get({ url: `${systemTaskRerunUrl}/${id}` });
}

View File

@ -17,6 +17,7 @@ export const projectBatchOpenTaskUrl = '/project/task-center/schedule/batch-enab
export const projectBatchCloseTaskUrl = '/project/task-center/schedule/batch-disable'; // 项目-任务中心-系统后台任务-批量关闭任务 export const projectBatchCloseTaskUrl = '/project/task-center/schedule/batch-disable'; // 项目-任务中心-系统后台任务-批量关闭任务
export const projectEditCronUrl = '/project/task-center/schedule/update-cron'; // 项目-任务中心-系统后台任务-编辑 cron 表达式 export const projectEditCronUrl = '/project/task-center/schedule/update-cron'; // 项目-任务中心-系统后台任务-编辑 cron 表达式
export const projectBatchTaskReportUrl = '/project/task-center/exec-task/batch/page'; // 项目-任务中心-批量任务报告列表 export const projectBatchTaskReportUrl = '/project/task-center/exec-task/batch/page'; // 项目-任务中心-批量任务报告列表
export const projectTaskRerunUrl = '/project/task-center/exec-task/rerun'; // 项目-任务中心-重跑
export const systemScheduleListUrl = '/system/task-center/schedule/page'; // 系统任务-系统后台任务列表 export const systemScheduleListUrl = '/system/task-center/schedule/page'; // 系统任务-系统后台任务列表
export const systemExecuteTaskListUrl = '/system/task-center/exec-task/page'; // 系统任务-执行任务列表 export const systemExecuteTaskListUrl = '/system/task-center/exec-task/page'; // 系统任务-执行任务列表
@ -39,6 +40,7 @@ export const systemEditCronUrl = '/system/task-center/schedule/update-cron'; //
export const systemBatchTaskReportUrl = '/system/task-center/exec-task/batch/page'; // 系统任务-批量任务报告列表 export const systemBatchTaskReportUrl = '/system/task-center/exec-task/batch/page'; // 系统任务-批量任务报告列表
export const systemProjectOptionsUrl = '/system/task-center/project/options'; // 系统任务-项目列表 export const systemProjectOptionsUrl = '/system/task-center/project/options'; // 系统任务-项目列表
export const systemOrgOptionsUrl = '/system/task-center/organization/options'; // 系统任务-组织列表 export const systemOrgOptionsUrl = '/system/task-center/organization/options'; // 系统任务-组织列表
export const systemTaskRerunUrl = '/system/task-center/exec-task/rerun'; // 系统任务-任务中心-重跑
export const organizationScheduleListUrl = '/organization/task-center/schedule/page'; // 组织任务-系统后台任务列表 export const organizationScheduleListUrl = '/organization/task-center/schedule/page'; // 组织任务-系统后台任务列表
export const organizationExecuteTaskListUrl = '/organization/task-center/exec-task/page'; // 组织任务-执行任务列表 export const organizationExecuteTaskListUrl = '/organization/task-center/exec-task/page'; // 组织任务-执行任务列表
@ -59,3 +61,4 @@ export const organizationBatchCloseTaskUrl = '/organization/task-center/schedule
export const organizationEditCronUrl = '/organization/task-center/schedule/update-cron'; // 组织任务-系统后台任务-编辑 cron 表达式 export const organizationEditCronUrl = '/organization/task-center/schedule/update-cron'; // 组织任务-系统后台任务-编辑 cron 表达式
export const organizationBatchTaskReportUrl = '/organization/task-center/exec-task/batch/page'; // 组织任务-批量任务报告列表 export const organizationBatchTaskReportUrl = '/organization/task-center/exec-task/batch/page'; // 组织任务-批量任务报告列表
export const organizationProjectOptionsUrl = '/organization/task-center/project/options'; // 组织任务-项目列表 export const organizationProjectOptionsUrl = '/organization/task-center/project/options'; // 组织任务-项目列表
export const organizationTaskRerunUrl = '/organization/task-center/exec-task/rerun'; // 组织任务-任务中心-重跑

View File

@ -404,19 +404,6 @@ export interface ApiCaseChangeHistoryParams extends TableQueryParams {
export interface ApiCaseDependencyParams extends TableQueryParams { export interface ApiCaseDependencyParams extends TableQueryParams {
resourceId: string; resourceId: string;
} }
// 用例-执行历史-请求参数
export interface ApiCaseExecuteHistoryItem {
id: string;
num: string;
name: string;
operationUser: string;
createUser: string;
startTime: number;
status: RequestCaseStatus;
triggerMode: string;
deleted: boolean;
}
export interface syncItem { export interface syncItem {
header: boolean; header: boolean;
body: boolean; body: boolean;

View File

@ -18,6 +18,7 @@ import {
ScenarioStepType, ScenarioStepType,
WhileConditionType, WhileConditionType,
} from '@/enums/apiEnum'; } from '@/enums/apiEnum';
import type { ExecuteResultEnum, ExecuteStatusEnum } from '@/enums/taskCenter';
import { BatchApiParams, TableQueryParams } from '../common'; import { BatchApiParams, TableQueryParams } from '../common';
import { import {
@ -193,8 +194,14 @@ export interface ExecuteHistoryItem {
operationUser: string; operationUser: string;
createUser: string; createUser: string;
startTime: number; startTime: number;
status: string; status: ExecuteResultEnum;
triggerMode: string; triggerMode: string;
execStatus: ExecuteStatusEnum;
deleted: boolean;
historyDeleted: boolean;
integrated: boolean;
testPlanId?: string;
testPlanNum?: string;
} }
// 场景-变更历史列表查询参数 // 场景-变更历史列表查询参数

View File

@ -1,4 +1,4 @@
import type { ExecuteStatusEnum, ExecuteTaskType, ExecuteTriggerMode } from '@/enums/taskCenter'; import type { ExecuteResultEnum, ExecuteStatusEnum, ExecuteTaskType, ExecuteTriggerMode } from '@/enums/taskCenter';
import type { TableQueryParams } from './common'; import type { TableQueryParams } from './common';
@ -37,7 +37,7 @@ export interface TaskCenterTaskItem {
taskName: string; taskName: string;
status: string; // 执行状态 status: string; // 执行状态
caseCount: number; caseCount: number;
result: string; // 执行结果 result: ExecuteResultEnum; // 执行结果
taskType: ExecuteTaskType; // 任务类型 taskType: ExecuteTaskType; // 任务类型
resourceId: string; resourceId: string;
triggerMode: ExecuteTriggerMode; // 执行方式 triggerMode: ExecuteTriggerMode; // 执行方式
@ -61,7 +61,7 @@ export interface TaskCenterTaskDetailItem {
resourceName: string; resourceName: string;
taskOrigin: string; // 任务来源 taskOrigin: string; // 任务来源
status: ExecuteStatusEnum; // 执行状态 status: ExecuteStatusEnum; // 执行状态
result: string; // 执行结果 result: ExecuteResultEnum; // 执行结果
resourcePoolId: string; // 资源池ID resourcePoolId: string; // 资源池ID
resourcePoolNode: string; // 资源池节点 resourcePoolNode: string; // 资源池节点
resourceType: string; // 资源类型 resourceType: string; // 资源类型

View File

@ -63,7 +63,15 @@
</template> </template>
</ms-base-table> </ms-base-table>
</div> </div>
<caseAndScenarioReportDrawer v-model:visible="showResponse" :report-id="activeReportId" /> <caseExecuteResultDrawer
v-if="showResponse"
:id="activeReport.id"
v-model:visible="showResponse"
:user-name="activeReport.createUser"
:status="activeReport.execStatus"
:result="activeReport.status"
:resource-name="activeReport.name"
/>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
@ -74,7 +82,6 @@
import { MsTableColumn } from '@/components/pure/ms-table/type'; import { MsTableColumn } from '@/components/pure/ms-table/type';
import useTable from '@/components/pure/ms-table/useTable'; import useTable from '@/components/pure/ms-table/useTable';
import MsTag from '@/components/pure/ms-tag/ms-tag.vue'; import MsTag from '@/components/pure/ms-tag/ms-tag.vue';
import caseAndScenarioReportDrawer from '@/views/api-test/components/caseAndScenarioReportDrawer.vue';
import ExecutionStatus from '@/views/api-test/report/component/reportStatus.vue'; import ExecutionStatus from '@/views/api-test/report/component/reportStatus.vue';
import { getApiCaseExecuteHistory } from '@/api/modules/api-test/management'; import { getApiCaseExecuteHistory } from '@/api/modules/api-test/management';
@ -82,13 +89,17 @@
import useAppStore from '@/store/modules/app'; import useAppStore from '@/store/modules/app';
import { hasAnyPermission } from '@/utils/permission'; import { hasAnyPermission } from '@/utils/permission';
import { ApiCaseExecuteHistoryItem } from '@/models/apiTest/management'; import { ExecuteHistoryItem } from '@/models/apiTest/scenario';
import { ReportExecStatus } from '@/enums/apiEnum'; // import { ReportExecStatus } from '@/enums/apiEnum';
import { ReportEnum, ReportStatus, TriggerModeLabel } from '@/enums/reportEnum'; import { ReportEnum, ReportStatus, TriggerModeLabel } from '@/enums/reportEnum';
import { FilterSlotNameEnum } from '@/enums/tableFilterEnum'; import { FilterSlotNameEnum } from '@/enums/tableFilterEnum';
import { triggerModeOptions } from '@/views/api-test/report/utils'; import { triggerModeOptions } from '@/views/api-test/report/utils';
const caseExecuteResultDrawer = defineAsyncComponent(
() => import('@/views/taskCenter/component/caseExecuteResultDrawer.vue')
);
const appStore = useAppStore(); const appStore = useAppStore();
const { t } = useI18n(); const { t } = useI18n();
const statusList = computed(() => { const statusList = computed(() => {
@ -100,14 +111,14 @@
}); });
}); });
const ExecStatusList = computed(() => { // const ExecStatusList = computed(() => {
return Object.values(ReportExecStatus).map((e) => { // return Object.values(ReportExecStatus).map((e) => {
return { // return {
value: e, // value: e,
key: e, // key: e,
}; // };
}); // });
}); // });
const showResponse = ref(false); const showResponse = ref(false);
@ -206,10 +217,10 @@
} }
const activeReportIndex = ref<number>(0); const activeReportIndex = ref<number>(0);
const activeReportId = ref(''); const activeReport = ref<ExecuteHistoryItem>({} as ExecuteHistoryItem);
async function showResult(record: ApiCaseExecuteHistoryItem, rowIndex: number) { async function showResult(record: ExecuteHistoryItem, rowIndex: number) {
activeReportId.value = record.id; activeReport.value = record;
activeReportIndex.value = rowIndex; activeReportIndex.value = rowIndex;
showResponse.value = true; showResponse.value = true;
} }

View File

@ -53,7 +53,14 @@
</template> </template>
</ms-base-table> </ms-base-table>
<!-- 场景报告抽屉 --> <!-- 场景报告抽屉 -->
<caseAndScenarioReportDrawer v-model:visible="showScenarioReportVisible" is-scenario :report-id="reportId" /> <scenarioExecuteResultDrawer
v-if="showScenarioReportVisible"
:id="activeRecord.id"
v-model:visible="showScenarioReportVisible"
:user-name="activeRecord.createUser"
:status="activeRecord.execStatus"
:result="activeRecord.status"
/>
</div> </div>
</template> </template>
@ -66,7 +73,6 @@
import { MsTableColumn } from '@/components/pure/ms-table/type'; import { MsTableColumn } from '@/components/pure/ms-table/type';
import useTable from '@/components/pure/ms-table/useTable'; import useTable from '@/components/pure/ms-table/useTable';
import MsTag from '@/components/pure/ms-tag/ms-tag.vue'; import MsTag from '@/components/pure/ms-tag/ms-tag.vue';
import caseAndScenarioReportDrawer from '@/views/api-test/components/caseAndScenarioReportDrawer.vue';
import ExecutionStatus from '@/views/api-test/report/component/reportStatus.vue'; import ExecutionStatus from '@/views/api-test/report/component/reportStatus.vue';
import { getExecuteHistory } from '@/api/modules/api-test/scenario'; import { getExecuteHistory } from '@/api/modules/api-test/scenario';
@ -80,6 +86,10 @@
import { triggerModeOptions } from '@/views/api-test/report/utils'; import { triggerModeOptions } from '@/views/api-test/report/utils';
const scenarioExecuteResultDrawer = defineAsyncComponent(
() => import('@/views/taskCenter/component/scenarioExecuteResultDrawer.vue')
);
const tableQueryParams = ref<any>(); const tableQueryParams = ref<any>();
const keyword = ref(''); const keyword = ref('');
@ -193,12 +203,12 @@
} }
const showScenarioReportVisible = ref(false); const showScenarioReportVisible = ref(false);
const reportId = ref(''); const activeRecord = ref<ExecuteHistoryItem>({} as ExecuteHistoryItem);
const color = 'rgb(var(--primary-7))'; const color = 'rgb(var(--primary-7))';
function showResult(record: ExecuteHistoryItem) { function showResult(record: ExecuteHistoryItem) {
reportId.value = record.id; activeRecord.value = record;
showScenarioReportVisible.value = true; showScenarioReportVisible.value = true;
} }

View File

@ -482,6 +482,12 @@
showDrag: true, showDrag: true,
width: 350, width: 350,
}, },
{
title: 'common.createTime',
dataIndex: 'createTime',
showDrag: true,
width: 180,
},
{ {
title: hasOperationPermission.value ? 'common.operation' : '', title: hasOperationPermission.value ? 'common.operation' : '',
slotName: 'action', slotName: 'action',
@ -519,6 +525,7 @@
reviewers: item.reviewers.map((e: ReviewDetailReviewersItem) => e.userName), reviewers: item.reviewers.map((e: ReviewDetailReviewersItem) => e.userName),
moduleName: props.treePathMap[item.moduleId].path, moduleName: props.treePathMap[item.moduleId].path,
fullModuleName: props.treePathMap[item.moduleId].fullPath, fullModuleName: props.treePathMap[item.moduleId].fullPath,
createTime: dayjs(item.createTime).format('YYYY-MM-DD HH:mm:ss'),
cycle: cycle:
item.startTime && item.endTime item.startTime && item.endTime
? `${dayjs(item.startTime).format('YYYY-MM-DD HH:mm:ss')} - ${dayjs(item.endTime).format( ? `${dayjs(item.startTime).format('YYYY-MM-DD HH:mm:ss')} - ${dayjs(item.endTime).format(

View File

@ -2,8 +2,8 @@
<MsDrawer v-model:visible="visible" :width="1200" :footer="false"> <MsDrawer v-model:visible="visible" :width="1200" :footer="false">
<template #title> <template #title>
<div class="flex flex-1 items-center gap-[8px] overflow-hidden"> <div class="flex flex-1 items-center gap-[8px] overflow-hidden">
<a-tag :color="executeResultMap[props.record.result]?.color"> <a-tag :color="executeResultMap[props.result]?.color">
{{ t(executeResultMap[props.record.result]?.label || '-') }} {{ t(executeResultMap[props.result]?.label || '-') }}
</a-tag> </a-tag>
<div class="one-line-text flex-1">{{ detail.name }}</div> <div class="one-line-text flex-1">{{ detail.name }}</div>
</div> </div>
@ -17,7 +17,7 @@
<a-spin :loading="loading" class="block min-h-[200px]"> <a-spin :loading="loading" class="block min-h-[200px]">
<MsDescription :descriptions="detail.description" :column="3" :line-gap="8" one-line-value> <MsDescription :descriptions="detail.description" :column="3" :line-gap="8" one-line-value>
<template #value="{ item }"> <template #value="{ item }">
<execStatus v-if="item.key === 'status'" :status="props.record.status" size="small" /> <execStatus v-if="item.key === 'status'" :status="props.status" size="small" />
<a-tooltip <a-tooltip
v-else v-else
:content="`${item.value}`" :content="`${item.value}`"
@ -59,12 +59,16 @@
import { getCaseTaskReport } from '@/api/modules/api-test/report'; import { getCaseTaskReport } from '@/api/modules/api-test/report';
import { useI18n } from '@/hooks/useI18n'; import { useI18n } from '@/hooks/useI18n';
import { TaskCenterTaskDetailItem } from '@/models/taskCenter'; import { ExecuteResultEnum, ExecuteStatusEnum } from '@/enums/taskCenter';
import { executeResultMap, executeStatusMap } from './config'; import { executeResultMap, executeStatusMap } from './config';
const props = defineProps<{ const props = defineProps<{
record: TaskCenterTaskDetailItem; id: string;
status: ExecuteStatusEnum;
userName: string;
result: ExecuteResultEnum;
resourceName: string;
}>(); }>();
const { t } = useI18n(); const { t } = useI18n();
@ -76,11 +80,11 @@
async function init() { async function init() {
try { try {
loading.value = true; loading.value = true;
const res = await getCaseTaskReport(props.record.id); const res = await getCaseTaskReport(props.id);
const { apiReportDetailDTOList } = res; const { apiReportDetailDTOList } = res;
const [caseDetail] = apiReportDetailDTOList; const [caseDetail] = apiReportDetailDTOList;
detail.value = { detail.value = {
name: caseDetail?.requestName || props.record.resourceName, name: caseDetail?.requestName || props.resourceName,
description: [ description: [
{ {
label: t('ms.taskCenter.executeStatus'), label: t('ms.taskCenter.executeStatus'),
@ -89,7 +93,7 @@
}, },
{ {
label: t('ms.taskCenter.operationUser'), label: t('ms.taskCenter.operationUser'),
value: props.record.userName, value: props.userName,
}, },
{ {
label: t('ms.taskCenter.taskCreateTime'), label: t('ms.taskCenter.taskCreateTime'),
@ -138,7 +142,7 @@
watch( watch(
() => visible.value, () => visible.value,
(val) => { (val) => {
if (props.record.id && val) { if (props.id && val) {
init(); init();
} }
}, },

View File

@ -80,13 +80,6 @@
> >
{{ t('common.stop') }} {{ t('common.stop') }}
</MsButton> </MsButton>
<!-- <MsButton
v-if="record.status === ExecuteStatusEnum.COMPLETED && record.result === ExecuteResultEnum.ERROR"
v-permission="['SYSTEM_USER:READ+DELETE']"
@click="rerunTask(record)"
>
{{ t('ms.taskCenter.rerun') }}
</MsButton> -->
<MsButton v-if="record.status !== ExecuteStatusEnum.PENDING" @click="checkExecuteResult(record)"> <MsButton v-if="record.status !== ExecuteStatusEnum.PENDING" @click="checkExecuteResult(record)">
{{ t('ms.taskCenter.executeResult') }} {{ t('ms.taskCenter.executeResult') }}
</MsButton> </MsButton>
@ -94,13 +87,20 @@
</ms-base-table> </ms-base-table>
<caseExecuteResultDrawer <caseExecuteResultDrawer
v-if="caseExecuteResultDrawerVisible" v-if="caseExecuteResultDrawerVisible"
:id="activeRecord.id"
v-model:visible="caseExecuteResultDrawerVisible" v-model:visible="caseExecuteResultDrawerVisible"
:record="activeRecord" :user-name="activeRecord.userName"
:status="activeRecord.status"
:result="activeRecord.result"
:resource-name="activeRecord.resourceName"
/> />
<scenarioExecuteResultDrawer <scenarioExecuteResultDrawer
v-if="scenarioExecuteResultDrawerVisible" v-if="scenarioExecuteResultDrawerVisible"
:id="activeRecord.id"
v-model:visible="scenarioExecuteResultDrawerVisible" v-model:visible="scenarioExecuteResultDrawerVisible"
:record="activeRecord" :user-name="activeRecord.userName"
:status="activeRecord.status"
:result="activeRecord.result"
/> />
</template> </template>
@ -662,24 +662,6 @@
} }
} }
// async function rerunTask(record: TaskCenterTaskDetailItem) {
// try {
// // await deleteUserInfo({
// // selectIds,
// // selectAll: !!params?.selectAll,
// // excludeIds: params?.excludeIds || [],
// // condition: { keyword: keyword.value },
// // });
// Message.success(t('common.executionSuccess'));
// resetSelector();
// await loadList();
// initCurrentPageResourcePoolsStatus();
// } catch (error) {
// // eslint-disable-next-line no-console
// console.log(error);
// }
// }
watch( watch(
() => propsRes.value.data, () => propsRes.value.data,
() => { () => {

View File

@ -62,13 +62,13 @@
<MsButton v-else v-permission="[getCurrentPermission('DELETE')]" @click="deleteTask(record)"> <MsButton v-else v-permission="[getCurrentPermission('DELETE')]" @click="deleteTask(record)">
{{ t('common.delete') }} {{ t('common.delete') }}
</MsButton> </MsButton>
<!-- <MsButton <MsButton
v-if="record.status === ExecuteStatusEnum.COMPLETED && record.result === ExecuteResultEnum.ERROR" v-if="record.result === ExecuteResultEnum.ERROR"
v-permission="['SYSTEM_USER:READ+DELETE']" v-permission="['SYSTEM_USER:READ+DELETE']"
@click="rerunTask(record)" @click="rerunTask(record)"
> >
{{ t('ms.taskCenter.rerun') }} {{ t('ms.taskCenter.rerun') }}
</MsButton> --> </MsButton>
<MsButton <MsButton
v-if="record.status === ExecuteStatusEnum.COMPLETED" v-if="record.status === ExecuteStatusEnum.COMPLETED"
v-permission="['SYSTEM_USER:READ+DELETE']" v-permission="['SYSTEM_USER:READ+DELETE']"
@ -140,6 +140,7 @@
organizationDeleteTask, organizationDeleteTask,
organizationProjectOptions, organizationProjectOptions,
organizationStopTask, organizationStopTask,
organizationTaskRerun,
} from '@/api/modules/taskCenter/organization'; } from '@/api/modules/taskCenter/organization';
import { import {
getProjectExecuteTaskList, getProjectExecuteTaskList,
@ -148,6 +149,7 @@
projectBatchStopTask, projectBatchStopTask,
projectDeleteTask, projectDeleteTask,
projectStopTask, projectStopTask,
projectTaskRerun,
} from '@/api/modules/taskCenter/project'; } from '@/api/modules/taskCenter/project';
import { import {
getSystemExecuteTaskList, getSystemExecuteTaskList,
@ -158,6 +160,7 @@
systemOrgOptions, systemOrgOptions,
systemProjectOptions, systemProjectOptions,
systemStopTask, systemStopTask,
systemTaskRerun,
} from '@/api/modules/taskCenter/system'; } from '@/api/modules/taskCenter/system';
import { useI18n } from '@/hooks/useI18n'; import { useI18n } from '@/hooks/useI18n';
import useModal from '@/hooks/useModal'; import useModal from '@/hooks/useModal';
@ -172,7 +175,7 @@
import { 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 { ExecuteStatusEnum, ExecuteTaskType } from '@/enums/taskCenter'; import { ExecuteResultEnum, ExecuteStatusEnum, ExecuteTaskType } from '@/enums/taskCenter';
import { executeMethodMap, executeResultMap, executeStatusMap } from './config'; import { executeMethodMap, executeResultMap, executeStatusMap } from './config';
@ -432,7 +435,7 @@
}, },
], ],
}; };
const { propsRes, propsEvent, loadList, setLoadListParams, resetSelector } = useTable( const { propsRes, propsEvent, loadList, setLoadListParams, setLoading, resetSelector } = useTable(
currentExecuteTaskList, currentExecuteTaskList,
{ {
tableKey: TableKeyEnum.TASK_CENTER_CASE_TASK, tableKey: TableKeyEnum.TASK_CENTER_CASE_TASK,
@ -532,6 +535,7 @@
maskClosable: false, maskClosable: false,
onBeforeOk: async () => { onBeforeOk: async () => {
try { try {
setLoading(true);
if (isBatch) { if (isBatch) {
await currentBatchDeleteTask({ await currentBatchDeleteTask({
selectIds: params?.selectedIds || [], selectIds: params?.selectedIds || [],
@ -548,6 +552,8 @@
} catch (error) { } catch (error) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log(error); console.log(error);
} finally {
setLoading(false);
} }
}, },
hideCancel: false, hideCancel: false,
@ -585,6 +591,7 @@
maskClosable: false, maskClosable: false,
onBeforeOk: async () => { onBeforeOk: async () => {
try { try {
setLoading(true);
if (isBatch) { if (isBatch) {
await currentBatchStopTask({ await currentBatchStopTask({
selectIds: params?.selectedIds || [], selectIds: params?.selectedIds || [],
@ -601,6 +608,8 @@
} catch (error) { } catch (error) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log(error); console.log(error);
} finally {
setLoading(false);
} }
}, },
hideCancel: false, hideCancel: false,
@ -624,23 +633,26 @@
} }
} }
// async function rerunTask(record: TaskCenterTaskItem) { const currentRerunTask = {
// try { system: systemTaskRerun,
// // await deleteUserInfo({ project: projectTaskRerun,
// // selectIds, org: organizationTaskRerun,
// // selectAll: !!params?.selectAll, }[props.type];
// // excludeIds: params?.excludeIds || [], async function rerunTask(record: TaskCenterTaskItem) {
// // condition: { keyword: keyword.value }, try {
// // }); setLoading(true);
// Message.success(t('common.executionSuccess')); await currentRerunTask(record.id);
// resetSelector(); Message.success(t('common.executionSuccess'));
// await loadList(); resetSelector();
// initTaskStatistics(); await loadList();
// } catch (error) { initTaskStatistics();
// // eslint-disable-next-line no-console } catch (error) {
// console.log(error); // eslint-disable-next-line no-console
// } console.log(error);
// } } finally {
setLoading(false);
}
}
/** /**
* 报告详情 showReportDetail * 报告详情 showReportDetail

View File

@ -2,8 +2,8 @@
<MsDrawer v-model:visible="visible" :width="1200" :footer="false"> <MsDrawer v-model:visible="visible" :width="1200" :footer="false">
<template #title> <template #title>
<div class="flex flex-1 items-center gap-[8px] overflow-hidden"> <div class="flex flex-1 items-center gap-[8px] overflow-hidden">
<a-tag :color="executeResultMap[props.record.result]?.color"> <a-tag :color="executeResultMap[props.result]?.color">
{{ t(executeResultMap[props.record.result]?.label || '-') }} {{ t(executeResultMap[props.result]?.label || '-') }}
</a-tag> </a-tag>
<div class="one-line-text flex-1">{{ detail.name }}</div> <div class="one-line-text flex-1">{{ detail.name }}</div>
</div> </div>
@ -17,7 +17,7 @@
<a-spin :loading="loading" class="block min-h-[200px]"> <a-spin :loading="loading" class="block min-h-[200px]">
<MsDescription :descriptions="detail.description" :column="3" :line-gap="8" one-line-value> <MsDescription :descriptions="detail.description" :column="3" :line-gap="8" one-line-value>
<template #value="{ item }"> <template #value="{ item }">
<execStatus v-if="item.key === 'status'" :status="props.record.status" size="small" /> <execStatus v-if="item.key === 'status'" :status="props.status" size="small" />
<a-tooltip <a-tooltip
v-else v-else
:content="`${item.value}`" :content="`${item.value}`"
@ -68,12 +68,15 @@
import { getScenarioTaskReport, getScenarioTaskReportStep } from '@/api/modules/api-test/report'; import { getScenarioTaskReport, getScenarioTaskReportStep } from '@/api/modules/api-test/report';
import { useI18n } from '@/hooks/useI18n'; import { useI18n } from '@/hooks/useI18n';
import { TaskCenterTaskDetailItem } from '@/models/taskCenter'; import { ExecuteResultEnum, ExecuteStatusEnum } from '@/enums/taskCenter';
import { executeResultMap, executeStatusMap } from './config'; import { executeResultMap, executeStatusMap } from './config';
const props = defineProps<{ const props = defineProps<{
record: TaskCenterTaskDetailItem; id: string;
status: ExecuteStatusEnum;
userName: string;
result: ExecuteResultEnum;
}>(); }>();
const { t } = useI18n(); const { t } = useI18n();
@ -85,17 +88,17 @@
async function init() { async function init() {
try { try {
loading.value = true; loading.value = true;
const res = await getScenarioTaskReport(props.record.id); const res = await getScenarioTaskReport(props.id);
detail.value = { detail.value = {
description: [ description: [
{ {
label: t('ms.taskCenter.executeStatus'), label: t('ms.taskCenter.executeStatus'),
key: 'status', key: 'status',
value: t(executeStatusMap[props.record.status].label), value: t(executeStatusMap[props.status].label),
}, },
{ {
label: t('ms.taskCenter.operationUser'), label: t('ms.taskCenter.operationUser'),
value: props.record.userName, value: props.userName,
}, },
{ {
label: t('ms.taskCenter.taskCreateTime'), label: t('ms.taskCenter.taskCreateTime'),
@ -142,9 +145,9 @@
} }
watch( watch(
() => props.record.id, () => props.id,
() => { () => {
if (props.record.id) { if (props.id) {
init(); init();
} }
}, },

View File

@ -77,8 +77,8 @@
import MsBaseTable from '@/components/pure/ms-table/base-table.vue'; import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
import type { MsTableColumn } from '@/components/pure/ms-table/type'; import type { MsTableColumn } from '@/components/pure/ms-table/type';
import useTable from '@/components/pure/ms-table/useTable'; import useTable from '@/components/pure/ms-table/useTable';
import ReportDrawer from '@/views/test-plan/report/component/reportDrawer.vue';
import ExecutionStatus from '@/views/test-plan/report/component/reportStatus.vue'; import ExecutionStatus from '@/views/test-plan/report/component/reportStatus.vue';
import ReportDrawer from '@/views/test-plan/testPlan/detail/reportDrawer.vue';
import { getReportDetailPage, getReportDetailSharePage } from '@/api/modules/test-plan/report'; import { getReportDetailPage, getReportDetailSharePage } from '@/api/modules/test-plan/report';
import { useI18n } from '@/hooks/useI18n'; import { useI18n } from '@/hooks/useI18n';

View File

@ -29,6 +29,7 @@
</template> </template>
</ms-base-table> </ms-base-table>
</div> </div>
<executeResultDrawer v-model:visible="resultVisible" :plan-detail="resultRecord" />
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
@ -39,16 +40,15 @@
import MsBaseTable from '@/components/pure/ms-table/base-table.vue'; import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
import { MsTableColumn } from '@/components/pure/ms-table/type'; import { MsTableColumn } from '@/components/pure/ms-table/type';
import useTable from '@/components/pure/ms-table/useTable'; import useTable from '@/components/pure/ms-table/useTable';
import executeResultDrawer from '../executeResultDrawer.vue';
import ExecutionStatus from '@/views/api-test/report/component/reportStatus.vue'; import ExecutionStatus from '@/views/api-test/report/component/reportStatus.vue';
import { getPlanDetailExecuteHistory } from '@/api/modules/test-plan/testPlan'; import { getPlanDetailExecuteHistory } from '@/api/modules/test-plan/testPlan';
import { useI18n } from '@/hooks/useI18n'; import { useI18n } from '@/hooks/useI18n';
import useOpenNewPage from '@/hooks/useOpenNewPage';
import { hasAnyPermission } from '@/utils/permission'; import { hasAnyPermission } from '@/utils/permission';
import type { PlanDetailExecuteHistoryItem } from '@/models/testPlan/testPlan'; import type { PlanDetailExecuteHistoryItem } from '@/models/testPlan/testPlan';
import { PlanReportStatus, ReportEnum, TriggerModeLabel } from '@/enums/reportEnum'; import { PlanReportStatus, ReportEnum, TriggerModeLabel } from '@/enums/reportEnum';
import { TestPlanRouteEnum } from '@/enums/routeEnum';
import { FilterSlotNameEnum } from '@/enums/tableFilterEnum'; import { FilterSlotNameEnum } from '@/enums/tableFilterEnum';
import { triggerModeOptions } from '@/views/api-test/report/utils'; import { triggerModeOptions } from '@/views/api-test/report/utils';
@ -60,7 +60,7 @@
const { t } = useI18n(); const { t } = useI18n();
const route = useRoute(); const route = useRoute();
const { openNewPage } = useOpenNewPage(); // const { openNewPage } = useOpenNewPage();
const statusResultOptions = computed(() => { const statusResultOptions = computed(() => {
return Object.keys(PlanReportStatus).map((key) => { return Object.keys(PlanReportStatus).map((key) => {
@ -140,12 +140,12 @@
loadList(); loadList();
} }
const resultVisible = ref(false);
const resultRecord = ref<PlanDetailExecuteHistoryItem>({} as PlanDetailExecuteHistoryItem);
// //
function toReport(record: PlanDetailExecuteHistoryItem) { function toReport(record: PlanDetailExecuteHistoryItem) {
openNewPage(TestPlanRouteEnum.TEST_PLAN_REPORT_DETAIL, { resultVisible.value = true;
id: record.id, resultRecord.value = record;
type: props.isGroup ? 'GROUP' : undefined,
});
} }
function getStartAndEndTime(record: PlanDetailExecuteHistoryItem) { function getStartAndEndTime(record: PlanDetailExecuteHistoryItem) {

View File

@ -0,0 +1,352 @@
<template>
<MsDrawer v-model:visible="visible" :width="1200" :footer="false">
<template #title>
<div class="flex flex-1 items-center gap-[8px] overflow-hidden">
<a-tag :color="executeResultMap[props.planDetail.execResult]?.color">
{{ t(executeResultMap[props.planDetail.execResult]?.label || '-') }}
</a-tag>
<div class="one-line-text flex-1">{{ detail.name }}</div>
</div>
<div class="flex justify-end">
<MsButton type="icon" status="secondary" class="!rounded-[var(--border-radius-small)]" @click="init">
<MsIcon type="icon-icon_reset_outlined" class="mr-[8px]" size="14" />
{{ t('common.refresh') }}
</MsButton>
</div>
</template>
<a-spin :loading="loading" class="block min-h-[200px]">
<MsDescription :descriptions="detail.description" :column="3" :line-gap="8" one-line-value>
<template #value="{ item }">
<execStatus
v-if="item.key === 'status'"
:status="props.planDetail.execResult as unknown as ExecuteStatusEnum"
size="small"
/>
<a-select
v-else-if="item.key === 'testPlan'"
v-model:model-value="activePlan"
:options="testPlanGroups"
></a-select>
<executeRatePopper
v-else-if="item.key === 'rate'"
v-model:visible="executeRateVisible"
v-model:record="detail"
:execute-task-statistics-request="fakeStatisticsRequest"
/>
<a-tooltip
v-else
:content="`${item.value}`"
:disabled="item.value === undefined || item.value === null || item.value?.toString() === ''"
:position="item.tooltipPosition ?? 'tl'"
>
<div class="w-[fit-content]">
{{ item.value === undefined || item.value === null || item.value?.toString() === '' ? '-' : item.value }}
</div>
</a-tooltip>
</template>
</MsDescription>
<div class="flex items-center justify-between">
<MsTab v-model:active-key="activeTable" :content-tab-list="contentTabList" :show-badge="false" />
<a-input-search
v-model:model-value="keyword"
:placeholder="t('report.detail.caseDetailSearchPlaceholder')"
allow-clear
class="w-[240px]"
@search="searchList"
@press-enter="searchList"
@clear="searchList"
/>
</div>
<div class="mt-[8px]">
<MsBaseTable
v-bind="currentCaseTable.propsRes.value"
:row-class="getRowClass"
v-on="currentCaseTable.propsEvent.value"
>
<template #num="{ record }">
<MsButton type="text" @click="toDetail(record)">{{ record.num }}</MsButton>
</template>
<template #[FilterSlotNameEnum.CASE_MANAGEMENT_CASE_LEVEL]="{ filterContent }">
<caseLevel :case-level="filterContent.value" />
</template>
<template #priority="{ record }">
<caseLevel :case-level="record.priority" />
</template>
<template #[FilterSlotNameEnum.API_TEST_CASE_API_LAST_EXECUTE_STATUS]="{ filterContent }">
<ExecutionStatus :module-type="ReportEnum.API_REPORT" :status="filterContent.value" />
</template>
<template #lastExecResult="{ record }">
<ExecutionStatus
:module-type="ReportEnum.API_REPORT"
:status="record.executeResult"
:class="[!record.executeResult ? '' : 'cursor-pointer']"
@click="showReport(record)"
/>
</template>
</MsBaseTable>
</div>
</a-spin>
</MsDrawer>
<CaseAndScenarioReportDrawer
v-model:visible="reportVisible"
:report-id="apiReportId"
do-not-show-share
:is-scenario="activeTable === 'scenario'"
:report-detail="activeTable === 'scenario' ? reportScenarioDetail : reportCaseDetail"
:get-report-step-detail="activeTable === 'scenario' ? reportStepDetail : reportCaseStepDetail"
/>
</template>
<script setup lang="ts">
import dayjs from 'dayjs';
import MsButton from '@/components/pure/ms-button/index.vue';
import MsDescription, { Description } from '@/components/pure/ms-description/index.vue';
import MsDrawer from '@/components/pure/ms-drawer/index.vue';
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
import MsTab from '@/components/pure/ms-tab/index.vue';
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
import type { MsTableColumn } from '@/components/pure/ms-table/type';
import useTable from '@/components/pure/ms-table/useTable';
import caseLevel from '@/components/business/ms-case-associate/caseLevel.vue';
import CaseAndScenarioReportDrawer from '@/views/api-test/components/caseAndScenarioReportDrawer.vue';
import ExecutionStatus from '@/views/api-test/report/component/reportStatus.vue';
import execStatus from '@/views/taskCenter/component/execStatus.vue';
import executeRatePopper from '@/views/taskCenter/component/executeRatePopper.vue';
import { getCaseTaskReport } from '@/api/modules/api-test/report';
import {
getApiPage,
getScenarioPage,
reportCaseDetail,
reportCaseStepDetail,
reportScenarioDetail,
reportStepDetail,
} from '@/api/modules/test-plan/report';
import { useI18n } from '@/hooks/useI18n';
import useOpenNewPage from '@/hooks/useOpenNewPage';
import { ApiOrScenarioCaseItem } from '@/models/testPlan/report';
import { PlanDetailExecuteHistoryItem } from '@/models/testPlan/testPlan';
import { ReportEnum } from '@/enums/reportEnum';
import { ApiTestRouteEnum } from '@/enums/routeEnum';
import { FilterSlotNameEnum } from '@/enums/tableFilterEnum';
import { ExecuteStatusEnum } from '@/enums/taskCenter';
import { casePriorityOptions, lastReportStatusListOptions } from '@/views/api-test/components/config';
import { executeResultMap, executeStatusMap } from '@/views/taskCenter/component/config';
const props = defineProps<{
planDetail: PlanDetailExecuteHistoryItem;
}>();
const { openNewPage } = useOpenNewPage();
const { t } = useI18n();
const visible = defineModel<boolean>('visible', { required: true });
const loading = ref(false);
const detail = ref<any>({ description: [] });
const testPlanGroups = ref<any[]>([]);
const executeRateVisible = ref(false);
async function fakeStatisticsRequest() {
return Promise.resolve([]);
}
async function init() {
try {
loading.value = true;
const res = await getCaseTaskReport(props.planDetail.id);
const { apiReportDetailDTOList } = res;
const [caseDetail] = apiReportDetailDTOList;
detail.value = {
name: caseDetail?.requestName,
description: [
{
label: t('ms.taskCenter.executeStatus'),
key: 'status',
value: t(executeStatusMap[res.status].label),
},
{
label: t('ms.taskCenter.taskCreateTime'),
value: res.createTime ? dayjs(res.createTime).format('YYYY-MM-DD HH:mm:ss') : '-',
},
{
label: t('ms.taskCenter.operationUser'),
value: props.planDetail.operationUser,
},
{
label: t('ms.taskCenter.taskStartTime'),
value: res.startTime ? dayjs(res.startTime).format('YYYY-MM-DD HH:mm:ss') : '-',
},
{
label: t('ms.taskCenter.executeFinishedRate'),
key: 'rate',
value: res.result,
},
{
label: t('ms.taskCenter.taskEndTime'),
value: res.endTime ? dayjs(res.endTime).format('YYYY-MM-DD HH:mm:ss') : '-',
},
{
label: t('menu.testPlan'),
key: 'testPlan',
value: '',
},
] as Description[],
};
testPlanGroups.value = [
{
id: '1',
name: 'A',
},
{
id: '2',
name: 'B',
},
];
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
} finally {
loading.value = false;
}
}
const columns: MsTableColumn = [
{
title: 'ID',
dataIndex: 'num',
slotName: 'num',
sortIndex: 1,
width: 100,
},
{
title: 'common.name',
dataIndex: 'name',
width: 150,
showTooltip: true,
},
{
title: 'report.detail.level',
dataIndex: 'priority',
slotName: 'priority',
filterConfig: {
options: casePriorityOptions,
filterSlotName: FilterSlotNameEnum.CASE_MANAGEMENT_CASE_LEVEL,
},
width: 150,
},
{
title: 'common.belongModule',
dataIndex: 'moduleName',
showTooltip: true,
width: 200,
},
{
title: 'testPlan.featureCase.executor',
dataIndex: 'executeUser',
showTooltip: true,
width: 150,
},
{
title: 'common.executionResult',
dataIndex: 'executeResult',
slotName: 'lastExecResult',
filterConfig: {
options: lastReportStatusListOptions.value,
filterSlotName: FilterSlotNameEnum.API_TEST_CASE_API_LAST_EXECUTE_STATUS,
emptyFilter: true,
},
width: 150,
},
];
const activePlan = ref('1');
const activeTable = ref<'case' | 'scenario'>('case');
const contentTabList = [
{ value: 'case', label: t('report.detail.apiCaseDetails') },
{ value: 'scenario', label: t('report.detail.scenarioCaseDetails') },
];
const keyword = ref('');
const useApiTable = useTable(getApiPage, {
scroll: { x: '100%' },
columns,
showSelectorAll: false,
heightUsed: 236,
showSetting: false,
paginationSize: 'mini',
});
const useScenarioTable = useTable(getScenarioPage, {
scroll: { x: '100%' },
columns,
showSelectorAll: false,
showSetting: false,
heightUsed: 236,
paginationSize: 'mini',
});
const currentCaseTable = computed(() => (activeTable.value === 'case' ? useApiTable : useScenarioTable));
//
const reportVisible = ref(false);
const apiReportId = ref<string>('');
const selectedReportId = ref<string>('');
function showReport(record: ApiOrScenarioCaseItem) {
if (!record.reportId) {
return;
}
if (!record.executeResult || record.executeResult === 'STOPPED') return;
reportVisible.value = true;
apiReportId.value = record.reportId;
selectedReportId.value = record.reportId;
}
function getRowClass(record: ApiOrScenarioCaseItem) {
return record.reportId === selectedReportId.value ? 'selected-row-class' : '';
}
function searchList() {
currentCaseTable.value.setLoadListParams({ keyword: keyword.value });
currentCaseTable.value.loadList();
}
//
function toDetail(record: PlanDetailExecuteHistoryItem) {
if (activeTable.value === 'scenario') {
openNewPage(ApiTestRouteEnum.API_TEST_SCENARIO, {
id: record.id,
pId: record.id,
});
} else {
openNewPage(ApiTestRouteEnum.API_TEST_MANAGEMENT, {
cId: record.id,
pId: record.id,
});
}
}
watch(
() => visible.value,
(val) => {
if (props.planDetail.id && val) {
init();
}
},
{ immediate: true }
);
</script>
<style lang="less" scoped>
:deep(.ms-description-item) {
@apply items-center;
margin-bottom: 8px;
font-size: 12px;
line-height: 16px;
}
</style>