feat(任务中心): 任务中心接口联调

This commit is contained in:
baiqi 2024-10-22 15:05:18 +08:00 committed by Craftsman
parent c1a59f4cc0
commit e7ea9f975e
14 changed files with 466 additions and 235 deletions

View File

@ -1,7 +1,7 @@
import MSR from '@/api/http'; import MSR from '@/api/http';
import * as reportUrl from '@/api/requrls/api-test/report'; import * as reportUrl from '@/api/requrls/api-test/report';
import type { GetShareId, ReportDetail, ReportStepDetail } from '@/models/apiTest/report'; import type { GetShareId, ReportDetail, ReportStepDetail, ReportStepDetailItem } from '@/models/apiTest/report';
import type { TableQueryParams } from '@/models/common'; import type { TableQueryParams } from '@/models/common';
import { ReportEnum } from '@/enums/reportEnum'; import { ReportEnum } from '@/enums/reportEnum';
@ -111,4 +111,17 @@ export function downloadFile(data: string | undefined) {
return MSR.post({ url: reportUrl.DownloadFileUrl, data, responseType: 'blob' }, { isTransformResponse: false }); return MSR.post({ url: reportUrl.DownloadFileUrl, data, responseType: 'blob' }, { isTransformResponse: false });
} }
export default {}; // 获取用例任务报告
export function getCaseTaskReport(taskId: string) {
return MSR.get<ReportStepDetailItem[]>({ url: `${reportUrl.caseTaskReportUrl}/${taskId}` });
}
// 获取场景任务报告
export function getScenarioTaskReport(taskId: string) {
return MSR.get<ReportDetail>({ url: `${reportUrl.scenarioTaskReportUrl}/${taskId}` });
}
// 获取场景任务报告-步骤
export function getScenarioTaskReportStep(taskId: string, stepId: string) {
return MSR.get<ReportStepDetailItem>({ url: `${reportUrl.scenarioTaskReportStepUrl}/${taskId}/${stepId}` });
}

View File

@ -47,3 +47,12 @@ export const getShareReportInfoUrl = '/api/report/share/get';
export const getShareTimeUrl = '/api/report/share/get-share-time'; export const getShareTimeUrl = '/api/report/share/get-share-time';
// 下载文件地址 // 下载文件地址
export const DownloadFileUrl = '/api/test/download'; export const DownloadFileUrl = '/api/test/download';
// 用例任务报告
export const caseTaskReportUrl = '/api/report/case/task-report';
// 场景任务报告
export const scenarioTaskReportUrl = '/api/report/scenario/task-step';
// 场景任务报告-步骤
export const scenarioTaskReportStepUrl = '/api/report/scenario/task-report';

View File

