fix(测试计划): 修复测试计划&测试计划组&报告部分优先级较高bug

This commit is contained in:
xinxin.wu 2024-06-19 13:02:29 +08:00 committed by 刘瑞斌
parent 9d6f5bd1b0
commit e62149ae10
18 changed files with 135 additions and 30 deletions

View File

@ -90,6 +90,7 @@
(e: 'getModuleCount', params: TableQueryParams): void; (e: 'getModuleCount', params: TableQueryParams): void;
(e: 'refresh'): void; (e: 'refresh'): void;
(e: 'initModules'): void; (e: 'initModules'): void;
(e: 'update:selectedIds'): void;
}>(); }>();
const lastReportStatusListOptions = computed(() => { const lastReportStatusListOptions = computed(() => {
@ -268,7 +269,17 @@
loadCaseList(); loadCaseList();
} }
); );
const innerSelectedIds = defineModel<string[]>('selectedIds', { required: true });
const selectIds = computed(() => {
return [...propsRes.value.selectedKeys];
});
watch(
() => selectIds.value,
(val) => {
innerSelectedIds.value = val;
}
);
watch( watch(
() => props.activeModule, () => props.activeModule,
(val) => { (val) => {

View File

@ -70,6 +70,7 @@
(e: 'getModuleCount', params: TableQueryParams): void; (e: 'getModuleCount', params: TableQueryParams): void;
(e: 'refresh'): void; (e: 'refresh'): void;
(e: 'initModules'): void; (e: 'initModules'): void;
(e: 'update:selectedIds'): void;
}>(); }>();
const requestMethodsOptions = computed(() => { const requestMethodsOptions = computed(() => {
@ -215,6 +216,18 @@
} }
); );
const innerSelectedIds = defineModel<string[]>('selectedIds', { required: true });
const selectIds = computed(() => {
return [...propsRes.value.selectedKeys];
});
watch(
() => selectIds.value,
(val) => {
innerSelectedIds.value = val;
}
);
watch( watch(
() => props.showType, () => props.showType,
(val) => { (val) => {

View File

@ -80,6 +80,7 @@
(e: 'getModuleCount', params: TableQueryParams): void; (e: 'getModuleCount', params: TableQueryParams): void;
(e: 'refresh'): void; (e: 'refresh'): void;
(e: 'initModules'): void; (e: 'initModules'): void;
(e: 'update:selectedIds'): void;
}>(); }>();
const reviewResultOptions = computed(() => { const reviewResultOptions = computed(() => {
@ -277,6 +278,19 @@
} }
); );
const innerSelectedIds = defineModel<string[]>('selectedIds', { required: true });
const selectIds = computed(() => {
return [...propsRes.value.selectedKeys];
});
watch(
() => selectIds.value,
(val) => {
innerSelectedIds.value = val;
}
);
watch( watch(
() => props.activeModule, () => props.activeModule,
() => { () => {

View File

@ -184,6 +184,7 @@
<CaseTable <CaseTable
v-if="associationType === CaseLinkEnum.FUNCTIONAL" v-if="associationType === CaseLinkEnum.FUNCTIONAL"
ref="functionalTableRef" ref="functionalTableRef"
v-model:selectedIds="selectedIds"
:association-type="associateType" :association-type="associateType"
:get-page-api-type="getPageApiType" :get-page-api-type="getPageApiType"
:active-module="activeFolder" :active-module="activeFolder"
@ -200,6 +201,7 @@
<ApiTable <ApiTable
v-if="associationType === CaseLinkEnum.API && showType === 'API'" v-if="associationType === CaseLinkEnum.API && showType === 'API'"
ref="apiTableRef" ref="apiTableRef"
v-model:selectedIds="selectedIds"
:get-page-api-type="getPageApiType" :get-page-api-type="getPageApiType"
:extra-table-params="props.extraTableParams" :extra-table-params="props.extraTableParams"
:association-type="associateType" :association-type="associateType"
@ -217,6 +219,7 @@
<ApiCaseTable <ApiCaseTable
v-if="associationType === CaseLinkEnum.API && showType === 'CASE'" v-if="associationType === CaseLinkEnum.API && showType === 'CASE'"
ref="caseTableRef" ref="caseTableRef"
v-model:selectedIds="selectedIds"
:get-page-api-type="getPageApiType" :get-page-api-type="getPageApiType"
:extra-table-params="props.extraTableParams" :extra-table-params="props.extraTableParams"
:association-type="associateType" :association-type="associateType"
@ -234,6 +237,7 @@
<ScenarioCaseTable <ScenarioCaseTable
v-if="associationType === CaseLinkEnum.SCENARIO" v-if="associationType === CaseLinkEnum.SCENARIO"
ref="scenarioTableRef" ref="scenarioTableRef"
v-model:selectedIds="selectedIds"
:association-type="associateType" :association-type="associateType"
:modules-count="modulesCount" :modules-count="modulesCount"
:active-module="activeFolder" :active-module="activeFolder"
@ -257,7 +261,12 @@
<a-button type="secondary" :disabled="props.confirmLoading" class="mr-[12px]" @click="cancel"> <a-button type="secondary" :disabled="props.confirmLoading" class="mr-[12px]" @click="cancel">
{{ t('common.cancel') }} {{ t('common.cancel') }}
</a-button> </a-button>
<a-button :loading="props.confirmLoading" type="primary" @click="handleConfirm"> <a-button
:loading="props.confirmLoading"
type="primary"
:disabled="!selectedIds.length"
@click="handleConfirm"
>
{{ t('ms.case.associate.associate') }} {{ t('ms.case.associate.associate') }}
</a-button> </a-button>
</slot> </slot>
@ -339,6 +348,7 @@
const associationType = ref<keyof typeof CaseLinkEnum>('FUNCTIONAL'); const associationType = ref<keyof typeof CaseLinkEnum>('FUNCTIONAL');
const activeFolder = ref('all'); const activeFolder = ref('all');
const selectedIds = ref<string[]>([]);
const selectedKeys = computed({ const selectedKeys = computed({
get: () => [activeFolder.value], get: () => [activeFolder.value],
@ -501,6 +511,16 @@
} }
selectPopVisible.value = false; selectPopVisible.value = false;
keyword.value = ''; keyword.value = '';
selectedIds.value = [];
}
);
watch(
() => showType.value,
(val) => {
if (val) {
selectedIds.value = [];
}
} }
); );

View File

@ -77,6 +77,7 @@
(e: 'getModuleCount', params: TableQueryParams): void; (e: 'getModuleCount', params: TableQueryParams): void;
(e: 'refresh'): void; (e: 'refresh'): void;
(e: 'initModules'): void; (e: 'initModules'): void;
(e: 'update:selectedIds'): void;
}>(); }>();
const appStore = useAppStore(); const appStore = useAppStore();
@ -225,6 +226,18 @@
const tableRef = ref<InstanceType<typeof MsBaseTable>>(); const tableRef = ref<InstanceType<typeof MsBaseTable>>();
const innerSelectedIds = defineModel<string[]>('selectedIds', { required: true });
const selectIds = computed(() => {
return [...propsRes.value.selectedKeys];
});
watch(
() => selectIds.value,
(val) => {
innerSelectedIds.value = val;
}
);
watch( watch(
() => props.currentProject, () => props.currentProject,
() => { () => {

View File

@ -25,7 +25,7 @@
> >
<template #tree-slot-title="node"> <template #tree-slot-title="node">
<a-tooltip :content="`${node.name}`" position="tl"> <a-tooltip :content="`${node.name}`" position="tl">
<div class="one-line-text w-[300px] text-[var(--color-text-1)]">{{ node.name }}</div> <div class="one-line-text w-[300px]">{{ node.name }}</div>
</a-tooltip> </a-tooltip>
</template> </template>
</a-tree-select> </a-tree-select>

View File

@ -41,6 +41,15 @@
{{ characterLimit(record.name) }} {{ characterLimit(record.name) }}
</div> </div>
</template> </template>
<template #integrated="{ record }">
<MsTag theme="light" :type="record.integrated ? 'primary' : undefined">
{{
record.integrated
? t('report.detail.testPlanGroup.testGroupReport')
: t('report.detail.testPlanGroup.testReport')
}}
</MsTag>
</template>
<!-- 通过率 --> <!-- 通过率 -->
<template #passRateColumn> <template #passRateColumn>
@ -101,6 +110,7 @@
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 MsTag from '@/components/pure/ms-tag/ms-tag.vue';
import ExecStatus from '@/views/test-plan/report/component/execStatus.vue'; import ExecStatus from '@/views/test-plan/report/component/execStatus.vue';
import ExecutionStatus from '@/views/test-plan/report/component/reportStatus.vue'; import ExecutionStatus from '@/views/test-plan/report/component/reportStatus.vue';
@ -181,10 +191,16 @@
sortDirections: ['ascend', 'descend'], sortDirections: ['ascend', 'descend'],
sorter: true, sorter: true,
}, },
ellipsis: true,
showDrag: false, showDrag: false,
columnSelectorDisabled: true, columnSelectorDisabled: true,
}, },
{
title: 'report.type',
slotName: 'integrated',
dataIndex: 'integrated',
width: 150,
showDrag: true,
},
{ {
title: 'report.plan.name', title: 'report.plan.name',
slotName: 'planName', slotName: 'planName',
@ -192,7 +208,6 @@
width: 200, width: 200,
showInTable: true, showInTable: true,
showTooltip: true, showTooltip: true,
ellipsis: true,
showDrag: true, showDrag: true,
columnSelectorDisabled: true, columnSelectorDisabled: true,
}, },

View File

@ -9,20 +9,12 @@
<template #priority="{ record }"> <template #priority="{ record }">
<caseLevel :case-level="record.priority" /> <caseLevel :case-level="record.priority" />
</template> </template>
<template #[FilterSlotNameEnum.CASE_MANAGEMENT_EXECUTE_RESULT]="{ filterContent }"> <template #[FilterSlotNameEnum.API_TEST_CASE_API_LAST_EXECUTE_STATUS]="{ filterContent }">
<ExecuteResult :execute-result="filterContent.key" /> <ExecutionStatus :module-type="ReportEnum.API_REPORT" :status="filterContent.value" />
</template> </template>
<template #lastExecResult="{ record }"> <template #lastExecResult="{ record }">
<ExecuteResult v-if="record.executeResult" :execute-result="record.executeResult" /> <ExecutionStatus :module-type="ReportEnum.API_REPORT" :status="record.executeResult" />
<span v-else>-</span>
<!-- TOTO 暂时不上 -->
<!-- <MsIcon
v-show="record.lastExecResult !== LastExecuteResults.PENDING"
type="icon-icon_take-action_outlined"
class="ml-[8px] cursor-pointer text-[rgb(var(--primary-5))]"
size="16"
@click="showReport(record)"
/> -->
</template> </template>
</MsBaseTable> </MsBaseTable>
<CaseAndScenarioReportDrawer <CaseAndScenarioReportDrawer
@ -39,15 +31,16 @@
import type { MsTableColumn } from '@/components/pure/ms-table/type'; import type { 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 CaseAndScenarioReportDrawer from '@/views/api-test/components/caseAndScenarioReportDrawer.vue'; import CaseAndScenarioReportDrawer from '@/views/api-test/components/caseAndScenarioReportDrawer.vue';
import ExecutionStatus from '@/views/api-test/report/component/reportStatus.vue';
import { getApiPage, getScenarioPage } from '@/api/modules/test-plan/report'; import { getApiPage, getScenarioPage } from '@/api/modules/test-plan/report';
import { ApiOrScenarioCaseItem } from '@/models/testPlan/report'; import { ApiOrScenarioCaseItem } from '@/models/testPlan/report';
import { ReportEnum } from '@/enums/reportEnum';
import { FilterSlotNameEnum } from '@/enums/tableFilterEnum'; import { FilterSlotNameEnum } from '@/enums/tableFilterEnum';
import { casePriorityOptions } from '@/views/api-test/components/config'; import { casePriorityOptions, lastReportStatusListOptions } from '@/views/api-test/components/config';
import { executionResultMap } from '@/views/case-management/caseManagementFeature/components/utils'; import { executionResultMap } from '@/views/case-management/caseManagementFeature/components/utils';
const props = defineProps<{ const props = defineProps<{
@ -88,10 +81,8 @@
dataIndex: 'executeResult', dataIndex: 'executeResult',
slotName: 'lastExecResult', slotName: 'lastExecResult',
filterConfig: { filterConfig: {
valueKey: 'key', options: lastReportStatusListOptions.value,
labelKey: 'statusText', filterSlotName: FilterSlotNameEnum.API_TEST_CASE_API_LAST_EXECUTE_STATUS,
options: Object.values(executionResultMap),
filterSlotName: FilterSlotNameEnum.CASE_MANAGEMENT_EXECUTE_RESULT,
}, },
width: 150, width: 150,
showDrag: true, showDrag: true,

View File

@ -1,5 +1,5 @@
<template> <template>
<ReportHeader v-if="!props.isDrawer" :detail="detail" :share-id="shareId" /> <ReportHeader v-if="!props.isDrawer" :detail="detail" :share-id="shareId" :is-group="false" />
<div class="analysis-wrapper"> <div class="analysis-wrapper">
<div class="analysis min-w-[238px]"> <div class="analysis min-w-[238px]">
<div class="block-title">{{ t('report.detail.api.reportAnalysis') }}</div> <div class="block-title">{{ t('report.detail.api.reportAnalysis') }}</div>

View File

@ -48,6 +48,7 @@
const props = defineProps<{ const props = defineProps<{
detail: PlanReportDetail; detail: PlanReportDetail;
shareId?: string; shareId?: string;
isGroup?: boolean;
}>(); }>();
const appStore = useAppStore(); const appStore = useAppStore();
@ -63,7 +64,9 @@
projectId: appStore.currentProjectId, projectId: appStore.currentProjectId,
}); });
const { origin } = window.location; const { origin } = window.location;
shareLink.value = `${origin}/#/${RouteEnum.SHARE}/${RouteEnum.SHARE_REPORT_TEST_PLAN}${res.shareUrl}`; shareLink.value = `${origin}/#/${RouteEnum.SHARE}/${RouteEnum.SHARE_REPORT_TEST_PLAN}${res.shareUrl}&type=${
props.isGroup ? 'GROUP' : 'TEST_PLAN'
}`;
if (isSupported) { if (isSupported) {
copy(shareLink.value); copy(shareLink.value);
Message.info(t('bugManagement.detail.shareTip')); Message.info(t('bugManagement.detail.shareTip'));

View File

@ -1,5 +1,5 @@
<template> <template>
<ReportHeader v-if="!props.isDrawer" :detail="detail" :share-id="shareId" /> <ReportHeader v-if="!props.isDrawer" :detail="detail" :share-id="shareId" is-group />
<div class="analysis-wrapper"> <div class="analysis-wrapper">
<div class="analysis min-w-[238px]"> <div class="analysis min-w-[238px]">
<div class="block-title">{{ t('report.detail.api.reportAnalysis') }}</div> <div class="block-title">{{ t('report.detail.api.reportAnalysis') }}</div>

View File

@ -8,7 +8,7 @@
</div> </div>
</template> </template>
<template #headerRight> <template #headerRight>
<PlanDetailHeaderRight :share-id="shareId" :detail="detail" /> <PlanDetailHeaderRight :is-group="props.isGroup" :share-id="shareId" :detail="detail" />
</template> </template>
</MsCard> </MsCard>
</template> </template>
@ -25,6 +25,7 @@
detail: PlanReportDetail; detail: PlanReportDetail;
shareId?: string; shareId?: string;
isDrawer?: boolean; isDrawer?: boolean;
isGroup: boolean;
}>(); }>();
</script> </script>

View File

@ -1,5 +1,6 @@
<template> <template>
<PlanDetail :detail-info="detail" /> <PlanGroupDetail v-if="isGroup" :detail-info="detail" />
<PlanDetail v-else :detail-info="detail" />
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
@ -8,6 +9,7 @@
import { cloneDeep } from 'lodash-es'; import { cloneDeep } from 'lodash-es';
import PlanDetail from '@/views/test-plan/report/detail/component/planDetail.vue'; import PlanDetail from '@/views/test-plan/report/detail/component/planDetail.vue';
import PlanGroupDetail from '@/views/test-plan/report/detail/component/planGroupDetail.vue';
import { getReportDetail, planGetShareHref } from '@/api/modules/test-plan/report'; import { getReportDetail, planGetShareHref } from '@/api/modules/test-plan/report';
import { defaultReportDetail } from '@/config/testPlan'; import { defaultReportDetail } from '@/config/testPlan';
@ -18,7 +20,7 @@
const route = useRoute(); const route = useRoute();
const router = useRouter(); const router = useRouter();
const reportId = ref<string>(route.query.id as string); const reportId = ref<string>(route.query.id as string);
const isGroup = computed(() => route.query.type === 'GROUP');
const detail = ref<PlanReportDetail>(cloneDeep(defaultReportDetail)); const detail = ref<PlanReportDetail>(cloneDeep(defaultReportDetail));
async function getShareDetail() { async function getShareDetail() {

View File

@ -47,4 +47,6 @@ export default {
'report.detail.testPlanGroup.result': '结果', 'report.detail.testPlanGroup.result': '结果',
'report.detail.testPlanGroup.useCasesCount': '用例数', 'report.detail.testPlanGroup.useCasesCount': '用例数',
'report.detail.testPlanGroup.viewReport': '查看报告', 'report.detail.testPlanGroup.viewReport': '查看报告',
'report.detail.testPlanGroup.testGroupReport': '测试组报告',
'report.detail.testPlanGroup.testReport': '测试报告',
}; };

View File

@ -122,6 +122,7 @@
theme="outline" theme="outline"
class="ml-2" class="ml-2"
:tooltip-disabled="true" :tooltip-disabled="true"
@click="handleScheduledTask(record)"
>{{ t('testPlan.testPlanIndex.timing') }}</MsTag >{{ t('testPlan.testPlanIndex.timing') }}</MsTag
> >
<template #content> <template #content>
@ -1005,6 +1006,7 @@
name: TestPlanRouteEnum.TEST_PLAN_INDEX_DETAIL, name: TestPlanRouteEnum.TEST_PLAN_INDEX_DETAIL,
query: { query: {
id, id,
type: 'featureCase',
}, },
}); });
} else { } else {
@ -1251,6 +1253,9 @@
const planSourceId = ref<string>(); const planSourceId = ref<string>();
const planType = ref<keyof typeof testPlanTypeEnum>(testPlanTypeEnum.TEST_PLAN); const planType = ref<keyof typeof testPlanTypeEnum>(testPlanTypeEnum.TEST_PLAN);
function handleScheduledTask(record: TestPlanItem) { function handleScheduledTask(record: TestPlanItem) {
if (!hasAnyPermission(['PROJECT_TEST_PLAN:READ+EXECUTE'])) {
return;
}
planType.value = record.type; planType.value = record.type;
planSourceId.value = record.id; planSourceId.value = record.id;
taskForm.value = defaultCountDetailMap.value[record.id]?.scheduleConfig; taskForm.value = defaultCountDetailMap.value[record.id]?.scheduleConfig;

View File

@ -4,6 +4,7 @@
class="ms-modal-form ms-modal-small" class="ms-modal-form ms-modal-small"
title-align="start" title-align="start"
:mask-closable="false" :mask-closable="false"
@close="handleCancel"
> >
<template #title> <template #title>
{{ {{

View File

@ -481,6 +481,9 @@
} }
onBeforeMount(() => { onBeforeMount(() => {
if (route.query.type === 'featureCase') {
activeTab.value = 'featureCase';
}
initDetail(); initDetail();
initPlanTree(); initPlanTree();
}); });

View File

@ -36,7 +36,14 @@
}, },
}" }"
allow-search allow-search
/> :filter-tree-node="filterTreeNode"
>
<template #tree-slot-title="node">
<a-tooltip :content="`${node.name}`" position="tl">
<div class="one-line-text w-[300px]">{{ node.name }}</div>
</a-tooltip>
</template>
</a-tree-select>
</a-form-item> </a-form-item>
<a-form-item field="tags" :label="t('common.tag')"> <a-form-item field="tags" :label="t('common.tag')">
<MsTagsInput v-model:modelValue="form.tags"></MsTagsInput> <MsTagsInput v-model:modelValue="form.tags"></MsTagsInput>
@ -54,7 +61,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue'; import { ref } from 'vue';
import { FormInstance, Message } from '@arco-design/web-vue'; import { FormInstance, Message, TreeNodeData } from '@arco-design/web-vue';
import { cloneDeep } from 'lodash-es'; import { cloneDeep } from 'lodash-es';
import MsTagsInput from '@/components/pure/ms-tags-input/index.vue'; import MsTagsInput from '@/components/pure/ms-tags-input/index.vue';
@ -160,6 +167,10 @@
} }
} }
function filterTreeNode(searchValue: string, nodeData: TreeNodeData) {
return (nodeData as ModuleTreeNode).name.toLowerCase().indexOf(searchValue.toLowerCase()) > -1;
}
const okText = computed(() => { const okText = computed(() => {
return props.planGroupId ? t('common.update') : t('common.create'); return props.planGroupId ? t('common.update') : t('common.create');
}); });