feat(测试计划): 测试计划报告详情明细列表展示调整不包含联调

This commit is contained in:
xinxin.wu 2024-09-06 18:57:49 +08:00 committed by 刘瑞斌
parent 186ca8edf0
commit fa5a4200bb
16 changed files with 562 additions and 112 deletions

View File

@ -157,6 +157,9 @@
() => props.autoFocus,
(val) => {
editor.value?.setOptions({ autofocus: val });
if (val) {
editor.value?.chain().focus();
}
},
{
immediate: true,

View File

@ -49,6 +49,7 @@
<MsCheckbox
v-if="attrs.selectorType === 'checkbox'"
:value="getChecked(record)"
:disabled="getDisabled(record)"
:indeterminate="getIndeterminate(record)"
@click.stop
@change="rowSelectChange(record)"
@ -233,7 +234,11 @@
</template>
<template #expand-icon="{ expanded, record }">
<!-- @desc: 这里为了树级别展开折叠如果子级别children不存在不展示展开折叠所以原本组件的隐藏掉改成自定义便于控制展示隐藏 -->
<slot v-if="record.children && record.children.length" name="expand-icon" v-bind="{ expanded, record }">
<slot
v-if="(record.children && record.children.length) || $attrs.expandable"
name="expand-icon"
v-bind="{ expanded, record }"
>
<div
:class="`${
expanded ? 'expanded-border bg-[rgb(var(--primary-1))]' : 'not-expanded-border bg-[var(--color-text-n8)]'
@ -877,6 +882,10 @@
return false;
}
function getDisabled(record: TableData) {
const disableKey = (attrs?.rowSelectionDisabledConfig as MsTableRowSelectionDisabledConfig)?.disabledKey;
return disableKey ? record[disableKey] : false;
}
onMounted(async () => {
await initColumn();
batchLeft.value = getBatchLeft();

View File

@ -17,12 +17,13 @@
/>
</template>
<template v-if="!props.onlyPageSize">
<div class="flex-col">
<div class="mt-[16px] flex-col pl-[14px]">
<div v-for="item in nonSortColumn" :key="item.dataIndex" class="column-item">
<div>{{ t((item.title || item.columnTitle) as string) }}</div>
<a-switch
v-if="item.slotName !== SpecialColumnEnum.OPERATION"
v-model="item.showInTable"
:disabled="item.columnSelectorDisabled"
size="small"
type="line"
@change="handleSwitchChange"
@ -147,9 +148,9 @@
}
.column-item {
display: flex;
padding: 8px;
flex-flow: row nowrap;
justify-content: space-between;
align-items: center;
@apply flex flex-nowrap items-center justify-between;
&:hover {
border-radius: 6px;
background: var(--color-text-n9);

View File

@ -5,15 +5,16 @@
<div
v-for="item in option"
:key="item"
class="flex h-[28px] w-[49px] cursor-pointer items-center justify-center rounded-[4px]"
class="flex h-[28px] flex-1 cursor-pointer items-center justify-center rounded-[4px]"
:class="{
'w-[75px] bg-[var(--color-bg-1)] text-[rgb(var(--primary-5))]': item === props.modelValue,
'ml-[4px]': props.modelValue === 10,
'mr-[4px]': props.modelValue === 50,
'ml-[2px]': props.modelValue === 10,
'mr-[2px]': props.modelValue === 50,
}"
@click="handleClick(item)"
>{{ item }}</div
>
{{ item }}
</div>
</div>
</template>

View File

@ -38,6 +38,7 @@ export interface MsTableRowSelectionDisabledConfig {
disabledChildren?: boolean; // 是否禁用子节点选择
parentKey?: string; // 父节点Key
checkStrictly?: boolean; // 父子节点选择是否关联,关联存在半选状态,不关联不存在,选择父即父,选择子即子
disabledKey?: string; // 是否禁用选择
}
export interface MsTableColumnData extends TableColumnData {

View File

@ -92,6 +92,9 @@ export enum TableKeyEnum {
ASSOCIATE_CASE_API_SCENARIO = 'associateCaseApiScenario',
ASSOCIATE_CASE = 'associateCase',
JSON_SCHEMA = 'jsonSchema',
TEST_PLAN_REPORT_FUNCTIONAL_TABLE = 'testPlanReportFunctionTable',
TEST_PLAN_REPORT_API_TABLE = 'testPlanReportApiTable',
TEST_PLAN_REPORT_SCENARIO_TABLE = 'testPlanReportScenarioTable',
}
// 具有特殊功能的列

View File

@ -1019,10 +1019,12 @@
emit(
'init',
{
...tableParams,
moduleIds: tableParams.moduleIds,
current: propsRes.value.msPagination?.current,
pageSize: propsRes.value.msPagination?.pageSize,
filter: propsRes.value.filter,
projectId: currentProjectId.value,
keyword: keyword.value,
},
refreshModule
);

View File

@ -9,15 +9,15 @@
class="mr-2"
@click="associatedDemand"
>
{{ t('caseManagement.featureCase.associatedDemand') }}</a-button
>
{{ t('caseManagement.featureCase.associatedDemand') }}
</a-button>
<a-button
v-permission="['FUNCTIONAL_CASE:READ+ADD', 'FUNCTIONAL_CASE:READ+UPDATE', 'FUNCTIONAL_CASE:READ+DELETE']"
type="outline"
@click="addDemand"
>
{{ t('caseManagement.featureCase.addDemand') }}</a-button
>
{{ t('caseManagement.featureCase.addDemand') }}
</a-button>
</div>
<a-input-search
@ -28,7 +28,7 @@
@search="searchList"
@press-enter="searchList"
@clear="searchList"
></a-input-search>
/>
</div>
<AssociatedDemandTable
ref="demandRef"
@ -64,10 +64,10 @@
@cancel="handleDrawerCancel"
>
<div class="flex items-center justify-between">
<div
><span class="font-medium">{{ platName }}</span
><span class="ml-1 text-[var(--color-text-4)]">({{ propsRes?.msPagination?.total || 0 }})</span></div
>
<div>
<span class="font-medium">{{ platName }}</span>
<span class="ml-1 text-[var(--color-text-4)]">({{ propsRes?.msPagination?.total || 0 }})</span>
</div>
<a-input-search
v-model="platformKeyword"
:max-length="255"
@ -213,6 +213,7 @@
showSetting: false,
rowSelectionDisabledConfig: {
checkStrictly: false,
disabledKey: 'disabled',
},
});
@ -242,7 +243,7 @@
const tableRef = ref();
const initData = async () => {
tableRef.value?.initColumn(fullColumns);
setLoadListParams({ keyword: platformKeyword.value, projectId: currentProjectId.value });
setLoadListParams({ keyword: platformKeyword.value, projectId: currentProjectId.value, caseId: props.caseId });
loadList();
};
@ -254,6 +255,7 @@
current: 1,
pageSize: 10,
projectId: currentProjectId.value,
caseId: props.caseId,
});
customFields.value = (res.data.customHeaders || []).map((item: any) => {
return {

View File

@ -132,7 +132,6 @@
<div class="error-report">
{{ t('project.menu.rule.hasBeenEnabled') }}
<span class="text-[rgb(var(--primary-5))]" @click="pushFar(true)">
<!-- TODO 待测试字段后台还没有补充 -->
{{ allValueMap['ENABLE_FAKE_ERROR_NUM'] || 0 }}
</span>
{{ t('project.menu.rule.bar') }}

View File

@ -1,39 +1,41 @@
<template>
<MsBaseTable v-bind="currentCaseTable.propsRes.value" v-on="currentCaseTable.propsEvent.value">
<template #num="{ record }">
<MsButton type="text" @click="toDetail(record)">{{ record.num }}</MsButton>
</template>
<template #[FilterSlotNameEnum.CASE_MANAGEMENT_CASE_LEVEL]="{ filterContent }">
<caseLevel :case-level="filterContent.value" />
</template>
<template #priority="{ record }">
<caseLevel :case-level="record.priority" />
</template>
<template #[FilterSlotNameEnum.API_TEST_CASE_API_LAST_EXECUTE_STATUS]="{ filterContent }">
<ExecutionStatus :module-type="ReportEnum.API_REPORT" :status="filterContent.value" />
</template>
<div :class="`${props.enabledTestSet ? 'test-set-wrapper test-set-cell' : ''}`">
<MsBaseTable v-bind="currentCaseTable.propsRes.value" v-on="currentCaseTable.propsEvent.value">
<template #num="{ record }">
<MsButton type="text" @click="toDetail(record)">{{ record.num }}</MsButton>
</template>
<template #[FilterSlotNameEnum.CASE_MANAGEMENT_CASE_LEVEL]="{ filterContent }">
<caseLevel :case-level="filterContent.value" />
</template>
<template #priority="{ record }">
<caseLevel :case-level="record.priority" />
</template>
<template #[FilterSlotNameEnum.API_TEST_CASE_API_LAST_EXECUTE_STATUS]="{ filterContent }">
<ExecutionStatus :module-type="ReportEnum.API_REPORT" :status="filterContent.value" />
</template>
<template #lastExecResult="{ record }">
<ExecutionStatus
:module-type="ReportEnum.API_REPORT"
:status="record.executeResult"
:class="[!record.executeResult ? '' : 'cursor-pointer']"
@click="showReport(record)"
/>
</template>
</MsBaseTable>
<CaseAndScenarioReportDrawer
v-model:visible="reportVisible"
:report-id="apiReportId"
do-not-show-share
:is-scenario="props.activeType === ReportCardTypeEnum.SCENARIO_CASE_DETAIL"
:report-detail="
props.activeType === ReportCardTypeEnum.SCENARIO_CASE_DETAIL ? reportScenarioDetail : reportCaseDetail
"
:get-report-step-detail="
props.activeType === ReportCardTypeEnum.SCENARIO_CASE_DETAIL ? reportStepDetail : reportCaseStepDetail
"
/>
<template #lastExecResult="{ record }">
<ExecutionStatus
:module-type="ReportEnum.API_REPORT"
:status="record.executeResult"
:class="[!record.executeResult ? '' : 'cursor-pointer']"
@click="showReport(record)"
/>
</template>
</MsBaseTable>
<CaseAndScenarioReportDrawer
v-model:visible="reportVisible"
:report-id="apiReportId"
do-not-show-share
:is-scenario="props.activeType === ReportCardTypeEnum.SCENARIO_CASE_DETAIL"
:report-detail="
props.activeType === ReportCardTypeEnum.SCENARIO_CASE_DETAIL ? reportScenarioDetail : reportCaseDetail
"
:get-report-step-detail="
props.activeType === ReportCardTypeEnum.SCENARIO_CASE_DETAIL ? reportStepDetail : reportCaseStepDetail
"
/>
</div>
</template>
<script setup lang="ts" async>
@ -54,42 +56,54 @@
reportStepDetail,
} from '@/api/modules/test-plan/report';
import useOpenNewPage from '@/hooks/useOpenNewPage';
import useTableStore from '@/hooks/useTableStore';
import { ApiOrScenarioCaseItem } from '@/models/testPlan/report';
import type { PlanDetailApiCaseItem } from '@/models/testPlan/testPlan';
import { ReportEnum } from '@/enums/reportEnum';
import { ApiTestRouteEnum } from '@/enums/routeEnum';
import { TableKeyEnum } from '@/enums/tableEnum';
import { FilterSlotNameEnum } from '@/enums/tableFilterEnum';
import { ReportCardTypeEnum } from '@/enums/testPlanReportEnum';
import { casePriorityOptions, lastReportStatusListOptions } from '@/views/api-test/components/config';
import { detailTableExample } from '@/views/test-plan/report/detail/component/reportConfig';
const tableStore = useTableStore();
const { openNewPage } = useOpenNewPage();
const props = defineProps<{
reportId: string;
enabledTestSet: boolean;
shareId?: string;
activeType: ReportCardTypeEnum;
isPreview?: boolean;
isGroup?: boolean;
}>();
const innerKeyword = defineModel<string>('keyword', {
required: true,
});
const staticColumns: MsTableColumn = [
{
title: 'ID',
dataIndex: 'num',
slotName: 'num',
sortIndex: 1,
fixed: 'left',
width: 100,
showInTable: true,
showTooltip: true,
columnSelectorDisabled: true,
},
{
title: 'common.name',
dataIndex: 'name',
width: 150,
showTooltip: true,
showInTable: true,
columnSelectorDisabled: true,
},
{
title: 'report.detail.level',
@ -100,6 +114,7 @@
filterSlotName: FilterSlotNameEnum.CASE_MANAGEMENT_CASE_LEVEL,
},
width: 150,
showInTable: true,
showDrag: true,
},
{
@ -111,6 +126,7 @@
filterSlotName: FilterSlotNameEnum.API_TEST_CASE_API_LAST_EXECUTE_STATUS,
},
width: 150,
showInTable: true,
showDrag: true,
},
];
@ -120,6 +136,8 @@
dataIndex: 'planName',
showTooltip: true,
width: 200,
showInTable: true,
showDrag: true,
},
];
const lastStaticColumns: MsTableColumn = [
@ -128,6 +146,7 @@
dataIndex: 'moduleName',
showTooltip: true,
width: 200,
showInTable: true,
showDrag: true,
},
{
@ -135,6 +154,7 @@
dataIndex: 'executeUser',
showTooltip: true,
width: 130,
showInTable: true,
showDrag: true,
},
{
@ -142,8 +162,15 @@
dataIndex: 'bugCount',
slotName: 'bugCount',
width: 100,
showInTable: true,
showDrag: true,
},
{
title: '',
slotName: 'operation',
dataIndex: 'operation',
width: 30,
},
];
const columns = computed(() => {
@ -153,17 +180,30 @@
return [...staticColumns, ...lastStaticColumns];
});
const keyMap: Record<string, any> = {
API_CASE_DETAIL: TableKeyEnum.TEST_PLAN_REPORT_API_TABLE,
SCENARIO_CASE_DETAIL: TableKeyEnum.TEST_PLAN_REPORT_SCENARIO_TABLE,
};
const useApiTable = useTable(getApiPage, {
tableKey: TableKeyEnum.TEST_PLAN_REPORT_API_TABLE,
scroll: { x: '100%' },
columns: columns.value,
showSelectorAll: false,
showSetting: false,
heightUsed: 236,
showSetting: props.isPreview,
isSimpleSetting: true,
paginationSize: 'mini',
});
const useScenarioTable = useTable(getScenarioPage, {
tableKey: TableKeyEnum.TEST_PLAN_REPORT_SCENARIO_TABLE,
scroll: { x: '100%' },
columns: columns.value,
showSelectorAll: false,
showSetting: false,
showSetting: props.isPreview,
heightUsed: 236,
isSimpleSetting: true,
paginationSize: 'mini',
});
const currentCaseTable = computed(() => {
@ -174,6 +214,7 @@
currentCaseTable.value.setLoadListParams({
reportId: props.reportId,
shareId: props.shareId ?? undefined,
keyword: innerKeyword.value,
});
currentCaseTable.value.loadList();
}
@ -218,4 +259,12 @@
immediate: true,
}
);
defineExpose({
loadCaseList,
});
await tableStore.initColumn(keyMap[props.activeType], columns.value, 'drawer');
</script>
<style lang="less" scoped></style>

