feat(接口测试): 断言表格结果列支持筛选/排序

This commit is contained in:
baiqi 2024-11-01 15:28:24 +08:00 committed by Craftsman
parent 3d69fd433d
commit eb847d43ab
5 changed files with 106 additions and 15 deletions

View File

@ -44,6 +44,23 @@
</div>
</a-tooltip>
</template>
<template #columnFilter="{ column }">
<DefaultFilter
v-if="(column.filterConfig && column.filterConfig.options?.length) || column?.filterConfig?.remoteMethod"
v-model:checked-list="column.filterCheckedList"
class="ml-[4px]"
:options="column.filterConfig.options"
:data-index="column.dataIndex"
v-bind="column.filterConfig"
:filter="filterData"
@handle-confirm="(v) => handleFilterConfirm(v, column.dataIndex as string, column.isCustomParam || false)"
@click.stop="null"
>
<template #item="{ filterItem }">
<slot :name="column.filterConfig.filterSlotName" :filter-content="filterItem"> </slot>
</template>
</DefaultFilter>
</template>
<template
v-for="item of props.columns.filter((e) => e.slotName !== undefined)"
:key="item.toString()"
@ -232,6 +249,7 @@
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
import DefaultFilter from '@/components/pure/ms-table/comp/defaultFilter.vue';
import type { MsTableColumnData, MsTableProps } from '@/components/pure/ms-table/type';
import useTable from '@/components/pure/ms-table/useTable';
import MsTableMoreAction from '@/components/pure/ms-table-more-action/index.vue';
@ -322,6 +340,13 @@
): void;
(e: 'rowSelect', rowKeys: (string | number)[], _rowKey: string | number, record: TableData): void;
(e: 'selectAll', checked: boolean): void;
(
e: 'filterChange',
dataIndex: string,
value: string[] | (string | number | boolean)[] | undefined,
isCustomParam: boolean
): void;
(e: 'sorterChange', value: { [key: string]: string }): void;
}>();
const { t } = useI18n();
@ -330,6 +355,8 @@
const expandedKeys = defineModel<string[]>('expandedKeys', { default: [] });
const originalSelectedKeys = defineModel<(string | number)[]>('originalSelectedKeys', { default: [] });
const filterData = ref({});
async function initColumns() {
if (props.showSetting && props.tableKey) {
await tableStore.initColumn(props.tableKey, props.columns);
@ -369,9 +396,20 @@
});
emit('change', propsRes.value.data);
};
propsEvent.value.sorterChange = (value: { [key: string]: string }) => {
emit('sorterChange', value);
};
const dataLength = computed(() => propsRes.value.data.length);
const handleFilterConfirm = (
value: string[] | (string | number | boolean)[] | undefined,
dataIndex: string,
isCustomParam: boolean
) => {
emit('filterChange', dataIndex, value, isCustomParam);
};
//
const formRef = ref<FormInstance>();
async function validRepeat(
@ -565,6 +603,9 @@
padding: 5px 8px !important;
line-height: 1.5715;
}
.arco-table-cell-with-sorter {
margin-left: 0;
}
}
.arco-table-td {
height: 32px;

View File

@ -114,24 +114,29 @@
@init-data="handleInitColumn"
@page-size-change="pageSizeChange"
/>
<DefaultFilter
<slot
v-else-if="
!props.notShowTableFilter &&
((item.filterConfig && item.filterConfig.options?.length) || item?.filterConfig?.remoteMethod)
"
v-model:checked-list="item.filterCheckedList"
class="ml-[4px]"
:options="item.filterConfig.options"
:data-index="item.dataIndex"
v-bind="item.filterConfig"
:filter="filterData"
@handle-confirm="(v) => handleFilterConfirm(v, item.dataIndex as string, item.isCustomParam || false)"
@click.stop="null"
name="columnFilter"
:column="item"
>
<template #item="{ filterItem }">
<slot :name="item.filterConfig.filterSlotName" :filter-content="filterItem"> </slot>
</template>
</DefaultFilter>
<DefaultFilter
v-model:checked-list="item.filterCheckedList"
class="ml-[4px]"
:options="item.filterConfig.options"
:data-index="item.dataIndex"
v-bind="item.filterConfig"
:filter="filterData"
@handle-confirm="(v) => handleFilterConfirm(v, item.dataIndex as string, item.isCustomParam || false)"
@click.stop="null"
>
<template #item="{ filterItem }">
<slot :name="item.filterConfig.filterSlotName" :filter-content="filterItem"> </slot>
</template>
</DefaultFilter>
</slot>
</div>
</template>
<template #cell="{ column, record, rowIndex }">

View File

@ -7,6 +7,7 @@ export enum FilterSlotNameEnum {
CASE_MANAGEMENT_BUG_STATE = 'CASE_MANAGEMENT_BUG_STATE', // 缺陷状态
API_TEST_API_REQUEST_METHODS = 'API_TEST_API_REQUEST_METHODS', // 接口测试请求方式
API_TEST_API_REQUEST_API_STATUS = 'API_TEST_API_REQUEST_API_STATUS', // 接口测试接口状态
API_TEST_API_RESPONSE_ASSERTION_STATUS = 'API_TEST_API_RESPONSE_ASSERTION_STATUS', // 接口测试-响应断言状态
TEST_PLAN_REPORT_EXEC_STATUS = 'TEST_PLAN_REPORT_EXEC_STATUS',
PROJECT_MANAGEMENT_COMMON_SCRIPT = 'PROJECT_MANAGEMENT_COMMON_SCRIPT', // 项目管理公共脚本脚本状态
GLOBAL_TASK_CENTER_API_CASE_STATUS = 'GLOBAL_TASK_CENTER_API_CASE_STATUS', // 任务中心执行状态

View File

@ -294,7 +294,7 @@ export function filterTree<T>(
const node = (tree as TreeNode<T>[])[i];
// 如果节点满足过滤条件,则保留该节点,并递归过滤子节点
if (filterFn(node, i, parentNode)) {
const newNode = cloneDeep(node);
const newNode = cloneDeep({ ...node, [customChildrenKey]: [] });
if (node[customChildrenKey] && node[customChildrenKey].length > 0) {
// 递归过滤子节点,并将过滤后的子节点添加到当前节点中
newNode[customChildrenKey] = filterTree(node[customChildrenKey], filterFn, customChildrenKey, node);

View File

@ -1,10 +1,12 @@
<template>
<a-scrollbar class="h-full overflow-y-auto">
<MsFormTable
:data="props.requestResult?.responseResult.assertions"
:data="tableData"
:columns="columns"
:selectable="false"
:scroll="props.scroll"
@filter-change="handleFilterChange"
@sorter-change="handleSortChange"
>
<template #assertionItem="{ record }">
<a-tooltip :content="record.name">
@ -33,6 +35,11 @@
{{ record.pass === true ? t('common.success') : t('common.fail') }}
</MsTag>
</template>
<template #[FilterSlotNameEnum.API_TEST_API_RESPONSE_ASSERTION_STATUS]="{ filterContent }">
<MsTag :type="filterContent.value === true ? 'success' : 'danger'" theme="light">
{{ filterContent.value === true ? t('common.success') : t('common.fail') }}
</MsTag>
</template>
</MsFormTable>
</a-scrollbar>
</template>
@ -47,6 +54,7 @@
import { RequestResult, ResponseAssertionTableItem } from '@/models/apiTest/common';
import { FullResponseAssertionType } from '@/enums/apiEnum';
import { FilterSlotNameEnum } from '@/enums/tableFilterEnum';
import { responseAssertionTypeMap } from '@/views/api-test/components/config';
@ -94,6 +102,23 @@
title: 'apiTestDebug.status',
dataIndex: 'pass',
slotName: 'status',
sortable: {
sortDirections: ['ascend', 'descend'],
sorter: true,
},
filterConfig: {
options: [
{
value: true,
label: t('common.success'),
},
{
value: false,
label: t('common.fail'),
},
],
filterSlotName: FilterSlotNameEnum.API_TEST_API_RESPONSE_ASSERTION_STATUS,
},
width: 120,
},
{
@ -105,6 +130,25 @@
width: 300,
},
];
const tableData = ref(props.requestResult?.responseResult.assertions);
function handleFilterChange(dataIndex: string, value: string[] | (string | number | boolean)[] | undefined) {
if (value && value.length > 0) {
tableData.value = props.requestResult?.responseResult.assertions.filter((item) => {
return (value as boolean[]).includes(item.pass);
});
} else {
tableData.value = props.requestResult?.responseResult.assertions;
}
}
function handleSortChange(sorter: { [key: string]: string }) {
const dataIndex = Object.keys(sorter)[0] as keyof ResponseAssertionTableItem;
tableData.value = tableData.value?.sort((a, b) => {
const sortResult = a[dataIndex] > b[dataIndex] ? -1 : 1;
return sorter[dataIndex] === 'asc' ? sortResult : -sortResult;
});
}
</script>
<style lang="less" scoped>