feat(测试计划): 脑图执行用例-缺陷和更多

This commit is contained in:
teukkk 2024-07-30 16:13:12 +08:00 committed by 刘瑞斌
parent c56ca2975d
commit b9128515e1
6 changed files with 152 additions and 32 deletions

View File

@ -208,6 +208,10 @@
onBeforeMount(() => {
loadBugList();
});
defineExpose({
handleShowTypeChange,
});
</script>
<style lang="less" scoped>

View File

@ -33,10 +33,10 @@
</MsButton>
</a-tooltip>
<template #content>
<a-doption v-permission="['PROJECT_BUG:READ+ADD']" value="new">
<a-doption v-permission="['PROJECT_BUG:READ+ADD']" value="new" @click="showAddDefectDrawer = true">
{{ t('testPlan.featureCase.noBugDataNewBug') }}
</a-doption>
<a-doption v-permission="['PROJECT_BUG:READ']" value="link">
<a-doption v-permission="['PROJECT_BUG:READ']" value="link" @click="showLinkDefectDrawer = true">
{{ t('caseManagement.featureCase.linkDefect') }}
</a-doption>
</template>
@ -82,6 +82,7 @@
/>
<BugList
v-else-if="activeExtraKey === 'bug'"
ref="bugListRef"
:active-case="activeCaseInfo"
is-test-plan-case
:show-disassociate-button="props.canEdit && hasAnyPermission(['PROJECT_TEST_PLAN:READ+EXECUTE'])"
@ -95,10 +96,29 @@
/>
</template>
</MsMinderEditor>
<LinkDefectDrawer
v-model:visible="showLinkDefectDrawer"
:case-id="selectNode?.data?.caseId ?? ''"
:drawer-loading="linkDrawerLoading"
:show-selector-all="false"
@save="associateSuccessHandler"
/>
<AddDefectDrawer
v-model:visible="showAddDefectDrawer"
:case-id="selectNode?.data?.id ?? ''"
:extra-params="{
testPlanCaseId: selectNode?.data?.id,
caseId: selectNode?.data?.caseId,
testPlanId: props.planId,
}"
@success="handleAddBugDone"
/>
</div>
</template>
<script setup lang="ts">
import { Message } from '@arco-design/web-vue';
import MsButton from '@/components/pure/ms-button/index.vue';
import MsDescription, { Description } from '@/components/pure/ms-description/index.vue';
import MsMinderEditor from '@/components/pure/ms-minder-editor/minderEditor.vue';
@ -114,16 +134,19 @@
import { MsFileItem } from '@/components/pure/ms-upload/types';
import Attachment from '@/components/business/ms-minders/featureCaseMinder/attachment.vue';
import BugList from '@/components/business/ms-minders/featureCaseMinder/bugList.vue';
import AddDefectDrawer from '@/views/case-management/caseManagementFeature/components/tabContent/tabBug/addDefectDrawer.vue';
import LinkDefectDrawer from '@/views/case-management/caseManagementFeature/components/tabContent/tabBug/linkDefectDrawer.vue';
import ReviewCommentList from '@/views/case-management/caseManagementFeature/components/tabContent/tabComment/reviewCommentList.vue';
import { getCasePlanMinder } from '@/api/modules/case-management/caseReview';
import { executeHistory, getCaseDetail } from '@/api/modules/test-plan/testPlan';
import { associateBugToPlan, executeHistory, getCaseDetail } from '@/api/modules/test-plan/testPlan';
import { useI18n } from '@/hooks/useI18n';
import useMinderStore from '@/store/modules/components/minder-editor/index';
import useTestPlanFeatureCaseStore from '@/store/modules/testPlan/testPlanFeatureCase';
import { findNodeByKey, mapTree, replaceNodeInTree } from '@/utils';
import { hasAllPermission, hasAnyPermission } from '@/utils/permission';
import type { TableQueryParams } from '@/models/common';
import { ModuleTreeNode } from '@/models/common';
import type { ExecuteHistoryItem } from '@/models/testPlan/testPlan';
import { MinderEventName, MinderKeyEnum } from '@/enums/minderEnum';
@ -143,6 +166,7 @@
const emit = defineEmits<{
(e: 'operation', type: string, node: MinderJsonNode): void;
(e: 'handleAddBugDone'): void;
}>();
const { t } = useI18n();
@ -531,6 +555,37 @@
extraVisible.value = false;
}
//
const showLinkDefectDrawer = ref(false);
const showAddDefectDrawer = ref(false);
const linkDrawerLoading = ref(false);
const bugListRef = ref<InstanceType<typeof BugList>>();
function handleAddBugDone() {
if (extraVisible.value && activeExtraKey.value === 'bug') {
bugListRef.value?.handleShowTypeChange();
}
emit('handleAddBugDone');
}
async function associateSuccessHandler(params: TableQueryParams) {
try {
linkDrawerLoading.value = true;
await associateBugToPlan({
...params,
testPlanCaseId: selectNode.value.data?.id,
caseId: selectNode.value.data?.caseId,
testPlanId: props.planId,
});
Message.success(t('caseManagement.featureCase.associatedSuccess'));
linkDrawerLoading.value = false;
handleAddBugDone();
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
} finally {
linkDrawerLoading.value = false;
}
}
defineExpose({
initCaseTree,
});
@ -542,5 +597,6 @@
}
:deep(.ms-list) {
margin: 0;
height: 100%;
}
</style>

