From 455a85e98e525ba14a32ccb00341a4cd509fc593 Mon Sep 17 00:00:00 2001 From: teukkk Date: Thu, 8 Aug 2024 19:23:18 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E6=B5=8B=E8=AF=95=E7=94=A8=E4=BE=8B):=20e?= =?UTF-8?q?xcel=E5=92=8Cxmind=E5=AF=BC=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../modules/case-management/featureCase.ts | 31 +- .../requrls/case-management/featureCase.ts | 12 + .../pure/ms-export-drawer/index.vue | 33 +- frontend/src/locale/en-US/common.ts | 4 + frontend/src/locale/zh-CN/common.ts | 4 + frontend/src/views/bug-management/index.vue | 15 +- .../components/caseTable.vue | 289 ++++++++++++++++-- .../components/exportExcelDrawer.vue | 208 ------------- .../caseManagementFeature/locale/en-US.ts | 9 +- .../caseManagementFeature/locale/zh-CN.ts | 9 +- 10 files changed, 363 insertions(+), 251 deletions(-) delete mode 100644 frontend/src/views/case-management/caseManagementFeature/components/exportExcelDrawer.vue diff --git a/frontend/src/api/modules/case-management/featureCase.ts b/frontend/src/api/modules/case-management/featureCase.ts index f6635359f1..d4fd2ea3d1 100644 --- a/frontend/src/api/modules/case-management/featureCase.ts +++ b/frontend/src/api/modules/case-management/featureCase.ts @@ -17,6 +17,7 @@ import { CancelAssociationDemandUrl, cancelDisassociate, cancelPreAndPostCaseUrl, + CheckCaseExportTaskUrl, checkFileIsUpdateUrl, CreateCaseModuleTreeUrl, CreateCaseUrl, @@ -32,7 +33,9 @@ import { DownloadXMindTemplateUrl, dragSortUrl, EditorUploadFileUrl, + ExportExcelCaseUrl, exportExcelCheckUrl, + ExportXMindCaseUrl, exportXMindCheckUrl, FollowerCaseUrl, GetAssociatedCaseIdsUrl, @@ -43,6 +46,8 @@ import { GetAssociationPublicCaseModuleCountUrl, GetAssociationPublicCasePageUrl, GetAssociationPublicModuleTreeUrl, + GetCaseDownloadFileUrl, + GetCaseExportConfigUrl, GetCaseListUrl, GetCaseMinderTreeUrl, GetCaseMinderUrl, @@ -74,6 +79,7 @@ import { RecoverRecycleCaseListUrl, RestoreCaseListUrl, SaveCaseMinderUrl, + StopCaseExportUrl, TransferFileUrl, UpdateCaseModuleTreeUrl, UpdateCaseUrl, @@ -446,7 +452,30 @@ export function importExcelOrXMindCase(data: { request: ImportExcelType; fileLis '' ); } - +// 导出excel +export function exportExcelCase(data: TableQueryParams) { + return MSR.post({ url: ExportExcelCaseUrl, data }); +} +// 导出XMind +export function exportXMindCase(data: TableQueryParams) { + return MSR.post({ url: ExportXMindCaseUrl, data }); +} +// 检查是否有导出任务 +export function checkCaseExportTask() { + return MSR.get({ url: CheckCaseExportTaskUrl }); +} +// 获取导出的文件 +export function getCaseDownloadFile(projectId: string, fileId: string) { + return MSR.get({ url: `${GetCaseDownloadFileUrl}/${projectId}/${fileId}` }); +} +// 停止导出 +export function stopCaseExport(taskId: string) { + return MSR.get({ url: `${StopCaseExportUrl}/${taskId}` }); +} +// 获取导出字段配置 +export function getCaseExportConfig(projectId: string) { + return MSR.get({ url: `${GetCaseExportConfigUrl}/${projectId}` }); +} // 拖拽排序 export function dragSort(data: DragCase) { return MSR.post({ url: dragSortUrl, data }); diff --git a/frontend/src/api/requrls/case-management/featureCase.ts b/frontend/src/api/requrls/case-management/featureCase.ts index f2181c1116..78a0b7c1a8 100644 --- a/frontend/src/api/requrls/case-management/featureCase.ts +++ b/frontend/src/api/requrls/case-management/featureCase.ts @@ -152,6 +152,18 @@ export const exportXMindCheckUrl = '/functional/case/pre-check/xmind'; export const importExcelCaseUrl = '/functional/case/import/excel'; // 导入xmind文件 export const importXMindCaseUrl = '/functional/case/import/xmind'; +// 导出excel文件 +export const ExportExcelCaseUrl = '/functional/case/export/excel'; +// 导出XMind文件 +export const ExportXMindCaseUrl = '/functional/case/export/xmind'; +// 检查是否有导出任务 +export const CheckCaseExportTaskUrl = '/functional/case/check/export-task'; +// 导出字段配置 +export const GetCaseExportConfigUrl = '/functional/case/export/columns'; +// 下载导出的文件 +export const GetCaseDownloadFileUrl = '/functional/case/download/file'; +// 停止导出 +export const StopCaseExportUrl = '/functional/case/stop'; // 用例拖拽排序 export const dragSortUrl = '/functional/case/edit/pos'; // 获取变更历史 diff --git a/frontend/src/components/pure/ms-export-drawer/index.vue b/frontend/src/components/pure/ms-export-drawer/index.vue index 2633bc4783..cf956b29dc 100644 --- a/frontend/src/components/pure/ms-export-drawer/index.vue +++ b/frontend/src/components/pure/ms-export-drawer/index.vue @@ -14,7 +14,14 @@ @cancel="handleDrawerCancel" >
@@ -57,7 +64,7 @@
-
+
- -
+ +
-
+
- -
+ +
- - + /> - + + + import('@/components/pure/ms-export-drawer/index.vue')); + const { openModal } = useModal(); const { t } = useI18n(); const router = useRouter(); @@ -638,20 +698,21 @@ const platformInfo = ref>({}); const tableBatchActions = { baseAction: [ - // { - // label: 'caseManagement.featureCase.export', - // eventTag: 'export', - // children: [ - // { - // label: 'caseManagement.featureCase.exportExcel', - // eventTag: 'exportExcel', - // }, - // { - // label: 'caseManagement.featureCase.exportXMind', - // eventTag: 'exportXMind', - // }, - // ], - // }, + { + label: 'caseManagement.featureCase.export', + eventTag: 'export', + permission: ['FUNCTIONAL_CASE:READ+EXPORT'], + children: [ + { + label: 'caseManagement.featureCase.exportExcelXlsx', + eventTag: 'exportExcel', + }, + { + label: 'caseManagement.featureCase.exportXMind', + eventTag: 'exportXMind', + }, + ], + }, { label: 'common.edit', eventTag: 'batchEdit', @@ -1010,11 +1071,187 @@ } } - const showExportExcelVisible = ref(false); + const exportType = ref<'exportExcel' | 'exportXMind'>('exportExcel'); + const showExportVisible = ref(false); + const exportLoading = ref(false); + const exportOptionData = ref({}); + const isMerge = ref(false); + async function getCaseExportData() { + try { + const res = await getCaseExportConfig(currentProjectId.value); + exportOptionData.value = res; + } catch (error) { + // eslint-disable-next-line no-console + console.log(error); + } + } - // 导出Excel - function handleShowExportExcel() { - showExportExcelVisible.value = true; + const websocket = ref(); + const reportId = ref(''); + const taskId = ref(''); + + // 下载文件 + async function downloadFile() { + try { + const response = await getCaseDownloadFile(currentProjectId.value, reportId.value); + const fileName = response?.headers.get('content-disposition').split('filename=')[1]; + downloadByteFile(response.blob(), fileName); + } catch (error) { + // eslint-disable-next-line no-console + console.log(error); + } + } + // 提示:导出成功 + function showExportSuccessfulMessage(count: number) { + Message.success({ + content: () => + h('div', { class: 'flex flex-col gap-[8px] items-start' }, [ + h('div', { class: 'font-medium' }, t('common.exportSuccessful')), + h('div', { class: 'flex items-center gap-[12px]' }, [ + h('div', t('caseManagement.featureCase.exportCaseCount', { number: count })), + h( + MsButton, + { + type: 'text', + onClick() { + downloadFile(); + }, + }, + { default: () => t('common.downloadFile') } + ), + ]), + ]), + duration: 999999999, // 一直展示,除非手动关闭 + closable: true, + }); + } + + const isShowExportingMessage = ref(false); // 正在导出提示显示中 + const exportingMessage = ref(); + // 取消导出 + async function cancelExport() { + try { + await stopCaseExport(taskId.value); + exportingMessage.value.close(); + websocket.value?.close(); + } catch (error) { + // eslint-disable-next-line no-console + console.log(error); + } + } + // 提示:正在导出 + function showExportingMessage() { + if (isShowExportingMessage.value) return; + isShowExportingMessage.value = true; + exportingMessage.value = Message.loading({ + content: () => + h('div', { class: 'flex items-center gap-[12px]' }, [ + h('div', t('common.exporting')), + h( + MsButton, + { + type: 'text', + onClick() { + cancelExport(); + }, + }, + { default: () => t('common.cancel') } + ), + ]), + duration: 999999999, // 一直展示,除非手动关闭 + closable: true, + onClose() { + isShowExportingMessage.value = false; + }, + }); + } + // 开启websocket监听,接收结果 + function startWebsocketGetExportResult() { + websocket.value = getSocket(reportId.value, '/ws/export'); + websocket.value.addEventListener('message', (event) => { + const data = JSON.parse(event.data); + if (data.msgType === 'EXEC_RESULT') { + exportingMessage.value.close(); + reportId.value = data.fileId; + taskId.value = data.taskId; + if (data.isSuccessful) { + showExportSuccessfulMessage(data.count); + } else { + Message.error({ + content: t('common.exportFailed'), + duration: 999999999, // 一直展示,除非手动关闭 + closable: true, + }); + } + websocket.value?.close(); + } + }); + } + + function getConfirmFields(option: MsExportDrawerOption[], columnType: string) { + return option + .filter((optionItem) => optionItem.columnType === columnType) + .map((item) => ({ id: item.key, name: item.text })); + } + const exportConfirm = async (option: MsExportDrawerOption[]) => { + try { + exportLoading.value = true; + const { selectedIds, selectAll, excludeIds } = batchParams.value; + reportId.value = getGenerateId(); + const params = { + projectId: currentProjectId.value, + selectIds: selectAll ? [] : selectedIds, + excludeIds: excludeIds || [], + moduleIds: props.activeFolder === 'all' ? [] : [props.activeFolder, ...props.offspringIds], + condition: { + keyword: keyword.value, + filter: propsRes.value.filter, + combine: batchParams.value.condition, + }, + selectAll, + systemFields: getConfirmFields(option, 'system'), + customFields: getConfirmFields(option, 'custom'), + fileId: reportId.value, + }; + let res; + if (exportType.value === 'exportExcel') { + res = await exportExcelCase({ + ...params, + otherFields: getConfirmFields(option, 'other'), + isMerge: isMerge.value, + }); + } else { + res = await exportXMindCase(params); + } + taskId.value = res.taskId; + startWebsocketGetExportResult(); + showExportingMessage(); + exportLoading.value = false; + showExportVisible.value = false; + resetSelector(); + } catch (error) { + // eslint-disable-next-line no-console + console.log(error); + } finally { + exportLoading.value = false; + } + }; + + async function batchExport() { + try { + const res = await checkCaseExportTask(); + if (!res.fileId.length) { + showExportVisible.value = true; + } else { + reportId.value = res.fileId; + taskId.value = res.taskId; + startWebsocketGetExportResult(); + showExportingMessage(); + } + } catch (error) { + // eslint-disable-next-line no-console + console.log(error); + } } const showEditModel = ref(false); @@ -1166,11 +1403,13 @@ showThirdDrawer.value = true; } - function handleTableBatch(event: BatchActionParams, params: BatchActionQueryParams) { + async function handleTableBatch(event: BatchActionParams, params: BatchActionQueryParams) { batchParams.value = params; switch (event.eventTag) { case 'exportExcel': - handleShowExportExcel(); + case 'exportXMind': + exportType.value = event.eventTag; + batchExport(); break; case 'batchEdit': batchEdit(); @@ -1513,6 +1752,7 @@ } await initFilter(); initData(); + getCaseExportData(); }); watch( @@ -1589,4 +1829,7 @@ padding: 4px 6px; } } + :deep(.arco-radio-group) { + display: flex; + } diff --git a/frontend/src/views/case-management/caseManagementFeature/components/exportExcelDrawer.vue b/frontend/src/views/case-management/caseManagementFeature/components/exportExcelDrawer.vue deleted file mode 100644 index ff0f495e3c..0000000000 --- a/frontend/src/views/case-management/caseManagementFeature/components/exportExcelDrawer.vue +++ /dev/null @@ -1,208 +0,0 @@ - - - - - diff --git a/frontend/src/views/case-management/caseManagementFeature/locale/en-US.ts b/frontend/src/views/case-management/caseManagementFeature/locale/en-US.ts index d10e95d692..8e4a40517a 100644 --- a/frontend/src/views/case-management/caseManagementFeature/locale/en-US.ts +++ b/frontend/src/views/case-management/caseManagementFeature/locale/en-US.ts @@ -54,8 +54,15 @@ export default { 'The deleted content will be put into the recycle bin, where data can be recovered', 'caseManagement.featureCase.deleteCaseTitle': 'Are you sure to delete the {name} use case?', 'caseManagement.featureCase.export': 'Export', - 'caseManagement.featureCase.exportExcel': 'Export spreadsheet (xlsx)', + 'caseManagement.featureCase.exportExcelXlsx': 'Export to Excel format (xlsx)', + 'caseManagement.featureCase.exportExcel': 'Export as Excel', + 'caseManagement.featureCase.exportExcel.exportFormat': 'Export Format', + 'caseManagement.featureCase.exportExcel.cellSplitting': 'Cell Splitting', + 'caseManagement.featureCase.exportExcel.cellSplittingTip': '1 step per cell, 1 test case occupies multiple rows', + 'caseManagement.featureCase.exportExcel.defaultTip': '1 test case per row, multiple steps in one cell', + 'caseManagement.featureCase.exportCaseCount': '{ number } test cases successfully exported', 'caseManagement.featureCase.exportXMind': 'Exporting Mind (xmind)', + 'caseManagement.featureCase.exportXMindNoUnit': 'Exporting XMind', 'caseManagement.featureCase.moveTo': 'Move to', 'caseManagement.featureCase.copyTo': 'Copy to', 'caseManagement.featureCase.associatedDemand': 'Associated demand', diff --git a/frontend/src/views/case-management/caseManagementFeature/locale/zh-CN.ts b/frontend/src/views/case-management/caseManagementFeature/locale/zh-CN.ts index 839b11482d..5e226d9092 100644 --- a/frontend/src/views/case-management/caseManagementFeature/locale/zh-CN.ts +++ b/frontend/src/views/case-management/caseManagementFeature/locale/zh-CN.ts @@ -54,8 +54,15 @@ export default { 'caseManagement.featureCase.deleteCaseTitle': '确认删除 {name} 用例吗?', 'caseManagement.featureCase.completedDeleteCaseTitle': '确认彻底删除 {name} 用例吗?', 'caseManagement.featureCase.export': '导出', - 'caseManagement.featureCase.exportExcel': '导出 Excel 表格 (xlsx)', + 'caseManagement.featureCase.exportExcel': '导出 Excel 格式', + 'caseManagement.featureCase.exportExcelXlsx': '导出 Excel 格式 (xlsx)', + 'caseManagement.featureCase.exportExcel.exportFormat': '导出格式', + 'caseManagement.featureCase.exportExcel.cellSplitting': '单元格拆分', + 'caseManagement.featureCase.exportExcel.cellSplittingTip': '1 个步骤占用 1 个单元格,1 条用例占用多行', + 'caseManagement.featureCase.exportExcel.defaultTip': '1 条用例占 1 行,多个步骤在 1 个单元格内', + 'caseManagement.featureCase.exportCaseCount': '{ number } 条用例已成功导出', 'caseManagement.featureCase.exportXMind': '导出思维导图 (xmind)', + 'caseManagement.featureCase.exportXMindNoUnit': '导出思维导图', 'caseManagement.featureCase.moveTo': '移动到', 'caseManagement.featureCase.copyTo': '复制到', 'caseManagement.featureCase.associatedDemand': '关联需求',