feat(测试用例): excel和xmind导出

This commit is contained in:
teukkk 2024-08-08 19:23:18 +08:00 committed by Craftsman
parent 3c08ffeb9c
commit 455a85e98e
10 changed files with 363 additions and 251 deletions

View File

@ -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 });

View File

@ -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';
// 获取变更历史 // 获取变更历史

View File

@ -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[]; //
} }

View File

@ -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',

View File

@ -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': '展开全部',

View File

@ -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"

View File

@ -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>

View File

@ -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>

View File

@ -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',

View File

@ -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': '关联需求',