feat(测试计划): 测试计划报告联调(不包含聚合报告报告明细)

This commit is contained in:
xinxin.wu 2024-06-14 22:56:19 +08:00 committed by Craftsman
parent 60a44a81d2
commit fed0ce3e10
34 changed files with 368 additions and 518 deletions

View File

@ -9,6 +9,7 @@ import {
ReportBugItem,
UpdateReportDetailParams,
} from '@/models/testPlan/report';
import { PlanReportDetail } from '@/models/testPlan/testPlanReport';
// 报告列表
export function reportList(data: TableQueryParams) {
@ -100,5 +101,13 @@ export function getShareApiPage(data: TableQueryParams) {
export function getShareScenarioPage(data: TableQueryParams) {
return MSR.post<CommonList<ApiOrScenarioCaseItem>>({ url: reportUrl.ReportShareScenarioUrl, data });
}
// 测试计划-聚合报告-报告明细
export function getReportDetailPage(data: TableQueryParams) {
return MSR.post<CommonList<PlanReportDetail>>({ url: reportUrl.ReportDetailPageUrl, data });
}
// 测试计划-聚合报告-报告明细-分享
export function getReportDetailSharePage(data: TableQueryParams) {
return MSR.post<CommonList<PlanReportDetail>>({ url: reportUrl.ReportDetailSharePageUrl, data });
}
export default {};

View File

@ -36,3 +36,7 @@ export const ReportIndependentScenarioUrl = '/test-plan/report/detail/scenario/c
export const ReportShareApiUrl = '/test-plan/report/share/detail/api/case/page';
// 测试计划-独立报告-场景用例-分享
export const ReportShareScenarioUrl = '/test-plan/report/share/detail/scenario/case/page';
// 测试计划-聚合报告-报告明细
export const ReportDetailPageUrl = '/test-plan/report/detail/plan/report/page';
// 测试计划-聚合报告-报告明细-分享
export const ReportDetailSharePageUrl = '/test-plan/report/share/detail/plan/report/page';

View File

@ -244,6 +244,11 @@
watch(
() => () => props.currentProject,
() => {
setPagination({
current: 1,
});
resetSelector();
resetFilterParams();
loadCaseList();
}
);
@ -252,6 +257,8 @@
() => props.activeModule,
(val) => {
if (val) {
resetSelector();
resetFilterParams();
loadCaseList();
}
}

View File

@ -206,6 +206,11 @@
watch(
() => [() => props.currentProject, () => props.protocols],
() => {
setPagination({
current: 1,
});
resetSelector();
resetFilterParams();
loadApiList();
}
);
@ -225,6 +230,8 @@
() => props.activeModule,
(val) => {
if (val) {
resetSelector();
resetFilterParams();
loadApiList();
}
}

View File

@ -283,6 +283,11 @@
() => props.currentProject,
(val) => {
if (val) {
setPagination({
current: 1,
});
resetSelector();
resetFilterParams();
loadCaseList();
}
},
@ -295,6 +300,8 @@
() => props.activeModule,
(val) => {
if (val) {
resetSelector();
resetFilterParams();
loadCaseList();
}
}

View File

@ -243,6 +243,11 @@
() => props.currentProject,
(val) => {
if (val) {
setPagination({
current: 1,
});
resetSelector();
resetFilterParams();
loadScenarioList();
}
},
@ -255,6 +260,8 @@
() => props.activeModule,
(val) => {
if (val) {
resetSelector();
resetFilterParams();
loadScenarioList();
}
}

View File

@ -45,7 +45,14 @@
</MsTag> -->
<slot name="right"></slot>
<MsTag no-margin size="large" class="cursor-pointer" theme="outline" @click="handleRefresh">
<MsTag
no-margin
size="large"
:tooltip-disabled="true"
class="cursor-pointer"
theme="outline"
@click="handleRefresh"
>
<MsIcon class="text-[16px] text-[var(color-text-4)]" :size="32" type="icon-icon_reset_outlined" />
</MsTag>
</div>

View File

@ -82,6 +82,7 @@ export const defaultReportDetail: PlanReportDetail = {
functionalCount: cloneDeep(defaultCount),
apiCaseCount: cloneDeep(defaultCount),
apiScenarioCount: cloneDeep(defaultCount),
planCount: 0,
};
export const statusConfig: StatusListType[] = [

View File

@ -72,8 +72,6 @@ export enum TableKeyEnum {
TEST_PLAN_DETAIL_API_CASE = 'testPlanDetailApiCase',
TEST_PLAN_DETAIL_API_SCENARIO = 'testPlanDetailApiScenario',
TEST_PLAN_REPORT_TABLE = 'testPlanReportTable',
TEST_PLAN_REPORT_DETAIL_BUG = 'testPlanReportDetailBug',
TEST_PLAN_REPORT_DETAIL_FEATURE_CASE = 'testPlanReportDetailFeatureCase',
TASK_API_CASE_SYSTEM = 'taskCenterApiCaseSystem',
TASK_API_CASE_ORGANIZATION = 'taskCenterApiCaseOrganization',
TASK_API_CASE_PROJECT = 'taskCenterApiCaseProject',

View File

@ -147,6 +147,7 @@ export interface FollowPlanParams {
export interface TestPlanBaseParams {
projectId?: string;
testPlanId: string;
triggerMode?: string;
}
export interface PlanDetailFeatureCaseItem {

View File

@ -21,6 +21,7 @@ export interface PlanReportDetail {
functionalCount: countDetail;
apiCaseCount: countDetail; // 接口场景用例分析-用例数
apiScenarioCount: countDetail; // 接口场景用例分析-用例数
planCount: number;
}
export type AnalysisType = 'FUNCTIONAL' | 'API' | 'SCENARIO';

View File

@ -2,7 +2,7 @@
<MsDetailDrawer
ref="detailDrawerRef"
v-model:visible="showDrawerVisible"
:width="900"
:width="850"
:footer="false"
:title="t('bugManagement.detail.title', { id: detailInfo?.num, name: detailInfo?.title })"
:tooltip-text="(detailInfo && detailInfo.title) || null"

View File

@ -2,7 +2,7 @@
<MsDetailDrawer
ref="detailDrawerRef"
v-model:visible="showDrawerVisible"
:width="960"
:width="850"
:footer="false"
:mask="false"
:title="t('caseManagement.featureCase.caseDetailTitle', { id: detailInfo?.num, name: detailInfo?.name })"

View File

@ -28,7 +28,7 @@
type="text"
class="one-line-text w-full"
:class="[hasJumpPermission ? 'text-[rgb(var(--primary-5))]' : '']"
@click="showDetail()"
@click="showDetail(record.resourceId)"
>{{ record.resourceNum }}
</div>
</template>
@ -37,7 +37,7 @@
v-if="!record.integrated"
class="one-line-text max-w-[300px]"
:class="[hasJumpPermission ? 'text-[rgb(var(--primary-5))]' : '']"
@click="showDetail()"
@click="showDetail(record.resourceId)"
>{{ record.resourceName }}
</div>
</template>
@ -75,7 +75,7 @@
<MsButton
class="!mr-0"
:disabled="record.historyDeleted || !hasAnyPermission(permissionsMap[props.group].report)"
@click="viewReport(record.id)"
@click="viewReport(record.id, record.integrated)"
>{{ t('project.taskCenter.viewReport') }}
</MsButton>
</a-tooltip>
@ -84,7 +84,7 @@
<MsButton
class="!mr-0"
:disabled="record.historyDeleted || !hasAnyPermission(permissionsMap[props.group].report)"
@click="viewReport(record.id)"
@click="viewReport(record.id, record.integrated)"
>{{ t('project.taskCenter.viewReport') }}
</MsButton>
</div>
@ -420,14 +420,17 @@
}
}
function viewReport(id: string) {
function viewReport(id: string, type: boolean) {
openNewPage(RouteEnum.TEST_PLAN_REPORT_DETAIL, {
id,
type: type ? 'GROUP' : 'TEST_PLAN',
});
}
function showDetail() {
// TODO
function showDetail(id: string) {
openNewPage(RouteEnum.TEST_PLAN_INDEX_DETAIL, {
id,
});
}
function stop(record: any) {

View File

@ -36,7 +36,7 @@
<div
type="text"
class="one-line-text flex w-full text-[rgb(var(--primary-5))]"
@click="showReportDetail(record.id)"
@click="showReportDetail(record.id, record.integrated)"
>
{{ characterLimit(record.name) }}
</div>
@ -56,7 +56,7 @@
</template>
<template #passRate="{ record }">
<div class="text-[var(--color-text-1)]">
{{ `${record.passRate}%` }}
{{ `${record.passRate || '0.00'}%` }}
</div>
</template>
<!-- 执行状态筛选 -->
@ -420,19 +420,17 @@
/**
* 报告详情 showReportDetail
*/
function showReportDetail(id: string) {
function showReportDetail(id: string, type: boolean) {
router.push({
name: TestPlanRouteEnum.TEST_PLAN_REPORT_DETAIL,
query: {
id,
type: type ? 'GROUP' : 'TEST_PLAN',
},
});
}
onBeforeMount(() => {
if (route.query.id) {
showReportDetail(route.query.id as string);
}
initData();
});
</script>

View File

@ -6,14 +6,14 @@
<template #[FilterSlotNameEnum.CASE_MANAGEMENT_CASE_LEVEL]="{ filterContent }">
<caseLevel :case-level="filterContent.value" />
</template>
<template #caseLevel="{ record }">
<CaseLevel :case-level="record.caseLevel" />
<template #priority="{ record }">
<caseLevel :case-level="record.priority" />
</template>
<template #[FilterSlotNameEnum.CASE_MANAGEMENT_EXECUTE_RESULT]="{ filterContent }">
<ExecuteResult :execute-result="filterContent.key" />
</template>
<template #lastExecResult="{ record }">
<ExecuteResult :execute-result="record.lastExecResult" />
<ExecuteResult :execute-result="record.executeResult" />
<!-- TOTO 暂时不上 -->
<!-- <MsIcon
v-show="record.lastExecResult !== LastExecuteResults.PENDING"
@ -37,15 +37,13 @@
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 caseLevel from '@/components/business/ms-case-associate/caseLevel.vue';
import ExecuteResult from '@/components/business/ms-case-associate/executeResult.vue';
import CaseAndScenarioReportDrawer from '@/views/api-test/components/caseAndScenarioReportDrawer.vue';
import { getApiPage, getScenarioPage, getShareApiPage, getShareScenarioPage } from '@/api/modules/test-plan/report';
import { ApiOrScenarioCaseItem } from '@/models/testPlan/report';
import { LastExecuteResults } from '@/enums/caseEnum';
import { TableKeyEnum } from '@/enums/tableEnum';
import { FilterSlotNameEnum } from '@/enums/tableFilterEnum';
import { casePriorityOptions } from '@/views/api-test/components/config';
@ -61,6 +59,7 @@
{
title: 'ID',
dataIndex: 'num',
slotName: 'num',
sortIndex: 1,
fixed: 'left',
width: 100,
@ -75,7 +74,7 @@
{
title: 'report.detail.level',
dataIndex: 'priority',
slotName: 'caseLevel',
slotName: 'priority',
filterConfig: {
options: casePriorityOptions,
filterSlotName: FilterSlotNameEnum.CASE_MANAGEMENT_CASE_LEVEL,
@ -85,7 +84,7 @@
},
{
title: 'common.executionResult',
dataIndex: 'lastExecResult',
dataIndex: 'executeResult',
slotName: 'lastExecResult',
filterConfig: {
valueKey: 'key',
@ -103,14 +102,6 @@
width: 200,
showDrag: true,
},
{
title: 'case.tableColumnCreateUser',
dataIndex: 'createUserName',
showTooltip: true,
width: 130,
showDrag: true,
},
{
title: 'testPlan.featureCase.executor',
dataIndex: 'executeUser',
@ -128,23 +119,16 @@
];
const reportApiList = () => {
if (props.activeTab === 'scenarioCase') {
return !props.shareId ? getShareScenarioPage : getScenarioPage;
return !props.shareId ? getScenarioPage : getShareScenarioPage;
}
return !props.shareId ? getShareApiPage : getApiPage;
return !props.shareId ? getApiPage : getShareApiPage;
};
const { propsRes, propsEvent, loadList, setLoadListParams } = useTable(
const { propsRes, propsEvent, loadList, setLoadListParams, resetFilterParams, resetPagination } = useTable(
reportApiList(),
{
scroll: { x: '100%' },
columns,
tableKey: TableKeyEnum.TEST_PLAN_REPORT_DETAIL_BUG,
showSelectorAll: false,
},
(record) => {
return {
...record,
lastExecResult: record.executeResult ?? LastExecuteResults.PENDING,
};
}
);
@ -172,6 +156,8 @@
watch(
() => props.activeTab,
() => {
resetFilterParams();
resetPagination();
loadCaseList();
}
);

View File

@ -10,17 +10,12 @@
import useTable from '@/components/pure/ms-table/useTable';
import { getReportBugList, getReportShareBugList } from '@/api/modules/test-plan/report';
import { useTableStore } from '@/store';
import { TableKeyEnum } from '@/enums/tableEnum';
const props = defineProps<{
reportId: string;
shareId?: string;
}>();
const tableStore = useTableStore();
const columns: MsTableColumn = [
{
title: 'ID',
@ -69,7 +64,6 @@
const { propsRes, propsEvent, loadList, setLoadListParams } = useTable(reportBugList(), {
scroll: { x: '100%' },
columns,
tableKey: TableKeyEnum.TEST_PLAN_REPORT_DETAIL_BUG,
showSelectorAll: false,
});
@ -83,6 +77,4 @@
loadCaseList();
}
});
await tableStore.initColumn(TableKeyEnum.TEST_PLAN_REPORT_DETAIL_BUG, columns, 'drawer');
</script>

View File

@ -0,0 +1,107 @@
<template>
<div class="block-title">{{ t('report.detail.executionAnalysis') }}</div>
<SetReportChart
size="160px"
:legend-data="legendData"
:options="executeCharOptions"
:request-total="getIndicators(detail.caseTotal) || 0"
/>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import SetReportChart from '@/views/api-test/report/component/case/setReportChart.vue';
import { commonConfig, seriesConfig, statusConfig } from '@/config/testPlan';
import { useI18n } from '@/hooks/useI18n';
import type { LegendData } from '@/models/apiTest/report';
import type { PlanReportDetail, StatusListType } from '@/models/testPlan/testPlanReport';
import { getIndicators } from '@/views/api-test/report/utils';
const { t } = useI18n();
const props = defineProps<{
detail: PlanReportDetail;
}>();
const legendData = ref<LegendData[]>([]);
//
const executeCharOptions = ref({
...commonConfig,
series: {
...seriesConfig,
data: [
{
value: 0,
name: t('common.success'),
itemStyle: {
color: '#00C261',
},
},
{
value: 0,
name: t('common.fakeError'),
itemStyle: {
color: '#FFC14E',
},
},
{
value: 0,
name: t('common.fail'),
itemStyle: {
color: '#ED0303',
},
},
{
value: 0,
name: t('common.unExecute'),
itemStyle: {
color: '#D4D4D8',
},
},
{
value: 0,
name: t('common.block'),
itemStyle: {
color: '#B379C8',
},
},
],
},
});
function initExecuteOptions() {
executeCharOptions.value.series.data = statusConfig.map((item: StatusListType) => {
return {
value: props.detail.executeCount[item.value] || 0,
name: t(item.label),
itemStyle: {
color: item.color,
borderWidth: 2,
borderColor: '#ffffff',
},
};
});
legendData.value = statusConfig.map((item: StatusListType) => {
const rate = (props.detail.executeCount[item.value] / props.detail.caseTotal) * 100;
return {
...item,
label: t(item.label),
count: props.detail.executeCount[item.value] || 0,
rote: `${Number.isNaN(rate) ? 0 : rate.toFixed(2)}%`,
};
}) as unknown as LegendData[];
}
watchEffect(() => {
if (props.detail) {
initExecuteOptions();
}
});
</script>
<style scoped></style>

View File

@ -22,9 +22,7 @@
import ExecuteResult from '@/components/business/ms-case-associate/executeResult.vue';
import { getReportFeatureCaseList, getReportShareFeatureCaseList } from '@/api/modules/test-plan/report';
import { useTableStore } from '@/store';
import { TableKeyEnum } from '@/enums/tableEnum';
import { FilterSlotNameEnum } from '@/enums/tableFilterEnum';
import { executionResultMap } from '@/views/case-management/caseManagementFeature/components/utils';
@ -32,10 +30,9 @@
const props = defineProps<{
reportId: string;
shareId?: string;
activeTab: string;
}>();
const tableStore = useTableStore();
const columns: MsTableColumn = [
{
title: 'ID',
@ -104,7 +101,6 @@
const { propsRes, propsEvent, loadList, setLoadListParams } = useTable(reportFeatureCaseList(), {
scroll: { x: '100%' },
columns,
tableKey: TableKeyEnum.TEST_PLAN_REPORT_DETAIL_FEATURE_CASE,
heightUsed: 20,
showSelectorAll: false,
});
@ -119,6 +115,4 @@
loadCaseList();
}
});
await tableStore.initColumn(TableKeyEnum.TEST_PLAN_REPORT_DETAIL_FEATURE_CASE, columns, 'drawer');
</script>

View File

@ -10,13 +10,7 @@
/>
</div>
<div class="analysis min-w-[410px]">
<div class="block-title">{{ t('report.detail.executionAnalysis') }}</div>
<SetReportChart
size="160px"
:legend-data="legendData"
:options="executeCharOptions"
:request-total="getIndicators(detail.caseTotal) || 0"
/>
<ExecuteAnalysis :detail="detail" />
</div>
</div>
@ -117,6 +111,8 @@
v-model:richText="richText"
:share-id="shareId"
:show-button="showButton"
:is-plan-group="false"
:detail="detail"
@update-summary="handleUpdateReportDetail"
@cancel="handleCancel"
@handle-summary="handleSummary"
@ -130,9 +126,14 @@
class="relative mb-[16px] border-b"
/>
<BugTable v-if="activeTab === 'bug'" :report-id="detail.id" :share-id="shareId" />
<FeatureCaseTable v-if="activeTab === 'featureCase'" :report-id="detail.id" :share-id="shareId" />
<FeatureCaseTable
v-if="activeTab === 'featureCase'"
:active-tab="activeTab"
:report-id="detail.id"
:share-id="shareId"
/>
<ApiAndScenarioTable
v-if="activeTab === 'apiCase'"
v-if="['apiCase', 'scenarioCase'].includes(activeTab)"
:report-id="detail.id"
:share-id="shareId"
:active-tab="activeTab"
@ -151,10 +152,10 @@
import MsCard from '@/components/pure/ms-card/index.vue';
import MsTab from '@/components/pure/ms-tab/index.vue';
import ReportMetricsItem from './ReportMetricsItem.vue';
import SetReportChart from '@/views/api-test/report/component/case/setReportChart.vue';
import SingleStatusProgress from '@/views/test-plan/report/component/singleStatusProgress.vue';
import ApiAndScenarioTable from '@/views/test-plan/report/detail/component/apiAndScenarioTable.vue';
import BugTable from '@/views/test-plan/report/detail/component/bugTable.vue';
import ExecuteAnalysis from '@/views/test-plan/report/detail/component/executeAnalysis.vue';
import FeatureCaseTable from '@/views/test-plan/report/detail/component/featureCaseTable.vue';
import ReportHeader from '@/views/test-plan/report/detail/component/reportHeader.vue';
import Summary from '@/views/test-plan/report/detail/component/summary.vue';
@ -164,7 +165,6 @@
import { useI18n } from '@/hooks/useI18n';
import { addCommasToNumber } from '@/utils';
import type { LegendData } from '@/models/apiTest/report';
import type {
countDetail,
PlanReportDetail,
@ -172,7 +172,7 @@
StatusListType,
} from '@/models/testPlan/testPlanReport';
import { getIndicators } from '@/views/api-test/report/utils';
import { getSummaryDetail } from '@/views/test-plan/report/utils';
const { t } = useI18n();
@ -197,53 +197,6 @@
*/
const shareId = ref<string>(route.query.shareId as string);
const legendData = ref<LegendData[]>([]);
//
const executeCharOptions = ref({
...commonConfig,
series: {
...seriesConfig,
data: [
{
value: 0,
name: t('common.success'),
itemStyle: {
color: '#00C261',
},
},
{
value: 0,
name: t('common.fakeError'),
itemStyle: {
color: '#FFC14E',
},
},
{
value: 0,
name: t('common.fail'),
itemStyle: {
color: '#ED0303',
},
},
{
value: 0,
name: t('common.unExecute'),
itemStyle: {
color: '#D4D4D8',
},
},
{
value: 0,
name: t('common.block'),
itemStyle: {
color: '#B379C8',
},
},
],
},
});
//
const functionCaseOptions = ref({
...commonConfig,
@ -293,30 +246,6 @@
},
});
//
function initExecuteOptions() {
executeCharOptions.value.series.data = statusConfig.map((item: StatusListType) => {
return {
value: detail.value.executeCount[item.value] || 0,
name: t(item.label),
itemStyle: {
color: item.color,
borderWidth: 2,
borderColor: '#ffffff',
},
};
});
legendData.value = statusConfig.map((item: StatusListType) => {
const rate = (detail.value.executeCount[item.value] / detail.value.caseTotal) * 100;
return {
...item,
label: t(item.label),
count: detail.value.executeCount[item.value] || 0,
rote: `${Number.isNaN(rate) ? 0 : rate.toFixed(2)}%`,
};
}) as unknown as LegendData[];
}
//
function getPassRateData(caseDetailCount: countDetail) {
const caseCountDetail = caseDetailCount || defaultCount;
@ -338,7 +267,6 @@
//
function initOptionsData() {
initExecuteOptions();
const { functionalCount, apiCaseCount, apiScenarioCount } = detail.value;
functionCaseOptions.value.series.data = getPassRateData(functionalCount);
apiCaseOptions.value.series.data = getPassRateData(apiCaseCount);
@ -388,38 +316,6 @@
},
]);
function getSummaryDetail(detailCount: countDetail) {
if (detailCount) {
const { success, error, fakeError, pending, block } = detailCount;
//
const hasExecutedCase = success + error + fakeError + block;
//
const caseTotal = hasExecutedCase + pending;
//
const executedCount = (hasExecutedCase / caseTotal) * 100;
const apiExecutedRate = `${Number.isNaN(executedCount) ? 0 : executedCount.toFixed(2)}%`;
//
const successCount = (success / caseTotal) * 100;
const successRate = `${Number.isNaN(successCount) ? 0 : successCount.toFixed(2)}%`;
return {
hasExecutedCase,
caseTotal,
apiExecutedRate,
successRate,
pending,
success,
};
}
return {
hasExecutedCase: 0,
caseTotal: 0,
apiExecutedRate: 0,
successRate: 0,
pending: 0,
success: 0,
};
}
const functionCasePassRate = computed(() => {
const apiCaseDetail = getSummaryDetail(detail.value.functionalCount || defaultCount);
return apiCaseDetail.successRate;
@ -472,38 +368,13 @@
});
});
const summaryContent = computed(() => {
const { functionalCount, apiCaseCount, apiScenarioCount } = detail.value;
const functionalCaseDetail = getSummaryDetail(functionalCount);
const apiCaseDetail = getSummaryDetail(apiCaseCount);
const apiScenarioDetail = getSummaryDetail(apiScenarioCount);
const allCaseTotal = functionalCaseDetail.caseTotal + apiCaseDetail.caseTotal + apiScenarioDetail.caseTotal;
const allHasExecutedCase =
functionalCaseDetail.hasExecutedCase + apiCaseDetail.hasExecutedCase + apiScenarioDetail.hasExecutedCase;
const allPendingCase = functionalCaseDetail.pending + apiCaseDetail.pending + apiScenarioDetail.pending;
const allSuccessCase = functionalCaseDetail.success + apiCaseDetail.success + apiScenarioDetail.success;
const allExecutedCount = (allHasExecutedCase / allCaseTotal) * 100;
const allExecutedRate = `${Number.isNaN(allExecutedCount) ? 0 : allExecutedCount.toFixed(2)}%`;
//
const allSuccessCount = (allSuccessCase / allCaseTotal) * 100;
const allSuccessRate = `${Number.isNaN(allSuccessCount) ? 0 : allSuccessCount.toFixed(2)}%`;
//
return `<p style=""><span color="" fontsize="">本次完成 ${detail.value.name}的功能测试,接口测试;共 ${allCaseTotal}条 用例,已执行 ${allHasExecutedCase} 条,未执行 ${allPendingCase} 条,执行率为 ${allExecutedRate}%,通过用例 ${allSuccessCase} 条,通过率为 ${allSuccessRate},达到/未达到通过阈值(通过阈值为${detail.value.passThreshold}%xxx计划满足/不满足发布要求。<br>
1本次测试包含${functionalCaseDetail.caseTotal}条功能测试用例执行了${functionalCaseDetail.hasExecutedCase}未执行${functionalCaseDetail.pending}执行率为${functionalCaseDetail.apiExecutedRate}通过用例${functionalCaseDetail.success}通过率为${functionalCaseDetail.successRate}共发现缺陷0个<br>
2本次测试包含${apiCaseDetail.caseTotal}条接口测试用例执行了${apiCaseDetail.hasExecutedCase}未执行${apiCaseDetail.pending}执行率为${apiCaseDetail.apiExecutedRate}通过用例${apiCaseDetail.success}通过率为${apiCaseDetail.successRate}共发现缺陷0个<br>
3本次测试包含${apiScenarioDetail.caseTotal}条场景测试用例执行了${apiScenarioDetail.hasExecutedCase}未执行${apiScenarioDetail.pending}执行率为${apiScenarioDetail.apiExecutedRate}%通过用例${apiScenarioDetail.success}通过率为${apiScenarioDetail.successRate}共发现缺陷0个</span></p>
`;
});
function handleCancel() {
richText.value = { summary: detail.value.summary };
showButton.value = false;
}
function handleSummary() {
richText.value.summary = summaryContent.value;
function handleSummary(content: string) {
richText.value.summary = content;
}
</script>

View File

@ -10,19 +10,15 @@
/>
</div>
<div class="analysis min-w-[410px]">
<div class="block-title">{{ t('report.detail.executionAnalysis') }}</div>
<SetReportChart
size="160px"
:legend-data="legendData"
:options="charOptions"
:request-total="getIndicators(detail.caseTotal) || 0"
/>
<ExecuteAnalysis :detail="detail" />
</div>
</div>
<Summary
v-model:richText="richText"
:share-id="shareId"
:show-button="showButton"
:is-plan-group="true"
:detail="detail"
@update-summary="handleUpdateReportDetail"
@cancel="handleCancel"
@handle-summary="handleSummary"
@ -57,7 +53,7 @@
import { cloneDeep } from 'lodash-es';
import MsCard from '@/components/pure/ms-card/index.vue';
import SetReportChart from '@/views/api-test/report/component/case/setReportChart.vue';
import ExecuteAnalysis from '@/views/test-plan/report/detail/component/executeAnalysis.vue';
import ReportDetailTable from '@/views/test-plan/report/detail/component/reportDetailTable.vue';
import ReportHeader from '@/views/test-plan/report/detail/component/reportHeader.vue';
import ReportMetricsItem from '@/views/test-plan/report/detail/component/ReportMetricsItem.vue';
@ -68,11 +64,8 @@
import { useI18n } from '@/hooks/useI18n';
import { addCommasToNumber } from '@/utils';
import type { LegendData } from '@/models/apiTest/report';
import type { PlanReportDetail, ReportMetricsItemModel } from '@/models/testPlan/testPlanReport';
import { getIndicators } from '@/views/api-test/report/utils';
const { t } = useI18n();
const route = useRoute();
@ -87,90 +80,23 @@
const detail = ref<PlanReportDetail>({ ...cloneDeep(defaultReportDetail) });
const shareId = ref<string>(route.query.shareId as string);
const charOptions = ref({
tooltip: {
show: false,
trigger: 'item',
},
legend: {
show: false,
},
series: {
name: '',
type: 'pie',
radius: ['62%', '80%'],
center: ['50%', '50%'],
avoidLabelOverlap: false,
label: {
show: false,
position: 'center',
},
emphasis: {
label: {
show: false,
fontSize: 40,
fontWeight: 'bold',
},
},
labelLine: {
show: false,
},
data: [
{
value: 0,
name: t('common.success'),
itemStyle: {
color: '#00C261',
},
},
{
value: 0,
name: t('common.fakeError'),
itemStyle: {
color: '#FFC14E',
},
},
{
value: 0,
name: t('common.fail'),
itemStyle: {
color: '#ED0303',
},
},
{
value: 0,
name: t('common.unExecute'),
itemStyle: {
color: '#D4D4D8',
},
},
{
value: 0,
name: t('common.block'),
itemStyle: {
color: '#B379C8',
},
},
],
},
});
const legendData = ref<LegendData[]>([]);
const reportAnalysisList = computed<ReportMetricsItemModel[]>(() => [
{
name: t('report.detail.testPlanTotal'),
value: detail.value.passThreshold,
value: addCommasToNumber(detail.value.planCount),
unit: t('report.detail.number'),
icon: 'plan_total',
},
{
name: t('report.detail.testPlanCaseTotal'),
value: detail.value.passRate,
value: addCommasToNumber(detail.value.caseTotal),
unit: t('report.detail.number'),
icon: 'case_total',
},
{
name: t('report.passRate'),
value: detail.value.executeRate,
value: detail.value.passRate,
unit: '%',
icon: 'passRate',
},
@ -182,9 +108,8 @@
},
]);
const summaryContent = ref<string>('');
const showButton = ref(false);
const richText = ref<{ summary: string; richTextTmpFileIds?: string[] }>({
summary: '',
});
@ -210,8 +135,8 @@
showButton.value = false;
}
function handleSummary() {
richText.value.summary = summaryContent.value;
function handleSummary(content: string) {
richText.value.summary = content;
}
const currentMode = ref<string>('drawer');
@ -219,6 +144,13 @@
currentMode.value = value as string;
};
watchEffect(() => {
if (props.detailInfo) {
detail.value = cloneDeep(props.detailInfo);
richText.value.summary = detail.value.summary;
}
});
onMounted(async () => {
nextTick(() => {
const editorContent = document.querySelector('.editor-content');

View File

@ -1,12 +1,13 @@
<template>
<PlanGroupDetail :detail-info="detail" />
<PlanGroupDetail v-if="props.isGroup" :detail-info="detail" @update-success="getDetail()" />
<PlanDetail v-else :detail-info="detail" @update-success="getDetail()" />
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { useRoute } from 'vue-router';
import { cloneDeep } from 'lodash-es';
import PlanDetail from '@/views/test-plan/report/detail/component/planDetail.vue';
import PlanGroupDetail from '@/views/test-plan/report/detail/component/planGroupDetail.vue';
import { getReportDetail } from '@/api/modules/test-plan/report';
@ -14,16 +15,17 @@
import type { PlanReportDetail } from '@/models/testPlan/testPlanReport';
const route = useRoute();
const reportId = ref<string>(route.query.id as string);
const props = defineProps<{
isGroup: boolean;
reportId: string;
}>();
const detail = ref<PlanReportDetail>(cloneDeep(defaultReportDetail));
async function getDetail() {
try {
detail.value = await getReportDetail(reportId.value);
detail.value = await getReportDetail(props.reportId);
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
}
}
@ -33,4 +35,4 @@
});
</script>
<style scoped></style>
<style scoped lang="less"></style>

View File

@ -11,6 +11,12 @@
</a-tooltip>
</div>
</template>
<template #resultStatus="{ record }">
<ExecutionStatus v-if="record.resultStatus !== '-'" :status="record.resultStatus" />
</template>
<template #[FilterSlotNameEnum.TEST_PLAN_STATUS_FILTER]="{ filterContent }">
<ExecutionStatus :status="filterContent.value" />
</template>
<template #operation="{ record }">
<MsButton class="!mx-0" @click="openReport(record)">{{ t('report.detail.testPlanGroup.viewReport') }}</MsButton>
</template>
@ -25,13 +31,17 @@
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 ExecutionStatus from '@/views/test-plan/report/component/reportStatus.vue';
import ReportDrawer from '@/views/test-plan/testPlan/detail/reportDrawer.vue';
import { getReportBugList, getReportShareBugList } from '@/api/modules/test-plan/report';
import { getReportDetailPage, getReportDetailSharePage } from '@/api/modules/test-plan/report';
import { useI18n } from '@/hooks/useI18n';
import useOpenNewPage from '@/hooks/useOpenNewPage';
import { PlanReportDetail } from '@/models/testPlan/testPlanReport';
import { PlanReportStatus } from '@/enums/reportEnum';
import { RouteEnum } from '@/enums/routeEnum';
import { FilterSlotNameEnum } from '@/enums/tableFilterEnum';
const { openNewPage } = useOpenNewPage();
@ -42,6 +52,16 @@
shareId?: string;
currentMode: string;
}>();
const statusResultOptions = computed(() => {
return Object.keys(PlanReportStatus).map((key) => {
return {
value: key,
label: PlanReportStatus[key].statusText,
};
});
});
const columns: MsTableColumn = [
{
title: 'testPlan.testPlanIndex.operation',
@ -60,35 +80,41 @@
},
{
title: 'report.detail.testPlanGroup.result',
dataIndex: 'result',
dataIndex: 'resultStatus',
slotName: 'resultStatus',
filterConfig: {
options: statusResultOptions.value,
filterSlotName: FilterSlotNameEnum.TEST_PLAN_STATUS_FILTER,
},
showTooltip: true,
width: 200,
},
{
title: 'report.detail.threshold',
dataIndex: 'threshold',
slotName: 'threshold',
dataIndex: 'passThreshold',
slotName: 'passThreshold',
width: 150,
},
{
title: 'report.passRate',
dataIndex: 'executeUser',
dataIndex: 'passRate',
slotName: 'passRate',
titleSlotName: 'passRateTitle',
showTooltip: true,
width: 150,
},
{
title: 'report.detail.testPlanGroup.useCasesCount',
dataIndex: 'bugCount',
dataIndex: 'caseTotal',
width: 100,
},
];
const reportBugList = () => {
return !props.shareId ? getReportBugList : getReportShareBugList;
const reportDetailList = () => {
return !props.shareId ? getReportDetailPage : getReportDetailSharePage;
};
const { propsRes, propsEvent, loadList, setLoadListParams } = useTable(reportBugList(), {
const { propsRes, propsEvent, loadList, setLoadListParams } = useTable(reportDetailList(), {
columns,
heightUsed: 20,
showSelectorAll: false,
@ -104,8 +130,10 @@
loadReportDetailList();
}
});
const reportVisible = ref(false);
function openReport(record: any) {
function openReport(record: PlanReportDetail) {
if (props.currentMode === 'drawer') {
reportVisible.value = true;
} else {

View File

@ -1,153 +0,0 @@
<template>
<MsBaseTable v-bind="propsRes" v-on="propsEvent">
<template #num="{ record }">
<MsButton type="text">{{ record.num }}</MsButton>
</template>
<template #[FilterSlotNameEnum.CASE_MANAGEMENT_CASE_LEVEL]="{ filterContent }">
<CaseLevel :case-level="filterContent.value" />
</template>
<template #caseLevel="{ record }">
<CaseLevel :case-level="record.caseLevel" />
</template>
<template #[FilterSlotNameEnum.CASE_MANAGEMENT_EXECUTE_RESULT]="{ filterContent }">
<ExecuteResult :execute-result="filterContent.key" />
</template>
<template #lastExecResult="{ record }">
<ExecuteResult :execute-result="record.lastExecResult" />
<MsIcon
v-show="record.lastExecResult !== LastExecuteResults.PENDING"
type="icon-icon_take-action_outlined"
class="ml-[8px] cursor-pointer text-[rgb(var(--primary-5))]"
size="16"
@click="showReport(record)"
/>
</template>
</MsBaseTable>
</template>
<script setup lang="ts">
import { onBeforeMount } from '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 { getReportBugList, getReportShareBugList } from '@/api/modules/test-plan/report';
import { getPlanDetailApiCaseList } from '@/api/modules/test-plan/testPlan';
import { useTableStore } from '@/store';
import type { PlanDetailApiScenarioItem } from '@/models/testPlan/testPlan';
import { LastExecuteResults } from '@/enums/caseEnum';
import { TableKeyEnum } from '@/enums/tableEnum';
import { FilterSlotNameEnum } from '@/enums/tableFilterEnum';
import { casePriorityOptions } from '@/views/api-test/components/config';
import { executionResultMap, getCaseLevels } from '@/views/case-management/caseManagementFeature/components/utils';
const props = defineProps<{
reportId: string;
shareId?: string;
}>();
const tableStore = useTableStore();
const columns: MsTableColumn = [
{
title: 'ID',
dataIndex: 'num',
sortIndex: 1,
fixed: 'left',
width: 100,
showTooltip: true,
},
{
title: 'common.name',
dataIndex: 'name',
width: 150,
showTooltip: true,
},
{
title: 'report.detail.level',
dataIndex: 'caseLevel',
slotName: 'caseLevel',
filterConfig: {
options: casePriorityOptions,
filterSlotName: FilterSlotNameEnum.CASE_MANAGEMENT_CASE_LEVEL,
},
width: 150,
showDrag: true,
},
{
title: 'common.executionResult',
dataIndex: 'lastExecResult',
slotName: 'lastExecResult',
filterConfig: {
valueKey: 'key',
labelKey: 'statusText',
options: Object.values(executionResultMap),
filterSlotName: FilterSlotNameEnum.CASE_MANAGEMENT_EXECUTE_RESULT,
},
width: 150,
showDrag: true,
},
{
title: 'common.belongModule',
dataIndex: 'moduleId',
showTooltip: true,
width: 200,
showDrag: true,
},
{
title: 'case.tableColumnCreateUser',
dataIndex: 'createUserName',
showTooltip: true,
width: 130,
showDrag: true,
},
{
title: 'testPlan.featureCase.executor',
dataIndex: 'executeUserName',
showTooltip: true,
width: 130,
showDrag: true,
},
{
title: 'testPlan.featureCase.bugCount',
dataIndex: 'bugCount',
slotName: 'bugCount',
width: 100,
showDrag: true,
},
];
const { propsRes, propsEvent, loadList, setLoadListParams } = useTable(getPlanDetailApiCaseList, {
scroll: { x: '100%' },
columns,
tableKey: TableKeyEnum.TEST_PLAN_REPORT_DETAIL_BUG,
showSelectorAll: false,
});
async function loadCaseList() {
setLoadListParams({ reportId: props.reportId, shareId: props.shareId ?? undefined });
loadList();
}
watchEffect(() => {
if (props.reportId) {
loadCaseList();
}
});
//
const reportVisible = ref(false);
const apiReportId = ref('');
function showReport(record: PlanDetailApiScenarioItem) {
reportVisible.value = true;
apiReportId.value = record.lastExecReportId;
}
await tableStore.initColumn(TableKeyEnum.TEST_PLAN_REPORT_DETAIL_BUG, columns, 'drawer');
</script>

View File

@ -43,17 +43,23 @@
import { useI18n } from '@/hooks/useI18n';
import { hasAnyPermission } from '@/utils/permission';
import type { PlanReportDetail } from '@/models/testPlan/testPlanReport';
import { getSummaryDetail } from '@/views/test-plan/report/utils';
const { t } = useI18n();
const props = defineProps<{
richText: { summary: string; richTextTmpFileIds?: string[] };
shareId?: string;
showButton: boolean;
isPlanGroup: boolean;
detail: PlanReportDetail;
}>();
const emit = defineEmits<{
(e: 'updateSummary'): void;
(e: 'cancel'): void;
(e: 'handleSummary'): void;
(e: 'handleSummary', content: string): void;
}>();
const innerSummary = useVModel(props, 'richText', emit);
@ -73,8 +79,34 @@
return data;
}
const summaryContent = computed(() => {
const { functionalCount, apiCaseCount, apiScenarioCount } = props.detail;
const functionalCaseDetail = getSummaryDetail(functionalCount);
const apiCaseDetail = getSummaryDetail(apiCaseCount);
const apiScenarioDetail = getSummaryDetail(apiScenarioCount);
const allCaseTotal = functionalCaseDetail.caseTotal + apiCaseDetail.caseTotal + apiScenarioDetail.caseTotal;
const allHasExecutedCase =
functionalCaseDetail.hasExecutedCase + apiCaseDetail.hasExecutedCase + apiScenarioDetail.hasExecutedCase;
const allSuccessCase = functionalCaseDetail.success + apiCaseDetail.success + apiScenarioDetail.success;
//
const allSuccessCount = (allSuccessCase / allCaseTotal) * 100;
const allSuccessRate = `${Number.isNaN(allSuccessCount) ? 0 : allSuccessCount.toFixed(2)}%`;
// TODO
if (props.isPlanGroup) {
return `<p style=""><span color="" fontsize=""> <strong>${props.detail.name}</strong>包含 ${props.detail.planCount}个子计划。
其中 X 个子计划通过 X 个子计划不通过</span></p>`;
}
//
return `<p style=""><span color="" fontsize=""> <strong>${props.detail.name}</strong> 包含功能测试、接口用例、场景用例, 共 ${allCaseTotal}条用例,已执行 ${allHasExecutedCase} 条,通过用例 ${allSuccessCase} 条,通过率为 ${allSuccessRate},达到/未达到通过阈值(通过阈值为${props.detail.passThreshold}%<strong>${props.detail.name}</strong> 计划满足/不满足发布要求。<br>
1本次测试包含${functionalCaseDetail.caseTotal}条功能测试用例执行了${functionalCaseDetail.hasExecutedCase}未执行${functionalCaseDetail.pending}执行率为${functionalCaseDetail.apiExecutedRate}通过用例${functionalCaseDetail.success}通过率为${functionalCaseDetail.successRate}共发现缺陷0个<br>
2本次测试包含${apiCaseDetail.caseTotal}条接口测试用例执行了${apiCaseDetail.hasExecutedCase}未执行${apiCaseDetail.pending}执行率为${apiCaseDetail.apiExecutedRate}通过用例${apiCaseDetail.success}通过率为${apiCaseDetail.successRate}共发现缺陷 ${props.detail.bugCount} <br>
3本次测试包含${apiScenarioDetail.caseTotal}条场景测试用例执行了${apiScenarioDetail.hasExecutedCase}未执行${apiScenarioDetail.pending}执行率为${apiScenarioDetail.apiExecutedRate}%通过用例${apiScenarioDetail.success}通过率为${apiScenarioDetail.successRate}共发现缺陷0个</span></p>
`;
});
function handleSummary() {
emit('handleSummary');
emit('handleSummary', summaryContent.value);
}
</script>

View File

@ -1,47 +0,0 @@
<template>
<PlanDetail :detail-info="detail" />
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { cloneDeep } from 'lodash-es';
import PlanDetail from '@/views/test-plan/report/detail/component/planDetail.vue';
import { getReportDetail, planGetShareHref } from '@/api/modules/test-plan/report';
import { defaultReportDetail } from '@/config/testPlan';
import { NOT_FOUND_RESOURCE } from '@/router/constants';
import type { PlanReportDetail } from '@/models/testPlan/testPlanReport';
const route = useRoute();
const router = useRouter();
const reportId = ref<string>(route.query.id as string);
const detail = ref<PlanReportDetail>(cloneDeep(defaultReportDetail));
async function getShareDetail() {
try {
const hrefShareDetail = await planGetShareHref(route.query.shareId as string);
reportId.value = hrefShareDetail.reportId;
if (hrefShareDetail.deleted) {
router.push({
name: NOT_FOUND_RESOURCE,
});
return;
}
detail.value = await getReportDetail(reportId.value, route.query.shareId as string);
} catch (error) {
console.log(error);
}
}
watchEffect(() => {
if (route.query.shareId) {
getShareDetail();
}
});
</script>
<style scoped></style>

View File

@ -1,7 +1,6 @@
<template>
<PlanDetail :detail-info="detail" @update-success="getDetail()" />
<!-- TODO 待联调计划组报告 -->
<!-- <PlanGroupDetail :detail-info="detail" @update-success="getDetail()" /> -->
<PlanGroupDetail v-if="isGroup" :detail-info="detail" @update-success="getDetail()" />
<PlanDetail v-else :detail-info="detail" @update-success="getDetail()" />
</template>
<script setup lang="ts">
@ -22,6 +21,8 @@
const detail = ref<PlanReportDetail>(cloneDeep(defaultReportDetail));
const isGroup = computed(() => route.query.type === 'GROUP');
async function getDetail() {
try {
detail.value = await getReportDetail(reportId.value);
@ -33,7 +34,6 @@
onBeforeMount(() => {
getDetail();
});
//
</script>
<style scoped lang="less"></style>

View File

@ -0,0 +1,35 @@
import type { countDetail } from '@/models/testPlan/testPlanReport';
export function getSummaryDetail(detailCount: countDetail) {
if (detailCount) {
const { success, error, fakeError, pending, block } = detailCount;
// 已执行用例
const hasExecutedCase = success + error + fakeError + block;
// 用例总数
const caseTotal = hasExecutedCase + pending;
// 执行率
const executedCount = (hasExecutedCase / caseTotal) * 100;
const apiExecutedRate = `${Number.isNaN(executedCount) ? 0 : executedCount.toFixed(2)}%`;
// 通过率
const successCount = (success / caseTotal) * 100;
const successRate = `${Number.isNaN(successCount) ? 0 : successCount.toFixed(2)}%`;
return {
hasExecutedCase,
caseTotal,
apiExecutedRate,
successRate,
pending,
success,
};
}
return {
hasExecutedCase: 0,
caseTotal: 0,
apiExecutedRate: 0,
successRate: 0,
pending: 0,
success: 0,
};
}
export default {};

View File

@ -37,7 +37,7 @@
field="targetId"
:label="t('testPlan.testPlanIndex.testPlanGroup')"
>
<a-select v-model="form.targetId" :placeholder="t('common.pleaseSelect')">
<a-select v-model="form.targetId" :placeholder="t('common.pleaseSelect')" allow-search>
<a-option v-for="item of groupList" :key="item.id" :value="item.id">
{{ item.name }}
</a-option>

View File

@ -157,13 +157,31 @@
</span>
</a-tooltip>
</template>
<template #planStartToEndTime="{ record }">
<div>
{{ record.plannedStartTime ? dayjs(record.plannedStartTime) : '-' }} {{ t('common.to') }}
<a-tooltip
class="ms-tooltip-red"
:content="t('testPlan.planStartToEndTimeTip')"
:disabled="record.execStatus !== LastExecuteResults.ERROR"
>
<span :class="[`${record.execStatus === LastExecuteResults.ERROR ? 'text-[rgb(var(--danger-6))' : ''}`]">
{{ record?.plannedEndTime ? dayjs(record.plannedEndTime) : '-' }}
</span>
</a-tooltip>
</div>
</template>
<template #actualStartToEndTime="{ record }">
{{ record?.actualStartTime ? dayjs(record.actualStartTime) : '-' }}{{ t('common.to')
}}{{ record.actualEndTime ? dayjs(record.actualEndTime) : '-' }}
</template>
<template #passRate="{ record }">
<div class="mr-[8px] w-[100px]">
<StatusProgress :status-detail="defaultCountDetailMap[record.id]" height="5px" />
</div>
<div class="text-[var(--color-text-1)]">
{{ `${defaultCountDetailMap[record.id] ? defaultCountDetailMap[record.id].passRate : '-'}%` }}
{{ `${defaultCountDetailMap[record.id]?.passRate ? defaultCountDetailMap[record.id].passRate : '-'}%` }}
</div>
</template>
<template #passRateTitleSlot="{ columnConfig }">
@ -221,6 +239,8 @@
<template #operation="{ record }">
<div class="flex items-center">
<!-- TODO 测试计划组手动生成报告 -->
<!-- <MsButton class="mr-2" @click="handleGenerateReport(record.id)">生成报告</MsButton> -->
<MsButton
v-if="
getFunctionalCount(record.id) > 0 &&
@ -313,7 +333,6 @@
:type="showType"
@save="handleMoveOrCopy"
/>
<!-- TODO 待联调[编辑] 字段加到统计里边 -->
<ScheduledModal
v-model:visible="showScheduledTaskModal"
:type="planType"
@ -398,6 +417,7 @@
PassRateCountDetail,
TestPlanItem,
} from '@/models/testPlan/testPlan';
import { LastExecuteResults } from '@/enums/caseEnum';
import { TestPlanRouteEnum } from '@/enums/routeEnum';
import { ColumnEditTypeEnum, TableKeyEnum } from '@/enums/tableEnum';
import { FilterSlotNameEnum } from '@/enums/tableFilterEnum';
@ -530,7 +550,6 @@
{
title: 'testPlan.testPlanIndex.planStartToEndTime',
slotName: 'planStartToEndTime',
dataIndex: 'planStartToEndTime',
showInTable: false,
sortable: {
sortDirections: ['ascend', 'descend'],
@ -542,7 +561,6 @@
{
title: 'testPlan.testPlanIndex.actualStartToEndTime',
slotName: 'actualStartToEndTime',
dataIndex: 'actualStartToEndTime',
showInTable: false,
sortable: {
sortDirections: ['ascend', 'descend'],
@ -763,7 +781,7 @@
showSetting: true,
heightUsed: 236,
paginationSize: 'mini',
showSelectorAll: true,
showSelectorAll: false,
draggable: { type: 'handle' },
draggableCondition: true,
});

View File

@ -132,7 +132,7 @@
const initForm: CreateTask = {
resourceId: '',
cron: '',
cron: '0 0 0/1 * * ?',
enable: false,
runConfig: { runMode: 'SERIAL' },
};

View File

@ -359,6 +359,7 @@
await generateReport({
projectId: appStore.currentProjectId,
testPlanId: detail.value.id as string,
triggerMode: 'MANUAL',
});
Message.success(t('testPlan.testPlanDetail.successfullyGenerated'));
} catch (error) {

View File

@ -145,4 +145,5 @@ export default {
'testPlan.plan': 'Test plan',
'testPlan.planTip':
'1. Create a test set for business classification testing; 2. Select the test set associated use case',
'testPlan.planStartToEndTimeTip': '测试计划已超时',
};

View File

@ -133,4 +133,5 @@ export default {
'testPlan.testPlanGroup.closeScheduleTaskSuccess': '关闭定时任务成功',
'testPlan.plan': '测试规划',
'testPlan.planTip': '1.创建测试集进行业务分类测试2.选择测试集关联用例',
'testPlan.planStartToEndTimeTip': '测试计划已超时',
};