feat(测试计划): 测试计划详情-功能用例列表-复制和排序

This commit is contained in:
teukkk 2024-05-16 16:58:52 +08:00 committed by 刘瑞斌
parent bc4fed9553
commit c170455ed5
8 changed files with 101 additions and 29 deletions

View File

@ -28,6 +28,7 @@ import {
planDetailBugPageUrl, planDetailBugPageUrl,
planPassRateUrl, planPassRateUrl,
RunFeatureCaseUrl, RunFeatureCaseUrl,
SortFeatureCaseUrl,
updateTestPlanModuleUrl, updateTestPlanModuleUrl,
UpdateTestPlanUrl, UpdateTestPlanUrl,
} from '@/api/requrls/test-plan/testPlan'; } from '@/api/requrls/test-plan/testPlan';
@ -48,6 +49,7 @@ import type {
PlanDetailFeatureCaseItem, PlanDetailFeatureCaseItem,
PlanDetailFeatureCaseListQueryParams, PlanDetailFeatureCaseListQueryParams,
RunFeatureCaseParams, RunFeatureCaseParams,
SortFeatureCaseParams,
TestPlanDetail, TestPlanDetail,
TestPlanItem, TestPlanItem,
UseCountType, UseCountType,
@ -166,6 +168,10 @@ export function getFeatureCaseModule(planId: string) {
export function disassociateCase(data: DisassociateCaseParams) { export function disassociateCase(data: DisassociateCaseParams) {
return MSR.post({ url: DisassociateCaseUrl, data }); return MSR.post({ url: DisassociateCaseUrl, data });
} }
// 计划详情-功能用例列表-拖拽排序
export const sortFeatureCase = (data: SortFeatureCaseParams) => {
return MSR.post({ url: SortFeatureCaseUrl, data });
};
// 计划详情-功能用例列表-批量取消关联用例 // 计划详情-功能用例列表-批量取消关联用例
export function batchDisassociateCase(data: BatchFeatureCaseParams) { export function batchDisassociateCase(data: BatchFeatureCaseParams) {
return MSR.post({ url: BatchDisassociateCaseUrl, data }); return MSR.post({ url: BatchDisassociateCaseUrl, data });

View File

@ -48,6 +48,8 @@ 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 SortFeatureCaseUrl = '/test-plan/functional/case/sort';
// 计划详情-功能用例-取消关联用例 // 计划详情-功能用例-取消关联用例
export const DisassociateCaseUrl = '/test-plan/functional/case/disassociate'; export const DisassociateCaseUrl = '/test-plan/functional/case/disassociate';
// 计划详情-功能用例-批量取消关联用例 // 计划详情-功能用例-批量取消关联用例

View File

@ -180,4 +180,5 @@ export default {
'common.responsiblePerson': 'Responsible person', 'common.responsiblePerson': 'Responsible person',
'common.updateUserName': 'Update user name', 'common.updateUserName': 'Update user name',
'common.updateTime': 'Update time', 'common.updateTime': 'Update time',
'common.belongProject': 'Belong to Project',
}; };

View File

@ -183,4 +183,5 @@ export default {
'common.responsiblePerson': '责任人', 'common.responsiblePerson': '责任人',
'common.updateUserName': '更新人', 'common.updateUserName': '更新人',
'common.updateTime': '更新时间', 'common.updateTime': '更新时间',
'common.belongProject': '所属项目',
}; };

View File

@ -2,10 +2,9 @@ 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 { BatchApiParams, DragSortParams } from '@/models/common';
import { LastExecuteResults } from '@/enums/caseEnum'; import { LastExecuteResults } from '@/enums/caseEnum';
import { BatchApiParams } from '../common';
export type planStatusType = 'PREPARED' | 'UNDERWAY' | 'COMPLETED' | 'ARCHIVED'; export type planStatusType = 'PREPARED' | 'UNDERWAY' | 'COMPLETED' | 'ARCHIVED';
export interface AssociateFunctionalCaseItem { export interface AssociateFunctionalCaseItem {
@ -188,6 +187,10 @@ export interface BatchUpdateCaseExecutorParams extends BatchFeatureCaseParams {
userId: string; userId: string;
} }
export interface SortFeatureCaseParams extends DragSortParams {
testPlanId: string;
}
export interface PassRateCountDetail { export interface PassRateCountDetail {
id: string; id: string;
passThreshold: number; passThreshold: number;

View File

@ -16,7 +16,13 @@
</template> </template>
</a-button> </a-button>
</div> </div>
<MsBaseTable v-bind="propsRes" :action-config="batchActions" v-on="propsEvent" @batch-action="handleTableBatch"> <MsBaseTable
v-bind="propsRes"
:action-config="batchActions"
v-on="propsEvent"
@batch-action="handleTableBatch"
@drag-change="handleDragChange"
>
<template #num="{ record }"> <template #num="{ record }">
<MsButton type="text" @click="toCaseDetail(record)">{{ record.num }}</MsButton> <MsButton type="text" @click="toCaseDetail(record)">{{ record.num }}</MsButton>
</template> </template>
@ -70,11 +76,18 @@
</MsPopconfirm> </MsPopconfirm>
<!-- TODO: 修改permission --> <!-- TODO: 修改permission -->
<a-divider <a-divider
v-if="props.repeatCase"
v-permission="['PROJECT_API_DEFINITION_CASE:READ+EXECUTE']" v-permission="['PROJECT_API_DEFINITION_CASE:READ+EXECUTE']"
direction="vertical" direction="vertical"
:margin="8" :margin="8"
></a-divider> ></a-divider>
<MsButton v-permission="['PROJECT_API_DEFINITION_CASE:READ+ADD']" type="text" class="!mr-0"> <MsButton
v-if="props.repeatCase"
v-permission="['PROJECT_API_DEFINITION_CASE:READ+ADD']"
type="text"
class="!mr-0"
@click="handleCopyCase(record)"
>
{{ t('common.copy') }} {{ t('common.copy') }}
</MsButton> </MsButton>
</template> </template>
@ -164,11 +177,13 @@
import ExecuteForm from '@/views/test-plan/testPlan/detail/featureCase/components/executeForm.vue'; import ExecuteForm from '@/views/test-plan/testPlan/detail/featureCase/components/executeForm.vue';
import { import {
associationCaseToPlan,
batchDisassociateCase, batchDisassociateCase,
batchExecuteCase, batchExecuteCase,
batchUpdateCaseExecutor, batchUpdateCaseExecutor,
disassociateCase, disassociateCase,
getPlanDetailFeatureCaseList, getPlanDetailFeatureCaseList,
sortFeatureCase,
} from '@/api/modules/test-plan/testPlan'; } from '@/api/modules/test-plan/testPlan';
import { defaultExecuteForm } from '@/config/testPlan'; import { defaultExecuteForm } from '@/config/testPlan';
import { useI18n } from '@/hooks/useI18n'; import { useI18n } from '@/hooks/useI18n';
@ -177,7 +192,7 @@
import useAppStore from '@/store/modules/app'; import useAppStore from '@/store/modules/app';
import { hasAnyPermission } from '@/utils/permission'; import { hasAnyPermission } from '@/utils/permission';
import { ModuleTreeNode } from '@/models/common'; import { DragSortParams, ModuleTreeNode } from '@/models/common';
import type { import type {
ExecuteFeatureCaseFormParams, ExecuteFeatureCaseFormParams,
PlanDetailFeatureCaseItem, PlanDetailFeatureCaseItem,
@ -200,11 +215,12 @@
offspringIds: string[]; offspringIds: string[];
planId: string; planId: string;
moduleTree: ModuleTreeNode[]; moduleTree: ModuleTreeNode[];
repeatCase: boolean;
}>(); }>();
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'getModuleCount', params: PlanDetailFeatureCaseListQueryParams): void; (e: 'getModuleCount', params: PlanDetailFeatureCaseListQueryParams): void;
(e: 'executeDone'): void; (e: 'refresh'): void;
}>(); }>();
const { t } = useI18n(); const { t } = useI18n();
@ -283,6 +299,14 @@
width: 200, width: 200,
showDrag: true, showDrag: true,
}, },
{
title: 'common.belongProject',
dataIndex: 'projectName',
showTooltip: true,
showInTable: false,
showDrag: true,
width: 150,
},
{ {
title: 'testPlan.featureCase.bugCount', title: 'testPlan.featureCase.bugCount',
dataIndex: 'bugCount', dataIndex: 'bugCount',
@ -410,6 +434,54 @@
loadList(); loadList();
} }
//
async function handleDragChange(params: DragSortParams) {
try {
await sortFeatureCase({ ...params, testPlanId: props.planId });
Message.success(t('caseManagement.featureCase.sortSuccess'));
loadCaseList();
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
}
}
//
async function handleCopyCase(record: PlanDetailFeatureCaseItem) {
try {
await associationCaseToPlan({
functionalSelectIds: [record.caseId],
testPlanId: props.planId,
});
Message.success(t('ms.case.associate.associateSuccess'));
resetCaseList();
emit('refresh');
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
}
}
//
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();
emit('refresh');
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
} finally {
disassociateLoading.value = false;
}
}
// //
function handleBatchDisassociateCase() { function handleBatchDisassociateCase() {
openModal({ openModal({
@ -426,6 +498,7 @@
}); });
Message.success(t('common.updateSuccess')); Message.success(t('common.updateSuccess'));
resetCaseList(); resetCaseList();
emit('refresh');
} catch (error) { } catch (error) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log(error); console.log(error);
@ -451,7 +524,7 @@
Message.success(t('common.updateSuccess')); Message.success(t('common.updateSuccess'));
resetSelector(); resetSelector();
loadList(); loadList();
emit('executeDone'); emit('refresh');
} catch (error) { } catch (error) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log(error); console.log(error);
@ -513,25 +586,6 @@
} }
} }
//
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) {
router.push({ router.push({

View File

@ -14,11 +14,12 @@
<CaseTable <CaseTable
ref="caseTableRef" ref="caseTableRef"
:plan-id="planId" :plan-id="planId"
:repeat-case="props.repeatCase"
:active-module="activeFolderId" :active-module="activeFolderId"
:offspring-ids="offspringIds" :offspring-ids="offspringIds"
:module-tree="moduleTree" :module-tree="moduleTree"
@get-module-count="getModuleCount" @get-module-count="getModuleCount"
@execute-done="emit('executeDone')" @refresh="emit('refresh')"
></CaseTable> ></CaseTable>
</template> </template>
</MsSplitBox> </MsSplitBox>
@ -37,8 +38,12 @@
import { ModuleTreeNode } from '@/models/common'; import { ModuleTreeNode } from '@/models/common';
import type { PlanDetailFeatureCaseListQueryParams } from '@/models/testPlan/testPlan'; import type { PlanDetailFeatureCaseListQueryParams } from '@/models/testPlan/testPlan';
const props = defineProps<{
repeatCase: boolean;
}>();
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'executeDone'): void; (e: 'refresh'): void;
}>(); }>();
const route = useRoute(); const route = useRoute();

View File

@ -88,7 +88,7 @@
</MsCard> </MsCard>
<!-- special-height的174: 上面卡片高度158 + mt的16 --> <!-- special-height的174: 上面卡片高度158 + mt的16 -->
<MsCard class="mt-[16px]" :special-height="174" simple has-breadcrumb no-content-padding> <MsCard class="mt-[16px]" :special-height="174" simple has-breadcrumb no-content-padding>
<FeatureCase v-if="activeTab === 'featureCase'" @execute-done="getStatistics" /> <FeatureCase v-if="activeTab === 'featureCase'" :repeat-case="detail.repeatCase" @refresh="getStatistics" />
<!-- TODO 先不上 --> <!-- TODO 先不上 -->
<!-- <BugManagement v-if="activeTab === 'defectList'" :plan-id="detail.id" /> --> <!-- <BugManagement v-if="activeTab === 'defectList'" :plan-id="detail.id" /> -->
</MsCard> </MsCard>