feat(测试用例): excel和xmind导出
This commit is contained in:
parent
3c08ffeb9c
commit
455a85e98e
|
@ -17,6 +17,7 @@ import {
|
||||||
CancelAssociationDemandUrl,
|
CancelAssociationDemandUrl,
|
||||||
cancelDisassociate,
|
cancelDisassociate,
|
||||||
cancelPreAndPostCaseUrl,
|
cancelPreAndPostCaseUrl,
|
||||||
|
CheckCaseExportTaskUrl,
|
||||||
checkFileIsUpdateUrl,
|
checkFileIsUpdateUrl,
|
||||||
CreateCaseModuleTreeUrl,
|
CreateCaseModuleTreeUrl,
|
||||||
CreateCaseUrl,
|
CreateCaseUrl,
|
||||||
|
@ -32,7 +33,9 @@ import {
|
||||||
DownloadXMindTemplateUrl,
|
DownloadXMindTemplateUrl,
|
||||||
dragSortUrl,
|
dragSortUrl,
|
||||||
EditorUploadFileUrl,
|
EditorUploadFileUrl,
|
||||||
|
ExportExcelCaseUrl,
|
||||||
exportExcelCheckUrl,
|
exportExcelCheckUrl,
|
||||||
|
ExportXMindCaseUrl,
|
||||||
exportXMindCheckUrl,
|
exportXMindCheckUrl,
|
||||||
FollowerCaseUrl,
|
FollowerCaseUrl,
|
||||||
GetAssociatedCaseIdsUrl,
|
GetAssociatedCaseIdsUrl,
|
||||||
|
@ -43,6 +46,8 @@ import {
|
||||||
GetAssociationPublicCaseModuleCountUrl,
|
GetAssociationPublicCaseModuleCountUrl,
|
||||||
GetAssociationPublicCasePageUrl,
|
GetAssociationPublicCasePageUrl,
|
||||||
GetAssociationPublicModuleTreeUrl,
|
GetAssociationPublicModuleTreeUrl,
|
||||||
|
GetCaseDownloadFileUrl,
|
||||||
|
GetCaseExportConfigUrl,
|
||||||
GetCaseListUrl,
|
GetCaseListUrl,
|
||||||
GetCaseMinderTreeUrl,
|
GetCaseMinderTreeUrl,
|
||||||
GetCaseMinderUrl,
|
GetCaseMinderUrl,
|
||||||
|
@ -74,6 +79,7 @@ import {
|
||||||
RecoverRecycleCaseListUrl,
|
RecoverRecycleCaseListUrl,
|
||||||
RestoreCaseListUrl,
|
RestoreCaseListUrl,
|
||||||
SaveCaseMinderUrl,
|
SaveCaseMinderUrl,
|
||||||
|
StopCaseExportUrl,
|
||||||
TransferFileUrl,
|
TransferFileUrl,
|
||||||
UpdateCaseModuleTreeUrl,
|
UpdateCaseModuleTreeUrl,
|
||||||
UpdateCaseUrl,
|
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) {
|
export function dragSort(data: DragCase) {
|
||||||
return MSR.post({ url: dragSortUrl, data });
|
return MSR.post({ url: dragSortUrl, data });
|
||||||
|
|
|
@ -152,6 +152,18 @@ export const exportXMindCheckUrl = '/functional/case/pre-check/xmind';
|
||||||
export const importExcelCaseUrl = '/functional/case/import/excel';
|
export const importExcelCaseUrl = '/functional/case/import/excel';
|
||||||
// 导入xmind文件
|
// 导入xmind文件
|
||||||
export const importXMindCaseUrl = '/functional/case/import/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';
|
export const dragSortUrl = '/functional/case/edit/pos';
|
||||||
// 获取变更历史
|
// 获取变更历史
|
||||||
|
|
|
@ -14,7 +14,14 @@
|
||||||
@cancel="handleDrawerCancel"
|
@cancel="handleDrawerCancel"
|
||||||
>
|
>
|
||||||
<template #title>
|
<template #title>
|
||||||
<slot name="title"></slot>
|
<slot name="title">
|
||||||
|
<div v-if="props.drawerTitleProps">
|
||||||
|
<span class="text-[var(--color-text-1)]">{{ props.drawerTitleProps.title }}</span>
|
||||||
|
<span v-if="props.drawerTitleProps.count" class="ml-1 text-[var(--color-text-4)]">
|
||||||
|
{{ t('common.selectedCount', { count: props.drawerTitleProps.count }) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</slot>
|
||||||
</template>
|
</template>
|
||||||
<div class="panel-wrapper">
|
<div class="panel-wrapper">
|
||||||
<div class="inner-wrapper">
|
<div class="inner-wrapper">
|
||||||
|
@ -57,7 +64,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</a-checkbox-group>
|
</a-checkbox-group>
|
||||||
<div class="text-[var(--color-text-4)]">
|
<div v-if="customList.length" class="text-[var(--color-text-4)]">
|
||||||
<a-checkbox
|
<a-checkbox
|
||||||
:model-value="isCheckedCustomAll"
|
:model-value="isCheckedCustomAll"
|
||||||
:indeterminate="customIndeterminate"
|
:indeterminate="customIndeterminate"
|
||||||
|
@ -68,8 +75,12 @@
|
||||||
</span>
|
</span>
|
||||||
</a-checkbox>
|
</a-checkbox>
|
||||||
</div>
|
</div>
|
||||||
<a-checkbox-group :model-value="selectedCustomIds" @change="(value) => handleGroupChange(value, 'custom')">
|
<a-checkbox-group
|
||||||
<div v-if="customList.length" class="mb-[32px]">
|
v-if="customList.length"
|
||||||
|
:model-value="selectedCustomIds"
|
||||||
|
@change="(value) => handleGroupChange(value, 'custom')"
|
||||||
|
>
|
||||||
|
<div class="mb-[32px]">
|
||||||
<div class="flex flex-row flex-wrap">
|
<div class="flex flex-row flex-wrap">
|
||||||
<a-checkbox
|
<a-checkbox
|
||||||
v-for="item in customList"
|
v-for="item in customList"
|
||||||
|
@ -84,7 +95,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</a-checkbox-group>
|
</a-checkbox-group>
|
||||||
<div class="flex flex-row items-center gap-[4px]">
|
<div v-if="otherList.length" class="flex flex-row items-center gap-[4px]">
|
||||||
<div class="text-[var(--color-text-4)]">
|
<div class="text-[var(--color-text-4)]">
|
||||||
<a-checkbox
|
<a-checkbox
|
||||||
:model-value="isCheckedOtherAll"
|
:model-value="isCheckedOtherAll"
|
||||||
|
@ -102,8 +113,12 @@
|
||||||
</a-checkbox>
|
</a-checkbox>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a-checkbox-group :model-value="selectedOtherIds" @change="(value) => handleGroupChange(value, 'other')">
|
<a-checkbox-group
|
||||||
<div v-if="otherList.length" class="mb-[32px]">
|
v-if="otherList.length"
|
||||||
|
:model-value="selectedOtherIds"
|
||||||
|
@change="(value) => handleGroupChange(value, 'other')"
|
||||||
|
>
|
||||||
|
<div class="mb-[32px]">
|
||||||
<div class="flex flex-row flex-wrap">
|
<div class="flex flex-row flex-wrap">
|
||||||
<a-checkbox
|
<a-checkbox
|
||||||
v-for="item in otherList"
|
v-for="item in otherList"
|
||||||
|
@ -206,6 +221,10 @@
|
||||||
systemTitle: string; // 已选字段| 环境
|
systemTitle: string; // 已选字段| 环境
|
||||||
selectedTitle: string; // 已选字段| 环境
|
selectedTitle: string; // 已选字段| 环境
|
||||||
};
|
};
|
||||||
|
drawerTitleProps?: {
|
||||||
|
title: string;
|
||||||
|
count?: number;
|
||||||
|
};
|
||||||
disabledCancelKeys?: string[]; // 禁止取消系统字段
|
disabledCancelKeys?: string[]; // 禁止取消系统字段
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -84,6 +84,10 @@ export default {
|
||||||
'common.quickAddMember': 'Quickly add members',
|
'common.quickAddMember': 'Quickly add members',
|
||||||
'common.filter': 'Filter',
|
'common.filter': 'Filter',
|
||||||
'common.export': 'Export',
|
'common.export': 'Export',
|
||||||
|
'common.exporting': 'Exporting',
|
||||||
|
'common.exportSuccessful': 'Export successful',
|
||||||
|
'common.exportFailed': 'Export failed',
|
||||||
|
'common.downloadFile': 'Download file',
|
||||||
'common.import': 'Import',
|
'common.import': 'Import',
|
||||||
'common.collapseAll': 'Collapse all',
|
'common.collapseAll': 'Collapse all',
|
||||||
'common.expandAll': 'Expand all',
|
'common.expandAll': 'Expand all',
|
||||||
|
|
|
@ -84,6 +84,10 @@ export default {
|
||||||
'common.pleaseSelect': '请选择',
|
'common.pleaseSelect': '请选择',
|
||||||
'common.quickAddMember': '快速添加成员',
|
'common.quickAddMember': '快速添加成员',
|
||||||
'common.export': '导出',
|
'common.export': '导出',
|
||||||
|
'common.exporting': '正在导出',
|
||||||
|
'common.exportSuccessful': '导出成功',
|
||||||
|
'common.exportFailed': '导出失败',
|
||||||
|
'common.downloadFile': '下载文件',
|
||||||
'common.import': '导入',
|
'common.import': '导入',
|
||||||
'common.collapseAll': '收起全部',
|
'common.collapseAll': '收起全部',
|
||||||
'common.expandAll': '展开全部',
|
'common.expandAll': '展开全部',
|
||||||
|
|
|
@ -128,17 +128,12 @@
|
||||||
:export-loading="exportLoading"
|
:export-loading="exportLoading"
|
||||||
:all-data="exportOptionData"
|
:all-data="exportOptionData"
|
||||||
:disabled-cancel-keys="['name']"
|
:disabled-cancel-keys="['name']"
|
||||||
|
:drawer-title-props="{
|
||||||
|
title: t('bugManagement.exportBug'),
|
||||||
|
count: currentSelectParams.currentSelectCount,
|
||||||
|
}"
|
||||||
@confirm="exportConfirm"
|
@confirm="exportConfirm"
|
||||||
>
|
/>
|
||||||
<template #title>
|
|
||||||
<div>
|
|
||||||
<span class="text-[var(--color-text-1)]">{{ t('bugManagement.exportBug') }}</span>
|
|
||||||
<span v-if="currentSelectParams.currentSelectCount" class="text-[var(--color-text-4)]">
|
|
||||||
({{ t('bugManagement.exportBugCount', { count: currentSelectParams.currentSelectCount }) }})
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</MsExportDrawer>
|
|
||||||
<BugDetailDrawer
|
<BugDetailDrawer
|
||||||
v-model:visible="detailVisible"
|
v-model:visible="detailVisible"
|
||||||
:detail-id="activeDetailId"
|
:detail-id="activeDetailId"
|
||||||
|
|
|
@ -277,7 +277,50 @@
|
||||||
@case-node-select="caseNodeSelect"
|
@case-node-select="caseNodeSelect"
|
||||||
></FeatureCaseTree>
|
></FeatureCaseTree>
|
||||||
</a-modal>
|
</a-modal>
|
||||||
<ExportExcelDrawer v-model:visible="showExportExcelVisible" />
|
<MsExportDrawer
|
||||||
|
v-model:visible="showExportVisible"
|
||||||
|
:export-loading="exportLoading"
|
||||||
|
:all-data="
|
||||||
|
exportType === 'exportExcel'
|
||||||
|
? exportOptionData
|
||||||
|
: { systemColumns: exportOptionData.systemColumns, customColumns: exportOptionData.customColumns }
|
||||||
|
"
|
||||||
|
:disabled-cancel-keys="['name']"
|
||||||
|
:drawer-title-props="{
|
||||||
|
title:
|
||||||
|
exportType === 'exportExcel'
|
||||||
|
? t('caseManagement.featureCase.exportExcel')
|
||||||
|
: t('caseManagement.featureCase.exportXMindNoUnit'),
|
||||||
|
count: batchParams.currentSelectCount,
|
||||||
|
}"
|
||||||
|
@confirm="exportConfirm"
|
||||||
|
>
|
||||||
|
<template v-if="exportType === 'exportExcel'" #footerLeft>
|
||||||
|
<div class="flex items-center gap-[8px]">
|
||||||
|
<div>{{ t('caseManagement.featureCase.exportExcel.exportFormat') }}</div>
|
||||||
|
<a-radio-group v-model:model-value="isMerge">
|
||||||
|
<a-radio :value="false">
|
||||||
|
{{ t('common.default') }}
|
||||||
|
<a-tooltip :content="t('caseManagement.featureCase.exportExcel.defaultTip')">
|
||||||
|
<MsIcon
|
||||||
|
class="ml-[4px] text-[var(--color-text-brand)] hover:text-[rgb(var(--primary-4))]"
|
||||||
|
type="icon-icon-maybe_outlined"
|
||||||
|
/>
|
||||||
|
</a-tooltip>
|
||||||
|
</a-radio>
|
||||||
|
<a-radio :value="true">
|
||||||
|
{{ t('caseManagement.featureCase.exportExcel.cellSplitting') }}
|
||||||
|
<a-tooltip :content="t('caseManagement.featureCase.exportExcel.cellSplittingTip')">
|
||||||
|
<MsIcon
|
||||||
|
class="ml-[4px] text-[var(--color-text-brand)] hover:text-[rgb(var(--primary-4))]"
|
||||||
|
type="icon-icon-maybe_outlined"
|
||||||
|
/>
|
||||||
|
</a-tooltip>
|
||||||
|
</a-radio>
|
||||||
|
</a-radio-group>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</MsExportDrawer>
|
||||||
<BatchEditModal
|
<BatchEditModal
|
||||||
v-model:visible="showEditModel"
|
v-model:visible="showEditModel"
|
||||||
:batch-params="batchParams"
|
:batch-params="batchParams"
|
||||||
|
@ -322,6 +365,7 @@
|
||||||
import { FilterFormItem, FilterResult, FilterType } from '@/components/pure/ms-advance-filter/type';
|
import { FilterFormItem, FilterResult, FilterType } from '@/components/pure/ms-advance-filter/type';
|
||||||
import MsButton from '@/components/pure/ms-button/index.vue';
|
import MsButton from '@/components/pure/ms-button/index.vue';
|
||||||
import MsDrawer from '@/components/pure/ms-drawer/index.vue';
|
import MsDrawer from '@/components/pure/ms-drawer/index.vue';
|
||||||
|
import { MsExportDrawerMap, MsExportDrawerOption } from '@/components/pure/ms-export-drawer/types';
|
||||||
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
|
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
|
||||||
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
|
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
|
||||||
import type { BatchActionParams, BatchActionQueryParams, MsTableColumn } from '@/components/pure/ms-table/type';
|
import type { BatchActionParams, BatchActionQueryParams, MsTableColumn } from '@/components/pure/ms-table/type';
|
||||||
|
@ -335,7 +379,6 @@
|
||||||
import BatchEditModal from './batchEditModal.vue';
|
import BatchEditModal from './batchEditModal.vue';
|
||||||
import CaseDetailDrawer from './caseDetailDrawer.vue';
|
import CaseDetailDrawer from './caseDetailDrawer.vue';
|
||||||
import FeatureCaseTree from './caseTree.vue';
|
import FeatureCaseTree from './caseTree.vue';
|
||||||
import ExportExcelDrawer from './exportExcelDrawer.vue';
|
|
||||||
import AddDemandModal from './tabContent/tabDemand/addDemandModal.vue';
|
import AddDemandModal from './tabContent/tabDemand/addDemandModal.vue';
|
||||||
import ThirdDemandDrawer from './tabContent/tabDemand/thirdDemandDrawer.vue';
|
import ThirdDemandDrawer from './tabContent/tabDemand/thirdDemandDrawer.vue';
|
||||||
|
|
||||||
|
@ -344,14 +387,21 @@
|
||||||
batchCopyToModules,
|
batchCopyToModules,
|
||||||
batchDeleteCase,
|
batchDeleteCase,
|
||||||
batchMoveToModules,
|
batchMoveToModules,
|
||||||
|
checkCaseExportTask,
|
||||||
deleteCaseRequest,
|
deleteCaseRequest,
|
||||||
dragSort,
|
dragSort,
|
||||||
|
exportExcelCase,
|
||||||
|
exportXMindCase,
|
||||||
getCaseDefaultFields,
|
getCaseDefaultFields,
|
||||||
getCaseDetail,
|
getCaseDetail,
|
||||||
|
getCaseDownloadFile,
|
||||||
|
getCaseExportConfig,
|
||||||
getCaseList,
|
getCaseList,
|
||||||
getCustomFieldsTable,
|
getCustomFieldsTable,
|
||||||
|
stopCaseExport,
|
||||||
updateCaseRequest,
|
updateCaseRequest,
|
||||||
} from '@/api/modules/case-management/featureCase';
|
} from '@/api/modules/case-management/featureCase';
|
||||||
|
import { getSocket } from '@/api/modules/project-management/commonScript';
|
||||||
import { getCaseRelatedInfo } from '@/api/modules/project-management/menuManagement';
|
import { getCaseRelatedInfo } from '@/api/modules/project-management/menuManagement';
|
||||||
import { getProjectOptions } from '@/api/modules/project-management/projectMember';
|
import { getProjectOptions } from '@/api/modules/project-management/projectMember';
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
|
@ -359,7 +409,15 @@
|
||||||
import { useAppStore, useTableStore } from '@/store';
|
import { useAppStore, useTableStore } from '@/store';
|
||||||
import useFeatureCaseStore from '@/store/modules/case/featureCase';
|
import useFeatureCaseStore from '@/store/modules/case/featureCase';
|
||||||
import useMinderStore from '@/store/modules/components/minder-editor';
|
import useMinderStore from '@/store/modules/components/minder-editor';
|
||||||
import { characterLimit, filterTreeNode, findNodeByKey, findNodePathByKey, mapTree } from '@/utils';
|
import {
|
||||||
|
characterLimit,
|
||||||
|
downloadByteFile,
|
||||||
|
filterTreeNode,
|
||||||
|
findNodeByKey,
|
||||||
|
findNodePathByKey,
|
||||||
|
getGenerateId,
|
||||||
|
mapTree,
|
||||||
|
} from '@/utils';
|
||||||
import { hasAnyPermission } from '@/utils/permission';
|
import { hasAnyPermission } from '@/utils/permission';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
|
@ -378,6 +436,8 @@
|
||||||
import { executionResultMap, getCaseLevels, getTableFields, statusIconMap } from './utils';
|
import { executionResultMap, getCaseLevels, getTableFields, statusIconMap } from './utils';
|
||||||
import { LabelValue } from '@arco-design/web-vue/es/tree-select/interface';
|
import { LabelValue } from '@arco-design/web-vue/es/tree-select/interface';
|
||||||
|
|
||||||
|
const MsExportDrawer = defineAsyncComponent(() => import('@/components/pure/ms-export-drawer/index.vue'));
|
||||||
|
|
||||||
const { openModal } = useModal();
|
const { openModal } = useModal();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
@ -638,20 +698,21 @@
|
||||||
const platformInfo = ref<Record<string, any>>({});
|
const platformInfo = ref<Record<string, any>>({});
|
||||||
const tableBatchActions = {
|
const tableBatchActions = {
|
||||||
baseAction: [
|
baseAction: [
|
||||||
// {
|
{
|
||||||
// label: 'caseManagement.featureCase.export',
|
label: 'caseManagement.featureCase.export',
|
||||||
// eventTag: 'export',
|
eventTag: 'export',
|
||||||
// children: [
|
permission: ['FUNCTIONAL_CASE:READ+EXPORT'],
|
||||||
// {
|
children: [
|
||||||
// label: 'caseManagement.featureCase.exportExcel',
|
{
|
||||||
// eventTag: 'exportExcel',
|
label: 'caseManagement.featureCase.exportExcelXlsx',
|
||||||
// },
|
eventTag: 'exportExcel',
|
||||||
// {
|
},
|
||||||
// label: 'caseManagement.featureCase.exportXMind',
|
{
|
||||||
// eventTag: 'exportXMind',
|
label: 'caseManagement.featureCase.exportXMind',
|
||||||
// },
|
eventTag: 'exportXMind',
|
||||||
// ],
|
},
|
||||||
// },
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: 'common.edit',
|
label: 'common.edit',
|
||||||
eventTag: 'batchEdit',
|
eventTag: 'batchEdit',
|
||||||
|
@ -1010,11 +1071,187 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const showExportExcelVisible = ref<boolean>(false);
|
const exportType = ref<'exportExcel' | 'exportXMind'>('exportExcel');
|
||||||
|
const showExportVisible = ref<boolean>(false);
|
||||||
|
const exportLoading = ref(false);
|
||||||
|
const exportOptionData = ref<MsExportDrawerMap>({});
|
||||||
|
const isMerge = ref<boolean>(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
|
const websocket = ref<WebSocket>();
|
||||||
function handleShowExportExcel() {
|
const reportId = ref('');
|
||||||
showExportExcelVisible.value = true;
|
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<boolean>(false);
|
const showEditModel = ref<boolean>(false);
|
||||||
|
@ -1166,11 +1403,13 @@
|
||||||
showThirdDrawer.value = true;
|
showThirdDrawer.value = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleTableBatch(event: BatchActionParams, params: BatchActionQueryParams) {
|
async function handleTableBatch(event: BatchActionParams, params: BatchActionQueryParams) {
|
||||||
batchParams.value = params;
|
batchParams.value = params;
|
||||||
switch (event.eventTag) {
|
switch (event.eventTag) {
|
||||||
case 'exportExcel':
|
case 'exportExcel':
|
||||||
handleShowExportExcel();
|
case 'exportXMind':
|
||||||
|
exportType.value = event.eventTag;
|
||||||
|
batchExport();
|
||||||
break;
|
break;
|
||||||
case 'batchEdit':
|
case 'batchEdit':
|
||||||
batchEdit();
|
batchEdit();
|
||||||
|
@ -1513,6 +1752,7 @@
|
||||||
}
|
}
|
||||||
await initFilter();
|
await initFilter();
|
||||||
initData();
|
initData();
|
||||||
|
getCaseExportData();
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
|
@ -1589,4 +1829,7 @@
|
||||||
padding: 4px 6px;
|
padding: 4px 6px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
:deep(.arco-radio-group) {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,208 +0,0 @@
|
||||||
<template>
|
|
||||||
<MsDrawer
|
|
||||||
v-model:visible="showDrawer"
|
|
||||||
:mask="false"
|
|
||||||
:title="t('caseManagement.featureCase.associatedFile')"
|
|
||||||
:ok-text="t('caseManagement.featureCase.associated')"
|
|
||||||
:ok-loading="drawerLoading"
|
|
||||||
:width="480"
|
|
||||||
unmount-on-close
|
|
||||||
:show-continue="false"
|
|
||||||
@confirm="handleDrawerConfirm"
|
|
||||||
@cancel="handleDrawerCancel"
|
|
||||||
>
|
|
||||||
<div class="header mb-6 flex justify-between">
|
|
||||||
<span class="font-medium">{{ t('caseManagement.featureCase.SelectExportRange') }}</span>
|
|
||||||
<span class="text-[rgb(var(--primary-5))]">{{ t('caseManagement.featureCase.clear') }}</span>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<a-checkbox class="mb-4" :model-value="checkedAll" :indeterminate="indeterminate" @change="handleChangeAll"
|
|
||||||
><div class="flex items-center">
|
|
||||||
<span class="mr-1">{{ t('caseManagement.featureCase.baseField') }}</span
|
|
||||||
><span
|
|
||||||
><icon-up
|
|
||||||
v-if="foldBaseFields"
|
|
||||||
class="text-[12px] text-[var(--color-text-brand)]"
|
|
||||||
@click="toggle('base')" /><icon-right
|
|
||||||
v-else
|
|
||||||
class="text-[12px] text-[var(--color-text-brand)]"
|
|
||||||
@click="toggle('base')"
|
|
||||||
/></span>
|
|
||||||
</div>
|
|
||||||
</a-checkbox>
|
|
||||||
</div>
|
|
||||||
<a-checkbox-group v-if="foldBaseFields" v-model="baseFields" class="checkboxContainer" @change="handleChange">
|
|
||||||
<div class="item checkbox">
|
|
||||||
<a-checkbox value="1">Option 1</a-checkbox>
|
|
||||||
</div>
|
|
||||||
<div class="item checkbox">
|
|
||||||
<a-checkbox value="1">Option 1</a-checkbox>
|
|
||||||
</div>
|
|
||||||
<div class="item checkbox">
|
|
||||||
<a-checkbox value="1">Option 1</a-checkbox>
|
|
||||||
</div>
|
|
||||||
<div class="item checkbox">
|
|
||||||
<a-checkbox value="1">Option 1</a-checkbox>
|
|
||||||
</div>
|
|
||||||
<div class="item checkbox">
|
|
||||||
<a-checkbox value="1">Option 1</a-checkbox>
|
|
||||||
</div>
|
|
||||||
</a-checkbox-group>
|
|
||||||
<!-- 自定义字段 -->
|
|
||||||
<div>
|
|
||||||
<a-checkbox class="mb-4" :model-value="checkedAll" :indeterminate="indeterminate" @change="handleChangeAll"
|
|
||||||
><div class="flex items-center">
|
|
||||||
<span class="mr-1">{{ t('caseManagement.featureCase.customField') }}</span
|
|
||||||
><span
|
|
||||||
><icon-up
|
|
||||||
v-if="foldCustomFields"
|
|
||||||
class="text-[12px] text-[var(--color-text-brand)]"
|
|
||||||
@click="toggle('custom')" /><icon-right
|
|
||||||
v-else
|
|
||||||
class="text-[12px] text-[var(--color-text-brand)]"
|
|
||||||
@click="toggle('custom')"
|
|
||||||
/></span>
|
|
||||||
</div>
|
|
||||||
</a-checkbox>
|
|
||||||
</div>
|
|
||||||
<a-checkbox-group v-if="foldCustomFields" v-model="customFields" class="checkboxContainer" @change="handleChange">
|
|
||||||
<div class="item checkbox">
|
|
||||||
<a-checkbox value="1">Option 1</a-checkbox>
|
|
||||||
</div>
|
|
||||||
<div class="item checkbox">
|
|
||||||
<a-checkbox value="1">Option 1</a-checkbox>
|
|
||||||
</div>
|
|
||||||
<div class="item checkbox">
|
|
||||||
<a-checkbox value="1">Option 1</a-checkbox>
|
|
||||||
</div>
|
|
||||||
<div class="item checkbox">
|
|
||||||
<a-checkbox value="1">Option 1</a-checkbox>
|
|
||||||
</div>
|
|
||||||
<div class="item checkbox">
|
|
||||||
<a-checkbox value="1">Option 1</a-checkbox>
|
|
||||||
</div>
|
|
||||||
</a-checkbox-group>
|
|
||||||
<!-- 其他字段 -->
|
|
||||||
<div>
|
|
||||||
<a-checkbox class="mb-4" :model-value="checkedAll" :indeterminate="indeterminate" @change="handleChangeAll"
|
|
||||||
><div class="flex items-center">
|
|
||||||
<span class="mr-1 flex items-center"
|
|
||||||
>{{ t('caseManagement.featureCase.otherFields') }}<span></span>
|
|
||||||
<a-tooltip
|
|
||||||
:content="t('caseManagement.featureCase.otherFieldsToolTip')"
|
|
||||||
position="top"
|
|
||||||
:mouse-enter-delay="500"
|
|
||||||
mini
|
|
||||||
>
|
|
||||||
<icon-question-circle
|
|
||||||
class="mx-1 text-[16px] text-[var(--color-text-brand)] hover:text-[rgb(var(--primary-5))]" /></a-tooltip
|
|
||||||
><icon-up
|
|
||||||
v-if="foldCustomFields"
|
|
||||||
class="text-[12px] text-[var(--color-text-brand)]"
|
|
||||||
@click="toggle('other')" /><icon-right
|
|
||||||
v-else
|
|
||||||
class="text-[12px] text-[var(--color-text-brand)]"
|
|
||||||
@click="toggle('other')"
|
|
||||||
/></span>
|
|
||||||
</div>
|
|
||||||
</a-checkbox>
|
|
||||||
</div>
|
|
||||||
<a-checkbox-group v-if="foldOtherFields" v-model="otherFields" class="checkboxContainer" @change="handleChange">
|
|
||||||
<div class="item checkbox">
|
|
||||||
<a-checkbox value="1">Option 1</a-checkbox>
|
|
||||||
</div>
|
|
||||||
<div class="item checkbox">
|
|
||||||
<a-checkbox value="1">Option 1</a-checkbox>
|
|
||||||
</div>
|
|
||||||
<div class="item checkbox">
|
|
||||||
<a-checkbox value="1">Option 1</a-checkbox>
|
|
||||||
</div>
|
|
||||||
<div class="item checkbox">
|
|
||||||
<a-checkbox value="1">Option 1</a-checkbox>
|
|
||||||
</div>
|
|
||||||
<div class="item checkbox">
|
|
||||||
<a-checkbox value="1">Option 1</a-checkbox>
|
|
||||||
</div>
|
|
||||||
</a-checkbox-group>
|
|
||||||
</MsDrawer>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { ref } from 'vue';
|
|
||||||
|
|
||||||
import MsDrawer from '@/components/pure/ms-drawer/index.vue';
|
|
||||||
|
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
|
||||||
|
|
||||||
const { t } = useI18n();
|
|
||||||
|
|
||||||
const props = defineProps<{
|
|
||||||
visible: boolean;
|
|
||||||
}>();
|
|
||||||
|
|
||||||
const emit = defineEmits<{
|
|
||||||
(e: 'update:visible', val: boolean): void;
|
|
||||||
}>();
|
|
||||||
|
|
||||||
const showDrawer = ref<boolean>(false);
|
|
||||||
const drawerLoading = ref<boolean>(false);
|
|
||||||
|
|
||||||
const checkedAll = ref<boolean>(false);
|
|
||||||
const indeterminate = ref<boolean>(false);
|
|
||||||
|
|
||||||
function handleChangeAll() {}
|
|
||||||
function handleChange() {}
|
|
||||||
|
|
||||||
function handleDrawerConfirm() {}
|
|
||||||
|
|
||||||
function handleDrawerCancel() {
|
|
||||||
showDrawer.value = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const foldBaseFields = ref<boolean>(true); // 默认展开
|
|
||||||
const baseFields = ref<string[]>([]);
|
|
||||||
|
|
||||||
const foldCustomFields = ref<boolean>(true);
|
|
||||||
const customFields = ref<string[]>([]);
|
|
||||||
|
|
||||||
const foldOtherFields = ref<boolean>(true);
|
|
||||||
const otherFields = ref<string[]>([]);
|
|
||||||
|
|
||||||
function toggle(foldType: string) {
|
|
||||||
if (foldType === 'base') {
|
|
||||||
foldBaseFields.value = !foldBaseFields.value;
|
|
||||||
} else if (foldType === 'custom') {
|
|
||||||
foldCustomFields.value = !foldCustomFields.value;
|
|
||||||
} else {
|
|
||||||
foldOtherFields.value = !foldOtherFields.value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => props.visible,
|
|
||||||
(val) => {
|
|
||||||
showDrawer.value = val;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => showDrawer.value,
|
|
||||||
(val) => {
|
|
||||||
emit('update:visible', val);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped lang="less">
|
|
||||||
.checkboxContainer {
|
|
||||||
display: grid;
|
|
||||||
margin-bottom: 16px;
|
|
||||||
grid-template-columns: repeat(auto-fit, minmax(116px, 1fr));
|
|
||||||
grid-gap: 16px;
|
|
||||||
.checkbox {
|
|
||||||
width: 90px;
|
|
||||||
white-space: nowrap;
|
|
||||||
@apply overflow-hidden text-ellipsis;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -54,8 +54,15 @@ export default {
|
||||||
'The deleted content will be put into the recycle bin, where data can be recovered',
|
'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.deleteCaseTitle': 'Are you sure to delete the {name} use case?',
|
||||||
'caseManagement.featureCase.export': 'Export',
|
'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.exportXMind': 'Exporting Mind (xmind)',
|
||||||
|
'caseManagement.featureCase.exportXMindNoUnit': 'Exporting XMind',
|
||||||
'caseManagement.featureCase.moveTo': 'Move to',
|
'caseManagement.featureCase.moveTo': 'Move to',
|
||||||
'caseManagement.featureCase.copyTo': 'Copy to',
|
'caseManagement.featureCase.copyTo': 'Copy to',
|
||||||
'caseManagement.featureCase.associatedDemand': 'Associated demand',
|
'caseManagement.featureCase.associatedDemand': 'Associated demand',
|
||||||
|
|
|
@ -54,8 +54,15 @@ export default {
|
||||||
'caseManagement.featureCase.deleteCaseTitle': '确认删除 {name} 用例吗?',
|
'caseManagement.featureCase.deleteCaseTitle': '确认删除 {name} 用例吗?',
|
||||||
'caseManagement.featureCase.completedDeleteCaseTitle': '确认彻底删除 {name} 用例吗?',
|
'caseManagement.featureCase.completedDeleteCaseTitle': '确认彻底删除 {name} 用例吗?',
|
||||||
'caseManagement.featureCase.export': '导出',
|
'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.exportXMind': '导出思维导图 (xmind)',
|
||||||
|
'caseManagement.featureCase.exportXMindNoUnit': '导出思维导图',
|
||||||
'caseManagement.featureCase.moveTo': '移动到',
|
'caseManagement.featureCase.moveTo': '移动到',
|
||||||
'caseManagement.featureCase.copyTo': '复制到',
|
'caseManagement.featureCase.copyTo': '复制到',
|
||||||
'caseManagement.featureCase.associatedDemand': '关联需求',
|
'caseManagement.featureCase.associatedDemand': '关联需求',
|
||||||
|
|
Loading…
Reference in New Issue