View File

@ -1,33 +1,48 @@
<template>
<MsBaseTable v-bind="propsRes" v-on="propsEvent">
<template #num="{ record }">
<MsButton :disabled="!props.isPreview" type="text" @click="toDetail(record)">{{ record.num }}</MsButton>
</template>
<template #caseLevel="{ record }">
<CaseLevel :case-level="record.priority" />
</template>
<template #[FilterSlotNameEnum.CASE_MANAGEMENT_EXECUTE_RESULT]="{ filterContent }">
<ExecuteResult :execute-result="filterContent.key" />
</template>
<template #lastExecResult="{ record }">
<ExecuteResult :execute-result="record.executeResult" />
<MsButton class="ml-[8px]" :disabled="!props.isPreview || !record.reportId" @click="openExecuteHistory(record)">{{
t('common.detail')
}}</MsButton>
</template>
</MsBaseTable>
<MsDrawer
v-model:visible="showDetailVisible"
:title="t('common.detail')"
:width="1200"
:footer="false"
no-content-padding
unmount-on-close
>
<div class="p-[16px]">
<ExecutionHistory show-step-result :loading="executeLoading" :execute-list="executeList" />
</div>
</MsDrawer>
<div :class="`${props.enabledTestSet ? 'test-set-wrapper test-set-cell' : ''}`">
<MsBaseTable v-bind="propsRes" v-on="propsEvent">
<template #num="{ record }">
<MsButton :disabled="!props.isPreview" type="text" @click="toDetail(record)">{{ record.num }}</MsButton>
</template>
<template #caseLevel="{ record }">
<CaseLevel :case-level="record.priority" />
</template>
<template #[FilterSlotNameEnum.CASE_MANAGEMENT_EXECUTE_RESULT]="{ filterContent }">
<ExecuteResult :execute-result="filterContent.key" />
</template>
<template #lastExecResult="{ record }">
<ExecuteResult :execute-result="record.executeResult" />
<MsButton class="ml-[8px]" :disabled="!props.isPreview || !record.reportId" @click="openExecuteHistory(record)">
{{ t('common.detail') }}
</MsButton>
</template>
<template #expand-icon="{ record, expanded }">
<!-- TODO 待联调 -->
<div
class="flex items-end gap-[2px] text-[var(--color-text-4)]"
:class="[
expanded ? '!text-[rgb(var(--primary-5))]' : '',
record.testSetCount === 0 ? 'cursor-not-allowed' : '',
]"
>
<MsIcon type="icon-icon_split_turn-down_arrow" />
<div v-if="record.testSetCount" class="break-keep">{{ record.testSetCount }}</div>
</div>
</template>
</MsBaseTable>
<MsDrawer
v-model:visible="showDetailVisible"
:title="t('common.detail')"
:width="1200"
:footer="false"
no-content-padding
unmount-on-close
>
<div class="p-[16px]">
<ExecutionHistory show-step-result :loading="executeLoading" :execute-list="executeList" />
</div>
</MsDrawer>
</div>
</template>
<script setup lang="ts">
@ -50,19 +65,24 @@
} from '@/api/modules/test-plan/report';
import { useI18n } from '@/hooks/useI18n';
import useOpenNewPage from '@/hooks/useOpenNewPage';
import useTableStore from '@/hooks/useTableStore';
import { FeatureCaseItem } from '@/models/testPlan/report';
import type { ExecuteHistoryItem } from '@/models/testPlan/testPlan';
import { CaseManagementRouteEnum } from '@/enums/routeEnum';
import { TableKeyEnum } from '@/enums/tableEnum';
import { FilterSlotNameEnum } from '@/enums/tableFilterEnum';
import { ReportCardTypeEnum } from '@/enums/testPlanReportEnum';
import { executionResultMap } from '@/views/case-management/caseManagementFeature/components/utils';
import { detailTableExample } from '@/views/test-plan/report/detail/component/reportConfig';
const tableStore = useTableStore();
const { openNewPage } = useOpenNewPage();
const props = defineProps<{
enabledTestSet: boolean; //
reportId: string;
shareId?: string;
isPreview?: boolean;
@ -70,6 +90,10 @@
}>();
const { t } = useI18n();
const innerKeyword = defineModel<string>('keyword', {
required: true,
});
const sortableConfig = computed<TableSortable | undefined>(() => {
return props.isPreview
? {
@ -85,11 +109,10 @@
dataIndex: 'num',
slotName: 'num',
sortIndex: 1,
sortable: cloneDeep(sortableConfig.value),
fixed: 'left',
width: 100,
ellipsis: true,
width: 150,
showTooltip: true,
showInTable: true,
columnSelectorDisabled: true,
},
{
title: 'case.caseName',
@ -97,6 +120,8 @@
showTooltip: true,
sortable: cloneDeep(sortableConfig.value),
width: 180,
showInTable: true,
columnSelectorDisabled: true,
},
{
title: 'common.executionResult',
@ -108,6 +133,8 @@
options: props.isPreview ? Object.values(executionResultMap) : [],
filterSlotName: FilterSlotNameEnum.CASE_MANAGEMENT_EXECUTE_RESULT,
},
showInTable: true,
showDrag: true,
width: 150,
},
];
@ -117,12 +144,16 @@
dataIndex: 'moduleName',
ellipsis: true,
showTooltip: true,
showInTable: true,
showDrag: true,
width: 200,
},
{
title: 'case.caseLevel',
dataIndex: 'priority',
slotName: 'caseLevel',
showInTable: true,
showDrag: true,
width: 120,
},
@ -130,13 +161,23 @@
title: 'testPlan.featureCase.executor',
dataIndex: 'executeUser',
showTooltip: true,
showInTable: true,
showDrag: true,
width: 150,
},
{
title: 'testPlan.featureCase.bugCount',
dataIndex: 'bugCount',
showInTable: true,
showDrag: true,
width: 100,
},
{
title: '',
slotName: 'operation',
dataIndex: 'operation',
width: 30,
},
];
const testPlanNameColumns: MsTableColumn = [
@ -144,6 +185,8 @@
title: 'report.plan.name',
dataIndex: 'planName',
showTooltip: true,
showInTable: true,
showDrag: true,
width: 200,
},
];
@ -158,15 +201,20 @@
const reportFeatureCaseList = () => {
return !props.shareId ? getReportFeatureCaseList : getReportShareFeatureCaseList;
};
const { propsRes, propsEvent, loadList, setLoadListParams } = useTable(reportFeatureCaseList(), {
scroll: { x: '100%' },
tableKey: TableKeyEnum.TEST_PLAN_REPORT_FUNCTIONAL_TABLE,
columns: columns.value,
heightUsed: 20,
scroll: { x: '100%' },
heightUsed: 236,
showSetting: props.isPreview,
isSimpleSetting: true,
paginationSize: 'mini',
showSelectorAll: false,
});
async function loadCaseList() {
setLoadListParams({ reportId: props.reportId, shareId: props.shareId ?? undefined });
setLoadListParams({ reportId: props.reportId, keyword: innerKeyword.value, shareId: props.shareId ?? undefined });
loadList();
}
//
@ -218,4 +266,12 @@
showDetailVisible.value = true;
getExecuteStep();
}
defineExpose({
loadCaseList,
});
await tableStore.initColumn(TableKeyEnum.TEST_PLAN_REPORT_FUNCTIONAL_TABLE, columns.value, 'drawer');
</script>
<style lang="less" scoped></style>