@ -9,7 +9,7 @@
multiple multiple
allow-clear allow-clear
check-strictly check-strictly
:max-tag-count="maxTagCount" :max-tag-count="props.shouldCalculateMaxTag ? maxTagCount : 1"
:virtual-list-props="props.virtualListProps" :virtual-list-props="props.virtualListProps"
:placeholder="props.placeholder" :placeholder="props.placeholder"
:loading="props.loading" :loading="props.loading"
@ -51,11 +51,11 @@
v-model:model-value="innerValue" v-model:model-value="innerValue"
class="ms-cascader" class="ms-cascader"
:options="props.options" :options="props.options"
:trigger-props="{ contentClass: `ms-cascader-popper ms-cascader-popper--${props.optionSize}` }" :trigger-props="{ contentClass: `ms-cascader-popper-native ms-cascader-popper--${props.optionSize}` }"
:multiple="props.multiple" :multiple="props.multiple"
allow-clear allow-clear
:check-strictly="props.strictly" :check-strictly="props.strictly"
:max-tag-count="maxTagCount" :max-tag-count="props.shouldCalculateMaxTag ? maxTagCount : 1"
:placeholder="props.placeholder" :placeholder="props.placeholder"
:virtual-list-props="props.virtualListProps" :virtual-list-props="props.virtualListProps"
:loading="props.loading" :loading="props.loading"
@ -121,6 +121,7 @@
labelPathMode?: boolean; // label labelPathMode?: boolean; // label
valueKey?: string; valueKey?: string;
labelKey?: string; // labelKey labelKey?: string; // labelKey
shouldCalculateMaxTag?: boolean; //
} }
const props = withDefaults(defineProps<MsCascaderProps>(), { const props = withDefaults(defineProps<MsCascaderProps>(), {
@ -129,6 +130,7 @@
pathMode: false, pathMode: false,
valueKey: 'value', valueKey: 'value',
labelKey: 'label', labelKey: 'label',
shouldCalculateMaxTag: true,
}); });
const emit = defineEmits(['update:modelValue', 'update:level', 'change']); const emit = defineEmits(['update:modelValue', 'update:level', 'change']);
@ -161,6 +163,9 @@
// //
innerLevel.value = val[0] as string; innerLevel.value = val[0] as string;
} }
if (props.shouldCalculateMaxTag !== false && props.multiple) {
calculateMaxTag();
}
}, },
{ {
immediate: true, immediate: true,
@ -175,11 +180,11 @@
selectedLabelObj = {}; selectedLabelObj = {};
for (let i = 0; i < val.length; i++) { for (let i = 0; i < val.length; i++) {
const item = val[i]; const item = val[i];
const value = typeof item === 'object' ? item.value : item; const value = typeof item === 'object' ? item[props.valueKey] : item;
if (!props.labelPathMode) { if (!props.labelPathMode) {
selectedLabelObj[value] = t((item.label || '').split('/').pop() || ''); selectedLabelObj[value] = t((item[props.labelKey] || '').split('/').pop() || '');
} else { } else {
selectedLabelObj[value] = t(item.label || ''); selectedLabelObj[value] = t(item[props.labelKey] || '');
} }
} }
} }
@ -218,8 +223,10 @@
innerLevel.value = ''; innerLevel.value = '';
} }
} }
if (props.shouldCalculateMaxTag) {
calculateMaxTag(); calculateMaxTag();
} }
}
// TODO: arco-design cascader label/ path-mode // TODO: arco-design cascader label/ path-mode
function getInputLabel(data: CascaderOption) { function getInputLabel(data: CascaderOption) {
@ -258,7 +265,8 @@
} }
} }
} }
.ms-cascader-popper { .ms-cascader-popper,
.ms-cascader-popper-native {
.arco-cascader-panel { .arco-cascader-panel {
.arco-cascader-panel-column { .arco-cascader-panel-column {
.arco-cascader-column-content { .arco-cascader-column-content {
@ -275,6 +283,10 @@
background-color: rgb(var(--primary-1)); background-color: rgb(var(--primary-1));
} }
} }
}
}
.ms-cascader-popper {
.arco-cascader-panel {
.arco-cascader-panel-column:first-child { .arco-cascader-panel-column:first-child {
.arco-checkbox { .arco-checkbox {
@apply hidden; @apply hidden;

View File

@ -76,6 +76,7 @@ export enum TableKeyEnum {
TASK_CENTER_CASE_TASK = 'taskCenterCaseTask', TASK_CENTER_CASE_TASK = 'taskCenterCaseTask',
TASK_CENTER_CASE_TASK_DETAIL = 'taskCenterCaseTaskDetail', TASK_CENTER_CASE_TASK_DETAIL = 'taskCenterCaseTaskDetail',
TASK_CENTER_SYSTEM_TASK = 'taskCenterSystemTask', TASK_CENTER_SYSTEM_TASK = 'taskCenterSystemTask',
TASK_CENTER_BATCH_TASK = 'taskCenterBatchTask',
TASK_SCHEDULE_TASK_API_IMPORT_SYSTEM = 'taskCenterScheduleApiImportSystem', TASK_SCHEDULE_TASK_API_IMPORT_SYSTEM = 'taskCenterScheduleApiImportSystem',
TASK_SCHEDULE_TASK_API_SCENARIO_SYSTEM = 'taskCenterScheduleApiScenarioSystem', TASK_SCHEDULE_TASK_API_SCENARIO_SYSTEM = 'taskCenterScheduleApiScenarioSystem',
TASK_SCHEDULE_TASK_API_IMPORT_ORGANIZATION = 'taskCenterScheduleApiImportOrganization', TASK_SCHEDULE_TASK_API_IMPORT_ORGANIZATION = 'taskCenterScheduleApiImportOrganization',

View File

@ -1,5 +1,6 @@
import { RequestResult } from '@/models/apiTest/common'; import { RequestResult } from '@/models/apiTest/common';
import type { RequestMethods } from '@/enums/apiEnum'; import type { RequestMethods } from '@/enums/apiEnum';
import type { ExecuteStatusEnum } from '@/enums/taskCenter';
export interface LegendData { export interface LegendData {
label: string; label: string;
@ -73,7 +74,7 @@ export interface ReportStepDetailItem {
id: string; id: string;
reportId: string; reportId: string;
stepId: string; stepId: string;
status: string; status: ExecuteStatusEnum;
fakeCode: string; fakeCode: string;
requestName: string; requestName: string;
requestTime: number; requestTime: number;

View File

@ -1,4 +1,4 @@
import type { ExecuteTaskType, ExecuteTriggerMode } from '@/enums/taskCenter'; import type { ExecuteStatusEnum, ExecuteTaskType, ExecuteTriggerMode } from '@/enums/taskCenter';
import type { TableQueryParams } from './common'; import type { TableQueryParams } from './common';
@ -59,7 +59,7 @@ export interface TaskCenterTaskDetailItem {
resourceId: string; resourceId: string;
resourceName: string; resourceName: string;
taskOrigin: string; // 任务来源 taskOrigin: string; // 任务来源
status: string; // 执行状态 status: ExecuteStatusEnum; // 执行状态
result: string; // 执行结果 result: string; // 执行结果
resourcePoolId: string; // 资源池ID resourcePoolId: string; // 资源池ID
resourcePoolNode: string; // 资源池节点 resourcePoolNode: string; // 资源池节点

View File

@ -151,6 +151,8 @@
const props = defineProps<{ const props = defineProps<{
mode: 'tiled' | 'tab'; // | tab mode: 'tiled' | 'tab'; // | tab
hideResponseTime?: boolean; // hideResponseTime?: boolean; //
static?: boolean; //
staticInfo?: ReportStepDetailItem; //
stepItem?: ScenarioItemType; // stepItem?: ScenarioItemType; //
console?: string; console?: string;
isPriorityLocalExec?: boolean; isPriorityLocalExec?: boolean;
@ -386,7 +388,10 @@
const originStepId = ref<string | undefined>(''); const originStepId = ref<string | undefined>('');
watchEffect(() => { watchEffect(() => {
if (props.stepItem?.stepId && props.mode === 'tiled') { if (props.static && props.staticInfo) {
activeStepDetail.value = props.staticInfo;
activeStepDetailCopy.value = cloneDeep(props.staticInfo);
} else if (props.stepItem?.stepId && props.mode === 'tiled') {
const stepIds = props.stepItem?.stepChildren || []; const stepIds = props.stepItem?.stepChildren || [];
getStepDetail(isShowLoopControl.value ? stepIds[controlCurrent.value - 1].stepId : props.stepItem.stepId); getStepDetail(isShowLoopControl.value ? stepIds[controlCurrent.value - 1].stepId : props.stepItem.stepId);
} }
@ -394,7 +399,10 @@
onMounted(() => { onMounted(() => {
originStepId.value = props.stepItem?.stepId; originStepId.value = props.stepItem?.stepId;
if (props.stepItem?.stepId && !props.stepItem.fold) { if (props.static && props.staticInfo) {
activeStepDetail.value = props.staticInfo;
activeStepDetailCopy.value = cloneDeep(props.staticInfo);
} else if (props.stepItem?.stepId && !props.stepItem.fold) {
const stepIds = props.stepItem?.stepChildren || []; const stepIds = props.stepItem?.stepChildren || [];
getStepDetail(isShowLoopControl.value ? stepIds[controlCurrent.value - 1].stepId : props.stepItem.stepId); getStepDetail(isShowLoopControl.value ? stepIds[controlCurrent.value - 1].stepId : props.stepItem.stepId);
} }

View File

@ -4,7 +4,7 @@
<template #name="{ record }"> <template #name="{ record }">
<a-button type="text" class="max-w-full justify-start px-0" @click="showReportDetail(record)"> <a-button type="text" class="max-w-full justify-start px-0" @click="showReportDetail(record)">
<div class="one-line-text"> <div class="one-line-text">
{{ record.num }} {{ record.name }}
</div> </div>
</a-button> </a-button>
</template> </template>
@ -25,7 +25,8 @@
<ExecutionStatus :module-type="props.moduleType" :status="filterContent.value" /> <ExecutionStatus :module-type="props.moduleType" :status="filterContent.value" />
</template> </template>
<template #execStatus="{ record }"> <template #execStatus="{ record }">
<ExecStatus :status="record.execStatus" /> <ExecStatus v-if="record.execStatus" :status="record.execStatus" />
<span v-else>-</span>
</template> </template>
<template #[FilterSlotNameEnum.API_TEST_CASE_API_REPORT_EXECUTE_RESULT]="{ filterContent }"> <template #[FilterSlotNameEnum.API_TEST_CASE_API_REPORT_EXECUTE_RESULT]="{ filterContent }">
<ExecStatus :status="filterContent.value" /> <ExecStatus :status="filterContent.value" />
@ -38,12 +39,37 @@
<template #triggerMode="{ record }"> <template #triggerMode="{ record }">
<span>{{ t(TriggerModeLabel[record.triggerMode as keyof typeof TriggerModeLabel]) }}</span> <span>{{ t(TriggerModeLabel[record.triggerMode as keyof typeof TriggerModeLabel]) }}</span>
</template> </template>
<template #operationTime="{ record }"> <template #createTime="{ record }">
<span>{{ dayjs(record.operationTime).format('YYYY-MM-DD HH:mm:ss') }}</span> <span>{{ dayjs(record.createTime).format('YYYY-MM-DD HH:mm:ss') }}</span>
</template> </template>
</ms-base-table> </ms-base-table>
</MsDrawer> </MsDrawer>
<ReportDrawer v-model:visible="reportVisible" :report-id="independentReportId" /> <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">
@ -54,31 +80,35 @@
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 MsTag from '@/components/pure/ms-tag/ms-tag.vue'; import MsTag from '@/components/pure/ms-tag/ms-tag.vue';
import CaseReportDrawer from '@/views/api-test/report/component/caseReportDrawer.vue';
import ReportDetailDrawer from '@/views/api-test/report/component/reportDetailDrawer.vue';
import ExecutionStatus from '@/views/api-test/report/component/reportStatus.vue'; import ExecutionStatus from '@/views/api-test/report/component/reportStatus.vue';
import ExecStatus from '@/views/test-plan/report/component/execStatus.vue'; import ExecStatus from '@/views/test-plan/report/component/execStatus.vue';
import ReportDrawer from '@/views/test-plan/testPlan/detail/reportDrawer.vue';
import { getShareTime } from '@/api/modules/api-test/report';
import { organizationBatchTaskReportList } from '@/api/modules/taskCenter/organization'; import { organizationBatchTaskReportList } from '@/api/modules/taskCenter/organization';
import { projectBatchTaskReportList } from '@/api/modules/taskCenter/project'; import { projectBatchTaskReportList } from '@/api/modules/taskCenter/project';
import { systemBatchTaskReportList } from '@/api/modules/taskCenter/system'; import { systemBatchTaskReportList } from '@/api/modules/taskCenter/system';
import { useI18n } from '@/hooks/useI18n'; import { useI18n } from '@/hooks/useI18n';
import useTableStore from '@/hooks/useTableStore';
import useAppStore from '@/store/modules/app'; import useAppStore from '@/store/modules/app';
import { TaskCenterBatchTaskReportItem } from '@/models/taskCenter'; import { TaskCenterTaskItem } from '@/models/taskCenter';
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 { TableKeyEnum } from '@/enums/tableEnum';
import { FilterSlotNameEnum } from '@/enums/tableFilterEnum'; import { FilterSlotNameEnum } from '@/enums/tableFilterEnum';
import { ExecuteTaskType } from '@/enums/taskCenter';
import { executeMethodMap } from './config';
const props = defineProps<{ const props = defineProps<{
range: 'system' | 'project' | 'org'; range: 'system' | 'project' | 'org';
type: 'CASE' | 'SCENARIO'; type: 'CASE' | 'SCENARIO';
moduleType: keyof typeof ReportEnum; moduleType: keyof typeof ReportEnum;
taskId: string;
batchType: ExecuteTaskType;
}>(); }>();
const { t } = useI18n(); const { t } = useI18n();
const tableStore = useTableStore();
const appStore = useAppStore(); const appStore = useAppStore();
const visible = defineModel<boolean>('visible', { required: true }); const visible = defineModel<boolean>('visible', { required: true });
@ -125,13 +155,13 @@
sortDirections: ['ascend', 'descend'], sortDirections: ['ascend', 'descend'],
sorter: true, sorter: true,
}, },
ellipsis: true, fixed: 'left',
}, },
{ {
title: 'report.type', title: 'report.type',
slotName: 'integrated', slotName: 'integrated',
dataIndex: 'integrated', dataIndex: 'integrated',
width: 150, width: 120,
}, },
{ {
title: 'report.result', title: 'report.result',
@ -146,7 +176,7 @@
sorter: true, sorter: true,
}, },
showInTable: true, showInTable: true,
width: 200, width: 120,
}, },
{ {
title: 'report.status', title: 'report.status',
@ -161,27 +191,34 @@
sorter: true, sorter: true,
}, },
showInTable: true, showInTable: true,
width: 200, width: 120,
}, },
{ {
title: 'report.trigger.mode', title: 'report.trigger.mode',
dataIndex: 'triggerMode', dataIndex: 'triggerMode',
slotName: 'triggerMode', slotName: 'triggerMode',
showInTable: true, showInTable: true,
width: 150, filterConfig: {
options: Object.keys(executeMethodMap).map((key) => ({
label: t(executeMethodMap[key]),
value: key,
})),
filterSlotName: FilterSlotNameEnum.GLOBAL_TASK_CENTER_EXEC_METHOD,
},
width: 100,
}, },
{ {
title: 'report.operator', title: 'report.operator',
slotName: 'createUserName', slotName: 'createUserName',
dataIndex: 'createUserName', dataIndex: 'createUserName',
showInTable: true, showInTable: true,
width: 300, width: 150,
showTooltip: true, showTooltip: true,
}, },
{ {
title: 'report.operating', title: 'report.operating',
dataIndex: 'startTime', dataIndex: 'createTime',
slotName: 'startTime', slotName: 'createTime',
width: 180, width: 180,
sortable: { sortable: {
sortDirections: ['ascend', 'descend'], sortDirections: ['ascend', 'descend'],
@ -190,8 +227,6 @@
}, },
]; ];
await tableStore.initColumn(TableKeyEnum.API_TEST_REPORT, columns, 'drawer');
const currentList = { const currentList = {
system: systemBatchTaskReportList, system: systemBatchTaskReportList,
org: organizationBatchTaskReportList, org: organizationBatchTaskReportList,
@ -200,7 +235,7 @@
const { propsRes, propsEvent, loadList, setLoadListParams } = useTable( const { propsRes, propsEvent, loadList, setLoadListParams } = useTable(
currentList, currentList,
{ {
tableKey: TableKeyEnum.API_TEST_REPORT, columns,
scroll: { scroll: {
x: '100%', x: '100%',
}, },
@ -224,6 +259,8 @@
keyword: keyword.value, keyword: keyword.value,
projectId: appStore.currentProjectId, projectId: appStore.currentProjectId,
moduleType: props.moduleType, moduleType: props.moduleType,
taskId: props.taskId,
batchType: props.batchType,
filter: { filter: {
integrated: typeFilter.value, integrated: typeFilter.value,
...filterParams, ...filterParams,
@ -236,13 +273,55 @@
initData(dataIndex, value); initData(dataIndex, value);
} }
const reportVisible = ref(false); /**
const independentReportId = ref<string>(''); * 报告详情 showReportDetail
*/
const activeDetailId = ref<string>('');
const activeReportIndex = ref<number>(0);
const showDetailDrawer = ref<boolean>(false);
const showCaseDetailDrawer = ref<boolean>(false);
function showReportDetail(record: TaskCenterBatchTaskReportItem) { function showReportDetail(record: TaskCenterTaskItem) {
independentReportId.value = record.id; activeDetailId.value = record.id;
reportVisible.value = true; if ([ExecuteTaskType.API_SCENARIO_BATCH, ExecuteTaskType.TEST_PLAN_API_SCENARIO_BATCH].includes(props.batchType)) {
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);
}
}
watch(
() => visible.value,
(val) => {
if (val) {
initData();
getTime();
}
},
{ immediate: true }
);
</script> </script>
<style lang="less" scoped></style> <style lang="less" scoped></style>

View File

@ -2,21 +2,22 @@
<MsDrawer v-model:visible="visible" :width="800" :footer="false"> <MsDrawer v-model:visible="visible" :width="800" :footer="false">
<template #title> <template #title>
<div class="flex items-center gap-[8px]"> <div class="flex items-center gap-[8px]">
<a-tag :color="executeResultMap[detail.executeResult]?.color"> <a-tag :color="executeResultMap[props.record.result]?.color">
{{ t(executeResultMap[detail.executeResult]?.label) }} {{ t(executeResultMap[props.record.result]?.label) }}
</a-tag> </a-tag>
<div>{{ detail.name }}</div> <div>{{ detail.name }}</div>
</div> </div>
<div class="flex flex-1 justify-end"> <div class="flex flex-1 justify-end">
<MsButton type="icon" status="secondary" class="!rounded-[var(--border-radius-small)]" @click="refresh"> <MsButton type="icon" status="secondary" class="!rounded-[var(--border-radius-small)]" @click="init">
<MsIcon type="icon-icon_reset_outlined" class="mr-[8px]" size="14" /> <MsIcon type="icon-icon_reset_outlined" class="mr-[8px]" size="14" />
{{ t('common.refresh') }} {{ t('common.refresh') }}
</MsButton> </MsButton>
</div> </div>
</template> </template>
<a-spin :loading="loading" class="block">
<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="item.value as ReportExecStatus" size="small" /> <execStatus v-if="item.key === 'status'" :status="props.record.status" size="small" />
<a-tooltip <a-tooltip
v-else v-else
:content="`${item.value}`" :content="`${item.value}`"
@ -31,15 +32,17 @@
</MsDescription> </MsDescription>
<div class="mt-[8px]"> <div class="mt-[8px]">
<StepDetailContent <StepDetailContent
v-if="visible && detail.content"
mode="tiled" mode="tiled"
show-type="CASE" show-type="CASE"
:step-item="detail.scenarioDetail" static
:console="detail.console" :static-info="detail"
:is-definition="true" :is-definition="true"
:get-report-step-detail="props.getReportStepDetail" :get-report-step-detail="getCaseTaskReport"
:report-id="detail.scenarioDetail?.reportId" :report-id="detail.reportId"
/> />
</div> </div>
</a-spin>
</MsDrawer> </MsDrawer>
</template> </template>
@ -53,55 +56,55 @@
import execStatus from './execStatus.vue'; import execStatus from './execStatus.vue';
import StepDetailContent from '@/views/api-test/components/requestComposition/response/result/index.vue'; import StepDetailContent from '@/views/api-test/components/requestComposition/response/result/index.vue';
import { getCaseTaskReport } from '@/api/modules/api-test/report';
import { useI18n } from '@/hooks/useI18n'; import { useI18n } from '@/hooks/useI18n';
import { ReportExecStatus } from '@/enums/apiEnum'; import { TaskCenterTaskDetailItem } from '@/models/taskCenter';
import { executeResultMap } from './config'; import { executeResultMap, executeStatusMap } from './config';
const props = defineProps<{ const props = defineProps<{
id: string; record: TaskCenterTaskDetailItem;
getReportStepDetail?: (...args: any) => Promise<any>; //
}>(); }>();
const { t } = useI18n(); const { t } = useI18n();
const visible = defineModel<boolean>('visible', { required: true }); const visible = defineModel<boolean>('visible', { required: true });
const loading = ref(false);
const detail = ref<any>({ description: [] }); const detail = ref<any>({ description: [] });
watch( async function init() {
() => props.id, try {
async () => { loading.value = true;
if (props.id) { const res = await getCaseTaskReport(props.record.id);
const [caseDetail] = res;
detail.value = { detail.value = {
id: props.id, name: caseDetail.requestName,
name: '测试用例名称',
executeResult: 'SUCCESS',
description: [ description: [
{ {
label: t('ms.taskCenter.executeStatus'), label: t('ms.taskCenter.executeStatus'),
key: 'status', key: 'status',
value: 'COMPLETED', value: t(executeStatusMap[props.record.status].label),
}, },
{ {
label: t('ms.taskCenter.operationUser'), label: t('ms.taskCenter.operationUser'),
value: 'admin', value: props.record.executor,
}, },
{ {
label: t('ms.taskCenter.taskCreateTime'), label: t('ms.taskCenter.taskCreateTime'),
value: dayjs(1626844800000).format('YYYY-MM-DD HH:mm:ss'), value: dayjs(props.record.startTime).format('YYYY-MM-DD HH:mm:ss'),
}, },
{ {
label: t('ms.taskCenter.taskResource'), label: t('ms.taskCenter.taskResource'),
value: '测试计划', value: props.record.resourceName,
}, },
{ {
label: t('ms.taskCenter.threadID'), label: t('ms.taskCenter.threadID'),
value: '1231231231', value: props.record.threadId,
}, },
{ {
label: t('ms.taskCenter.taskStartTime'), label: t('ms.taskCenter.taskStartTime'),
value: dayjs(1626844800000).format('YYYY-MM-DD HH:mm:ss'), value: dayjs(props.record.startTime).format('YYYY-MM-DD HH:mm:ss'),
}, },
{ {
label: t('ms.taskCenter.executeEnvInfo'), label: t('ms.taskCenter.executeEnvInfo'),
@ -110,18 +113,28 @@
}, },
{ {
label: t('ms.taskCenter.taskEndTime'), label: t('ms.taskCenter.taskEndTime'),
value: dayjs(1626844800000).format('YYYY-MM-DD HH:mm:ss'), value: dayjs(props.record.endTime).format('YYYY-MM-DD HH:mm:ss'),
}, },
] as Description[], ] as Description[],
...caseDetail,
}; };
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
} finally {
loading.value = false;
}
}
watch(
() => visible.value,
(val) => {
if (props.record.id && val) {
init();
} }
}, },
{ immediate: true } { immediate: true }
); );
function refresh() {
console.log('refresh');
}
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>

View File

@ -12,15 +12,18 @@
<MsCascader <MsCascader
v-model:model-value="resourcePool" v-model:model-value="resourcePool"
mode="native" mode="native"
multiple
:options="resourcePoolOptions" :options="resourcePoolOptions"
:placeholder="t('common.pleaseSelect')" :placeholder="t('common.pleaseSelect')"
option-size="small" option-size="small"
label-key="name"
value-key="id"
class="w-[240px]" class="w-[240px]"
:prefix="t('ms.taskCenter.resourcePool')" :prefix="t('ms.taskCenter.resourcePool')"
:virtual-list-props="{ height: 200 }"
strictly
label-path-mode label-path-mode
@change="searchTask" @clear="searchTask"
@popup-visible-change="handleResourcePoolVisibleChange"
@change="handleResourcePoolChange"
> >
</MsCascader> </MsCascader>
<MsTag no-margin size="large" :tooltip-disabled="true" class="cursor-pointer" theme="outline" @click="searchTask"> <MsTag no-margin size="large" :tooltip-disabled="true" class="cursor-pointer" theme="outline" @click="searchTask">
@ -69,13 +72,13 @@
> >
{{ t('ms.taskCenter.rerun') }} {{ t('ms.taskCenter.rerun') }}
</MsButton> --> </MsButton> -->
<MsButton v-if="record.status === ExecuteStatusEnum.COMPLETED" @click="checkExecuteResult(record)"> <MsButton v-if="record.status !== ExecuteStatusEnum.PENDING" @click="checkExecuteResult(record)">
{{ t('ms.taskCenter.executeResult') }} {{ t('ms.taskCenter.executeResult') }}
</MsButton> </MsButton>
</template> </template>
</ms-base-table> </ms-base-table>
<caseExecuteResultDrawer :id="executeResultId" v-model:visible="caseExecuteResultDrawerVisible" /> <caseExecuteResultDrawer v-model:visible="caseExecuteResultDrawerVisible" :record="activeRecord" />
<scenarioExecuteResultDrawer :id="executeResultId" v-model:visible="scenarioExecuteResultDrawerVisible" /> <scenarioExecuteResultDrawer v-model:visible="scenarioExecuteResultDrawerVisible" :record="activeRecord" />
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
@ -119,7 +122,8 @@
import { useI18n } from '@/hooks/useI18n'; import { useI18n } from '@/hooks/useI18n';
import useModal from '@/hooks/useModal'; import useModal from '@/hooks/useModal';
import useTableStore from '@/hooks/useTableStore'; import useTableStore from '@/hooks/useTableStore';
import { characterLimit } from '@/utils'; import { useAppStore } from '@/store';
import { characterLimit, mapTree } from '@/utils';
import { TaskCenterTaskDetailItem } from '@/models/taskCenter'; import { TaskCenterTaskDetailItem } from '@/models/taskCenter';
import { TableKeyEnum } from '@/enums/tableEnum'; import { TableKeyEnum } from '@/enums/tableEnum';
@ -135,10 +139,11 @@
const { t } = useI18n(); const { t } = useI18n();
const { openModal } = useModal(); const { openModal } = useModal();
const appStore = useAppStore();
const tableStore = useTableStore(); const tableStore = useTableStore();
const keyword = ref(''); const keyword = ref('');
const resourcePool = ref([]); const resourcePool = ref<string[]>([]);
const resourcePoolOptions = ref<CascaderOption[]>([]); const resourcePoolOptions = ref<CascaderOption[]>([]);
const tableSelected = ref<string[]>([]); const tableSelected = ref<string[]>([]);
const batchModalParams = ref(); const batchModalParams = ref();
@ -334,11 +339,57 @@
} }
); );
const resourcePoolIds = ref<Set<string>>(new Set([]));
const resourcePoolNodes = ref<Set<string>>(new Set([]));
function searchTask() { function searchTask() {
setLoadListParams({ keyword: keyword.value, resourcePools: resourcePool.value }); setLoadListParams({
keyword: keyword.value,
resourcePoolIds: Array.from(resourcePoolIds.value),
resourcePoolNodes: Array.from(resourcePoolNodes.value),
});
loadList(); loadList();
} }
function handleResourcePoolVisibleChange(val: boolean) {
if (!val) {
searchTask();
}
}
function handleResourcePoolChange(value: string[]) {
if (resourcePool.value.length < value.length) {
//
const lastValue = value[value.length - 1];
const resourceClass = resourcePoolOptions.value.find((e) => e.value === lastValue);
if (resourceClass && resourceClass.children && resourceClass.children.length > 0) {
const childIds = resourceClass.children.map((e) => e.value as string);
resourcePool.value.push(...value, ...childIds);
resourcePool.value = Array.from(new Set(resourcePool.value));
childIds.forEach((e) => {
resourcePoolNodes.value.add(e);
});
}
if (resourceClass) {
//
resourcePoolIds.value.add(resourceClass.value as string);
} else {
//
resourcePoolNodes.value.add(lastValue);
}
} else {
//
const lastValue = value[value.length - 1];
const resourceClass = resourcePoolOptions.value.find((e) => e.value === lastValue);
if (resourceClass) {
//
resourcePoolIds.value.delete(resourceClass.value as string);
} else {
//
resourcePoolNodes.value.delete(lastValue);
}
}
}
const currentStopTask = { const currentStopTask = {
system: systemStopTaskDetail, system: systemStopTaskDetail,
project: projectStopTaskDetail, project: projectStopTaskDetail,
@ -409,11 +460,11 @@
} }
} }
const executeResultId = ref(''); const activeRecord = ref<TaskCenterTaskDetailItem>({} as TaskCenterTaskDetailItem);
const caseExecuteResultDrawerVisible = ref(false); const caseExecuteResultDrawerVisible = ref(false);
const scenarioExecuteResultDrawerVisible = ref(false); const scenarioExecuteResultDrawerVisible = ref(false);
function checkExecuteResult(record: TaskCenterTaskDetailItem) { function checkExecuteResult(record: TaskCenterTaskDetailItem) {
executeResultId.value = record.id; activeRecord.value = record;
if (record.resourceType === 'API_SCENARIO') { if (record.resourceType === 'API_SCENARIO') {
scenarioExecuteResultDrawerVisible.value = true; scenarioExecuteResultDrawerVisible.value = true;
} else { } else {
@ -422,15 +473,19 @@
} }
const currentResourcePoolRequest = { const currentResourcePoolRequest = {
system: getProjectTaskCenterResourcePools, system: getSystemTaskCenterResourcePools,
project: getOrgTaskCenterResourcePools, project: getProjectTaskCenterResourcePools,
org: getSystemTaskCenterResourcePools, org: getOrgTaskCenterResourcePools,
}[props.type]; }[props.type];
async function initResourcePools() { async function initResourcePools() {
try { try {
const res = await currentResourcePoolRequest(); const res = await currentResourcePoolRequest();
resourcePoolOptions.value = res; resourcePoolOptions.value = mapTree(res, (node) => ({
label: node.name,
value: node.id,
children: node.children,
}));
} catch (error) { } catch (error) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log(error); console.log(error);
@ -518,6 +573,13 @@
} }
); );
watch(
() => appStore.currentProjectId,
() => {
searchTask();
}
);
onMounted(async () => { onMounted(async () => {
if (props.id) { if (props.id) {
keyword.value = props.id; keyword.value = props.id;

View File

@ -131,9 +131,11 @@
</ms-base-table> </ms-base-table>
<batchTaskReportDrawer <batchTaskReportDrawer
v-model:visible="taskReportDrawerVisible" v-model:visible="taskReportDrawerVisible"
:range="reportModuleType" :range="props.type"
:type="reportModuleType" :type="reportType"
:module-type="reportModuleType" :module-type="reportModuleType"
:task-id="reportBatchTaskId"
:batch-type="reportBatchType"
/> />
<CaseReportDrawer <CaseReportDrawer
v-model:visible="showCaseDetailDrawer" v-model:visible="showCaseDetailDrawer"
@ -257,7 +259,7 @@
title: 'ms.taskCenter.executeStatus', title: 'ms.taskCenter.executeStatus',
dataIndex: 'status', dataIndex: 'status',
slotName: 'status', slotName: 'status',
width: 90, width: 100,
filterConfig: { filterConfig: {
options: Object.keys(executeStatusMap).map((key) => ({ options: Object.keys(executeStatusMap).map((key) => ({
label: t(executeStatusMap[key as ExecuteStatusEnum].label), label: t(executeStatusMap[key as ExecuteStatusEnum].label),
@ -270,7 +272,7 @@
title: 'ms.taskCenter.executeMethod', title: 'ms.taskCenter.executeMethod',
dataIndex: 'triggerMode', dataIndex: 'triggerMode',
slotName: 'triggerMode', slotName: 'triggerMode',
width: 90, width: 100,
filterConfig: { filterConfig: {
options: Object.keys(executeMethodMap).map((key) => ({ options: Object.keys(executeMethodMap).map((key) => ({
label: t(executeMethodMap[key]), label: t(executeMethodMap[key]),
@ -283,7 +285,7 @@
title: 'ms.taskCenter.executeResult', title: 'ms.taskCenter.executeResult',
dataIndex: 'result', dataIndex: 'result',
slotName: 'result', slotName: 'result',
width: 90, width: 100,
filterConfig: { filterConfig: {
options: Object.keys(executeResultMap).map((key) => ({ options: Object.keys(executeResultMap).map((key) => ({
label: t(executeResultMap[key].label), label: t(executeResultMap[key].label),
@ -686,13 +688,17 @@
const taskReportDrawerVisible = ref(false); const taskReportDrawerVisible = ref(false);
const reportModuleType = ref(); const reportModuleType = ref();
const reportBatchType = ref(); const reportType = ref<'CASE' | 'SCENARIO'>('CASE');
const reportBatchType = ref<ExecuteTaskType>(ExecuteTaskType.API_CASE);
const reportBatchTaskId = ref('');
function checkReport(record: TaskCenterTaskItem) { function checkReport(record: TaskCenterTaskItem) {
if (record.taskType.includes('BATCH')) { if (record.taskType.includes('BATCH')) {
reportModuleType.value = record.taskType.includes('CASE') reportModuleType.value = record.taskType.includes('CASE')
? ReportEnum.API_REPORT ? ReportEnum.API_REPORT
: ReportEnum.API_SCENARIO_REPORT; : ReportEnum.API_SCENARIO_REPORT;
reportBatchType.value = record.taskType.includes('CASE') ? 'CASE' : 'SCENARIO'; reportType.value = record.taskType.includes('CASE') ? 'CASE' : 'SCENARIO';
reportBatchType.value = record.taskType;
reportBatchTaskId.value = record.id;
taskReportDrawerVisible.value = true; taskReportDrawerVisible.value = true;
} else if ( } else if (
[ [
@ -726,6 +732,13 @@
} }
); );
watch(
() => appStore.currentProjectId,
() => {
searchTask();
}
);
await tableStore.initColumn(TableKeyEnum.TASK_CENTER_CASE_TASK, columns, 'drawer'); await tableStore.initColumn(TableKeyEnum.TASK_CENTER_CASE_TASK, columns, 'drawer');
</script> </script>

View File

@ -12,11 +12,12 @@
import { useI18n } from '@/hooks/useI18n'; import { useI18n } from '@/hooks/useI18n';
import { ReportExecStatus } from '@/enums/apiEnum'; import { ReportExecStatus } from '@/enums/apiEnum';
import { ExecuteStatusEnum } from '@/enums/taskCenter';
import { executeStatusMap } from './config'; import { executeStatusMap } from './config';
const props = defineProps<{ const props = defineProps<{
status: ReportExecStatus; status: ReportExecStatus | ExecuteStatusEnum;
size?: 'small' | 'medium' | 'large'; size?: 'small' | 'medium' | 'large';
}>(); }>();
const { t } = useI18n(); const { t } = useI18n();

View File

@ -2,18 +2,19 @@
<MsDrawer v-model:visible="visible" :width="800" :footer="false"> <MsDrawer v-model:visible="visible" :width="800" :footer="false">
<template #title> <template #title>
<div class="flex items-center gap-[8px]"> <div class="flex items-center gap-[8px]">
<a-tag :color="executeResultMap[detail.executeResult]?.color"> <a-tag :color="executeResultMap[props.record.result]?.color">
{{ t(executeResultMap[detail.executeResult]?.label) }} {{ t(executeResultMap[props.record.result]?.label) }}
</a-tag> </a-tag>
<div>{{ detail.name }}</div> <div>{{ detail.name }}</div>
</div> </div>
<div class="flex flex-1 justify-end"> <div class="flex flex-1 justify-end">
<MsButton type="icon" status="secondary" class="!rounded-[var(--border-radius-small)]" @click="refresh"> <MsButton type="icon" status="secondary" class="!rounded-[var(--border-radius-small)]" @click="init">
<MsIcon type="icon-icon_reset_outlined" class="mr-[8px]" size="14" /> <MsIcon type="icon-icon_reset_outlined" class="mr-[8px]" size="14" />
{{ t('common.refresh') }} {{ t('common.refresh') }}
</MsButton> </MsButton>
</div> </div>
</template> </template>
<a-spin :loading="loading" class="block">
<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="item.value as ReportExecStatus" size="small" /> <execStatus v-if="item.key === 'status'" :status="item.value as ReportExecStatus" size="small" />
@ -43,12 +44,13 @@
v-model:keyword-name="keywordName" v-model:keyword-name="keywordName"
:key-words="cascaderKeywords" :key-words="cascaderKeywords"
show-type="API" show-type="API"
:get-report-step-detail="props.getReportStepDetail" :get-report-step-detail="getScenarioTaskReportStep"
:active-type="activeTab" :active-type="activeTab"
:report-detail="detail || []" :report-detail="detail || []"
class="p-[16px]" class="p-[16px]"
/> />
</div> </div>
</a-spin>
</MsDrawer> </MsDrawer>
</template> </template>
@ -63,55 +65,54 @@
import reportInfoHeader from '@/views/api-test/report/component/step/reportInfoHeaders.vue'; import reportInfoHeader from '@/views/api-test/report/component/step/reportInfoHeaders.vue';
import TiledList from '@/views/api-test/report/component/tiledList.vue'; import TiledList from '@/views/api-test/report/component/tiledList.vue';
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 { ReportExecStatus } from '@/enums/apiEnum'; import { ReportExecStatus } from '@/enums/apiEnum';
import { executeResultMap } from './config'; import { executeResultMap, executeStatusMap } from './config';
const props = defineProps<{ const props = defineProps<{
id: string; record: TaskCenterTaskDetailItem;
getReportStepDetail?: (...args: any) => Promise<any>; //
}>(); }>();
const { t } = useI18n(); const { t } = useI18n();
const visible = defineModel<boolean>('visible', { required: true }); const visible = defineModel<boolean>('visible', { required: true });
const loading = ref(false);
const detail = ref<any>({ description: [], children: [] }); const detail = ref<any>({ description: [], children: [] });
watch( async function init() {
() => props.id, try {
async () => { loading.value = true;
if (props.id) { const res = await getScenarioTaskReport(props.record.id);
detail.value = { detail.value = {
id: props.id,
name: '测试用例名称',
executeResult: 'SUCCESS',
description: [ description: [
{ {
label: t('ms.taskCenter.executeStatus'), label: t('ms.taskCenter.executeStatus'),
key: 'status', key: 'status',
value: 'COMPLETED', value: t(executeStatusMap[props.record.status].label),
}, },
{ {
label: t('ms.taskCenter.operationUser'), label: t('ms.taskCenter.operationUser'),
value: 'admin', value: res.creatUserName,
}, },
{ {
label: t('ms.taskCenter.taskCreateTime'), label: t('ms.taskCenter.taskCreateTime'),
value: dayjs(1626844800000).format('YYYY-MM-DD HH:mm:ss'), value: dayjs(res.startTime).format('YYYY-MM-DD HH:mm:ss'),
}, },
{ {
label: t('ms.taskCenter.taskResource'), label: t('ms.taskCenter.taskResource'),
value: '测试计划', value: props.record.resourceName,
}, },
{ {
label: t('ms.taskCenter.threadID'), label: t('ms.taskCenter.threadID'),
value: '1231231231', value: props.record.threadId,
}, },
{ {
label: t('ms.taskCenter.taskStartTime'), label: t('ms.taskCenter.taskStartTime'),
value: dayjs(1626844800000).format('YYYY-MM-DD HH:mm:ss'), value: dayjs(res.startTime).format('YYYY-MM-DD HH:mm:ss'),
}, },
{ {
label: t('ms.taskCenter.executeEnvInfo'), label: t('ms.taskCenter.executeEnvInfo'),
@ -120,11 +121,24 @@
}, },
{ {
label: t('ms.taskCenter.taskEndTime'), label: t('ms.taskCenter.taskEndTime'),
value: dayjs(1626844800000).format('YYYY-MM-DD HH:mm:ss'), value: res.endTime ? dayjs(res.endTime).format('YYYY-MM-DD HH:mm:ss') : '-',
}, },
] as Description[], ] as Description[],
children: [], ...res,
}; };
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
} finally {
loading.value = false;
}
}
watch(
() => props.record.id,
() => {
if (props.record.id) {
init();
} }
}, },
{ immediate: true } { immediate: true }
@ -146,10 +160,6 @@
function resetHandler() { function resetHandler() {
tiledListRef.value?.initStepTree(); tiledListRef.value?.initStepTree();
} }
function refresh() {
console.log('refresh');
}
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>

View File

@ -104,6 +104,7 @@
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';
import { characterLimit } from '@/utils'; import { characterLimit } from '@/utils';
import { hasAnyPermission } from '@/utils/permission'; import { hasAnyPermission } from '@/utils/permission';
@ -123,6 +124,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 batchModalParams = ref(); const batchModalParams = ref();
@ -464,6 +466,13 @@
onMounted(() => { onMounted(() => {
loadList(); loadList();
}); });
watch(
() => appStore.currentProjectId,
() => {
searchTask();
}
);
</script> </script>
<style lang="less" scoped></style> <style lang="less" scoped></style>