feat(接口测试): 定义api支持高级搜索&优化视图交互

This commit is contained in:
teukkk 2024-09-06 19:21:09 +08:00 committed by 刘瑞斌
parent fa5a4200bb
commit 1320bfe264
14 changed files with 265 additions and 146 deletions

View File

@ -60,10 +60,10 @@
}); });
} }
function validateForm(cb: () => void) { function validateForm(cb: (param?: any) => void, params?: any) {
formRef.value?.validate(async (errors) => { formRef.value?.validate(async (errors) => {
if (!errors) { if (!errors) {
cb(); cb(params);
} }
}); });
} }

View File

@ -51,7 +51,7 @@
{{ t(option.title as string) }} {{ t(option.title as string) }}
</a-option> </a-option>
<a-divider <a-divider
v-if="(props?.customList || [])?.length && (currentConfigList || []).length - 1 === currentOptionsIndex" v-if="(props?.customList || [])?.length && !option.customField && currentOptions(item.dataIndex as string)[currentOptionsIndex+1]?.customField"
class="!my-1" class="!my-1"
/> />
</div> </div>
@ -189,25 +189,28 @@
</a-button> </a-button>
</div> </div>
</a-form> </a-form>
<MsButton type="text" class="mt-[5px]" @click="handleAddItem"> <MsButton type="text" class="mt-[5px] w-[fit-content]" @click="handleAddItem">
<MsIcon type="icon-icon_add_outlined" class="mr-[3px]" /> <MsIcon type="icon-icon_add_outlined" class="mr-[3px]" />
{{ t('advanceFilter.addCondition') }} {{ t('advanceFilter.addCondition') }}
</MsButton> </MsButton>
<template #footer> <template #footer>
<div v-if="!isSaveAsView" class="flex items-center gap-[8px]"> <div v-if="!isSaveAsView" class="flex items-center gap-[8px]">
<a-button type="primary" @click="handleFilter">{{ t('common.filter') }}</a-button> <a-button v-if="!formModel?.id" type="primary" @click="handleSaveAndFilter">{{
t('advanceFilter.saveAndFilter')
}}</a-button>
<a-button v-if="formModel?.id" type="primary" @click="handleFilter">{{ t('common.filter') }}</a-button>
<a-button class="mr-[16px]" @click="handleReset">{{ t('common.reset') }}</a-button> <a-button class="mr-[16px]" @click="handleReset">{{ t('common.reset') }}</a-button>
<MsButton <MsButton
v-if="!isInternalViews(formModel?.id)" v-if="!isInternalViews(formModel?.id) && formModel?.id"
type="text" type="text"
:loading="saveLoading" :loading="saveLoading"
class="!text-[var(--color-text-1)]" class="!text-[var(--color-text-1)]"
@click="handleSaveView" @click="handleSaveView()"
> >
{{ t('common.save') }} {{ t('common.save') }}
</MsButton> </MsButton>
<MsButton <MsButton
v-if="formModel?.id && !isInternalViews(formModel?.id)" v-if="(formModel?.id && !isInternalViews(formModel?.id)) || formModel?.id === 'all_data'"
type="text" type="text"
class="!text-[var(--color-text-1)]" class="!text-[var(--color-text-1)]"
@click="handleToSaveAs" @click="handleToSaveAs"
@ -262,6 +265,7 @@
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'handleFilter', value: FilterResult): void; (e: 'handleFilter', value: FilterResult): void;
(e: 'refreshViewList'): void; (e: 'refreshViewList'): void;
(e: 'changeViewToFirstCustom'): void;
}>(); }>();
const visible = defineModel<boolean>('visible', { required: true }); const visible = defineModel<boolean>('visible', { required: true });
@ -397,12 +401,22 @@
} }
function getParams() { function getParams() {
const conditions = formModel.value.list.map(({ value, operator, customField, dataIndex }) => ({ const conditions = formModel.value.list.map(({ type, value, operator, customField, dataIndex }) => {
value, let timeValue;
//
if (type === FilterType.DATE_PICKER) {
timeValue =
operator === OperatorEnum.BETWEEN
? [new Date(value[0]).getTime(), new Date(value[1]).getTime()]
: new Date(value).getTime();
}
return {
value: timeValue ?? value,
operator, operator,
customField: customField ?? false, customField: customField ?? false,
name: dataIndex, name: dataIndex,
})); };
});
return { searchMode: formModel.value.searchMode, conditions }; return { searchMode: formModel.value.searchMode, conditions };
} }
@ -449,7 +463,7 @@
// //
const saveLoading = ref(false); const saveLoading = ref(false);
function realSaveView() { function realSaveView(isChangeView = false) {
formRef.value?.validate(async (errors) => { formRef.value?.validate(async (errors) => {
if (!errors) { if (!errors) {
try { try {
@ -466,7 +480,11 @@
} }
Message.success(t('common.saveSuccess')); Message.success(t('common.saveSuccess'));
savedFormModel.value = cloneDeep(formModel.value); savedFormModel.value = cloneDeep(formModel.value);
if (!isChangeView) {
emit('refreshViewList'); emit('refreshViewList');
} else {
emit('changeViewToFirstCustom');
}
} catch (error) { } catch (error) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log(error); console.log(error);
@ -476,11 +494,11 @@
} }
}); });
} }
function handleSaveView() { function handleSaveView(isChangeView = false) {
if (viewNameInputRef.value) { if (viewNameInputRef.value) {
viewNameInputRef.value?.validateForm(realSaveView); viewNameInputRef.value?.validateForm(realSaveView, isChangeView);
} else { } else {
realSaveView(); realSaveView(isChangeView);
} }
} }
@ -531,6 +549,9 @@
async function handleAddView() { async function handleAddView() {
saveAsViewNameInputRef.value?.validateForm(realAddView); saveAsViewNameInputRef.value?.validateForm(realAddView);
} }
function handleSaveAndFilter() {
handleSaveView(true);
}
defineExpose({ defineExpose({
resetToNewViewForm, resetToNewViewForm,

View File

@ -77,6 +77,7 @@ export const CustomTypeMaps: Record<string, any> = {
propsKey: 'selectProps', propsKey: 'selectProps',
props: { props: {
mode: 'static', mode: 'static',
multiple: true,
valueKey: 'value', valueKey: 'value',
labelKey: 'text', labelKey: 'text',
options: [], options: [],
@ -171,10 +172,13 @@ export const CustomTypeMaps: Record<string, any> = {
// TODO lmy 计划详情功能用例增加:测试点;接口定义、计划详情接口用例增加:协议; // TODO lmy 计划详情功能用例增加:测试点;接口定义、计划详情接口用例增加:协议;
export function getAllDataDefaultConditions(viewType: ViewTypeEnum) { export function getAllDataDefaultConditions(viewType: ViewTypeEnum) {
const conditions = [ const conditions = [
{ name: 'id', operator: OperatorEnum.CONTAINS }, { name: 'num', operator: OperatorEnum.CONTAINS },
{ name: 'name', operator: OperatorEnum.CONTAINS }, { name: 'name', operator: OperatorEnum.CONTAINS },
{ name: 'moduleId', operator: OperatorEnum.BELONG_TO }, { name: 'moduleId', operator: OperatorEnum.BELONG_TO },
]; ];
if ([ViewTypeEnum.API_DEFINITION].includes(viewType)) {
return [...conditions, { name: 'protocol', operator: OperatorEnum.BELONG_TO }];
}
return conditions; return conditions;
} }

View File

@ -127,6 +127,7 @@
:member-options="memberOptions" :member-options="memberOptions"
@handle-filter="handleFilter" @handle-filter="handleFilter"
@refresh-view-list="getUserViewList" @refresh-view-list="getUserViewList"
@change-view-to-first-custom="changeViewToFirstCustom"
/> />
</template> </template>
@ -149,7 +150,6 @@
import { FilterFormItem, FilterResult, ViewItem } from './type'; import { FilterFormItem, FilterResult, ViewItem } from './type';
const props = defineProps<{ const props = defineProps<{
rowCount: number;
filterConfigList: FilterFormItem[]; // filterConfigList: FilterFormItem[]; //
customFieldsConfigList?: FilterFormItem[]; // customFieldsConfigList?: FilterFormItem[]; //
searchPlaceholder?: string; searchPlaceholder?: string;
@ -161,7 +161,7 @@
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'keywordSearch', value: string | undefined): void; // keyword TODO: v-model:keyword (e: 'keywordSearch', value: string | undefined): void; // keyword TODO: v-model:keyword
(e: 'advSearch', value: FilterResult, viewId: string): void; // (e: 'advSearch', value: FilterResult, viewId: string, isAdvancedSearchMode: boolean): void; //
(e: 'refresh', value: FilterResult): void; (e: 'refresh', value: FilterResult): void;
}>(); }>();
@ -275,7 +275,7 @@
// //
isAdvancedSearchMode.value = currentView.value !== internalViews.value[0].id || haveConditions; isAdvancedSearchMode.value = currentView.value !== internalViews.value[0].id || haveConditions;
filterResult.value = filter; filterResult.value = filter;
emit('advSearch', filter, currentView.value); emit('advSearch', filter, currentView.value, isAdvancedSearchMode.value);
}; };
const handleRefresh = () => { const handleRefresh = () => {
@ -295,11 +295,17 @@
function clearFilter() { function clearFilter() {
if (currentView.value === internalViews.value[0].id) { if (currentView.value === internalViews.value[0].id) {
filterDrawerRef.value?.handleReset(); filterDrawerRef.value?.handleReset();
handleFilter({ searchMode: 'AND', conditions: [] });
} else { } else {
currentView.value = internalViews.value[0].id; currentView.value = internalViews.value[0].id;
} }
} }
async function changeViewToFirstCustom() {
await getUserViewList();
currentView.value = customViews.value[0].id;
}
defineExpose({ defineExpose({
isAdvancedSearchMode, isAdvancedSearchMode,
}); });

View File

@ -42,4 +42,5 @@ export default {
'advanceFilter.filterContentRequired': 'Filter content cannot be empty', 'advanceFilter.filterContentRequired': 'Filter content cannot be empty',
'advanceFilter.filterTip': 'Filter mode, module filtering can only be operated in the current filter', 'advanceFilter.filterTip': 'Filter mode, module filtering can only be operated in the current filter',
'advanceFilter.maxViewTip': 'Up to 10 views can be added', 'advanceFilter.maxViewTip': 'Up to 10 views can be added',
'advanceFilter.saveAndFilter': 'Save And filter',
}; };

View File

@ -42,4 +42,5 @@ export default {
'advanceFilter.filterContentRequired': '筛选内容不能为空', 'advanceFilter.filterContentRequired': '筛选内容不能为空',
'advanceFilter.filterTip': '筛选模式,模块过滤仅可在当前过滤器中操作', 'advanceFilter.filterTip': '筛选模式,模块过滤仅可在当前过滤器中操作',
'advanceFilter.maxViewTip': '最多可添加 10 个视图', 'advanceFilter.maxViewTip': '最多可添加 10 个视图',
'advanceFilter.saveAndFilter': '保存并筛选',
}; };

View File

@ -26,7 +26,6 @@ export default function useShortCut(shortcuts: Shortcuts, options: MinderOperati
const { minderCopy, minderCut, minderPaste } = useMinderOperation(options); const { minderCopy, minderCut, minderPaste } = useMinderOperation(options);
const handleKeyDown = (event: KeyboardEvent) => { const handleKeyDown = (event: KeyboardEvent) => {
event.preventDefault();
const nodes: MinderJsonNode[] = window.minder.getSelectedNodes(); const nodes: MinderJsonNode[] = window.minder.getSelectedNodes();
if (nodes.length === 0) { if (nodes.length === 0) {
return; return;

View File

@ -31,4 +31,6 @@ export enum FilterType {
export enum ViewTypeEnum { export enum ViewTypeEnum {
FUNCTIONAL_CASE = 'functional-case', FUNCTIONAL_CASE = 'functional-case',
API_DEFINITION = 'api-definition',
REVIEW_FUNCTIONAL_CASE = 'review-functional-case',
} }

View File

@ -1,35 +1,28 @@
<template> <template>
<div :class="['p-[0_16px_8px_16px]', props.class]"> <div :class="['p-[0_16px_8px_16px]', props.class]">
<div class="mb-[16px] flex items-center justify-end"> <MsAdvanceFilter
<div class="flex items-center gap-[8px]"> ref="msAdvanceFilterRef"
<a-input-search v-model:keyword="keyword"
v-model:model-value="keyword" :view-type="ViewTypeEnum.API_DEFINITION"
:placeholder="t('apiTestManagement.searchPlaceholder')" :filter-config-list="filterConfigList"
allow-clear :search-placeholder="t('apiTestManagement.searchPlaceholder')"
class="mr-[8px] w-[240px]" @keyword-search="loadApiList(false)"
@search="loadApiList(false)" @adv-search="handleAdvSearch"
@press-enter="loadApiList(false)" @refresh="loadApiList(false)"
@clear="loadApiList(false)"
/> />
<a-button type="outline" class="arco-btn-outline--secondary !p-[8px]" @click="loadApiList(false)">
<template #icon>
<icon-refresh class="text-[var(--color-text-4)]" />
</template>
</a-button>
</div>
</div>
<ms-base-table <ms-base-table
ref="apiTableRef" ref="apiTableRef"
v-bind="propsRes" v-bind="propsRes"
:action-config="batchActions" :action-config="batchActions"
:first-column-width="44" :first-column-width="44"
no-disable no-disable
class="mt-[16px]"
:not-show-table-filter="isAdvancedSearchMode"
filter-icon-align-left filter-icon-align-left
v-on="propsEvent" v-on="propsEvent"
@selected-change="handleTableSelect" @selected-change="handleTableSelect"
@batch-action="handleTableBatch" @batch-action="handleTableBatch"
@drag-change="handleTableDragSort" @drag-change="handleTableDragSort"
@module-change="loadApiList(false)"
> >
<template #[FilterSlotNameEnum.API_TEST_API_REQUEST_METHODS]="{ filterContent }"> <template #[FilterSlotNameEnum.API_TEST_API_REQUEST_METHODS]="{ filterContent }">
<apiMethodName :method="filterContent.value" /> <apiMethodName :method="filterContent.value" />
@ -176,7 +169,7 @@
:disabled="batchForm.attr === ''" :disabled="batchForm.attr === ''"
> >
<a-option v-for="item of valueOptions" :key="item.value" :value="item.value"> <a-option v-for="item of valueOptions" :key="item.value" :value="item.value">
{{ t(item.name) }} {{ item.name }}
</a-option> </a-option>
</a-select> </a-select>
</a-form-item> </a-form-item>
@ -294,6 +287,8 @@
import { FormInstance, Message } from '@arco-design/web-vue'; import { FormInstance, Message } from '@arco-design/web-vue';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import MsAdvanceFilter from '@/components/pure/ms-advance-filter/index.vue';
import { FilterFormItem, FilterResult } from '@/components/pure/ms-advance-filter/type';
import MsButton from '@/components/pure/ms-button/index.vue'; import MsButton from '@/components/pure/ms-button/index.vue';
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';
@ -307,7 +302,6 @@
import apiStatus from '@/views/api-test/components/apiStatus.vue'; import apiStatus from '@/views/api-test/components/apiStatus.vue';
import moduleTree from '@/views/api-test/management/components/moduleTree.vue'; import moduleTree from '@/views/api-test/management/components/moduleTree.vue';
import { getProtocolList } from '@/api/modules/api-test/common';
import { import {
batchDeleteDefinition, batchDeleteDefinition,
batchMoveDefinition, batchMoveDefinition,
@ -329,7 +323,8 @@
import { ProtocolItem } from '@/models/apiTest/common'; import { ProtocolItem } from '@/models/apiTest/common';
import { ApiDefinitionDetail, ApiDefinitionGetModuleParams } from '@/models/apiTest/management'; import { ApiDefinitionDetail, ApiDefinitionGetModuleParams } from '@/models/apiTest/management';
import { DragSortParams } from '@/models/common'; import { DragSortParams, ModuleTreeNode } from '@/models/common';
import { FilterType, ViewTypeEnum } from '@/enums/advancedFilterEnum';
import { RequestDefinitionStatus, RequestImportFormat, RequestMethods } from '@/enums/apiEnum'; import { RequestDefinitionStatus, RequestImportFormat, RequestMethods } from '@/enums/apiEnum';
import { CacheTabTypeEnum } from '@/enums/cacheTabEnum'; import { CacheTabTypeEnum } from '@/enums/cacheTabEnum';
import { TableKeyEnum } from '@/enums/tableEnum'; import { TableKeyEnum } from '@/enums/tableEnum';
@ -347,6 +342,7 @@
selectedProtocols: string[]; // selectedProtocols: string[]; //
readOnly?: boolean; // readOnly?: boolean; //
refreshTimeStamp?: number; refreshTimeStamp?: number;
moduleTreeData?: ModuleTreeNode[];
memberOptions: { label: string; value: string }[]; memberOptions: { label: string; value: string }[];
}>(); }>();
const emit = defineEmits<{ const emit = defineEmits<{
@ -354,6 +350,7 @@
(e: 'openCopyApiTab', record: ApiDefinitionDetail): void; (e: 'openCopyApiTab', record: ApiDefinitionDetail): void;
(e: 'addApiTab'): void; (e: 'addApiTab'): void;
(e: 'import'): void; (e: 'import'): void;
(e: 'handleAdvSearch', isStartAdvance: boolean): void;
( (
e: 'openEditApiTab', e: 'openEditApiTab',
options: { apiInfo: ApiDefinitionDetail; isCopy: boolean; isExecute: boolean; isEdit: boolean } options: { apiInfo: ApiDefinitionDetail; isCopy: boolean; isExecute: boolean; isEdit: boolean }
@ -380,22 +377,7 @@
]) ])
); );
// TODO: store const protocolList = inject<Ref<ProtocolItem[]>>('protocols', ref([]));
const protocolList = ref<ProtocolItem[]>([]);
async function initProtocolList() {
try {
const res = await getProtocolList(appStore.currentOrgId);
protocolList.value = res.map((e) => ({
protocol: e.protocol,
polymorphicName: e.polymorphicName,
pluginId: e.pluginId,
}));
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
}
}
const requestMethodsOptions = computed(() => { const requestMethodsOptions = computed(() => {
const otherMethods = protocolList.value const otherMethods = protocolList.value
.filter((e) => e.protocol !== 'HTTP') .filter((e) => e.protocol !== 'HTTP')
@ -421,6 +403,24 @@
}; };
}); });
}); });
const apiStatusOptions = [
{
name: t('apiTestManagement.processing'),
value: RequestDefinitionStatus.PROCESSING,
},
{
name: t('apiTestManagement.done'),
value: RequestDefinitionStatus.DONE,
},
{
name: t('apiTestManagement.deprecate'),
value: RequestDefinitionStatus.DEPRECATED,
},
{
name: t('apiTestManagement.debugging'),
value: RequestDefinitionStatus.DEBUGGING,
},
];
const apiTableRef = ref(); const apiTableRef = ref();
let columns: MsTableColumn = [ let columns: MsTableColumn = [
{ {
@ -574,7 +574,8 @@
); );
} }
const { propsRes, propsEvent, loadList, setLoadListParams, resetSelector } = useTable( const { propsRes, propsEvent, viewId, advanceFilter, setAdvanceFilter, loadList, setLoadListParams, resetSelector } =
useTable(
getDefinitionPage, getDefinitionPage,
{ {
columns: props.readOnly ? columns : [], columns: props.readOnly ? columns : [],
@ -590,7 +591,6 @@
draggable: hasAnyPermission(['PROJECT_API_DEFINITION:READ+UPDATE']) ? { type: 'handle', width: 32 } : undefined, draggable: hasAnyPermission(['PROJECT_API_DEFINITION:READ+UPDATE']) ? { type: 'handle', width: 32 } : undefined,
heightUsed: 272, heightUsed: 272,
paginationSize: 'mini', paginationSize: 'mini',
showSubdirectory: true,
}, },
(item) => ({ (item) => ({
...item, ...item,
@ -635,9 +635,11 @@
}, },
]; ];
const msAdvanceFilterRef = ref<InstanceType<typeof MsAdvanceFilter>>();
const isAdvancedSearchMode = computed(() => msAdvanceFilterRef.value?.isAdvancedSearchMode);
async function getModuleIds() { async function getModuleIds() {
let moduleIds: string[] = []; let moduleIds: string[] = [];
if (props.activeModule !== 'all') { if (props.activeModule !== 'all' && !isAdvancedSearchMode.value) {
moduleIds = [props.activeModule]; moduleIds = [props.activeModule];
const getAllChildren = await tableStore.getSubShow(TableKeyEnum.API_TEST); const getAllChildren = await tableStore.getSubShow(TableKeyEnum.API_TEST);
if (getAllChildren) { if (getAllChildren) {
@ -653,11 +655,13 @@
keyword: keyword.value, keyword: keyword.value,
projectId: appStore.currentProjectId, projectId: appStore.currentProjectId,
moduleIds, moduleIds,
protocols: props.selectedProtocols, protocols: isAdvancedSearchMode.value ? protocolList.value.map((item) => item.protocol) : props.selectedProtocols,
filter: propsRes.value.filter, filter: propsRes.value.filter,
viewId: viewId.value,
combineSearch: advanceFilter,
}; };
if (!hasRefreshTree && typeof refreshModuleTreeCount === 'function') { if (!hasRefreshTree && typeof refreshModuleTreeCount === 'function' && !isAdvancedSearchMode.value) {
refreshModuleTreeCount({ refreshModuleTreeCount({
keyword: keyword.value, keyword: keyword.value,
filter: propsRes.value.filter, filter: propsRes.value.filter,
@ -688,6 +692,105 @@
} }
); );
const filterConfigList = computed<FilterFormItem[]>(() => [
{
title: 'caseManagement.featureCase.tableColumnID',
dataIndex: 'num',
type: FilterType.INPUT,
},
{
title: 'apiTestManagement.apiName',
dataIndex: 'name',
type: FilterType.INPUT,
},
{
title: 'common.belongModule',
dataIndex: 'moduleId',
type: FilterType.TREE_SELECT,
treeSelectData: props.moduleTreeData,
treeSelectProps: {
fieldNames: {
title: 'name',
key: 'id',
children: 'children',
},
multiple: true,
treeCheckable: true,
treeCheckStrictly: true,
maxTagCount: 1,
},
},
{
title: 'apiTestManagement.protocol',
dataIndex: 'protocol',
type: FilterType.SELECT,
selectProps: {
multiple: true,
labelKey: 'protocol',
valueKey: 'protocol',
options: protocolList.value,
},
},
{
title: 'apiTestManagement.apiType',
dataIndex: 'method',
type: FilterType.SELECT,
selectProps: {
multiple: true,
labelKey: 'key',
options: requestMethodsOptions.value,
},
},
{
title: 'apiTestManagement.path',
dataIndex: 'path',
type: FilterType.INPUT,
},
{
title: 'apiTestManagement.apiStatus',
dataIndex: 'status',
type: FilterType.SELECT,
selectProps: {
multiple: true,
labelKey: 'name',
options: apiStatusOptions,
},
},
{
title: 'apiTestManagement.caseTotal',
dataIndex: 'caseTotal',
type: FilterType.NUMBER,
},
{
title: 'common.tag',
dataIndex: 'tags',
type: FilterType.TAGS_INPUT,
},
{
title: 'common.creator',
dataIndex: 'createUser',
type: FilterType.MEMBER,
},
{
title: 'common.createTime',
dataIndex: 'createTime',
type: FilterType.DATE_PICKER,
},
{
title: 'common.updateTime',
dataIndex: 'updateTime',
type: FilterType.DATE_PICKER,
},
]);
//
const handleAdvSearch = async (filter: FilterResult, id: string, isStartAdvance: boolean) => {
resetSelector();
emit('handleAdvSearch', isStartAdvance);
keyword.value = '';
setAdvanceFilter(filter, id);
await loadApiList(false); //
};
async function handleStatusChange(record: ApiDefinitionDetail) { async function handleStatusChange(record: ApiDefinitionDetail) {
try { try {
await updateDefinition({ await updateDefinition({
@ -702,7 +805,6 @@
} }
function onMountedLoad() { function onMountedLoad() {
initProtocolList();
if (props.selectedProtocols.length > 0) { if (props.selectedProtocols.length > 0) {
loadApiList(true); loadApiList(true);
} }
@ -731,6 +833,20 @@
excludeIds: [], excludeIds: [],
currentSelectCount: 0, currentSelectCount: 0,
}); });
async function getBatchConditionParams() {
const selectModules = await getModuleIds();
return {
condition: {
keyword: keyword.value,
filter: propsRes.value.filter,
viewId: viewId.value,
combineSearch: advanceFilter,
},
projectId: appStore.currentProjectId,
protocols: isAdvancedSearchMode.value ? protocolList.value.map((item) => item.protocol) : props.selectedProtocols,
moduleIds: selectModules,
};
}
/** /**
* 删除接口 * 删除接口
@ -757,18 +873,13 @@
onBeforeOk: async () => { onBeforeOk: async () => {
try { try {
if (isBatch) { if (isBatch) {
const batchConditionParams = await getBatchConditionParams();
await batchDeleteDefinition({ await batchDeleteDefinition({
selectIds, selectIds,
selectAll: !!params?.selectAll, selectAll: !!params?.selectAll,
excludeIds: params?.excludeIds || [], excludeIds: params?.excludeIds || [],
condition: { ...batchConditionParams,
keyword: keyword.value,
filter: propsRes.value.filter,
},
projectId: appStore.currentProjectId,
moduleIds: await getModuleIds(),
deleteAll: true, deleteAll: true,
protocols: props.selectedProtocols,
}); });
} else { } else {
await deleteDefinition(record?.id as string); await deleteDefinition(record?.id as string);
@ -842,24 +953,7 @@
const valueOptions = computed(() => { const valueOptions = computed(() => {
switch (batchForm.value.attr) { switch (batchForm.value.attr) {
case 'status': case 'status':
return [ return apiStatusOptions;
{
name: 'apiTestManagement.processing',
value: RequestDefinitionStatus.PROCESSING,
},
{
name: 'apiTestManagement.done',
value: RequestDefinitionStatus.DONE,
},
{
name: 'apiTestManagement.deprecate',
value: RequestDefinitionStatus.DEPRECATED,
},
{
name: 'apiTestManagement.debugging',
value: RequestDefinitionStatus.DEBUGGING,
},
];
default: default:
return []; return [];
} }
@ -881,17 +975,12 @@
if (!errors) { if (!errors) {
try { try {
batchUpdateLoading.value = true; batchUpdateLoading.value = true;
const batchConditionParams = await getBatchConditionParams();
await batchUpdateDefinition({ await batchUpdateDefinition({
selectIds: batchParams.value?.selectedIds || [], selectIds: batchParams.value?.selectedIds || [],
selectAll: !!batchParams.value?.selectAll, selectAll: !!batchParams.value?.selectAll,
excludeIds: batchParams.value?.excludeIds || [], excludeIds: batchParams.value?.excludeIds || [],
condition: { ...batchConditionParams,
keyword: keyword.value,
filter: propsRes.value.filter,
},
projectId: appStore.currentProjectId,
moduleIds: await getModuleIds(),
protocols: props.selectedProtocols,
type: batchForm.value.attr, type: batchForm.value.attr,
append: batchForm.value.append, append: batchForm.value.append,
[batchForm.value.attr]: batchForm.value.attr === 'tags' ? batchForm.value.values : batchForm.value.value, [batchForm.value.attr]: batchForm.value.attr === 'tags' ? batchForm.value.values : batchForm.value.value,
@ -923,18 +1012,13 @@
async function handleApiMove() { async function handleApiMove() {
try { try {
batchMoveApiLoading.value = true; batchMoveApiLoading.value = true;
const batchConditionParams = await getBatchConditionParams();
await batchMoveDefinition({ await batchMoveDefinition({
selectIds: isBatchMove.value ? batchParams.value?.selectedIds || [] : [activeApi.value?.id || ''], selectIds: isBatchMove.value ? batchParams.value?.selectedIds || [] : [activeApi.value?.id || ''],
selectAll: !!batchParams.value?.selectAll, selectAll: !!batchParams.value?.selectAll,
excludeIds: batchParams.value?.excludeIds || [], excludeIds: batchParams.value?.excludeIds || [],
condition: { ...batchConditionParams,
keyword: keyword.value,
filter: propsRes.value.filter,
},
projectId: appStore.currentProjectId,
moduleIds: await getModuleIds(),
moduleId: selectedModuleKeys.value[0], moduleId: selectedModuleKeys.value[0],
protocols: props.selectedProtocols,
}); });
Message.success(t('common.batchMoveSuccess')); Message.success(t('common.batchMoveSuccess'));
if (isBatchMove.value) { if (isBatchMove.value) {
@ -996,18 +1080,13 @@
async function exportApi() { async function exportApi() {
try { try {
exportLoading.value = true; exportLoading.value = true;
const batchConditionParams = await getBatchConditionParams();
const result = await exportApiDefinition( const result = await exportApiDefinition(
{ {
selectIds: tableSelected.value as string[], selectIds: tableSelected.value as string[],
selectAll: !!batchParams.value?.selectAll, selectAll: !!batchParams.value?.selectAll,
excludeIds: batchParams.value?.excludeIds || [], excludeIds: batchParams.value?.excludeIds || [],
condition: { ...batchConditionParams,
keyword: keyword.value,
filter: propsRes.value.filter,
},
projectId: appStore.currentProjectId,
moduleIds: await getModuleIds(),
protocols: props.selectedProtocols,
exportApiCase: exportApiCase.value, exportApiCase: exportApiCase.value,
exportApiMock: exportApiMock.value, exportApiMock: exportApiMock.value,
sort: propsRes.value.sorter || {}, sort: propsRes.value.sorter || {},

View File

@ -6,6 +6,7 @@
class="flex-1 pt-[8px]" class="flex-1 pt-[8px]"
:active-module="props.activeModule" :active-module="props.activeModule"
:offspring-ids="props.offspringIds" :offspring-ids="props.offspringIds"
:module-tree-data="props.moduleTree"
:selected-protocols="props.selectedProtocols" :selected-protocols="props.selectedProtocols"
:refresh-time-stamp="refreshTableTimeStamp" :refresh-time-stamp="refreshTableTimeStamp"
:member-options="memberOptions" :member-options="memberOptions"
@ -14,6 +15,7 @@
@add-api-tab="addApiTab" @add-api-tab="addApiTab"
@import="() => emit('import')" @import="() => emit('import')"
@open-edit-api-tab="openApiTab" @open-edit-api-tab="openApiTab"
@handle-adv-search="(val) => emit('handleAdvSearch', val)"
/> />
</keep-alive> </keep-alive>
<div v-if="activeApiTab.id !== 'all'" class="flex-1 overflow-hidden"> <div v-if="activeApiTab.id !== 'all'" class="flex-1 overflow-hidden">
@ -184,6 +186,7 @@
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'deleteApi', id: string): void; (e: 'deleteApi', id: string): void;
(e: 'import'): void; (e: 'import'): void;
(e: 'handleAdvSearch', isStartAdvance: boolean): void;
(e: 'openCaseTab', apiCaseDetail: ApiCaseDetail): void; (e: 'openCaseTab', apiCaseDetail: ApiCaseDetail): void;
}>(); }>();

View File

@ -56,6 +56,7 @@
@import="emit('import')" @import="emit('import')"
@open-case-tab="(apiCaseDetail:ApiCaseDetail)=>newCaseTab(apiCaseDetail.id)" @open-case-tab="(apiCaseDetail:ApiCaseDetail)=>newCaseTab(apiCaseDetail.id)"
@delete-api="(id) => handleDeleteApiFromModuleTree(id)" @delete-api="(id) => handleDeleteApiFromModuleTree(id)"
@handle-adv-search="(val) => emit('handleAdvSearch', val)"
/> />
<apiCase <apiCase
v-show="(activeApiTab.id === 'all' && currentTab === 'case') || activeApiTab.type === 'case'" v-show="(activeApiTab.id === 'all' && currentTab === 'case') || activeApiTab.type === 'case'"
@ -131,6 +132,7 @@
}>(); }>();
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'import'): void; (e: 'import'): void;
(e: 'handleAdvSearch', isStartAdvance: boolean): void;
}>(); }>();
const appStore = useAppStore(); const appStore = useAppStore();
const route = useRoute(); const route = useRoute();
@ -399,9 +401,4 @@
<style lang="less" scoped> <style lang="less" scoped>
.ms-input-group--prepend(); .ms-input-group--prepend();
:deep(.arco-select-view-prefix) {
margin-right: 8px;
padding-right: 0;
border-right: 1px solid var(--color-text-input-border);
}
</style> </style>

View File

@ -641,6 +641,7 @@
defineExpose({ defineExpose({
refresh, refresh,
initModuleCount, initModuleCount,
setActiveFolder,
}); });
</script> </script>

View File

@ -1,6 +1,6 @@
<template> <template>
<MsCard simple no-content-padding> <MsCard simple no-content-padding>
<MsSplitBox :size="300" :max="0.5"> <MsSplitBox :not-show-first="isAdvancedSearchMode" :size="300" :max="0.5">
<template #first> <template #first>
<div class="flex flex-col"> <div class="flex flex-col">
<div class="p-[16px]" :style="{ height: `calc(100vh - 120px)` }"> <div class="p-[16px]" :style="{ height: `calc(100vh - 120px)` }">
@ -56,6 +56,7 @@
:offspring-ids="offspringIds" :offspring-ids="offspringIds"
:selected-protocols="selectedProtocols" :selected-protocols="selectedProtocols"
@import="importDrawerVisible = true" @import="importDrawerVisible = true"
@handle-adv-search="handleAdvSearch"
/> />
</div> </div>
</template> </template>
@ -195,6 +196,12 @@
} }
}; };
const isAdvancedSearchMode = ref(false);
function handleAdvSearch(isStartAdvance: boolean) {
isAdvancedSearchMode.value = isStartAdvance;
moduleTreeRef.value?.setActiveFolder('all');
}
/** 向子孙组件提供方法和值 */ /** 向子孙组件提供方法和值 */
provide('setActiveApi', setActiveApi); provide('setActiveApi', setActiveApi);
provide('refreshModuleTree', refreshModuleTree); provide('refreshModuleTree', refreshModuleTree);

View File

@ -10,7 +10,6 @@
:filter-config-list="filterConfigList" :filter-config-list="filterConfigList"
:custom-fields-config-list="searchCustomFields" :custom-fields-config-list="searchCustomFields"
:search-placeholder="t('caseManagement.featureCase.searchPlaceholder')" :search-placeholder="t('caseManagement.featureCase.searchPlaceholder')"
:row-count="filterRowCount"
:count="props.modulesCount[props.activeFolder] || 0" :count="props.modulesCount[props.activeFolder] || 0"
:name="moduleNamePath" :name="moduleNamePath"
@keyword-search="fetchData" @keyword-search="fetchData"
@ -474,7 +473,6 @@
const minderStore = useMinderStore(); const minderStore = useMinderStore();
const keyword = ref<string>(''); const keyword = ref<string>('');
const filterRowCount = ref(0);
const groupKeyword = ref<string>(''); const groupKeyword = ref<string>('');
const showType = ref<string>('list'); const showType = ref<string>('list');
@ -776,7 +774,7 @@
const filterConfigList = computed<FilterFormItem[]>(() => [ const filterConfigList = computed<FilterFormItem[]>(() => [
{ {
title: 'caseManagement.featureCase.tableColumnID', title: 'caseManagement.featureCase.tableColumnID',
dataIndex: 'id', dataIndex: 'num',
type: FilterType.INPUT, type: FilterType.INPUT,
}, },
{ {