View File

@ -12,6 +12,7 @@
:preview-url="`${ReportPlanPreviewImageUrl}/${appStore.currentProjectId}`"
class="mt-[8px]"
:editable="props.canEdit"
:auto-focus="autoFocus"
@click="handleRichClick"
@update="emit('handleSetSave')"
/>
@ -67,12 +68,14 @@
}>();
const innerSummary = ref(props.richText);
const autoFocus = ref<boolean>(false);
function handleCancel() {
autoFocus.value = false;
emit('cancel');
}
function handleUpdateReportDetail() {
autoFocus.value = false;
emit('updateSummary', innerSummary.value);
}
@ -134,9 +137,10 @@
}
const msRichTextRef = ref<InstanceType<typeof MsRichText>>();
function handleRichClick() {
if (!props.shareId) {
msRichTextRef.value?.focus();
autoFocus.value = true;
emit('handleClick');
}
}

View File

@ -0,0 +1,199 @@
<template>
<MsBaseTable
v-if="enabledTestSet"
v-bind="propsRes"
:expanded-keys="expandedKeys"
:expandable="expandable"
v-on="propsEvent"
@expand="(record) => handleExpand(record.id as string)"
>
<template #expand-icon="{ record, expanded }">
<div
class="flex items-end gap-[2px] text-[var(--color-text-4)]"
:class="[
expanded ? '!text-[rgb(var(--primary-5))]' : '',
record.testSetCount === 0 ? 'cursor-not-allowed' : '',
]"
>
<MsIcon type="icon-icon_split_turn-down_arrow" />
<div v-if="record.testSetCount" class="break-keep">{{ record.testSetCount }}</div>
</div>
</template>
</MsBaseTable>
</template>
<script setup lang="ts">
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
import type { MsTableColumn } from '@/components/pure/ms-table/type';
import useTable from '@/components/pure/ms-table/useTable';
import ApiAndScenarioTable from '@/views/test-plan/report/detail/component/system-card/apiAndScenarioTable.vue';
import FeatureCaseTable from '@/views/test-plan/report/detail/component/system-card/featureCaseTable.vue';
import { getReportFeatureCaseList, getReportShareFeatureCaseList } from '@/api/modules/test-plan/report';
import { ReportCardTypeEnum } from '@/enums/testPlanReportEnum';
import { detailTableExample } from '@/views/test-plan/report/detail/component/reportConfig';
const props = defineProps<{
enabledTestSet: boolean;
activeType: ReportCardTypeEnum; //
keyword: string;
reportId: string;
shareId?: string;
isPreview?: boolean;
isGroup?: boolean;
}>();
const expandedKeys = ref<string[]>([]);
const expandable = reactive({
title: '',
width: 30,
expandedRowRender: (record: Record<string, any>) => {
if (record.testSetCount) {
if (props.activeType === ReportCardTypeEnum.FUNCTIONAL_DETAIL) {
return h(FeatureCaseTable, {
keyword: props.keyword,
reportId: props.reportId,
shareId: props.shareId,
isPreview: props.isPreview,
isGroup: props.isGroup,
enabledTestSet: props.enabledTestSet,
});
}
if (
props.activeType === ReportCardTypeEnum.API_CASE_DETAIL ||
props.activeType === ReportCardTypeEnum.SCENARIO_CASE_DETAIL
) {
return h(ApiAndScenarioTable, {
activeType: props.activeType,
keyword: props.keyword,
reportId: props.reportId,
shareId: props.shareId,
isPreview: props.isPreview,
isGroup: props.isGroup,
enabledTestSet: props.enabledTestSet,
});
}
}
return undefined;
},
});
const handleExpand = (rowKey: string) => {
if (expandedKeys.value.includes(rowKey)) {
expandedKeys.value = expandedKeys.value.filter((key) => key !== rowKey);
} else {
expandedKeys.value = [...expandedKeys.value, rowKey];
}
};
const testSetColumns: MsTableColumn = [
{
title: 'ms.case.associate.testSet',
dataIndex: 'testSetName',
showInTable: true,
showDrag: true,
width: 200,
},
{
title: 'report.plan.name',
dataIndex: 'planName',
showInTable: true,
showDrag: true,
width: 300,
},
];
const reportFeatureCaseList = () => {
return !props.shareId ? getReportFeatureCaseList : getReportShareFeatureCaseList;
};
const { propsRes, propsEvent, loadList, setLoadListParams } = useTable(reportFeatureCaseList(), {
columns: testSetColumns,
scroll: { x: '100%' },
heightUsed: 320,
showSelectorAll: false,
});
// TODO
async function loadCaseList() {
if (props.enabledTestSet) {
expandedKeys.value = [];
}
// setLoadListParams({ reportId: props.reportId, keyword: props.keyword, shareId: props.shareId ?? undefined });
// loadList();
}
watch(
[() => props.reportId, () => props.isPreview],
() => {
if (props.reportId && props.isPreview) {
// TODO
// loadCaseList();
} else {
propsRes.value.data = detailTableExample[ReportCardTypeEnum.FUNCTIONAL_DETAIL];
}
},
{
immediate: true,
}
);
defineExpose({
loadCaseList,
});
</script>
<style scoped lang="less">
:deep(.arco-table-tr-expand .arco-table-td) {
border-bottom: 1px solid var(--color-text-n8) !important;
background: none;
}
:deep(.arco-table-tr-expand) {
& .arco-table-td:last-child {
border-bottom: 1px solid transparent !important;
background: none;
}
}
:deep(.arco-table-tr-expand) {
background: var(--color-text-n9) !important;
}
:deep(.arco-table .arco-table-expand-btn:hover) {
border-color: transparent;
}
.ms-table-expand :deep(.arco-scrollbar-container + .arco-scrollbar-track-direction-vertical) {
left: 0 !important;
}
:deep(.arco-table-content + .arco-scrollbar-track-direction-vertical .arco-scrollbar-thumb-direction-vertical) {
height: 0 !important;
}
:deep(.arco-table-tr) {
.arco-table-th {
.arco-table-cell {
padding: 8px 16px !important;
}
}
}
:deep(.arco-table-tr-expand .arco-table-cell) {
padding: 12px !important;
}
:deep(.arco-table .arco-table-tr-expand .arco-table-td .arco-table) {
margin: 0 !important;
}
:deep(.arco-table-tr-expand):hover {
.arco-table-tr {
.arco-table-td {
background: none !important;
}
.arco-table-col-fixed-left.arco-table-col-fixed-left-last {
background: none !important;
}
.arco-table-td.arco-table-col-fixed-left::before {
background: none !important;
}
}
}
:deep(.arco-table-tr-expand .arco-table-td .arco-table .arco-table-td) {
border-color: var(--color-text-n8) !important;
}
</style>

