feat(测试计划): 测试计划详情-功能用例取消关联用例联调

This commit is contained in:
teukkk 2024-05-14 15:54:15 +08:00 committed by 刘瑞斌
parent 7110f47191
commit 0c3ed10966
7 changed files with 151 additions and 34 deletions

View File

@ -5,9 +5,11 @@ import {
archivedPlanUrl, archivedPlanUrl,
batchCopyPlanUrl, batchCopyPlanUrl,
batchDeletePlanUrl, batchDeletePlanUrl,
BatchDisassociateCaseUrl,
batchMovePlanUrl, batchMovePlanUrl,
deletePlanUrl, deletePlanUrl,
DeleteTestPlanModuleUrl, DeleteTestPlanModuleUrl,
DisassociateCaseUrl,
GetFeatureCaseModuleCountUrl, GetFeatureCaseModuleCountUrl,
GetFeatureCaseModuleUrl, GetFeatureCaseModuleUrl,
GetPlanDetailFeatureCaseListUrl, GetPlanDetailFeatureCaseListUrl,
@ -27,6 +29,8 @@ import type { CommonList, MoveModules, TableQueryParams } from '@/models/common'
import { ModuleTreeNode } from '@/models/common'; import { ModuleTreeNode } from '@/models/common';
import type { import type {
AddTestPlanParams, AddTestPlanParams,
BatchFeatureCaseParams,
DisassociateCaseParams,
PlanDetailBugItem, PlanDetailBugItem,
PlanDetailFeatureCaseItem, PlanDetailFeatureCaseItem,
PlanDetailFeatureCaseListQueryParams, PlanDetailFeatureCaseListQueryParams,
@ -124,3 +128,11 @@ export function getFeatureCaseModuleCount(data: PlanDetailFeatureCaseListQueryPa
export function getFeatureCaseModule(planId: string) { export function getFeatureCaseModule(planId: string) {
return MSR.get<ModuleTreeNode[]>({ url: `${GetFeatureCaseModuleUrl}/${planId}` }); return MSR.get<ModuleTreeNode[]>({ url: `${GetFeatureCaseModuleUrl}/${planId}` });
} }
// 计划详情-功能用例列表-取消关联用例
export function disassociateCase(data: DisassociateCaseParams) {
return MSR.post({ url: DisassociateCaseUrl, data });
}
// 计划详情-功能用例列表-批量取消关联用例
export function batchDisassociateCase(data: BatchFeatureCaseParams) {
return MSR.post({ url: BatchDisassociateCaseUrl, data });
}

View File

@ -38,3 +38,7 @@ export const GetPlanDetailFeatureCaseListUrl = '/test-plan/functional/case/page'
export const GetFeatureCaseModuleCountUrl = '/test-plan/functional/case/module/count'; export const GetFeatureCaseModuleCountUrl = '/test-plan/functional/case/module/count';
// 计划详情-功能用例模块树 // 计划详情-功能用例模块树
export const GetFeatureCaseModuleUrl = '/test-plan/functional/case/tree'; export const GetFeatureCaseModuleUrl = '/test-plan/functional/case/tree';
// 计划详情-功能用例-取消关联用例
export const DisassociateCaseUrl = '/test-plan/functional/case/disassociate';
// 计划详情-功能用例-批量取消关联用例
export const BatchDisassociateCaseUrl = '/test-plan/functional/case/batch/disassociate';

View File

@ -1,3 +1,5 @@
import type { BatchActionQueryParams } from '@/components/pure/ms-table/type';
import type { customFieldsItem } from '@/models/caseManagement/featureCase'; import type { customFieldsItem } from '@/models/caseManagement/featureCase';
import type { TableQueryParams } from '@/models/common'; import type { TableQueryParams } from '@/models/common';
import { LastExecuteResults } from '@/enums/caseEnum'; import { LastExecuteResults } from '@/enums/caseEnum';
@ -148,5 +150,14 @@ export interface PlanDetailFeatureCaseListQueryParams extends TableQueryParams {
testPlanId: string; testPlanId: string;
projectId: string; projectId: string;
} }
export interface DisassociateCaseParams {
testPlanId: string;
id: string;
}
export interface BatchFeatureCaseParams extends BatchActionQueryParams {
testPlanId: string;
moduleIds?: string[];
}
export default {}; export default {};

View File

@ -46,14 +46,23 @@
</a-select> </a-select>
<span v-else class="text-[var(--color-text-2)]"><ExecuteResult :execute-result="record.lastExecResult" /></span> <span v-else class="text-[var(--color-text-2)]"><ExecuteResult :execute-result="record.lastExecResult" /></span>
</template> </template>
<template #operation> <template #operation="{ record }">
<MsButton v-permission="['PROJECT_API_DEFINITION_CASE:READ+EXECUTE']" type="text" class="!mr-0"> <MsButton v-permission="['PROJECT_API_DEFINITION_CASE:READ+EXECUTE']" type="text" class="!mr-0">
{{ t('common.execute') }} {{ t('common.execute') }}
</MsButton> </MsButton>
<a-divider v-permission="['PROJECT_TEST_PLAN:READ+ASSOCIATION']" direction="vertical" :margin="8"></a-divider> <a-divider v-permission="['PROJECT_TEST_PLAN:READ+ASSOCIATION']" direction="vertical" :margin="8"></a-divider>
<MsButton v-permission="['PROJECT_TEST_PLAN:READ+ASSOCIATION']" type="text" class="!mr-0"> <MsPopconfirm
{{ t('common.cancelLink') }} :title="t('testPlan.featureCase.disassociateTip', { name: record.name })"
</MsButton> :sub-title-tip="t('testPlan.featureCase.disassociateTipContent')"
:ok-text="t('common.confirm')"
:loading="disassociateLoading"
type="error"
@confirm="(val, done) => handleDisassociateCase(record, done)"
>
<MsButton v-permission="['PROJECT_TEST_PLAN:READ+ASSOCIATION']" type="text" class="!mr-0">
{{ t('common.cancelLink') }}
</MsButton>
</MsPopconfirm>
<!-- TODO: 修改permission --> <!-- TODO: 修改permission -->
<a-divider <a-divider
v-permission="['PROJECT_API_DEFINITION_CASE:READ+EXECUTE']" v-permission="['PROJECT_API_DEFINITION_CASE:READ+EXECUTE']"
@ -71,16 +80,23 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed, onBeforeMount, ref } from 'vue'; import { computed, onBeforeMount, ref } from 'vue';
import { useRoute, useRouter } from 'vue-router'; import { useRoute, useRouter } from 'vue-router';
import { Message } from '@arco-design/web-vue';
import MsButton from '@/components/pure/ms-button/index.vue'; import MsButton from '@/components/pure/ms-button/index.vue';
import MsPopconfirm from '@/components/pure/ms-popconfirm/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';
import useTable from '@/components/pure/ms-table/useTable'; import useTable from '@/components/pure/ms-table/useTable';
import CaseLevel from '@/components/business/ms-case-associate/caseLevel.vue'; import CaseLevel from '@/components/business/ms-case-associate/caseLevel.vue';
import ExecuteResult from '@/components/business/ms-case-associate/executeResult.vue'; import ExecuteResult from '@/components/business/ms-case-associate/executeResult.vue';
import { getPlanDetailFeatureCaseList } from '@/api/modules/test-plan/testPlan'; import {
batchDisassociateCase,
disassociateCase,
getPlanDetailFeatureCaseList,
} from '@/api/modules/test-plan/testPlan';
import { useI18n } from '@/hooks/useI18n'; import { useI18n } from '@/hooks/useI18n';
import useModal from '@/hooks/useModal';
import useTableStore from '@/hooks/useTableStore'; import useTableStore from '@/hooks/useTableStore';
import useAppStore from '@/store/modules/app'; import useAppStore from '@/store/modules/app';
import { hasAnyPermission } from '@/utils/permission'; import { hasAnyPermission } from '@/utils/permission';
@ -106,7 +122,7 @@
}>(); }>();
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'init', params: PlanDetailFeatureCaseListQueryParams): void; (e: 'getModuleCount', params: PlanDetailFeatureCaseListQueryParams): void;
}>(); }>();
const { t } = useI18n(); const { t } = useI18n();
@ -114,8 +130,13 @@
const router = useRouter(); const router = useRouter();
const appStore = useAppStore(); const appStore = useAppStore();
const tableStore = useTableStore(); const tableStore = useTableStore();
const { openModal } = useModal();
const keyword = ref(''); const keyword = ref('');
const tableParams = ref<PlanDetailFeatureCaseListQueryParams>({
testPlanId: props.planId,
projectId: appStore.currentProjectId,
});
// TODO: Permission // TODO: Permission
const hasOperationPermission = computed(() => const hasOperationPermission = computed(() =>
@ -235,10 +256,6 @@
eventTag: 'execute', eventTag: 'execute',
permission: ['PROJECT_TEST_PLAN:READ+EXECUTE'], permission: ['PROJECT_TEST_PLAN:READ+EXECUTE'],
}, },
{
label: 'testPlan.featureCase.sort',
eventTag: 'sort',
},
{ {
label: 'testPlan.featureCase.changeExecutor', label: 'testPlan.featureCase.changeExecutor',
eventTag: 'changeExecutor', eventTag: 'changeExecutor',
@ -261,25 +278,6 @@
], ],
}; };
const tableSelected = ref<(string | number)[]>([]); //
const batchParams = ref<BatchActionQueryParams>({
selectedIds: [],
selectAll: false,
excludeIds: [],
currentSelectCount: 0,
});
//
function handleTableBatch(event: BatchActionParams, params: BatchActionQueryParams) {
tableSelected.value = params?.selectedIds || [];
batchParams.value = params;
switch (event.eventTag) {
case 'execute':
break;
default:
break;
}
}
async function getModuleIds() { async function getModuleIds() {
let moduleIds: string[] = []; let moduleIds: string[] = [];
if (props.activeModule !== 'all') { if (props.activeModule !== 'all') {
@ -293,16 +291,16 @@
} }
async function loadCaseList() { async function loadCaseList() {
const selectModules = await getModuleIds(); const selectModules = await getModuleIds();
const params: PlanDetailFeatureCaseListQueryParams = { tableParams.value = {
testPlanId: props.planId, testPlanId: props.planId,
projectId: appStore.currentProjectId, projectId: appStore.currentProjectId,
moduleIds: selectModules, moduleIds: selectModules,
keyword: keyword.value, keyword: keyword.value,
}; };
setLoadListParams(params); setLoadListParams(tableParams.value);
loadList(); loadList();
emit('init', { emit('getModuleCount', {
...params, ...tableParams.value,
current: propsRes.value.msPagination?.current, current: propsRes.value.msPagination?.current,
pageSize: propsRes.value.msPagination?.pageSize, pageSize: propsRes.value.msPagination?.pageSize,
}); });
@ -310,6 +308,90 @@
onBeforeMount(() => { onBeforeMount(() => {
loadCaseList(); loadCaseList();
}); });
watch(
() => props.activeModule,
() => {
loadCaseList();
}
);
const tableSelected = ref<(string | number)[]>([]); //
const batchParams = ref<BatchActionQueryParams>({
selectIds: [],
selectAll: false,
excludeIds: [],
condition: {},
currentSelectCount: 0,
});
function resetCaseList() {
resetSelector();
emit('getModuleCount', {
...tableParams.value,
current: propsRes.value.msPagination?.current,
pageSize: propsRes.value.msPagination?.pageSize,
});
loadList();
}
//
function handleBatchDisassociateCase() {
openModal({
type: 'warning',
title: t('caseManagement.caseReview.disassociateConfirmTitle', { count: batchParams.value.currentSelectCount }),
content: t('testPlan.featureCase.batchDisassociateTipContent'),
okText: t('common.cancelLink'),
cancelText: t('common.cancel'),
onBeforeOk: async () => {
try {
await batchDisassociateCase({
...batchParams.value,
...tableParams.value,
});
Message.success(t('common.updateSuccess'));
resetCaseList();
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
}
},
hideCancel: false,
});
}
//
function handleTableBatch(event: BatchActionParams, params: BatchActionQueryParams) {
tableSelected.value = params?.selectedIds || [];
batchParams.value = params;
switch (event.eventTag) {
case 'execute':
break;
case 'disassociate':
handleBatchDisassociateCase();
break;
default:
break;
}
}
//
const disassociateLoading = ref(false);
async function handleDisassociateCase(record: PlanDetailFeatureCaseItem, done?: () => void) {
try {
disassociateLoading.value = true;
await disassociateCase({ testPlanId: props.planId, id: record.id });
if (done) {
done();
}
Message.success(t('common.unLinkSuccess'));
resetCaseList();
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
} finally {
disassociateLoading.value = false;
}
}
// //
function toCaseDetail(record: PlanDetailFeatureCaseItem) { function toCaseDetail(record: PlanDetailFeatureCaseItem) {

View File

@ -17,7 +17,7 @@
:active-module="activeFolderId" :active-module="activeFolderId"
:offspring-ids="offspringIds" :offspring-ids="offspringIds"
:module-tree="moduleTree" :module-tree="moduleTree"
@init="getModuleCount" @get-module-count="getModuleCount"
></CaseTable> ></CaseTable>
</template> </template>
</MsSplitBox> </MsSplitBox>

View File

@ -89,4 +89,9 @@ export default {
'testPlan.featureCase.executor': 'Executor', 'testPlan.featureCase.executor': 'Executor',
'testPlan.featureCase.changeExecutor': 'Change executor', 'testPlan.featureCase.changeExecutor': 'Change executor',
'testPlan.featureCase.sort': 'sort', 'testPlan.featureCase.sort': 'sort',
'testPlan.featureCase.disassociateTip': 'Are you sure to cancel the association {name}? ',
'testPlan.featureCase.disassociateTipContent':
'After cancellation, it will affect the statistics related to the test plan',
'testPlan.featureCase.batchDisassociateTipContent':
' After cancellation, associate again, and the execution result is unexecuted',
}; };

View File

@ -87,4 +87,7 @@ export default {
'testPlan.featureCase.executor': '执行人', 'testPlan.featureCase.executor': '执行人',
'testPlan.featureCase.changeExecutor': '修改执行人', 'testPlan.featureCase.changeExecutor': '修改执行人',
'testPlan.featureCase.sort': '排序', 'testPlan.featureCase.sort': '排序',
'testPlan.featureCase.disassociateTip': '确认取消关联 { name } 吗?',
'testPlan.featureCase.disassociateTipContent': '取消后,影响测试计划相关统计',
'testPlan.featureCase.batchDisassociateTipContent': '取消后,再次关联,执行结果为:未执行',
}; };