feat(测试计划): 测试计划脑图批量关联缺陷

This commit is contained in:
baiqi 2024-09-04 14:01:29 +08:00 committed by Craftsman
parent 32b27a95d7
commit 63c7dfdaf6
8 changed files with 159 additions and 44 deletions

View File

@ -11,9 +11,11 @@ import {
AssociatedBugToScenarioCaseUrl, AssociatedBugToScenarioCaseUrl,
BatchAddBugToApiCaseUrl, BatchAddBugToApiCaseUrl,
BatchAddBugToFunctionalCaseUrl, BatchAddBugToFunctionalCaseUrl,
BatchAddBugToMinderCaseUrl,
BatchAddBugToScenarioCaseUrl, BatchAddBugToScenarioCaseUrl,
batchArchivedPlanUrl, batchArchivedPlanUrl,
BatchAssociatedBugToCaseUrl, BatchAssociatedBugToCaseUrl,
BatchAssociatedBugToMinderCaseUrl,
batchCopyPlanUrl, batchCopyPlanUrl,
batchDeletePlanUrl, batchDeletePlanUrl,
BatchDisassociateApiCaseUrl, BatchDisassociateApiCaseUrl,
@ -504,3 +506,11 @@ export function batchLinkBugToApiCase(data: TableQueryParams) {
export function batchLinkBugToScenarioCase(data: TableQueryParams) { export function batchLinkBugToScenarioCase(data: TableQueryParams) {
return MSR.post({ url: BatchLinkBugToScenarioCaseUrl, data }); return MSR.post({ url: BatchLinkBugToScenarioCaseUrl, data });
} }
// 测试计划-详情-脑图批量关联缺陷
export function batchAssociatedBugToMinderCase(data: TableQueryParams) {
return MSR.post({ url: BatchAssociatedBugToMinderCaseUrl, data });
}
// 测试计划-详情-脑图批量新建缺陷
export function batchAddBugToMinderCase(data: { request: BugEditFormObject; fileList: File[] }) {
return MSR.uploadFile({ url: BatchAddBugToMinderCaseUrl }, data, '', true);
}

View File

@ -181,3 +181,8 @@ export const BatchAddBugToScenarioCaseUrl = '/test-plan/api/scenario/batch/add-b
export const BatchLinkBugToApiCaseUrl = '/test-plan/api/case/batch/associate-bug'; export const BatchLinkBugToApiCaseUrl = '/test-plan/api/case/batch/associate-bug';
// 测试计划-详情-场景用例列表-批量关联缺陷 // 测试计划-详情-场景用例列表-批量关联缺陷
export const BatchLinkBugToScenarioCaseUrl = '/test-plan/api/scenario/batch/associate-bug'; export const BatchLinkBugToScenarioCaseUrl = '/test-plan/api/scenario/batch/associate-bug';
export const BatchAddBugToCaseUrl = '/test-plan/functional/case/batch/add-bug';
// 测试计划-详情-用例列表-脑图批量关联缺陷
export const BatchAssociatedBugToMinderCaseUrl = '/test-plan/functional/case/minder/batch/associate-bug';
// 测试计划-详情-用例列表-脑图批量新建缺陷
export const BatchAddBugToMinderCaseUrl = '/test-plan/functional/case/minder/batch/add-bug';

View File

@ -14,24 +14,24 @@
:can-show-more-menu-node-operation="false" :can-show-more-menu-node-operation="false"
:more-menu-other-operation-list="canShowFloatMenu && hasOperationPermission ? moreMenuOtherOperationList : []" :more-menu-other-operation-list="canShowFloatMenu && hasOperationPermission ? moreMenuOtherOperationList : []"
disabled disabled
@node-batch-select="handleNodeBatchSelect"
@node-select="handleNodeSelect" @node-select="handleNodeSelect"
@node-unselect="handleNodeUnselect" @node-unselect="handleNodeUnselect"
> >
<template #extractMenu> <template #extractMenu>
<!-- 缺陷 --> <!-- 缺陷 -->
<a-dropdown trigger="hover" position="bl"> <a-dropdown trigger="hover" position="bl">
<a-tooltip <MsButton
v-if=" v-if="
props.canEdit && props.canEdit &&
showAssociateBugMenu && showAssociateBugMenu &&
hasAllPermission(['PROJECT_BUG:READ', 'PROJECT_TEST_PLAN:READ+EXECUTE']) hasAllPermission(['PROJECT_BUG:READ', 'PROJECT_TEST_PLAN:READ+EXECUTE'])
" "
:content="t('common.add')" type="icon"
class="ms-minder-node-float-menu-icon-button"
> >
<MsButton type="icon" class="ms-minder-node-float-menu-icon-button">
<MsIcon type="icon-icon_bug" class="text-[var(--color-text-4)]" /> <MsIcon type="icon-icon_bug" class="text-[var(--color-text-4)]" />
</MsButton> </MsButton>
</a-tooltip>
<template #content> <template #content>
<a-doption v-permission="['PROJECT_BUG:READ+ADD']" value="new" @click="showAddDefectDrawer = true"> <a-doption v-permission="['PROJECT_BUG:READ+ADD']" value="new" @click="showAddDefectDrawer = true">
{{ t('testPlan.featureCase.noBugDataNewBug') }} {{ t('testPlan.featureCase.noBugDataNewBug') }}
@ -48,12 +48,9 @@
position="bl" position="bl"
:click-outside-to-close="false" :click-outside-to-close="false"
popup-container="body" popup-container="body"
>
<a-tooltip
v-if="props.canEdit && hasAnyPermission(['PROJECT_TEST_PLAN:READ+EXECUTE'])"
:content="t('common.execute')"
> >
<MsButton <MsButton
v-if="props.canEdit && hasAnyPermission(['PROJECT_TEST_PLAN:READ+EXECUTE'])"
type="icon" type="icon"
:class="[ :class="[
'ms-minder-node-float-menu-icon-button', 'ms-minder-node-float-menu-icon-button',
@ -62,7 +59,6 @@
> >
<MsIcon type="icon-icon_play-round_filled" class="text-[var(--color-text-4)]" /> <MsIcon type="icon-icon_play-round_filled" class="text-[var(--color-text-4)]" />
</MsButton> </MsButton>
</a-tooltip>
<template #content> <template #content>
<div class="w-[440px] rounded bg-white p-[16px] shadow-[0_0_10px_rgba(0,0,0,0.05)]"> <div class="w-[440px] rounded bg-white p-[16px] shadow-[0_0_10px_rgba(0,0,0,0.05)]">
<ExecuteSubmit <ExecuteSubmit
@ -75,8 +71,8 @@
</template> </template>
</a-trigger> </a-trigger>
<!-- 查看详情 --> <!-- 查看详情 -->
<a-tooltip v-if="canShowDetail" :content="t('common.detail')">
<MsButton <MsButton
v-if="canShowDetail"
type="icon" type="icon"
:class="[ :class="[
'ms-minder-node-float-menu-icon-button', 'ms-minder-node-float-menu-icon-button',
@ -86,9 +82,7 @@
> >
<MsIcon type="icon-icon_describe_outlined" class="text-[var(--color-text-4)]" /> <MsIcon type="icon-icon_describe_outlined" class="text-[var(--color-text-4)]" />
</MsButton> </MsButton>
</a-tooltip>
</template> </template>
<template #extractTabContent> <template #extractTabContent>
<MsDescription <MsDescription
v-if="activeExtraKey === 'baseInfo'" v-if="activeExtraKey === 'baseInfo'"
@ -121,8 +115,37 @@
show-step-detail-trigger show-step-detail-trigger
/> />
</template> </template>
<template #batchMenu>
<a-dropdown trigger="hover" position="bl">
<MsButton
v-if="props.canEdit && hasAllPermission(['PROJECT_BUG:READ', 'PROJECT_TEST_PLAN:READ+EXECUTE'])"
type="icon"
class="ms-minder-node-float-menu-icon-button"
>
<MsIcon type="icon-icon_bug" class="text-[var(--color-text-4)]" />
</MsButton>
<template #content>
<a-doption v-permission="['PROJECT_BUG:READ+ADD']" value="new" @click="showBatchAddDefect">
{{ t('testPlan.featureCase.noBugDataNewBug') }}
</a-doption>
<a-doption v-permission="['PROJECT_BUG:READ']" value="link" @click="showBatchLinkDefect">
{{ t('caseManagement.featureCase.linkDefect') }}
</a-doption>
</template>
</a-dropdown>
</template>
</MsMinderEditor> </MsMinderEditor>
<LinkDefectDrawer <LinkDefectDrawer
v-if="isMinderOperation"
v-model:visible="showLinkDefectDrawer"
:case-id="selectNode?.data?.caseId ?? ''"
:drawer-loading="linkDrawerLoading"
:load-api="AssociatedBugApiTypeEnum.BUG_TOTAL_LIST"
:show-selector-all="false"
@save="associateSuccessHandler"
/>
<LinkDefectDrawer
v-else
v-model:visible="showLinkDefectDrawer" v-model:visible="showLinkDefectDrawer"
:case-id="selectNode?.data?.caseId ?? ''" :case-id="selectNode?.data?.caseId ?? ''"
:drawer-loading="linkDrawerLoading" :drawer-loading="linkDrawerLoading"
@ -137,7 +160,9 @@
testPlanCaseId: selectNode?.data?.id, testPlanCaseId: selectNode?.data?.id,
caseId: selectNode?.data?.caseId, caseId: selectNode?.data?.caseId,
testPlanId: props.planId, testPlanId: props.planId,
...batchMinderParams,
}" }"
:is-minder-batch="isMinderOperation"
@success="handleAddBugDone" @success="handleAddBugDone"
/> />
<a-modal <a-modal
@ -198,6 +223,7 @@
import { getCasePlanMinder } from '@/api/modules/case-management/caseReview'; import { getCasePlanMinder } from '@/api/modules/case-management/caseReview';
import { import {
associateBugToPlan, associateBugToPlan,
batchAssociatedBugToMinderCase,
batchExecuteCase, batchExecuteCase,
executeHistory, executeHistory,
getCaseDetail, getCaseDetail,
@ -274,6 +300,7 @@
...e, ...e,
data: { data: {
...e.data, ...e.data,
type: e.type || e.data?.type,
id: e.id || e.data?.id || '', id: e.id || e.data?.id || '',
text: e.name || e.data?.text || '', text: e.name || e.data?.text || '',
resource: modulesCount.value[e.id] !== undefined ? [moduleTag] : e.data?.resource, resource: modulesCount.value[e.id] !== undefined ? [moduleTag] : e.data?.resource,
@ -526,21 +553,41 @@
const showAddDefectDrawer = ref(false); const showAddDefectDrawer = ref(false);
const linkDrawerLoading = ref(false); const linkDrawerLoading = ref(false);
const bugListRef = ref<InstanceType<typeof BugList>>(); const bugListRef = ref<InstanceType<typeof BugList>>();
const batchMinderParams = ref({
minderModuleIds: [] as string[],
minderCaseIds: [] as string[],
minderProjectIds: [] as string[],
});
const isMinderOperation = ref(false);
function handleAddBugDone() { function handleAddBugDone() {
if (extraVisible.value && activeExtraKey.value === 'bug') { if (extraVisible.value && activeExtraKey.value === 'bug') {
bugListRef.value?.loadBugList(); bugListRef.value?.loadBugList();
} }
emit('refreshPlan'); emit('refreshPlan');
} }
async function associateSuccessHandler(params: TableQueryParams) { async function associateSuccessHandler(params: TableQueryParams) {
try { try {
linkDrawerLoading.value = true; linkDrawerLoading.value = true;
if (isMinderOperation.value) {
await batchAssociatedBugToMinderCase({
...params,
testPlanCaseId: '',
caseId: '',
testPlanId: props.planId,
bugIds: params.selectIds,
selectAll: batchMinderParams.value.minderModuleIds.includes('NONE'),
...batchMinderParams.value,
});
} else {
await associateBugToPlan({ await associateBugToPlan({
...params, ...params,
testPlanCaseId: selectNode.value.data?.id, testPlanCaseId: selectNode.value.data?.id,
caseId: selectNode.value.data?.caseId, caseId: selectNode.value.data?.caseId,
testPlanId: props.planId, testPlanId: props.planId,
}); });
}
Message.success(t('caseManagement.featureCase.associatedSuccess')); Message.success(t('caseManagement.featureCase.associatedSuccess'));
linkDrawerLoading.value = false; linkDrawerLoading.value = false;
handleAddBugDone(); handleAddBugDone();
@ -574,9 +621,11 @@
handleRenderNode(node, [actualResultNode]); handleRenderNode(node, [actualResultNode]);
} }
} }
function isActualResultNode(node: MinderJsonNode) { function isActualResultNode(node: MinderJsonNode) {
return node.data?.resource?.includes(actualResultTag) && node.parent?.data?.resource?.includes(caseTag); return node.data?.resource?.includes(actualResultTag) && node.parent?.data?.resource?.includes(caseTag);
} }
// // // //
function handleExecuteDone(status: LastExecuteResults, content: string) { function handleExecuteDone(status: LastExecuteResults, content: string) {
const curSelectNode = window.minder.getSelectedNode(); const curSelectNode = window.minder.getSelectedNode();
@ -602,6 +651,7 @@
} }
emit('refreshPlan'); emit('refreshPlan');
} }
async function handleShortCutExecute(status: LastExecuteResults) { async function handleShortCutExecute(status: LastExecuteResults) {
const selectedNodes: MinderJsonNode = window.minder.getSelectedNode(); const selectedNodes: MinderJsonNode = window.minder.getSelectedNode();
if (!selectedNodes?.data?.resource?.includes(caseTag)) return; if (!selectedNodes?.data?.resource?.includes(caseTag)) return;
@ -783,9 +833,33 @@
return null; return null;
} }
function setBatchMinderParams() {
batchMinderParams.value = {
minderModuleIds: [],
minderCaseIds: [],
minderProjectIds: [],
};
const selectedNodes: MinderJsonNode[] = window.minder.getSelectedNodes();
selectedNodes.forEach((node) => {
if (node.data?.resource?.includes(caseTag)) {
batchMinderParams.value.minderCaseIds.push(node.data?.id || '');
} else if (node.data?.type === 'PROJECT') {
batchMinderParams.value.minderProjectIds.push(node.data?.id || '');
} else if (node.data?.resource?.includes(moduleTag)) {
batchMinderParams.value.minderModuleIds.push(node.data?.id || '');
}
});
}
// //
async function handleNodeSelect(node: MinderJsonNode) { async function handleNodeSelect(node: MinderJsonNode) {
const { data } = node; const { data } = node;
if (node.data?.resource?.includes(moduleTag)) {
isMinderOperation.value = true; // /
setBatchMinderParams();
} else {
isMinderOperation.value = false;
}
// //
if (data?.type === 'tmp' && node.parent?.data?.resource?.includes(moduleTag)) { if (data?.type === 'tmp' && node.parent?.data?.resource?.includes(moduleTag)) {
canShowFloatMenu.value = false; canShowFloatMenu.value = false;
@ -849,14 +923,14 @@
// //
expendNodeAndChildren(node); expendNodeAndChildren(node);
node.layout(); node.layout();
} else if (data?.resource?.includes(moduleTag) && data.count > 0 && data.isLoaded !== true) { } else if (data?.resource?.includes(moduleTag)) {
// //
if (data.id !== 'NONE') { if (data.id !== 'NONE' && data.count > 0 && data.isLoaded !== true) {
await initNodeCases(node); await initNodeCases(node);
} }
extraVisible.value = false; extraVisible.value = false;
canShowDetail.value = false; canShowDetail.value = false;
showAssociateBugMenu.value = false; showAssociateBugMenu.value = true;
} else { } else {
extraVisible.value = false; extraVisible.value = false;
canShowDetail.value = false; canShowDetail.value = false;
@ -867,8 +941,23 @@
setPriorityView(true, 'P'); setPriorityView(true, 'P');
} }
function handleNodeBatchSelect() {
isMinderOperation.value = true;
}
function showBatchAddDefect() {
showAddDefectDrawer.value = true;
setBatchMinderParams();
}
function showBatchLinkDefect() {
showLinkDefectDrawer.value = true;
setBatchMinderParams();
}
function handleNodeUnselect() { function handleNodeUnselect() {
extraVisible.value = false; extraVisible.value = false;
isMinderOperation.value = false;
} }
const { unbindShortcuts } = useShortCut( const { unbindShortcuts } = useShortCut(

View File

@ -30,7 +30,11 @@
</template> </template>
</nodeFloatMenu> </nodeFloatMenu>
<nodeDropdown v-if="props.canShowDropdown" :dropdown-list="props.dropdownList" :checked-val="props.checkedVal" /> <nodeDropdown v-if="props.canShowDropdown" :dropdown-list="props.dropdownList" :checked-val="props.checkedVal" />
<batchMenu v-bind="props" /> <batchMenu v-bind="props">
<template #batchMenu>
<slot name="batchMenu"></slot>
</template>
</batchMenu>
</div> </div>
</template> </template>

View File

@ -52,7 +52,7 @@
class="ms-minder-dropdown" class="ms-minder-dropdown"
:popup-translate="[0, -4]" :popup-translate="[0, -4]"
position="tl" position="tl"
trigger="click" trigger="hover"
@select="(val) => handleMinderMenuSelect(val)" @select="(val) => handleMinderMenuSelect(val)"
> >
<a-tooltip :content="t('common.more')"> <a-tooltip :content="t('common.more')">

View File

@ -11,6 +11,9 @@
<template #extractMenu> <template #extractMenu>
<slot name="extractMenu"></slot> <slot name="extractMenu"></slot>
</template> </template>
<template #batchMenu>
<slot name="batchMenu"></slot>
</template>
</mainEditor> </mainEditor>
</div> </div>
<div class="ms-minder-editor-extra" :class="[extraVisible ? 'ms-minder-editor-extra--visible' : '']"> <div class="ms-minder-editor-extra" :class="[extraVisible ? 'ms-minder-editor-extra--visible' : '']">

View File

@ -54,6 +54,7 @@
import { import {
batchAddBugToApiCase, batchAddBugToApiCase,
batchAddBugToFunctionCase, batchAddBugToFunctionCase,
batchAddBugToMinderCase,
batchAddBugToScenarioCase, batchAddBugToScenarioCase,
} from '@/api/modules/test-plan/testPlan'; } from '@/api/modules/test-plan/testPlan';
import { useI18n } from '@/hooks/useI18n'; import { useI18n } from '@/hooks/useI18n';
@ -80,6 +81,7 @@
detailId: string; // id idid detailId: string; // id idid
name: string; // name: string; //
}; };
isMinderBatch?: boolean;
}>(); }>();
const emit = defineEmits<{ const emit = defineEmits<{
@ -139,6 +141,8 @@
props.extraParams && typeof props.extraParams === 'function' ? await props.extraParams() : props.extraParams; props.extraParams && typeof props.extraParams === 'function' ? await props.extraParams() : props.extraParams;
if (props.isBatch && props.caseType) { if (props.isBatch && props.caseType) {
await batchAddApiMap[props.caseType]({ request: { ...request, ...extraParam }, fileList }); await batchAddApiMap[props.caseType]({ request: { ...request, ...extraParam }, fileList });
} else if (props.isMinderBatch) {
await batchAddBugToMinderCase({ request: { ...request, ...props.extraParams }, fileList });
} else { } else {
await createOrUpdateBug({ request: { ...request, ...extraParam }, fileList }); await createOrUpdateBug({ request: { ...request, ...extraParam }, fileList });
} }

View File

@ -230,7 +230,7 @@
keyword: keyword.value, keyword: keyword.value,
searchMode: 'AND', searchMode: 'AND',
combine: {}, combine: {},
caseId: props.caseId, caseId: props.caseId || '',
condition: { keyword: keyword.value }, condition: { keyword: keyword.value },
}; };
showDrawer.value = false; showDrawer.value = false;
@ -245,7 +245,7 @@
currentCaseTable.value.setLoadListParams({ currentCaseTable.value.setLoadListParams({
keyword: keyword.value, keyword: keyword.value,
projectId: currentProjectId.value, projectId: currentProjectId.value,
sourceId: props.caseId, sourceId: props.caseId || '',
}); });
currentCaseTable.value.loadList(); currentCaseTable.value.loadList();
} }