View File

@ -0,0 +1,113 @@
<template>
<div class="mb-[8px] flex items-center justify-between">
<div class="font-medium"> {{ props.label }} </div>
<div v-if="props.isPreview" class="flex items-center">
<a-switch v-model:model-value="enabledTestSet" size="small" />
<span class="mx-[8px]"> {{ t('ms.case.associate.testSet') }}</span>
<a-input-search
v-model:model-value="keyword"
:placeholder="t('common.searchByIdName')"
allow-clear
class="w-[240px]"
@search="searchList"
@press-enter="searchList"
@clear="clearHandler"
/>
</div>
</div>
<!-- 开启测试集 -->
<TestSetTable
v-if="enabledTestSet"
ref="testSetTableRef"
v-model:keyword="keyword"
:active-type="props.activeType"
:report-id="props.reportId"
:share-id="props.shareId"
:is-preview="props.isPreview"
:is-group="props.isGroup"
:enabled-test-set="enabledTestSet"
/>
<!-- 功能用例明细 -->
<FeatureCaseTable
v-else-if="props.activeType === ReportCardTypeEnum.FUNCTIONAL_DETAIL && !enabledTestSet"
ref="featureCaseTableRef"
v-model:keyword="keyword"
:report-id="props.reportId"
:share-id="props.shareId"
:is-preview="props.isPreview"
:is-group="props.isGroup"
:enabled-test-set="enabledTestSet"
/>
<!-- 接口用例明细和场景用例明细 -->
<ApiAndScenarioTable
v-else-if="
(props.activeType === ReportCardTypeEnum.API_CASE_DETAIL ||
props.activeType === ReportCardTypeEnum.SCENARIO_CASE_DETAIL) &&
!enabledTestSet
"
ref="apiAndScenarioTableRef"
:active-type="props.activeType"
:report-id="props.reportId"
:share-id="props.shareId"
:is-preview="props.isPreview"
:is-group="props.isGroup"
:keyword="keyword"
:enabled-test-set="enabledTestSet"
/>
</template>
<script setup lang="ts">
import TestSetTable from './testSetTable.vue';
import ApiAndScenarioTable from '@/views/test-plan/report/detail/component/system-card/apiAndScenarioTable.vue';
import FeatureCaseTable from '@/views/test-plan/report/detail/component/system-card/featureCaseTable.vue';
import { useI18n } from '@/hooks/useI18n';
import { ReportCardTypeEnum } from '@/enums/testPlanReportEnum';
const props = defineProps<{
reportId: string;
activeType: ReportCardTypeEnum; //
label: string;
shareId?: string;
isPreview?: boolean;
isGroup?: boolean;
}>();
const { t } = useI18n();
const enabledTestSet = ref<boolean>(false);
const keyword = ref<string>('');
const testSetTableRef = ref<InstanceType<typeof TestSetTable>>();
const featureCaseTableRef = ref<InstanceType<typeof TestSetTable>>();
const apiAndScenarioTableRef = ref<InstanceType<typeof TestSetTable>>();
function searchList() {
if (enabledTestSet.value) {
testSetTableRef.value?.loadCaseList();
} else {
featureCaseTableRef.value?.loadCaseList();
apiAndScenarioTableRef.value?.loadCaseList();
}
}
function clearHandler() {
keyword.value = '';
searchList();
}
</script>
<style lang="less" scoped>
.test-set-wrapper {
padding-bottom: 16px;
border-radius: 12px;
@apply overflow-y-auto bg-white;
.ms-scroll-bar();
}
.test-set-cell {
:deep(.arco-table-cell) {
padding: 8px 16px !important;
}
}
</style>

