feat(报告): 测试计划批量导出 PDF 报告

This commit is contained in:
baiqi 2024-09-13 17:17:08 +08:00 committed by Craftsman
parent 69024bbe4c
commit 3f8c1da5e1
11 changed files with 330 additions and 198 deletions

View File

@ -182,3 +182,8 @@ export function logTestPlanReportExport(reportId: string) {
export function logTestPlanReportBatchExport(data: BatchApiParams) { export function logTestPlanReportBatchExport(data: BatchApiParams) {
return MSR.post({ url: reportUrl.TestPlanBatchReportExportUrl, data }); return MSR.post({ url: reportUrl.TestPlanBatchReportExportUrl, data });
} }
// 批量导出报告日志
export function testPlanBatchReportExportGetIds(data: BatchApiParams) {
return MSR.post<string[]>({ url: reportUrl.TestPlanBatchReportExportGetIdsUrl, data });
}

View File

@ -72,3 +72,5 @@ export const getReportShareLayoutUrl = '/test-plan/report/share/get-layout';
export const TestPlanReportExportUrl = '/test-plan/report/export'; export const TestPlanReportExportUrl = '/test-plan/report/export';
// 测试计划-报告-批量导出日志 // 测试计划-报告-批量导出日志
export const TestPlanBatchReportExportUrl = '/test-plan/report/batch-export'; export const TestPlanBatchReportExportUrl = '/test-plan/report/batch-export';
// 测试计划-报告-批量导出获取报告 ID 集合
export const TestPlanBatchReportExportGetIdsUrl = '/test-plan/report/batch-param';

View File

@ -18,19 +18,7 @@ export const PAGE_PDF_WIDTH_RATIO = CONTAINER_WIDTH / PDF_WIDTH; // 页面容器
// 实际每页高度 = PDF页面高度/页面容器宽度与 pdf 宽度的比例(这里比例*SCALE_RATIO 是因为html2canvas截图时生成的是 SCALE_RATIO 倍的清晰度) // 实际每页高度 = PDF页面高度/页面容器宽度与 pdf 宽度的比例(这里比例*SCALE_RATIO 是因为html2canvas截图时生成的是 SCALE_RATIO 倍的清晰度)
export const IMAGE_HEIGHT = Math.ceil(PAGE_HEIGHT * PAGE_PDF_WIDTH_RATIO * SCALE_RATIO); export const IMAGE_HEIGHT = Math.ceil(PAGE_HEIGHT * PAGE_PDF_WIDTH_RATIO * SCALE_RATIO);
const commonOdfTableConfig: Partial<UserOptions> = { export type PdfTableConfig = Pick<UserOptions, 'tableId' | 'columnStyles' | 'columns' | 'body'>;
headStyles: {
fillColor: '#793787',
},
styles: {
font: 'AlibabaPuHuiTi-3-55-Regular',
},
rowPageBreak: 'avoid',
margin: { top: 16, left: 16, right: 16, bottom: 16 },
tableWidth: PDF_WIDTH,
};
export type PdfTableConfig = Pick<UserOptions, 'columnStyles' | 'columns' | 'body'>;
/** /**
* PDF * PDF
@ -43,6 +31,7 @@ export default async function exportPDF(
name: string, name: string,
contentId: string, contentId: string,
autoTableConfig: PdfTableConfig[], autoTableConfig: PdfTableConfig[],
tableTitleMap?: Record<string, string>,
doneCallback?: () => void doneCallback?: () => void
) { ) {
const element = document.getElementById(contentId); const element = document.getElementById(contentId);
@ -95,6 +84,26 @@ export default async function exportPDF(
} }
const lastImagePageUseHeight = const lastImagePageUseHeight =
(canvasHeight > IMAGE_HEIGHT ? canvasHeight - IMAGE_HEIGHT : canvasHeight) / PAGE_PDF_WIDTH_RATIO / SCALE_RATIO; // 最后一页带图片的pdf页面被图片占用的高度 (canvasHeight > IMAGE_HEIGHT ? canvasHeight - IMAGE_HEIGHT : canvasHeight) / PAGE_PDF_WIDTH_RATIO / SCALE_RATIO; // 最后一页带图片的pdf页面被图片占用的高度
const commonOdfTableConfig: Partial<UserOptions> = {
headStyles: {
fillColor: '#793787',
},
styles: {
font: 'AlibabaPuHuiTi-3-55-Regular',
},
rowPageBreak: 'avoid',
margin: { top: 16, left: 16, right: 16, bottom: 16 },
tableWidth: PDF_WIDTH,
willDrawPage: (data) => {
const title = tableTitleMap?.[data.table.id as string];
if (title && data.cursor) {
pdf.text(title, 16, data.cursor.y + 4);
// 在文字后加入 8px 高的空白
data.cursor.y += 12;
}
},
};
autoTableConfig.forEach((config, index) => { autoTableConfig.forEach((config, index) => {
autoTable(pdf, { autoTable(pdf, {
...config, ...config,

View File

@ -48,8 +48,40 @@ export default function useOpenNewPage() {
); );
} }
function openNewPageWithParams(
name: RouteRecordName | undefined,
query: Record<string, any> = {},
params: Record<string, any> = {}
) {
const pId = query.pId || appStore.currentProjectId;
if (pId) {
// 如果传入参数指定了项目 id则使用传入的项目 id
delete query.pId;
}
const orgId = query.orgId || appStore.currentOrgId;
if (orgId) {
// 如果传入参数指定了组织 id则使用传入的组织 id
delete query.orgId;
}
const queryParams = new URLSearchParams(query).toString();
const newTab = window.open(
`${window.location.origin}#${router.resolve({ name }).fullPath}?orgId=${orgId}&pId=${pId}&${queryParams}`,
'_blank'
);
// 等待新标签页加载完成后发送消息
if (newTab) {
newTab.onload = () => {
setTimeout(() => {
newTab.postMessage(JSON.stringify(params), window.location.origin);
}, 300);
};
}
}
return { return {
openNewPage, openNewPage,
openNewPageWidthSingleParam, openNewPageWidthSingleParam,
openNewPageWithParams,
}; };
} }

View File

@ -1,5 +1,7 @@
import { TreeNodeData } from '@arco-design/web-vue'; import { TreeNodeData } from '@arco-design/web-vue';
import type { FilterResult } from '@/components/pure/ms-advance-filter/type';
import { RequestMethods } from '@/enums/apiEnum'; import { RequestMethods } from '@/enums/apiEnum';
// 请求返回结构 // 请求返回结构
@ -51,6 +53,7 @@ export interface BatchApiParams {
versionId?: string; // 版本 ID versionId?: string; // 版本 ID
refId?: string; // 版本来源 refId?: string; // 版本来源
protocols?: string[]; // 协议集合 protocols?: string[]; // 协议集合
combineSearch?: FilterResult;
} }
// 移动模块树 // 移动模块树

View File

@ -153,12 +153,12 @@ export function desensitize(str: string): string {
* @param str * @param str
* @returns * @returns
*/ */
export function characterLimit(str?: string): string { export function characterLimit(str?: string, length?: number): string {
if (!str) return ''; if (!str) return '';
if (str.length <= 20) { if (str.length <= (length || 20)) {
return str; return str;
} }
return `${str.slice(0, 20 - 3)}...`; return `${str.slice(0, length || 20 - 3)}...`;
} }
/** /**

View File

@ -271,7 +271,7 @@
dataIndex: 'operation', dataIndex: 'operation',
fixed: 'right', fixed: 'right',
title: hasAnyPermission(['PROJECT_API_REPORT:READ+DELETE']) ? 'common.operation' : '', title: hasAnyPermission(['PROJECT_API_REPORT:READ+DELETE']) ? 'common.operation' : '',
width: hasAnyPermission(['PROJECT_API_REPORT:READ+DELETE']) ? 130 : 50, width: hasAnyPermission(['PROJECT_API_REPORT:READ+DELETE']) ? 100 : 50,
}, },
]; ];

View File

@ -135,7 +135,7 @@
const keyword = ref<string>(''); const keyword = ref<string>('');
const router = useRouter(); const router = useRouter();
const route = useRoute(); const route = useRoute();
const { openNewPage } = useOpenNewPage(); const { openNewPage, openNewPageWithParams } = useOpenNewPage();
type ReportShowType = 'All' | 'INDEPENDENT' | 'INTEGRATED'; type ReportShowType = 'All' | 'INDEPENDENT' | 'INTEGRATED';
const showType = ref<ReportShowType>('All'); const showType = ref<ReportShowType>('All');
@ -340,6 +340,11 @@
eventTag: 'batchStop', eventTag: 'batchStop',
permission: ['PROJECT_TEST_PLAN_REPORT:READ+DELETE'], permission: ['PROJECT_TEST_PLAN_REPORT:READ+DELETE'],
}, },
{
label: 'common.export',
eventTag: 'batchExport',
permission: ['PROJECT_TEST_PLAN_REPORT:READ+EXPORT'],
},
], ],
}; };
@ -362,30 +367,40 @@
projectId: appStore.currentProjectId, projectId: appStore.currentProjectId,
}; };
openModal({ if (event.eventTag === 'batchExport') {
type: 'error', openNewPageWithParams(
title: t('report.delete.tip', { FullPageEnum.FULL_PAGE_TEST_PLAN_EXPORT_PDF,
count: params?.currentSelectCount || params?.selectedIds?.length, {
}), type: showType.value,
content: '', },
okText: t('common.confirmDelete'), batchParams.value
cancelText: t('common.cancel'), );
okButtonProps: { } else if (event.eventTag === 'batchStop') {
status: 'danger', openModal({
}, type: 'error',
onBeforeOk: async () => { title: t('report.delete.tip', {
try { count: params?.currentSelectCount || params?.selectedIds?.length,
await reportBathDelete(batchParams.value); }),
Message.success(t('apiTestDebug.deleteSuccess')); content: '',
resetSelector(); okText: t('common.confirmDelete'),
initData(); cancelText: t('common.cancel'),
} catch (error) { okButtonProps: {
// eslint-disable-next-line no-console status: 'danger',
console.log(error); },
} onBeforeOk: async () => {
}, try {
hideCancel: false, await reportBathDelete(batchParams.value);
}); Message.success(t('apiTestDebug.deleteSuccess'));
resetSelector();
initData();
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
}
},
hideCancel: false,
});
}
}; };
function searchList() { function searchList() {

View File

@ -1,5 +1,9 @@
<template> <template>
<a-spin :loading="loading" :tip="t('report.detail.exportingPdf')" class="report-detail-container"> <a-spin
:loading="batchLoading || loading"
:tip="batchLoading ? batchExportTip : t('report.detail.exportingPdf')"
class="report-detail-container"
>
<div id="report-detail" class="report-detail"> <div id="report-detail" class="report-detail">
<div class="report-header"> <div class="report-header">
<div class="flex-1 break-all">{{ detail.name }}</div> <div class="flex-1 break-all">{{ detail.name }}</div>
@ -139,7 +143,9 @@
getReportShareBugList, getReportShareBugList,
getReportShareFeatureCaseList, getReportShareFeatureCaseList,
getScenarioPage, getScenarioPage,
logTestPlanReportBatchExport,
logTestPlanReportExport, logTestPlanReportExport,
testPlanBatchReportExportGetIds,
} from '@/api/modules/test-plan/report'; } from '@/api/modules/test-plan/report';
import { import {
commonConfig, commonConfig,
@ -151,8 +157,9 @@
} from '@/config/testPlan'; } from '@/config/testPlan';
import exportPDF, { PAGE_PDF_WIDTH_RATIO, PdfTableConfig } from '@/hooks/useExportPDF'; import exportPDF, { PAGE_PDF_WIDTH_RATIO, PdfTableConfig } from '@/hooks/useExportPDF';
import { useI18n } from '@/hooks/useI18n'; import { useI18n } from '@/hooks/useI18n';
import { addCommasToNumber } from '@/utils'; import { addCommasToNumber, characterLimit } from '@/utils';
import { BatchApiParams } from '@/models/common';
import type { import type {
configItem, configItem,
countDetail, countDetail,
@ -175,13 +182,6 @@
const reportId = ref<string>(route.query.id as string); const reportId = ref<string>(route.query.id as string);
const isGroup = computed(() => route.query.type === 'GROUP'); const isGroup = computed(() => route.query.type === 'GROUP');
const loading = ref<boolean>(true); const loading = ref<boolean>(true);
const richText = ref<{ summary: string; richTextTmpFileIds?: string[] }>({
summary: '',
});
const reportForm = ref({
reportName: '',
});
/** /**
* 分享share * 分享share
@ -620,149 +620,165 @@
return status && iconTypeStatus[status] ? iconTypeStatus[status] : iconTypeStatus.DEFAULT; return status && iconTypeStatus[status] ? iconTypeStatus[status] : iconTypeStatus.DEFAULT;
} }
async function getDetail() { async function realExportPdf(name: string) {
const tableArr: PdfTableConfig[] = [];
if (!isDefaultLayout.value) {
await getDefaultLayout();
} else {
innerCardList.value = (isGroup.value ? cloneDeep(defaultGroupConfig) : cloneDeep(defaultSingleConfig)).filter(
(e: any) => [ReportCardTypeEnum.CUSTOM_CARD, ReportCardTypeEnum.SUMMARY].includes(e.value)
);
}
if (isGroup.value) {
await initGroupList();
tableArr.push({
tableId: 'group',
columnStyles: {
testPlanName: { cellWidth: 710 / PAGE_PDF_WIDTH_RATIO },
resultStatus: { cellWidth: 130 / PAGE_PDF_WIDTH_RATIO },
passThreshold: { cellWidth: 130 / PAGE_PDF_WIDTH_RATIO },
passRate: { cellWidth: 130 / PAGE_PDF_WIDTH_RATIO },
caseTotal: { cellWidth: 90 / PAGE_PDF_WIDTH_RATIO },
},
columns: groupColumns.map((item) => ({
...item,
title: t(item.title as string),
dataKey: item.dataIndex,
})) as ColumnInput[],
body: fullGroupList.value.map((e) => ({
...e,
resultStatus: t(getExecutionResult(e.resultStatus).label),
passRate: `${e.passRate}%`,
passThreshold: `${e.passThreshold}%`,
})) as RowInput[],
});
}
await Promise.all([initBugList(), initCaseList(), initApiList(), initScenarioList()]);
if (fullBugList.value.length > 0) {
tableArr.push({
tableId: 'bug',
columnStyles: {
num: { cellWidth: 120 / PAGE_PDF_WIDTH_RATIO },
title: { cellWidth: 600 / PAGE_PDF_WIDTH_RATIO },
status: { cellWidth: 110 / PAGE_PDF_WIDTH_RATIO },
handleUserName: { cellWidth: 270 / PAGE_PDF_WIDTH_RATIO },
relationCaseCount: { cellWidth: 90 / PAGE_PDF_WIDTH_RATIO },
},
columns: bugColumns.map((item) => ({
...item,
title: t(item.title as string),
dataKey: item.dataIndex,
})) as ColumnInput[],
body: fullBugList.value,
});
}
if (fullCaseList.value.length > 0) {
const columnStyles: Record<string, any> = {
num: { cellWidth: 100 / PAGE_PDF_WIDTH_RATIO },
name: { cellWidth: 240 / PAGE_PDF_WIDTH_RATIO },
executeResult: { cellWidth: 110 / PAGE_PDF_WIDTH_RATIO },
testPlanName: { cellWidth: 240 / PAGE_PDF_WIDTH_RATIO },
priority: { cellWidth: 110 / PAGE_PDF_WIDTH_RATIO },
moduleName: { cellWidth: 200 / PAGE_PDF_WIDTH_RATIO },
executeUser: { cellWidth: 100 / PAGE_PDF_WIDTH_RATIO },
relationCaseCount: { cellWidth: 90 / PAGE_PDF_WIDTH_RATIO },
};
if (!isGroup.value) {
delete columnStyles.testPlanName;
columnStyles.name.cellWidth = 480 / PAGE_PDF_WIDTH_RATIO;
}
tableArr.push({
tableId: 'case',
columnStyles,
columns: caseColumns.value.map((item) => ({
...item,
title: t(item.title as string),
dataKey: item.dataIndex,
})) as ColumnInput[],
body: fullCaseList.value.map((e: any) => ({
...e,
executeResult: t(lastExecuteResultMap[e.executeResult]?.statusText || '-'),
executeUser: e.executeUser?.name || '-',
})) as RowInput[],
});
}
const apiColumnStyles: Record<string, any> = {
num: { cellWidth: 150 / PAGE_PDF_WIDTH_RATIO },
name: { cellWidth: 240 / PAGE_PDF_WIDTH_RATIO },
executeResult: { cellWidth: 110 / PAGE_PDF_WIDTH_RATIO },
testPlanName: { cellWidth: 230 / PAGE_PDF_WIDTH_RATIO },
priority: { cellWidth: 80 / PAGE_PDF_WIDTH_RATIO },
moduleName: { cellWidth: 170 / PAGE_PDF_WIDTH_RATIO },
executeUser: { cellWidth: 120 / PAGE_PDF_WIDTH_RATIO },
bugCount: { cellWidth: 90 / PAGE_PDF_WIDTH_RATIO },
};
if (!isGroup.value) {
delete apiColumnStyles.testPlanName;
apiColumnStyles.name.cellWidth = 480 / PAGE_PDF_WIDTH_RATIO;
}
if (fullApiList.value.length > 0) {
tableArr.push({
tableId: 'apiCase',
columnStyles: apiColumnStyles,
columns: apiColumns.value.map((item) => ({
...item,
title: t(item.title as string),
dataKey: item.dataIndex,
})) as ColumnInput[],
body: fullApiList.value.map((e: any) => ({
...e,
executeResult: t(lastExecuteResultMap[e.executeResult]?.statusText || '-'),
executeUser: e.executeUser?.name || '-',
})) as RowInput[],
});
}
if (fullScenarioList.value.length > 0) {
tableArr.push({
tableId: 'scenario',
columnStyles: apiColumnStyles,
columns: apiColumns.value.map((item) => ({
...item,
title: t(item.title as string),
dataKey: item.dataIndex,
})) as ColumnInput[],
body: fullScenarioList.value.map((e: any) => ({
...e,
executeResult: t(lastExecuteResultMap[e.executeResult]?.statusText || '-'),
executeUser: e.executeUser?.name || '-',
})) as RowInput[],
});
}
await nextTick(async () => {
exportPDF(
name,
'report-detail',
tableArr,
{
group: t('report.detail.subPlanDetails'),
bug: t('report.detail.bugDetails'),
case: t('report.detail.featureCaseDetails'),
apiCase: t('report.detail.apiCaseDetails'),
scenario: t('report.detail.scenarioCaseDetails'),
},
() => {
loading.value = false;
Message.success(t('report.detail.exportPdfSuccess', { name: characterLimit(name, 50) }));
}
);
});
}
async function getDetail(_reportId?: string) {
try { try {
loading.value = true; loading.value = true;
detail.value = await getReportDetail(reportId.value); if (_reportId) {
const { defaultLayout, id, name, summary } = detail.value; reportId.value = _reportId;
isDefaultLayout.value = defaultLayout;
richText.value.summary = summary;
reportForm.value.reportName = name;
initOptionsData();
if (!defaultLayout && id) {
await getDefaultLayout();
await initGroupList();
nextTick(async () => {
exportPDF(
name,
'report-detail',
[
{
columnStyles: {
testPlanName: { cellWidth: 710 / PAGE_PDF_WIDTH_RATIO },
resultStatus: { cellWidth: 130 / PAGE_PDF_WIDTH_RATIO },
passThreshold: { cellWidth: 130 / PAGE_PDF_WIDTH_RATIO },
passRate: { cellWidth: 130 / PAGE_PDF_WIDTH_RATIO },
caseTotal: { cellWidth: 90 / PAGE_PDF_WIDTH_RATIO },
},
columns: groupColumns.map((item) => ({
...item,
title: t(item.title as string),
dataKey: item.dataIndex,
})) as ColumnInput[],
body: fullGroupList.value.map((e) => ({
...e,
resultStatus: t(getExecutionResult(e.resultStatus).label),
passRate: `${e.passRate}%`,
passThreshold: `${e.passThreshold}%`,
})) as RowInput[],
},
],
() => {
loading.value = false;
Message.success(t('report.detail.exportPdfSuccess'));
}
);
});
} else {
innerCardList.value = (isGroup.value ? cloneDeep(defaultGroupConfig) : cloneDeep(defaultSingleConfig)).filter(
(e: any) => [ReportCardTypeEnum.CUSTOM_CARD, ReportCardTypeEnum.SUMMARY].includes(e.value)
);
await Promise.all([initBugList(), initCaseList(), initApiList(), initScenarioList()]);
const tableArr: PdfTableConfig[] = [];
if (fullBugList.value.length > 0) {
tableArr.push({
columnStyles: {
num: { cellWidth: 120 / PAGE_PDF_WIDTH_RATIO },
title: { cellWidth: 600 / PAGE_PDF_WIDTH_RATIO },
status: { cellWidth: 110 / PAGE_PDF_WIDTH_RATIO },
handleUserName: { cellWidth: 270 / PAGE_PDF_WIDTH_RATIO },
relationCaseCount: { cellWidth: 90 / PAGE_PDF_WIDTH_RATIO },
},
columns: bugColumns.map((item) => ({
...item,
title: t(item.title as string),
dataKey: item.dataIndex,
})) as ColumnInput[],
body: fullBugList.value,
});
}
if (fullCaseList.value.length > 0) {
tableArr.push({
columnStyles: {
num: { cellWidth: 100 / PAGE_PDF_WIDTH_RATIO },
name: { cellWidth: 480 / PAGE_PDF_WIDTH_RATIO },
executeResult: { cellWidth: 110 / PAGE_PDF_WIDTH_RATIO },
priority: { cellWidth: 110 / PAGE_PDF_WIDTH_RATIO },
moduleName: { cellWidth: 200 / PAGE_PDF_WIDTH_RATIO },
executeUser: { cellWidth: 100 / PAGE_PDF_WIDTH_RATIO },
relationCaseCount: { cellWidth: 90 / PAGE_PDF_WIDTH_RATIO },
},
columns: caseColumns.value.map((item) => ({
...item,
title: t(item.title as string),
dataKey: item.dataIndex,
})) as ColumnInput[],
body: fullCaseList.value.map((e: any) => ({
...e,
executeResult: t(lastExecuteResultMap[e.executeResult]?.statusText || '-'),
executeUser: e.executeUser?.name || '-',
})) as RowInput[],
});
}
if (fullApiList.value.length > 0) {
tableArr.push({
columnStyles: {
num: { cellWidth: 130 / PAGE_PDF_WIDTH_RATIO },
name: { cellWidth: 450 / PAGE_PDF_WIDTH_RATIO },
executeResult: { cellWidth: 110 / PAGE_PDF_WIDTH_RATIO },
priority: { cellWidth: 80 / PAGE_PDF_WIDTH_RATIO },
moduleName: { cellWidth: 200 / PAGE_PDF_WIDTH_RATIO },
executeUser: { cellWidth: 130 / PAGE_PDF_WIDTH_RATIO },
bugCount: { cellWidth: 90 / PAGE_PDF_WIDTH_RATIO },
},
columns: apiColumns.value.map((item) => ({
...item,
title: t(item.title as string),
dataKey: item.dataIndex,
})) as ColumnInput[],
body: fullApiList.value.map((e: any) => ({
...e,
executeResult: t(lastExecuteResultMap[e.executeResult]?.statusText || '-'),
executeUser: e.executeUser?.name || '-',
})) as RowInput[],
});
}
if (apiColumns.value.length > 0) {
tableArr.push({
columnStyles: {
num: { cellWidth: 100 / PAGE_PDF_WIDTH_RATIO },
name: { cellWidth: 480 / PAGE_PDF_WIDTH_RATIO },
executeResult: { cellWidth: 110 / PAGE_PDF_WIDTH_RATIO },
priority: { cellWidth: 80 / PAGE_PDF_WIDTH_RATIO },
moduleName: { cellWidth: 200 / PAGE_PDF_WIDTH_RATIO },
executeUser: { cellWidth: 130 / PAGE_PDF_WIDTH_RATIO },
bugCount: { cellWidth: 90 / PAGE_PDF_WIDTH_RATIO },
},
columns: apiColumns.value.map((item) => ({
...item,
title: t(item.title as string),
dataKey: item.dataIndex,
})) as ColumnInput[],
body: fullScenarioList.value.map((e: any) => ({
...e,
executeResult: t(lastExecuteResultMap[e.executeResult]?.statusText || '-'),
executeUser: e.executeUser?.name || '-',
})) as RowInput[],
});
}
nextTick(async () => {
exportPDF(name, 'report-detail', tableArr, () => {
loading.value = false;
Message.success(t('report.detail.exportPdfSuccess'));
});
});
} }
detail.value = await getReportDetail(reportId.value);
const { defaultLayout, name } = detail.value;
isDefaultLayout.value = defaultLayout;
initOptionsData();
await realExportPdf(name);
} catch (error) { } catch (error) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log(error); console.log(error);
@ -770,13 +786,59 @@
} }
} }
async function logExport() { async function logExport(params?: BatchApiParams) {
await logTestPlanReportExport(route.query.id as string); if (params) {
await logTestPlanReportBatchExport(params);
} else {
await logTestPlanReportExport(route.query.id as string);
}
}
const batchLoading = ref<boolean>(false);
const batchIds = ref<string[]>([]);
const exportCurrent = ref<number>(0);
const exportTotal = ref<number>(0);
const batchExportTip = computed(() =>
t('report.detail.batchExportingPdf', { current: exportCurrent.value, total: exportTotal.value })
);
async function initBatchIds(params: BatchApiParams) {
try {
batchLoading.value = true;
batchIds.value = await testPlanBatchReportExportGetIds(params);
exportTotal.value = batchIds.value.length;
while (batchIds.value.length > 0) {
exportCurrent.value += 1;
// eslint-disable-next-line no-await-in-loop
await getDetail(batchIds.value.shift());
}
nextTick(() => {
//
Message.clear();
Message.success(t('report.detail.batchExportPdfSuccess'));
});
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
} finally {
batchLoading.value = false;
}
} }
onBeforeMount(() => { onBeforeMount(() => {
getDetail(); window.addEventListener('message', (event) => {
logExport(); if (event.origin !== window.location.origin) {
return;
}
// id
const batchParams = event.data;
initBatchIds(batchParams);
logExport(batchParams);
});
if (reportId.value) {
getDetail();
logExport();
}
}); });
</script> </script>

View File

@ -59,5 +59,7 @@ export default {
'report.detail.manualGenReportSuccess': 'The custom generated report was successful', 'report.detail.manualGenReportSuccess': 'The custom generated report was successful',
'report.detail.exportPdf': 'Export PDF', 'report.detail.exportPdf': 'Export PDF',
'report.detail.exportingPdf': 'Exporting PDF report...', 'report.detail.exportingPdf': 'Exporting PDF report...',
'report.detail.exportPdfSuccess': 'PDF report exported successfully', 'report.detail.exportPdfSuccess': '{name} report exported successfully',
'report.detail.batchExportPdfSuccess': 'Batch export of PDF reports completed',
'report.detail.batchExportingPdf': 'Export progress: {current}/{total}',
}; };

View File

@ -58,6 +58,8 @@ export default {
'report.detail.reportNameNotEmpty': '报告名称不能为空', 'report.detail.reportNameNotEmpty': '报告名称不能为空',
'report.detail.manualGenReportSuccess': '自定义生成报告成功', 'report.detail.manualGenReportSuccess': '自定义生成报告成功',
'report.detail.exportPdf': '导出 PDF', 'report.detail.exportPdf': '导出 PDF',
'report.detail.exportingPdf': 'PDF报告导出中...', 'report.detail.exportingPdf': 'PDF 报告导出中...',
'report.detail.exportPdfSuccess': 'PDF报告导出成功', 'report.detail.exportPdfSuccess': '{name} 报告导出成功',
'report.detail.batchExportPdfSuccess': '批量导出 PDF 报告已完成',
'report.detail.batchExportingPdf': '导出进度:{current}/{total}',
}; };