View File

@ -8,7 +8,7 @@
>
<template #title>
{{ t('testPlan.featureCase.batchChangeExecutor') }}
<div class="ml-1 text-[var(--color-text-4)]">
<div v-show="props.showTitleCount" class="ml-1 text-[var(--color-text-4)]">
{{
t('common.selectedCount', {
count: props.count,
@ -66,6 +66,7 @@
count: number;
params?: BatchUpdateCaseExecutorParams;
batchUpdateExecutor: (...args: any) => Promise<any>; //
showTitleCount: boolean;
}>();
const emit = defineEmits<{

View File

@ -116,6 +116,8 @@
:module-tree="moduleTree"
:plan-id="props.planId"
:can-edit="props.canEdit"
@operation="handleMinderOperation"
@handle-add-bug-done="emit('refresh')"
/>
</div>
<!-- 批量执行 -->
@ -147,6 +149,7 @@
v-model:visible="batchUpdateExecutorModalVisible"
:count="batchParams.currentSelectCount || tableSelected.length"
:params="batchUpdateParams"
:show-title-count="showType === 'list'"
:batch-update-executor="batchUpdateCaseExecutor"
@load-list="resetSelectorAndCaseList"
/>
@ -166,9 +169,11 @@
import { computed, onBeforeMount, ref } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { Message } from '@arco-design/web-vue';
import { cloneDeep } from 'lodash-es';
import { MsAdvanceFilter } from '@/components/pure/ms-advance-filter';
import MsButton from '@/components/pure/ms-button/index.vue';
import type { MinderJsonNode, MinderJsonNodeData } from '@/components/pure/ms-minder-editor/props';
import MsPopconfirm from '@/components/pure/ms-popconfirm/index.vue';
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
import type {
@ -180,6 +185,7 @@
import useTable from '@/components/pure/ms-table/useTable';
import CaseLevel from '@/components/business/ms-case-associate/caseLevel.vue';
import ExecuteResult from '@/components/business/ms-case-associate/executeResult.vue';
import { getMinderOperationParams } from '@/components/business/ms-minders/caseReviewMinder/utils';
import MsTestPlanFeatureCaseMinder from '@/components/business/ms-minders/testPlanFeatureCaseMinder/index.vue';
import BugCountPopover from './bugCountPopover.vue';
import BatchApiMoveModal from '@/views/test-plan/testPlan/components/batchApiMoveModal.vue';
@ -205,7 +211,7 @@
import { characterLimit } from '@/utils';
import { hasAllPermission, hasAnyPermission } from '@/utils/permission';
import { DragSortParams } from '@/models/common';
import { DragSortParams, ModuleTreeNode } from '@/models/common';
import type { ExecuteFeatureCaseFormParams, PlanDetailFeatureCaseItem } from '@/models/testPlan/testPlan';
import { LastExecuteResults } from '@/enums/caseEnum';
import { TestPlanRouteEnum } from '@/enums/routeEnum';
@ -227,6 +233,7 @@
const emit = defineEmits<{
(e: 'refresh'): void;
(e: 'selectParentNode', tree: ModuleTreeNode[]): void;
}>();
const { t } = useI18n();
@ -558,8 +565,10 @@
}
function resetSelectorAndCaseList() {
resetSelector();
loadList();
if (showType.value === 'list') {
resetSelector();
loadList();
}
}
//
@ -613,29 +622,39 @@
}
}
const batchUpdateParams = ref();
const minderSelectData = ref<MinderJsonNodeData>(); //
//
function handleBatchDisassociateCase() {
const count =
showType.value !== 'list'
? minderSelectData.value?.count
: batchParams.value.currentSelectCount || tableSelected.value.length;
const batchDisassociateTitle =
showType.value !== 'list' && minderSelectData.value?.resource?.includes(t('common.case'))
? t('testPlan.featureCase.disassociateTip', { name: characterLimit(minderSelectData.value?.text) })
: t('caseManagement.caseReview.disassociateConfirmTitle', { count });
openModal({
type: 'warning',
title: t('caseManagement.caseReview.disassociateConfirmTitle', {
count: batchParams.value.currentSelectCount || tableSelected.value.length,
}),
title: batchDisassociateTitle,
content: t('testPlan.featureCase.batchDisassociateTipContent'),
okText: t('common.cancelLink'),
cancelText: t('common.cancel'),
onBeforeOk: async () => {
try {
const tableParams = await getTableParams(true);
await batchDisassociateCase({
selectIds: tableSelected.value as string[],
selectAll: batchParams.value.selectAll,
excludeIds: batchParams.value?.excludeIds || [],
...tableParams,
});
await batchDisassociateCase(batchUpdateParams.value);
Message.success(t('common.updateSuccess'));
resetCaseList();
initModules();
const tree = cloneDeep(moduleTree.value);
emit('refresh');
await initModules();
await getModuleCount();
if (!Object.keys(modulesCount.value).includes(props.activeModule)) {
//
emit('selectParentNode', tree);
} else {
refresh(false);
}
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
@ -678,22 +697,11 @@
}
//
const batchUpdateParams = ref();
const batchUpdateExecutorModalVisible = ref(false);
const batchMoveModalVisible = ref(false);
//
async function handleTableBatch(event: BatchActionParams, params: BatchActionQueryParams) {
tableSelected.value = params?.selectedIds || [];
batchParams.value = { ...params, selectIds: params?.selectedIds };
const tableParams = await getTableParams(true);
batchUpdateParams.value = {
selectIds: tableSelected.value as string[],
selectAll: batchParams.value.selectAll,
excludeIds: batchParams.value?.excludeIds || [],
...tableParams,
};
switch (event.eventTag) {
function handleOperation(type?: string) {
switch (type) {
case 'execute':
batchExecuteModalVisible.value = true;
break;
@ -710,6 +718,30 @@
break;
}
}
//
function handleMinderOperation(type: string, node: MinderJsonNode) {
minderSelectData.value = node.data;
batchUpdateParams.value = {
...getMinderOperationParams(node),
testPlanId: props.planId,
projectId: node.data?.id !== 'NONE' ? node.data?.projectId : '',
};
handleOperation(type);
}
//
async function handleTableBatch(event: BatchActionParams, params: BatchActionQueryParams) {
tableSelected.value = params?.selectedIds || [];
batchParams.value = { ...params, selectIds: params?.selectedIds };
const tableParams = await getTableParams(true);
batchUpdateParams.value = {
selectIds: tableSelected.value as string[],
selectAll: batchParams.value.selectAll,
excludeIds: batchParams.value?.excludeIds || [],
...tableParams,
};
handleOperation(event.eventTag);
}
//
function toCaseDetail(record: PlanDetailFeatureCaseItem) {

View File

@ -135,6 +135,25 @@
emit('folderNodeSelect', _selectedKeys as string[], offspringIds, node.name, getNodeParentId(node));
}
/**
* 选中父节点
* @param tree 原来的模块树
*/
function selectParentNode(tree: ModuleTreeNode[]) {
mapTree(tree || [], (e) => {
if (e.id === selectedKeys.value[0]) {
if (e.parentId) {
selectedKeys.value = [e.parentId];
folderNodeSelect([e.parentId], e.parent);
} else {
setActiveFolder('all');
}
return e;
}
return e;
});
}
onBeforeMount(() => {
setIsExpandAll();
initModules();
@ -160,5 +179,6 @@
defineExpose({
setActiveFolder,
initModules,
selectParentNode,
});
</script>

View File

@ -22,6 +22,7 @@
:active-module="activeFolderId"
:offspring-ids="offspringIds"
:can-edit="props.canEdit"
@select-parent-node="selectParentNode"
@refresh="emit('refresh')"
></CaseTable>
</template>
@ -38,6 +39,8 @@
import useTestPlanFeatureCaseStore from '@/store/modules/testPlan/testPlanFeatureCase';
import { ModuleTreeNode } from '@/models/common';
const props = defineProps<{
canEdit: boolean;
treeType: 'MODULE' | 'COLLECTION';
@ -72,6 +75,10 @@
const caseTreeRef = ref<InstanceType<typeof CaseTree>>();
function selectParentNode(folderTree: ModuleTreeNode[]) {
caseTreeRef.value?.selectParentNode(folderTree);
}
function getCaseTableList() {
nextTick(async () => {
await caseTreeRef.value?.initModules();