View File

@ -140,7 +140,7 @@
</div>
<div class="wrapper-preview-card">
<div class="flex items-center justify-between">
<div v-if="item.value !== ReportCardTypeEnum.CUSTOM_CARD" class="mb-[8px] font-medium">
<div v-if="!notShowLabel.includes(item.value)" class="mb-[8px] font-medium">
{{ t(item.label) }}
</div>
<a-radio-group
@ -194,24 +194,14 @@
:share-id="shareId"
:is-preview="props.isPreview"
/>
<FeatureCaseTable
v-else-if="item.value === ReportCardTypeEnum.FUNCTIONAL_DETAIL"
:report-id="detail.id"
:share-id="shareId"
:is-preview="props.isPreview"
:is-group="props.isGroup"
/>
<ApiAndScenarioTable
v-else-if="
item.value === ReportCardTypeEnum.API_CASE_DETAIL ||
item.value === ReportCardTypeEnum.SCENARIO_CASE_DETAIL
"
<TestSetTable
v-else-if="hasTestSet.includes(item.value)"
:report-id="detail.id"
:share-id="shareId"
:active-type="item.value"
:is-preview="props.isPreview"
:is-group="props.isGroup"
:label="t(item.label)"
/>
<CustomRichText
v-else-if="item.value === ReportCardTypeEnum.CUSTOM_CARD"
@ -243,15 +233,14 @@
import MsChart from '@/components/pure/chart/index.vue';
import SingleStatusProgress from '@/views/test-plan/report/component/singleStatusProgress.vue';
import CustomRichText from '@/views/test-plan/report/detail/component/custom-card/customRichText.vue';
import ApiAndScenarioTable from '@/views/test-plan/report/detail/component/system-card/apiAndScenarioTable.vue';
import BugTable from '@/views/test-plan/report/detail/component/system-card/bugTable.vue';
import ExecuteAnalysis from '@/views/test-plan/report/detail/component/system-card/executeAnalysis.vue';
import FeatureCaseTable from '@/views/test-plan/report/detail/component/system-card/featureCaseTable.vue';
import ReportDetailTable from '@/views/test-plan/report/detail/component/system-card/reportDetailTable.vue';
import ReportHeader from '@/views/test-plan/report/detail/component/system-card/reportHeader.vue';
import ReportMetricsItem from '@/views/test-plan/report/detail/component/system-card/ReportMetricsItem.vue';
import Summary from '@/views/test-plan/report/detail/component/system-card/summary.vue';
import SystemTrigger from '@/views/test-plan/report/detail/component/system-card/systemTrigger.vue';
import TestSetTable from '@/views/test-plan/report/detail/component/system-card/testTableIndex.vue';
import { getReportLayout, updateReportDetail } from '@/api/modules/test-plan/report';
import {
@ -317,6 +306,19 @@
const getAnalysisHover = computed(() => (props.isPreview ? '' : 'hover-analysis cursor-not-allowed'));
const notShowLabel = [
ReportCardTypeEnum.CUSTOM_CARD,
ReportCardTypeEnum.FUNCTIONAL_DETAIL,
ReportCardTypeEnum.API_CASE_DETAIL,
ReportCardTypeEnum.SCENARIO_CASE_DETAIL,
];
const hasTestSet = [
ReportCardTypeEnum.FUNCTIONAL_DETAIL,
ReportCardTypeEnum.API_CASE_DETAIL,
ReportCardTypeEnum.SCENARIO_CASE_DETAIL,
];
/**
* 分享share
*/

View File

@ -925,10 +925,16 @@
filterParams.status = ['ARCHIVED'];
}
emit('init', {
...tableParams,
keyword: keyword.value,
type: showType.value,
projectId: appStore.currentProjectId,
moduleIds: tableParams.moduleIds,
current: propsRes.value.msPagination?.current,
pageSize: propsRes.value.msPagination?.pageSize,
filter: filterParams,
combine: {
...batchParams.value.condition,
},
});
}