feat(测试计划): 测试计划详情-接口用例-批量修改执行人&拖拽排序
This commit is contained in:
parent
49345ac62a
commit
b29118595d
|
@ -12,6 +12,7 @@ import {
|
|||
BatchEditTestPlanUrl,
|
||||
batchMovePlanUrl,
|
||||
BatchRunCaseUrl,
|
||||
BatchUpdateApiCaseExecutorUrl,
|
||||
BatchUpdateCaseExecutorUrl,
|
||||
ConfigScheduleUrl,
|
||||
copyTestPlanUrl,
|
||||
|
@ -44,6 +45,7 @@ import {
|
|||
PlanDetailExecuteHistoryUrl,
|
||||
planPassRateUrl,
|
||||
RunFeatureCaseUrl,
|
||||
SortApiCaseUrl,
|
||||
SortFeatureCaseUrl,
|
||||
TestPlanAndGroupCopyUrl,
|
||||
TestPlanApiAssociatedPageUrl,
|
||||
|
@ -67,6 +69,7 @@ import type {
|
|||
BatchApiCaseParams,
|
||||
BatchExecuteFeatureCaseParams,
|
||||
BatchFeatureCaseParams,
|
||||
BatchUpdateApiCaseExecutorParams,
|
||||
BatchUpdateCaseExecutorParams,
|
||||
CreateTask,
|
||||
DisassociateCaseParams,
|
||||
|
@ -84,6 +87,7 @@ import type {
|
|||
PlanDetailFeatureCaseItem,
|
||||
PlanDetailFeatureCaseListQueryParams,
|
||||
RunFeatureCaseParams,
|
||||
SortApiCaseParams,
|
||||
SortFeatureCaseParams,
|
||||
TestPlanBaseParams,
|
||||
TestPlanDetail,
|
||||
|
@ -273,6 +277,10 @@ export function getApiCaseModule(data: PlanDetailApiCaseTreeParams) {
|
|||
export function getApiCaseModuleCount(data: PlanDetailApiCaseQueryParams) {
|
||||
return MSR.post({ url: GetApiCaseModuleCountUrl, data });
|
||||
}
|
||||
// 计划详情-接口用例列表-拖拽排序
|
||||
export const sortApiCase = (data: SortApiCaseParams) => {
|
||||
return MSR.post({ url: SortApiCaseUrl, data });
|
||||
};
|
||||
// 计划详情-接口用例列表-取消关联用例
|
||||
export function disassociateApiCase(data: DisassociateCaseParams) {
|
||||
return MSR.post({ url: DisassociateApiCaseUrl, data });
|
||||
|
@ -281,10 +289,18 @@ export function disassociateApiCase(data: DisassociateCaseParams) {
|
|||
export function batchDisassociateApiCase(data: BatchApiCaseParams) {
|
||||
return MSR.post({ url: BatchDisassociateApiCaseUrl, data });
|
||||
}
|
||||
// 计划详情-接口用例列表-批量更新执行人
|
||||
export function batchUpdateApiCaseExecutor(data: BatchUpdateApiCaseExecutorParams) {
|
||||
return MSR.post({ url: BatchUpdateApiCaseExecutorUrl, data });
|
||||
}
|
||||
// 计划详情-接口场景列表 TODO 联调
|
||||
export function getPlanDetailApiScenarioList(data: PlanDetailFeatureCaseListQueryParams) {
|
||||
return MSR.post<CommonList<PlanDetailApiScenarioItem>>({ url: GetPlanDetailFeatureCaseListUrl, data });
|
||||
}
|
||||
// 计划详情-接口场景列表-批量更新执行人 TODO 联调
|
||||
export function batchUpdateApiScenarioExecutor(data: BatchUpdateApiCaseExecutorParams) {
|
||||
return MSR.post({ url: BatchUpdateApiCaseExecutorUrl, data });
|
||||
}
|
||||
// 计划详情-执行历史 TODO 联调
|
||||
export function getPlanDetailExecuteHistory(data: PlanDetailFeatureCaseListQueryParams) {
|
||||
return MSR.post<CommonList<PlanDetailExecuteHistoryItem>>({ url: PlanDetailExecuteHistoryUrl, data });
|
||||
|
|
|
@ -100,9 +100,13 @@ export const DeleteScheduleTaskUrl = 'test-plan/schedule-config-delete';
|
|||
export const GetPlanDetailApiCaseListUrl = '/test-plan/api/case/page';
|
||||
// 计划详情-接口用例模块树
|
||||
export const GetApiCaseModuleUrl = '/test-plan/api/case/tree';
|
||||
// 计划详情-接口用例-获取模块数量
|
||||
export const GetApiCaseModuleCountUrl = '/test-plan/api/case/module/count';
|
||||
// 计划详情-接口用例列表-拖拽排序
|
||||
export const SortApiCaseUrl = '/test-plan/api/case/sort';
|
||||
// 计划详情-接口用例列表-取消关联用例
|
||||
export const DisassociateApiCaseUrl = '/test-plan/api/case/disassociate';
|
||||
// 计划详情-接口用例列表-批量取消关联用例
|
||||
export const BatchDisassociateApiCaseUrl = '/test-plan/api/case/batch/disassociate';
|
||||
// 计划详情-接口用例-获取模块数量
|
||||
export const GetApiCaseModuleCountUrl = '/test-plan/api/case/module/count';
|
||||
// 计划详情-接口用例列表-批量更新执行人
|
||||
export const BatchUpdateApiCaseExecutorUrl = '/test-plan/api/case/batch/update/executor';
|
||||
|
|
|
@ -269,7 +269,7 @@ export interface PlanDetailApiCaseQueryParams extends TableQueryParams, TestPlan
|
|||
|
||||
export interface PlanDetailApiCaseTreeParams {
|
||||
testPlanId: string;
|
||||
treeType: 'MODULE' | 'COLLECTION';
|
||||
treeType: 'MODULE' | 'COLLECTION'; // 视图类型:模块是MODULE,测试集是COLLECTION
|
||||
}
|
||||
|
||||
export interface PlanDetailApiCaseItem {
|
||||
|
@ -281,7 +281,7 @@ export interface PlanDetailApiCaseItem {
|
|||
createUserName: string;
|
||||
lastExecResult: LastExecuteResults;
|
||||
lastExecTime: number;
|
||||
lastExecResultReportId: string;
|
||||
lastExecReportId: string; // 报告id
|
||||
executeUser: string;
|
||||
executeUserName: string;
|
||||
priority: string;
|
||||
|
@ -290,17 +290,24 @@ export interface PlanDetailApiCaseItem {
|
|||
projectName: string;
|
||||
environmentId: string;
|
||||
environmentName: string;
|
||||
testPlanCollectionId: string;
|
||||
collectEnvironmentId: string;
|
||||
testPlanCollectionId: string; // 测试集id
|
||||
}
|
||||
|
||||
export interface BatchApiCaseParams extends BatchActionQueryParams {
|
||||
testPlanId: string;
|
||||
moduleIds?: string[];
|
||||
collectionId?: string;
|
||||
collectionId?: string; // 测试集id
|
||||
protocols: string[];
|
||||
}
|
||||
|
||||
export interface BatchUpdateApiCaseExecutorParams extends BatchApiCaseParams {
|
||||
userId: string; // 执行人id
|
||||
}
|
||||
|
||||
export interface SortApiCaseParams extends DragSortParams {
|
||||
testCollectionId: string; // 测试集id
|
||||
}
|
||||
|
||||
// TODO: 联调
|
||||
export interface PlanDetailApiScenarioItem {
|
||||
id: string;
|
||||
|
|
|
@ -8,7 +8,13 @@
|
|||
>
|
||||
<template #expandLeft>
|
||||
<a-dropdown v-model:popup-visible="visible" :hide-on-select="false">
|
||||
<MsButton type="icon" status="secondary" class="!mr-[4px] p-[4px]" @click="visible = !visible">
|
||||
<MsButton
|
||||
v-show="typeof isExpandAll !== 'undefined'"
|
||||
type="icon"
|
||||
status="secondary"
|
||||
class="!mr-[4px] p-[4px]"
|
||||
@click="visible = !visible"
|
||||
>
|
||||
<MsIcon :type="`${showExpandApi ? 'icon-icon_more_outlined' : 'icon-icon_protocol'}`" />
|
||||
</MsButton>
|
||||
<template #content>
|
||||
|
@ -83,7 +89,8 @@
|
|||
}>();
|
||||
|
||||
const isExpandAll = defineModel<boolean>('isExpandAll', {
|
||||
required: true,
|
||||
required: false,
|
||||
default: undefined,
|
||||
});
|
||||
const isExpandApi = defineModel<boolean>('isExpandApi', {
|
||||
required: false,
|
||||
|
|
|
@ -0,0 +1,124 @@
|
|||
<template>
|
||||
<a-modal
|
||||
v-model:visible="visible"
|
||||
title-align="start"
|
||||
body-class="p-0"
|
||||
:cancel-button-props="{ disabled: batchLoading }"
|
||||
:ok-loading="batchLoading"
|
||||
:ok-button-props="{ disabled: batchUpdateExecutorDisabled }"
|
||||
:ok-text="t('common.update')"
|
||||
@before-ok="handleBatchUpdateExecutor"
|
||||
@close="resetBatchForm"
|
||||
>
|
||||
<template #title>
|
||||
{{ t('testPlan.featureCase.batchChangeExecutor') }}
|
||||
<div class="text-[var(--color-text-4)]">
|
||||
{{
|
||||
t('testPlan.testPlanIndex.selectedCount', {
|
||||
count: props.count,
|
||||
})
|
||||
}}
|
||||
</div>
|
||||
</template>
|
||||
<a-form ref="batchUpdateExecutorFormRef" :model="batchUpdateExecutorForm" layout="vertical">
|
||||
<a-form-item
|
||||
field="userId"
|
||||
:label="t('testPlan.featureCase.executor')"
|
||||
:rules="[{ required: true, message: t('testPlan.featureCase.requestExecutorRequired') }]"
|
||||
asterisk-position="end"
|
||||
class="mb-0"
|
||||
>
|
||||
<MsSelect
|
||||
v-model:modelValue="batchUpdateExecutorForm.userId"
|
||||
mode="static"
|
||||
:placeholder="t('common.pleaseSelect')"
|
||||
:loading="executorLoading"
|
||||
:options="userOptions"
|
||||
:search-keys="['label']"
|
||||
allow-search
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { FormInstance, Message, SelectOptionData } from '@arco-design/web-vue';
|
||||
|
||||
import MsSelect from '@/components/business/ms-select';
|
||||
|
||||
import { GetTestPlanUsers } from '@/api/modules/test-plan/testPlan';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import useAppStore from '@/store/modules/app';
|
||||
|
||||
import { ReviewUserItem } from '@/models/caseManagement/caseReview';
|
||||
import type { BatchUpdateApiCaseExecutorParams, BatchUpdateCaseExecutorParams } from '@/models/testPlan/testPlan';
|
||||
|
||||
const props = defineProps<{
|
||||
count: number;
|
||||
params?: BatchUpdateCaseExecutorParams | BatchUpdateApiCaseExecutorParams;
|
||||
batchUpdateExecutor: (...args: any) => Promise<any>; // 更新执行人接口
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'loadList'): void;
|
||||
}>();
|
||||
|
||||
const visible = defineModel<boolean>('visible', {
|
||||
required: true,
|
||||
});
|
||||
|
||||
const { t } = useI18n();
|
||||
const appStore = useAppStore();
|
||||
|
||||
const batchUpdateExecutorFormRef = ref<FormInstance>();
|
||||
const batchLoading = ref(false);
|
||||
const batchUpdateExecutorForm = ref<{ userId: string }>({ userId: '' });
|
||||
const batchUpdateExecutorDisabled = computed(() => !batchUpdateExecutorForm.value.userId.length);
|
||||
const userOptions = ref<SelectOptionData[]>([]);
|
||||
const executorLoading = ref(false);
|
||||
async function initUserOptions() {
|
||||
try {
|
||||
executorLoading.value = true;
|
||||
const res = await GetTestPlanUsers(appStore.currentProjectId, '');
|
||||
userOptions.value = res.map((e: ReviewUserItem) => ({ label: e.name, value: e.id }));
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
} finally {
|
||||
executorLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function handleBatchUpdateExecutor(done: (closed: boolean) => void) {
|
||||
batchUpdateExecutorFormRef.value?.validate(async (errors) => {
|
||||
if (!errors) {
|
||||
try {
|
||||
batchLoading.value = true;
|
||||
await props.batchUpdateExecutor({
|
||||
...props.params,
|
||||
...batchUpdateExecutorForm.value,
|
||||
});
|
||||
Message.success(t('common.updateSuccess'));
|
||||
emit('loadList');
|
||||
done(true);
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
done(false);
|
||||
} finally {
|
||||
batchLoading.value = false;
|
||||
done(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function resetBatchForm() {
|
||||
batchUpdateExecutorForm.value = { userId: '' };
|
||||
}
|
||||
|
||||
onBeforeMount(() => {
|
||||
initUserOptions();
|
||||
});
|
||||
</script>
|
|
@ -27,7 +27,7 @@
|
|||
<CaseLevel :case-level="filterContent.value" />
|
||||
</template>
|
||||
<template #caseLevel="{ record }">
|
||||
<CaseLevel :case-level="record.caseLevel" />
|
||||
<CaseLevel :case-level="record.priority" />
|
||||
</template>
|
||||
<template #[FilterSlotNameEnum.CASE_MANAGEMENT_EXECUTE_RESULT]="{ filterContent }">
|
||||
<ExecuteResult :execute-result="filterContent.key" />
|
||||
|
@ -62,24 +62,17 @@
|
|||
{{ t('common.cancelLink') }}
|
||||
</MsButton>
|
||||
</MsPopconfirm>
|
||||
<a-divider
|
||||
v-if="props.repeatCase"
|
||||
v-permission="['PROJECT_TEST_PLAN:READ+ASSOCIATION']"
|
||||
direction="vertical"
|
||||
:margin="8"
|
||||
></a-divider>
|
||||
<MsButton
|
||||
v-if="props.repeatCase"
|
||||
v-permission="['PROJECT_TEST_PLAN:READ+ASSOCIATION']"
|
||||
type="text"
|
||||
class="!mr-0"
|
||||
@click="handleCopyCase(record)"
|
||||
>
|
||||
{{ t('common.copy') }}
|
||||
</MsButton>
|
||||
</template>
|
||||
</MsBaseTable>
|
||||
<ReportDrawer v-model:visible="reportVisible" :report-id="reportId" />
|
||||
<!-- 批量修改执行人 -->
|
||||
<BatchUpdateExecutorModal
|
||||
v-model:visible="batchUpdateExecutorModalVisible"
|
||||
:count="batchParams.currentSelectCount || tableSelected.length"
|
||||
:params="batchUpdateExecutorParams"
|
||||
:batch-update-executor="batchUpdateApiCaseExecutor"
|
||||
@load-list="resetSelectorAndCaseList"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -101,14 +94,15 @@
|
|||
import CaseLevel from '@/components/business/ms-case-associate/caseLevel.vue';
|
||||
import ExecuteResult from '@/components/business/ms-case-associate/executeResult.vue';
|
||||
import apiStatus from '@/views/api-test/components/apiStatus.vue';
|
||||
import BatchUpdateExecutorModal from '@/views/test-plan/testPlan/components/batchUpdateExecutorModal.vue';
|
||||
import ReportDrawer from '@/views/test-plan/testPlan/detail/reportDrawer.vue';
|
||||
|
||||
import {
|
||||
associationCaseToPlan,
|
||||
batchDisassociateApiCase,
|
||||
batchUpdateApiCaseExecutor,
|
||||
disassociateApiCase,
|
||||
getPlanDetailApiCaseList,
|
||||
sortFeatureCase,
|
||||
sortApiCase,
|
||||
} from '@/api/modules/test-plan/testPlan';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import useModal from '@/hooks/useModal';
|
||||
|
@ -124,11 +118,7 @@
|
|||
import { FilterSlotNameEnum } from '@/enums/tableFilterEnum';
|
||||
|
||||
import { casePriorityOptions } from '@/views/api-test/components/config';
|
||||
import {
|
||||
executionResultMap,
|
||||
getCaseLevels,
|
||||
getModules,
|
||||
} from '@/views/case-management/caseManagementFeature/components/utils';
|
||||
import { executionResultMap, getModules } from '@/views/case-management/caseManagementFeature/components/utils';
|
||||
|
||||
const props = defineProps<{
|
||||
modulesCount: Record<string, number>; // 模块数量统计对象
|
||||
|
@ -137,9 +127,9 @@
|
|||
offspringIds: string[];
|
||||
planId: string;
|
||||
moduleTree: ModuleTreeNode[];
|
||||
repeatCase: boolean;
|
||||
canEdit: boolean;
|
||||
selectedProtocols: string[];
|
||||
treeType: 'MODULE' | 'COLLECTION';
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
|
@ -188,7 +178,7 @@
|
|||
},
|
||||
{
|
||||
title: 'case.caseLevel',
|
||||
dataIndex: 'caseLevel',
|
||||
dataIndex: 'priority',
|
||||
slotName: 'caseLevel',
|
||||
filterConfig: {
|
||||
options: casePriorityOptions,
|
||||
|
@ -242,7 +232,7 @@
|
|||
},
|
||||
{
|
||||
title: 'report.detail.api.executeEnv',
|
||||
dataIndex: 'executeEnv',
|
||||
dataIndex: 'environmentName',
|
||||
width: 150,
|
||||
showInTable: false,
|
||||
showDrag: true,
|
||||
|
@ -288,21 +278,11 @@
|
|||
return {
|
||||
...record,
|
||||
lastExecResult: record.lastExecResult ?? LastExecuteResults.PENDING,
|
||||
caseLevel: getCaseLevels(record.customFields),
|
||||
moduleId: getModules(record.moduleId, props.moduleTree),
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.canEdit,
|
||||
(val) => {
|
||||
tableProps.value.draggableCondition = hasAnyPermission(['PROJECT_TEST_PLAN:READ+UPDATE']) && val;
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
}
|
||||
);
|
||||
const tableRef = ref<InstanceType<typeof MsBaseTable>>();
|
||||
watch(
|
||||
() => hasOperationPermission.value,
|
||||
|
@ -347,6 +327,7 @@
|
|||
}
|
||||
return moduleIds;
|
||||
}
|
||||
const collectionId = computed(() => (props.activeModule === 'all' ? '' : props.activeModule));
|
||||
async function getTableParams(isBatch: boolean) {
|
||||
const selectModules = await getModuleIds();
|
||||
const commonParams = {
|
||||
|
@ -354,7 +335,7 @@
|
|||
projectId: appStore.currentProjectId,
|
||||
moduleIds: selectModules,
|
||||
protocols: props.selectedProtocols,
|
||||
collectionId: props.activeModule,
|
||||
collectionId: collectionId.value,
|
||||
};
|
||||
if (isBatch) {
|
||||
return {
|
||||
|
@ -372,6 +353,20 @@
|
|||
};
|
||||
}
|
||||
|
||||
watch(
|
||||
[() => props.canEdit, () => props.treeType, () => collectionId.value.length],
|
||||
() => {
|
||||
tableProps.value.draggableCondition =
|
||||
hasAnyPermission(['PROJECT_TEST_PLAN:READ+UPDATE']) &&
|
||||
props.canEdit &&
|
||||
props.treeType === 'COLLECTION' &&
|
||||
!!collectionId.value.length;
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
}
|
||||
);
|
||||
|
||||
async function loadCaseList() {
|
||||
const tableParams = await getTableParams(false);
|
||||
setLoadListParams(tableParams);
|
||||
|
@ -400,7 +395,7 @@
|
|||
const reportId = ref('');
|
||||
function showReport(record: PlanDetailApiCaseItem) {
|
||||
reportVisible.value = true;
|
||||
reportId.value = record.lastExecResultReportId;
|
||||
reportId.value = record.lastExecReportId;
|
||||
}
|
||||
|
||||
const tableSelected = ref<(string | number)[]>([]); // 表格选中的
|
||||
|
@ -421,11 +416,15 @@
|
|||
loadList();
|
||||
}
|
||||
|
||||
function resetSelectorAndCaseList() {
|
||||
resetSelector();
|
||||
loadList();
|
||||
}
|
||||
|
||||
// 拖拽排序
|
||||
async function handleDragChange(params: DragSortParams) {
|
||||
try {
|
||||
// TODO 联调
|
||||
await sortFeatureCase({ ...params, testPlanId: props.planId });
|
||||
await sortApiCase({ ...params, testCollectionId: collectionId.value });
|
||||
Message.success(t('caseManagement.featureCase.sortSuccess'));
|
||||
loadCaseList();
|
||||
} catch (error) {
|
||||
|
@ -434,23 +433,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
// 复制用例
|
||||
async function handleCopyCase(record: PlanDetailApiCaseItem) {
|
||||
try {
|
||||
// TODO 联调
|
||||
await associationCaseToPlan({
|
||||
functionalSelectIds: [record.id],
|
||||
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: PlanDetailApiCaseItem, done?: () => void) {
|
||||
|
@ -504,10 +486,15 @@
|
|||
});
|
||||
}
|
||||
|
||||
// 批量修改执行人
|
||||
const batchUpdateExecutorModalVisible = ref(false);
|
||||
const batchUpdateExecutorParams = ref();
|
||||
|
||||
// 处理表格选中后批量操作
|
||||
function handleTableBatch(event: BatchActionParams, params: BatchActionQueryParams) {
|
||||
async function handleTableBatch(event: BatchActionParams, params: BatchActionQueryParams) {
|
||||
tableSelected.value = params?.selectedIds || [];
|
||||
batchParams.value = { ...params, selectIds: params?.selectedIds };
|
||||
const tableParams = await getTableParams(true);
|
||||
switch (event.eventTag) {
|
||||
case 'execute':
|
||||
break;
|
||||
|
@ -515,6 +502,13 @@
|
|||
handleBatchDisassociateCase();
|
||||
break;
|
||||
case 'changeExecutor':
|
||||
batchUpdateExecutorParams.value = {
|
||||
selectIds: tableSelected.value as string[],
|
||||
selectAll: batchParams.value.selectAll,
|
||||
excludeIds: batchParams.value?.excludeIds || [],
|
||||
...tableParams,
|
||||
};
|
||||
batchUpdateExecutorModalVisible.value = true;
|
||||
break;
|
||||
case 'move':
|
||||
break;
|
||||
|
|
|
@ -88,7 +88,18 @@
|
|||
|
||||
const activeFolder = ref<string>('all');
|
||||
const allCount = ref(0);
|
||||
const isExpandAll = ref(false);
|
||||
const isExpandAll = ref<boolean | undefined>(false);
|
||||
|
||||
watch(
|
||||
() => props.treeType,
|
||||
(val) => {
|
||||
if (val === 'COLLECTION') {
|
||||
isExpandAll.value = undefined;
|
||||
} else {
|
||||
isExpandAll.value = false;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
function setActiveFolder(id: string) {
|
||||
activeFolder.value = id;
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
<CaseTable
|
||||
ref="caseTableRef"
|
||||
:plan-id="planId"
|
||||
:tree-type="props.treeType"
|
||||
:modules-count="modulesCount"
|
||||
:module-name="moduleName"
|
||||
:repeat-case="props.repeatCase"
|
||||
|
@ -97,8 +98,10 @@
|
|||
}
|
||||
|
||||
function getCaseTableList() {
|
||||
nextTick(() => {
|
||||
initModules();
|
||||
caseTableRef.value?.loadCaseList();
|
||||
});
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
|
|
|
@ -65,24 +65,17 @@
|
|||
{{ t('common.cancelLink') }}
|
||||
</MsButton>
|
||||
</MsPopconfirm>
|
||||
<a-divider
|
||||
v-if="props.repeatCase"
|
||||
v-permission="['PROJECT_TEST_PLAN:READ+ASSOCIATION']"
|
||||
direction="vertical"
|
||||
:margin="8"
|
||||
></a-divider>
|
||||
<MsButton
|
||||
v-if="props.repeatCase"
|
||||
v-permission="['PROJECT_TEST_PLAN:READ+ASSOCIATION']"
|
||||
type="text"
|
||||
class="!mr-0"
|
||||
@click="handleCopyCase(record)"
|
||||
>
|
||||
{{ t('common.copy') }}
|
||||
</MsButton>
|
||||
</template>
|
||||
</MsBaseTable>
|
||||
<ReportDrawer v-model:visible="reportVisible" :report-id="reportId" />
|
||||
<!-- 批量修改执行人 -->
|
||||
<BatchUpdateExecutorModal
|
||||
v-model:visible="batchUpdateExecutorModalVisible"
|
||||
:count="batchParams.currentSelectCount || tableSelected.length"
|
||||
:params="batchUpdateExecutorParams"
|
||||
:batch-update-executor="batchUpdateApiScenarioExecutor"
|
||||
@load-list="resetSelectorAndCaseList"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -104,11 +97,12 @@
|
|||
import CaseLevel from '@/components/business/ms-case-associate/caseLevel.vue';
|
||||
import ExecuteResult from '@/components/business/ms-case-associate/executeResult.vue';
|
||||
import apiStatus from '@/views/api-test/components/apiStatus.vue';
|
||||
import BatchUpdateExecutorModal from '@/views/test-plan/testPlan/components/batchUpdateExecutorModal.vue';
|
||||
import ReportDrawer from '@/views/test-plan/testPlan/detail/reportDrawer.vue';
|
||||
|
||||
import {
|
||||
associationCaseToPlan,
|
||||
batchDisassociateCase,
|
||||
batchUpdateApiScenarioExecutor,
|
||||
disassociateCase,
|
||||
getPlanDetailApiScenarioList,
|
||||
sortFeatureCase,
|
||||
|
@ -142,7 +136,6 @@
|
|||
offspringIds: string[];
|
||||
planId: string;
|
||||
moduleTree: ModuleTreeNode[];
|
||||
repeatCase: boolean;
|
||||
canEdit: boolean;
|
||||
}>();
|
||||
|
||||
|
@ -421,6 +414,11 @@
|
|||
loadList();
|
||||
}
|
||||
|
||||
function resetSelectorAndCaseList() {
|
||||
resetSelector();
|
||||
loadList();
|
||||
}
|
||||
|
||||
// 拖拽排序
|
||||
async function handleDragChange(params: DragSortParams) {
|
||||
try {
|
||||
|
@ -434,23 +432,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
// 复制用例
|
||||
async function handleCopyCase(record: PlanDetailApiScenarioItem) {
|
||||
try {
|
||||
// TODO 联调
|
||||
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: PlanDetailApiScenarioItem, done?: () => void) {
|
||||
|
@ -506,10 +487,15 @@
|
|||
});
|
||||
}
|
||||
|
||||
// 批量修改执行人
|
||||
const batchUpdateExecutorModalVisible = ref(false);
|
||||
const batchUpdateExecutorParams = ref();
|
||||
|
||||
// 处理表格选中后批量操作
|
||||
function handleTableBatch(event: BatchActionParams, params: BatchActionQueryParams) {
|
||||
async function handleTableBatch(event: BatchActionParams, params: BatchActionQueryParams) {
|
||||
tableSelected.value = params?.selectedIds || [];
|
||||
batchParams.value = { ...params, selectIds: params?.selectedIds };
|
||||
const tableParams = await getTableParams(true);
|
||||
switch (event.eventTag) {
|
||||
case 'execute':
|
||||
break;
|
||||
|
@ -517,6 +503,13 @@
|
|||
handleBatchDisassociateCase();
|
||||
break;
|
||||
case 'changeExecutor':
|
||||
batchUpdateExecutorParams.value = {
|
||||
selectIds: tableSelected.value as string[],
|
||||
selectAll: batchParams.value.selectAll,
|
||||
excludeIds: batchParams.value?.excludeIds || [],
|
||||
...tableParams,
|
||||
};
|
||||
batchUpdateExecutorModalVisible.value = true;
|
||||
break;
|
||||
case 'move':
|
||||
break;
|
||||
|
|
|
@ -78,21 +78,6 @@
|
|||
{{ t('common.cancelLink') }}
|
||||
</MsButton>
|
||||
</MsPopconfirm>
|
||||
<a-divider
|
||||
v-if="props.repeatCase"
|
||||
v-permission="['PROJECT_TEST_PLAN:READ+ASSOCIATION']"
|
||||
direction="vertical"
|
||||
:margin="8"
|
||||
></a-divider>
|
||||
<MsButton
|
||||
v-if="props.repeatCase"
|
||||
v-permission="['PROJECT_TEST_PLAN:READ+ASSOCIATION']"
|
||||
type="text"
|
||||
class="!mr-0"
|
||||
@click="handleCopyCase(record)"
|
||||
>
|
||||
{{ t('common.copy') }}
|
||||
</MsButton>
|
||||
</template>
|
||||
</MsBaseTable>
|
||||
<!-- 批量执行 -->
|
||||
|
@ -120,54 +105,20 @@
|
|||
<ExecuteForm v-model:form="batchExecuteForm" />
|
||||
</a-modal>
|
||||
<!-- 批量修改执行人 -->
|
||||
<a-modal
|
||||
<BatchUpdateExecutorModal
|
||||
v-model:visible="batchUpdateExecutorModalVisible"
|
||||
title-align="start"
|
||||
body-class="p-0"
|
||||
:cancel-button-props="{ disabled: batchLoading }"
|
||||
:ok-loading="batchLoading"
|
||||
:ok-button-props="{ disabled: batchUpdateExecutorDisabled }"
|
||||
:ok-text="t('common.update')"
|
||||
@before-ok="handleBatchUpdateExecutor"
|
||||
@close="resetBatchForm"
|
||||
>
|
||||
<template #title>
|
||||
{{ t('testPlan.featureCase.batchChangeExecutor') }}
|
||||
<div class="text-[var(--color-text-4)]">
|
||||
{{
|
||||
t('testPlan.testPlanIndex.selectedCount', {
|
||||
count: batchParams.currentSelectCount || tableSelected.length,
|
||||
})
|
||||
}}
|
||||
</div>
|
||||
</template>
|
||||
<a-form ref="batchUpdateExecutorFormRef" :model="batchUpdateExecutorForm" layout="vertical">
|
||||
<a-form-item
|
||||
field="userId"
|
||||
:label="t('testPlan.featureCase.executor')"
|
||||
:rules="[{ required: true, message: t('testPlan.featureCase.requestExecutorRequired') }]"
|
||||
asterisk-position="end"
|
||||
class="mb-0"
|
||||
>
|
||||
<MsSelect
|
||||
v-model:modelValue="batchUpdateExecutorForm.userId"
|
||||
mode="static"
|
||||
:placeholder="t('common.pleaseSelect')"
|
||||
:loading="executorLoading"
|
||||
:options="userOptions"
|
||||
:search-keys="['label']"
|
||||
allow-search
|
||||
:count="batchParams.currentSelectCount || tableSelected.length"
|
||||
:params="batchUpdateExecutorParams"
|
||||
:batch-update-executor="batchUpdateCaseExecutor"
|
||||
@load-list="resetSelectorAndCaseList"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onBeforeMount, ref } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { FormInstance, Message, SelectOptionData } from '@arco-design/web-vue';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
|
||||
import { MsAdvanceFilter } from '@/components/pure/ms-advance-filter';
|
||||
import MsButton from '@/components/pure/ms-button/index.vue';
|
||||
|
@ -182,18 +133,16 @@
|
|||
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 MsSelect from '@/components/business/ms-select';
|
||||
import BugCountPopover from './bugCountPopover.vue';
|
||||
import BatchUpdateExecutorModal from '@/views/test-plan/testPlan/components/batchUpdateExecutorModal.vue';
|
||||
import ExecuteForm from '@/views/test-plan/testPlan/detail/featureCase/components/executeForm.vue';
|
||||
|
||||
import {
|
||||
associationCaseToPlan,
|
||||
batchDisassociateCase,
|
||||
batchExecuteCase,
|
||||
batchUpdateCaseExecutor,
|
||||
disassociateCase,
|
||||
getPlanDetailFeatureCaseList,
|
||||
GetTestPlanUsers,
|
||||
runFeatureCase,
|
||||
sortFeatureCase,
|
||||
} from '@/api/modules/test-plan/testPlan';
|
||||
|
@ -205,7 +154,6 @@
|
|||
import { characterLimit } from '@/utils';
|
||||
import { hasAnyPermission } from '@/utils/permission';
|
||||
|
||||
import { ReviewUserItem } from '@/models/caseManagement/caseReview';
|
||||
import { DragSortParams, ModuleTreeNode } from '@/models/common';
|
||||
import type {
|
||||
ExecuteFeatureCaseFormParams,
|
||||
|
@ -231,7 +179,6 @@
|
|||
offspringIds: string[];
|
||||
planId: string;
|
||||
moduleTree: ModuleTreeNode[];
|
||||
repeatCase: boolean;
|
||||
canEdit: boolean;
|
||||
}>();
|
||||
|
||||
|
@ -492,6 +439,11 @@
|
|||
loadList();
|
||||
}
|
||||
|
||||
function resetSelectorAndCaseList() {
|
||||
resetSelector();
|
||||
loadList();
|
||||
}
|
||||
|
||||
// 拖拽排序
|
||||
async function handleDragChange(params: DragSortParams) {
|
||||
try {
|
||||
|
@ -504,22 +456,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
// 复制用例
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// 更新执行结果
|
||||
async function handleEditLastExecResult(record: PlanDetailFeatureCaseItem) {
|
||||
try {
|
||||
|
@ -619,61 +555,19 @@
|
|||
}
|
||||
}
|
||||
|
||||
// 批量修改执行人
|
||||
const batchUpdateExecutorFormRef = ref<FormInstance>();
|
||||
const batchUpdateExecutorModalVisible = ref(false);
|
||||
const batchUpdateExecutorForm = ref<{ userId: string }>({ userId: '' });
|
||||
const batchUpdateExecutorDisabled = computed(() => !batchUpdateExecutorForm.value.userId.length);
|
||||
const userOptions = ref<SelectOptionData[]>([]);
|
||||
const executorLoading = ref(false);
|
||||
async function initUserOptions() {
|
||||
try {
|
||||
executorLoading.value = true;
|
||||
const res = await GetTestPlanUsers(appStore.currentProjectId, '');
|
||||
userOptions.value = res.map((e: ReviewUserItem) => ({ label: e.name, value: e.id }));
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
} finally {
|
||||
executorLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function handleBatchUpdateExecutor() {
|
||||
batchUpdateExecutorFormRef.value?.validate(async (errors) => {
|
||||
if (!errors) {
|
||||
try {
|
||||
batchLoading.value = true;
|
||||
const tableParams = await getTableParams(true);
|
||||
await batchUpdateCaseExecutor({
|
||||
selectIds: tableSelected.value as string[],
|
||||
selectAll: batchParams.value.selectAll,
|
||||
excludeIds: batchParams.value?.excludeIds || [],
|
||||
...tableParams,
|
||||
...batchUpdateExecutorForm.value,
|
||||
});
|
||||
Message.success(t('common.updateSuccess'));
|
||||
resetSelector();
|
||||
loadList();
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
} finally {
|
||||
batchLoading.value = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function resetBatchForm() {
|
||||
batchExecuteForm.value = { ...defaultExecuteForm };
|
||||
batchUpdateExecutorForm.value = { userId: '' };
|
||||
}
|
||||
|
||||
// 批量修改执行人
|
||||
const batchUpdateExecutorModalVisible = ref(false);
|
||||
const batchUpdateExecutorParams = ref();
|
||||
|
||||
// 处理表格选中后批量操作
|
||||
function handleTableBatch(event: BatchActionParams, params: BatchActionQueryParams) {
|
||||
async function handleTableBatch(event: BatchActionParams, params: BatchActionQueryParams) {
|
||||
tableSelected.value = params?.selectedIds || [];
|
||||
batchParams.value = { ...params, selectIds: params?.selectedIds };
|
||||
const tableParams = await getTableParams(true);
|
||||
switch (event.eventTag) {
|
||||
case 'execute':
|
||||
batchExecuteModalVisible.value = true;
|
||||
|
@ -682,6 +576,12 @@
|
|||
handleBatchDisassociateCase();
|
||||
break;
|
||||
case 'changeExecutor':
|
||||
batchUpdateExecutorParams.value = {
|
||||
selectIds: tableSelected.value as string[],
|
||||
selectAll: batchParams.value.selectAll,
|
||||
excludeIds: batchParams.value?.excludeIds || [],
|
||||
...tableParams,
|
||||
};
|
||||
batchUpdateExecutorModalVisible.value = true;
|
||||
break;
|
||||
default:
|
||||
|
@ -707,7 +607,6 @@
|
|||
|
||||
onBeforeMount(() => {
|
||||
loadCaseList();
|
||||
initUserOptions();
|
||||
});
|
||||
|
||||
defineExpose({
|
||||
|
|
|
@ -19,6 +19,16 @@
|
|||
</a-tooltip>
|
||||
</template>
|
||||
<template #headerRight>
|
||||
<a-switch
|
||||
v-model="treeType"
|
||||
size="small"
|
||||
type="line"
|
||||
checked-value="COLLECTION"
|
||||
unchecked-value="MODULE"
|
||||
class="mr-[4px]"
|
||||
@change="loadActiveTabList"
|
||||
/>
|
||||
<span class="mr-[14px]">{{ t('testPlan.testPlanDetail.moduleView') }}</span>
|
||||
<MsButton
|
||||
v-if="hasAnyPermission(['PROJECT_TEST_PLAN:READ+ASSOCIATION']) && detail.status !== 'ARCHIVED'"
|
||||
type="button"
|
||||
|
@ -108,11 +118,10 @@
|
|||
@refresh="initDetail"
|
||||
/>
|
||||
<BugManagement v-if="activeTab === 'defectList'" :can-edit="detail.status !== 'ARCHIVED'" @refresh="initDetail" />
|
||||
<!-- TODO 切换模块视图 -->
|
||||
<ApiCase
|
||||
v-if="activeTab === 'apiCase'"
|
||||
ref="apiCaseRef"
|
||||
tree-type="MODULE"
|
||||
:tree-type="treeType"
|
||||
:repeat-case="detail.repeatCase"
|
||||
:can-edit="detail.status !== 'ARCHIVED'"
|
||||
@refresh="initDetail"
|
||||
|
@ -198,6 +207,7 @@
|
|||
const detail = ref<TestPlanDetail>({
|
||||
...testPlanDefaultDetail,
|
||||
});
|
||||
const treeType = ref<'MODULE' | 'COLLECTION'>('MODULE');
|
||||
|
||||
const countDetail = ref<PassRateCountDetail>({ ...defaultDetailCount });
|
||||
|
||||
|
@ -419,8 +429,7 @@
|
|||
const featureCaseRef = ref<InstanceType<typeof FeatureCase>>();
|
||||
const apiCaseRef = ref<InstanceType<typeof ApiCase>>();
|
||||
const apiScenarioRef = ref<InstanceType<typeof ApiScenario>>();
|
||||
function handleSuccess() {
|
||||
initDetail();
|
||||
function loadActiveTabList() {
|
||||
switch (activeTab.value) {
|
||||
case 'featureCase':
|
||||
featureCaseRef.value?.getCaseTableList();
|
||||
|
@ -435,6 +444,10 @@
|
|||
return '';
|
||||
}
|
||||
}
|
||||
function handleSuccess() {
|
||||
initDetail();
|
||||
loadActiveTabList();
|
||||
}
|
||||
|
||||
onBeforeMount(() => {
|
||||
initDetail();
|
||||
|
|
|
@ -89,6 +89,7 @@ export default {
|
|||
'testPlan.planForm.pickCases': 'Select cases',
|
||||
'testPlan.testPlanDetail.executed': 'Executed',
|
||||
'testPlan.testPlanDetail.generateReport': 'Generate report',
|
||||
'testPlan.testPlanDetail.moduleView': 'Module View',
|
||||
'testPlan.testPlanDetail.successfullyGenerated': 'Successfully generated',
|
||||
'testPlan.bugManagement.bug': 'Defect list',
|
||||
'testPlan.bugManagement.bugName': 'name',
|
||||
|
|
|
@ -84,6 +84,7 @@ export default {
|
|||
'testPlan.planForm.pickCases': '选择用例',
|
||||
'testPlan.testPlanDetail.executed': '已执行',
|
||||
'testPlan.testPlanDetail.generateReport': '生成报告',
|
||||
'testPlan.testPlanDetail.moduleView': '模块视图',
|
||||
'testPlan.testPlanDetail.successfullyGenerated': '生成成功',
|
||||
'testPlan.bugManagement.bug': '缺陷列表',
|
||||
'testPlan.bugManagement.bugName': '名称',
|
||||
|
|
Loading…
Reference in New Issue