feat(接口管理): 插件增删改查接口联调

This commit is contained in:
baiqi 2024-03-05 17:37:58 +08:00 committed by 刘瑞斌
parent 33fda25e5f
commit e0d1d6f3ed
24 changed files with 213 additions and 124 deletions

View File

@ -170,7 +170,6 @@
</template>
<script setup lang="ts">
import { useVModel } from '@vueuse/core';
import { Message, TagData } from '@arco-design/web-vue';
import MsButton from '@/components/pure/ms-button/index.vue';
@ -281,9 +280,10 @@
label: fileItem[props.fields.name] || fileItem.name || '',
});
} else {
innerFileList.value = [fileItem];
inputFileName.value = fileItem.name || '';
}
emit('change', _fileList, { ...fileItem, local: true });
emit('change', innerFileList.value, { ...fileItem, local: true });
nextTick(() => {
// emit
buttonDropDownVisible.value = false;
@ -302,7 +302,7 @@
//
watch(
() => innerFileList.value,
() => {
(arr) => {
getListFunParams.value.combine.hiddenIds = innerFileList.value
.filter((item) => !item.local)
.map((item) => item[props.fields.id] || item.uid);

View File

@ -269,6 +269,7 @@ export interface TimeWaitingProcessor extends ExecuteConditionProcessorCommon {
export type ExpressionType = RequestExtractExpressionEnum;
// 表达式配置
export interface ExpressionCommonConfig {
id?: number | string; // 前端渲染使用字段
enable: boolean; // 是否启用
expression: string;
extractType: ExpressionType; // 表达式类型

View File

@ -41,10 +41,10 @@
const config = statusMap[props.status];
return {
style: {
backgroundColor: config.bgColor,
color: config.color,
backgroundColor: config?.bgColor,
color: config?.color,
},
text: t(config.text),
text: t(config?.text),
};
});
</script>

View File

@ -244,7 +244,7 @@
<div v-else-if="condition.processorType === RequestConditionProcessor.EXTRACT">
<paramTable
ref="extractParamsTableRef"
v-model:params="condition.extractParams"
v-model:params="condition.extractors"
:default-param-item="defaultExtractParamItem"
:columns="extractParamsColumns"
:selectable="false"
@ -272,6 +272,7 @@
v-model:model-value="record.expression"
class="ms-params-input"
:max-length="255"
size="mini"
@input="() => handleExpressionChange(rowIndex)"
@change="() => handleExpressionChange(rowIndex)"
>
@ -649,7 +650,7 @@ if (!result){
const disabledExpressionSuffix = ref(false);
function handleExtractParamTableChange(resultArr: any[], isInit?: boolean) {
condition.value.extractParams = [...resultArr];
condition.value.extractors = [...resultArr];
if (!isInit) {
emit('change');
}
@ -697,13 +698,13 @@ if (!result){
* 提取参数表格-应用更多设置
*/
function applyMoreSetting(record: ExpressionConfig) {
condition.value.extractParams = condition.value.extractParams?.map((e) => {
condition.value.extractors = condition.value.extractors?.map((e) => {
if (e.id === activeRecord.value.id) {
record.moreSettingPopoverVisible = false;
return {
...activeRecord.value,
moreSettingPopoverVisible: false,
} as any; // TOOD:
};
}
return e;
});
@ -714,7 +715,7 @@ if (!result){
* 提取参数表格-保存快速提取的配置
*/
function handleFastExtractionApply(config: RegexExtract | JSONPathExtract | XPathExtract) {
condition.value.extractParams = condition.value.extractParams?.map((e) => {
condition.value.extractors = condition.value.extractors?.map((e) => {
if (e.id === activeRecord.value.id) {
return {
...e,
@ -726,7 +727,7 @@ if (!result){
fastExtractionVisible.value = false;
nextTick(() => {
extractParamsTableRef.value?.addTableLine(
condition.value.extractParams?.findIndex((e) => e.id === activeRecord.value.id) || 0
condition.value.extractors?.findIndex((e) => e.id === activeRecord.value.id) || 0
);
});
emit('change');

View File

@ -120,20 +120,23 @@
},
});
break;
// case RequestConditionProcessor.SQL:
// data.value.push({
// id,
// enableCommonScript: false,
// desc: '',
// enable: true,
// sqlSource: {
// scriptName: '',
// script: '',
// storageType: 'column',
// params: [],
// },
// });
// break;
case RequestConditionProcessor.SQL:
data.value.push({
id,
processorType: RequestConditionProcessor.SQL,
enableCommonScript: false,
description: '',
enable: true,
dataSourceId: '',
environmentId: '',
queryTimeout: 0,
resultVariable: '',
script: '',
variableNames: '',
variables: [],
extractParams: [],
});
break;
case RequestConditionProcessor.TIME_WAITING:
data.value.push({
id,

View File

@ -216,12 +216,13 @@
break;
case RequestExtractExpressionEnum.JSON_PATH:
try {
matchResult.value = JSONPath({
json: props.response ? JSON.parse(props.response) : '',
path: expressionForm.value.expression,
});
matchResult.value =
JSONPath({
json: props.response ? JSON.parse(props.response) : '',
path: expressionForm.value.expression,
}) || [];
} catch (error) {
matchResult.value = JSONPath({ json: props.response || '', path: expressionForm.value.expression });
matchResult.value = JSONPath({ json: props.response || '', path: expressionForm.value.expression }) || [];
}
break;
case RequestExtractExpressionEnum.REGEX:

View File

@ -5,7 +5,7 @@
{{ t('apiTestDebug.expressionMatchRule') }}
</div>
<a-radio-group v-model:model-value="expressionForm.expressionMatchingRule" size="small">
<a-radio :value="RequestExtractExpressionRuleType.EXPRESSION.toLowerCase()">
<a-radio :value="RequestExtractExpressionRuleType.EXPRESSION">
<div class="flex items-center">
{{ t('apiTestDebug.matchExpression') }}
<a-tooltip :content="t('apiTestDebug.matchExpressionTip')" :content-style="{ maxWidth: '500px' }">
@ -16,7 +16,7 @@
</a-tooltip>
</div>
</a-radio>
<a-radio :value="RequestExtractExpressionRuleType.GROUP.toLowerCase()">
<a-radio :value="RequestExtractExpressionRuleType.GROUP">
<div class="flex items-center">
{{ t('apiTestDebug.matchGroup') }}
<a-tooltip :content="t('apiTestDebug.matchGroupTip')" :content-style="{ maxWidth: '500px' }">
@ -34,7 +34,7 @@
{{ t('apiTestDebug.resultMatchRule') }}
</div>
<a-radio-group v-model:model-value="expressionForm.resultMatchingRule" size="small">
<a-radio :value="RequestExtractResultMatchingRule.RANDOM.toLowerCase()">
<a-radio :value="RequestExtractResultMatchingRule.RANDOM">
<div class="flex items-center">
{{ t('apiTestDebug.randomMatch') }}
<a-tooltip :content="t('apiTestDebug.randomMatchTip')" :content-style="{ maxWidth: '400px' }">
@ -45,7 +45,7 @@
</a-tooltip>
</div>
</a-radio>
<a-radio :value="RequestExtractResultMatchingRule.SPECIFIC.toLowerCase()">
<a-radio :value="RequestExtractResultMatchingRule.SPECIFIC">
<div class="flex items-center">
{{ t('apiTestDebug.specifyMatch') }}
<a-tooltip :content="t('apiTestDebug.specifyMatchTip')" :content-style="{ maxWidth: '400px' }">
@ -56,7 +56,7 @@
</a-tooltip>
</div>
</a-radio>
<a-radio :value="RequestExtractResultMatchingRule.ALL.toLowerCase()">
<a-radio :value="RequestExtractResultMatchingRule.ALL">
<div class="flex items-center">
{{ t('apiTestDebug.allMatch') }}
<a-tooltip :content="t('apiTestDebug.allMatchTip')" :content-style="{ maxWidth: '400px' }">

View File

@ -111,9 +111,9 @@
@change="(val) => handleTypeChange(val, record, rowIndex, columnConfig.addLineDisabled)"
/>
</template>
<template #expressionType="{ record, columnConfig, rowIndex }">
<template #extractType="{ record, columnConfig, rowIndex }">
<a-select
v-model:model-value="record.expressionType"
v-model:model-value="record.extractType"
:options="columnConfig.typeOptions || []"
class="param-input w-[110px]"
size="mini"

View File

@ -49,7 +49,6 @@
import { ref, watch } from 'vue';
import { Message } from '@arco-design/web-vue';
import { addDebugModule, updateDebug, updateDebugModule } from '@/api/modules/api-test/debug';
import { useI18n } from '@/hooks/useI18n';
import useAppStore from '@/store/modules/app';
@ -74,6 +73,9 @@
parentId?: string; // id
nodeId?: string; // id
popupOffset?: number;
addModuleApi?: (params: { projectId: string; parentId: string; name: string }) => Promise<any>;
updateModuleApi?: (params: { id: string; name: string }) => Promise<any>;
updateApiNodeApi?: (params: { id: string; name: string }) => Promise<any>;
}>();
const emit = defineEmits(['update:visible', 'close', 'addFinish', 'renameFinish', 'updateDescFinish']);
@ -121,26 +123,26 @@
if (!errors) {
try {
loading.value = true;
if (props.mode === 'add') {
if (props.mode === 'add' && props.addModuleApi) {
//
await addDebugModule({
await props.addModuleApi({
projectId: appStore.currentProjectId,
parentId: props.parentId || '',
name: form.value.field,
});
Message.success(t('common.addSuccess'));
emit('addFinish', form.value.field);
} else if (props.mode === 'rename' && props.nodeType === 'API') {
} else if (props.mode === 'rename' && props.nodeType === 'API' && props.updateApiNodeApi) {
//
await updateDebug({
await props.updateApiNodeApi({
id: props.nodeId || '',
name: form.value.field,
});
Message.success(t('common.updateSuccess'));
emit('renameFinish', form.value.field, props.nodeId);
} else if (props.mode === 'rename') {
} else if (props.mode === 'rename' && props.updateModuleApi) {
//
await updateDebugModule({
await props.updateModuleApi({
id: props.nodeId || '',
name: form.value.field,
});

View File

@ -86,7 +86,7 @@
<MsCodeEditor
v-model:model-value="currentBodyCode"
class="flex-1"
theme="MS-text"
theme="vs"
height="100%"
:show-full-screen="false"
:show-theme-change="false"
@ -121,7 +121,7 @@
params: ExecuteBody;
layout: 'horizontal' | 'vertical';
secondBoxHeight: number;
uploadTempFileApi?: (file: MsFileItem) => Promise<any>; //
uploadTempFileApi?: (file: File) => Promise<any>; //
fileSaveAsSourceId?: string | number; // id
fileSaveAsApi?: (params: TransferFileParams) => Promise<string>; //
fileModuleOptionsApi?: (projectId: string) => Promise<ModuleTreeNode[]>; //
@ -135,18 +135,18 @@
const { t } = useI18n();
const innerParams = useVModel(props, 'params', emit);
const fileList = ref<any[]>(
innerParams.value.binaryBody && innerParams.value.binaryBody.file ? [innerParams.value.binaryBody.file] : []
);
const fileList = ref<MsFileItem[]>([]);
async function handleFileChange(files: MsFileItem[]) {
if (files.length === 0) {
innerParams.value.binaryBody.file = undefined;
return;
onBeforeMount(() => {
if (innerParams.value.binaryBody && innerParams.value.binaryBody.file) {
fileList.value = [innerParams.value.binaryBody.file as unknown as MsFileItem];
}
});
async function handleFileChange() {
if (!props.uploadTempFileApi) return;
try {
if (fileList.value[0]?.local) {
if (fileList.value[0]?.local && fileList.value[0].file) {
appStore.showLoading();
const res = await props.uploadTempFileApi(fileList.value[0].file);
innerParams.value.binaryBody.file = {
@ -160,7 +160,7 @@
} else {
innerParams.value.binaryBody.file = {
...fileList.value[0],
fileId: fileList.value[0].uid,
fileId: fileList.value[0]?.uid,
fileName: fileList.value[0]?.originalName || '',
fileAlias: fileList.value[0]?.name || '',
local: false,

View File

@ -43,7 +43,9 @@
<a-input
v-model:model-value="requestVModel.url"
:max-length="255"
:placeholder="t('apiTestDebug.urlPlaceholder')"
:placeholder="
props.isDefinition ? t('apiTestDebug.definitionUrlPlaceholder') : t('apiTestDebug.urlPlaceholder')
"
allow-clear
@change="handleUrlChange"
/>
@ -51,8 +53,7 @@
</div>
<div class="ml-[16px]">
<a-dropdown-button
v-if="!requestVModel.executeLoading"
v-permission="[props.permissionMap.execute]"
v-if="!requestVModel.executeLoading && hasAnyPermission([props.permissionMap.execute])"
:disabled="requestVModel.executeLoading || (isHttpProtocol && !requestVModel.url)"
class="exec-btn"
@click="() => execute(isPriorityLocalExec ? 'localExec' : 'serverExec')"
@ -70,12 +71,14 @@
</a-dropdown-button>
<a-button v-else type="primary" class="mr-[12px]" @click="stopDebug">{{ t('common.stop') }}</a-button>
<a-dropdown
v-if="props.isDefinition"
v-permission="[props.permissionMap.create, props.permissionMap.update]"
v-if="props.isDefinition && hasAnyPermission([props.permissionMap.create, props.permissionMap.update])"
:loading="saveLoading || (isHttpProtocol && !requestVModel.url)"
@select="handleSelect"
>
<a-button :disabled="requestVModel.url.trim() === '' || requestVModel.name.trim() === ''" type="secondary">
<a-button
:disabled="(isHttpProtocol && requestVModel.url.trim() === '') || requestVModel.name.trim() === ''"
type="secondary"
>
{{ t('common.save') }}
</a-button>
<template #content>
@ -147,14 +150,14 @@
@change="handlePluginFormChange"
/>
</a-spin>
<debugHeader
<httpHeader
v-if="requestVModel.activeTab === RequestComposition.HEADER"
v-model:params="requestVModel.headers"
:layout="activeLayout"
:second-box-height="secondBoxHeight"
@change="handleActiveDebugChange"
/>
<debugBody
<httpBody
v-else-if="requestVModel.activeTab === RequestComposition.BODY"
v-model:params="requestVModel.body"
:layout="activeLayout"
@ -165,14 +168,14 @@
:file-module-options-api="props.fileModuleOptionsApi"
@change="handleActiveDebugChange"
/>
<debugQuery
<httpQuery
v-else-if="requestVModel.activeTab === RequestComposition.QUERY"
v-model:params="requestVModel.query"
:layout="activeLayout"
:second-box-height="secondBoxHeight"
@change="handleActiveDebugChange"
/>
<debugRest
<httpRest
v-else-if="requestVModel.activeTab === RequestComposition.REST"
v-model:params="requestVModel.rest"
:layout="activeLayout"
@ -182,6 +185,7 @@
<precondition
v-else-if="requestVModel.activeTab === RequestComposition.PRECONDITION"
v-model:config="requestVModel.children[0].preProcessorConfig"
:is-definition="props.isDefinition"
@change="handleActiveDebugChange"
/>
<postcondition
@ -190,14 +194,15 @@
:response="requestVModel.response?.requestResults[0]?.responseResult.body"
:layout="activeLayout"
:second-box-height="secondBoxHeight"
:is-definition="props.isDefinition"
@change="handleActiveDebugChange"
/>
<debugAuth
<auth
v-else-if="requestVModel.activeTab === RequestComposition.AUTH"
v-model:params="requestVModel.authConfig"
@change="handleActiveDebugChange"
/>
<debugSetting
<setting
v-else-if="requestVModel.activeTab === RequestComposition.SETTING"
v-model:params="requestVModel.otherConfig"
@change="handleActiveDebugChange"
@ -280,11 +285,11 @@
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
import MsSplitBox from '@/components/pure/ms-split-box/index.vue';
import MsTab from '@/components/pure/ms-tab/index.vue';
import debugAuth from './auth.vue';
import auth from './auth.vue';
import postcondition from './postcondition.vue';
import precondition from './precondition.vue';
import response from './response/index.vue';
import debugSetting from './setting.vue';
import setting from './setting.vue';
import apiMethodName from '@/views/api-test/components/apiMethodName.vue';
import apiMethodSelect from '@/views/api-test/components/apiMethodSelect.vue';
@ -323,10 +328,10 @@
import type { Api } from '@form-create/arco-design';
// Http
const debugHeader = defineAsyncComponent(() => import('./header.vue'));
const debugBody = defineAsyncComponent(() => import('./body.vue'));
const debugQuery = defineAsyncComponent(() => import('./query.vue'));
const debugRest = defineAsyncComponent(() => import('./rest.vue'));
const httpHeader = defineAsyncComponent(() => import('./header.vue'));
const httpBody = defineAsyncComponent(() => import('./body.vue'));
const httpQuery = defineAsyncComponent(() => import('./query.vue'));
const httpRest = defineAsyncComponent(() => import('./rest.vue'));
export interface RequestCustomAttr {
isNew: boolean;
@ -615,9 +620,13 @@
watch(
() => requestVModel.value.id,
() => {
async () => {
if (requestVModel.value.protocol !== 'HTTP') {
requestVModel.value.activeTab = RequestComposition.PLUGIN;
if (protocolOptions.value.length === 0) {
//
await initProtocolList();
}
initPluginScript();
}
},
@ -846,6 +855,7 @@
...parseRequestBodyResult,
projectId: appStore.currentProjectId,
frontendDebug: executeType === 'localExec',
isNew: requestVModel.value.isNew,
};
}
@ -1004,7 +1014,6 @@
}
onBeforeMount(() => {
initProtocolList();
initLocalConfig();
});

View File

@ -1,13 +1,13 @@
<template>
<condition
v-model:list="innerConfig.processors"
:condition-types="[RequestConditionProcessor.SCRIPT]"
:condition-types="conditionTypes"
add-text="apiTestDebug.postCondition"
:response="props.response"
:height-used="heightUsed"
@change="emit('change')"
>
<!-- <template #titleRight>
<template v-if="props.isDefinition" #titleRight>
<a-switch v-model:model-value="innerConfig.enableGlobal" size="small" type="line"></a-switch>
<div class="ml-[8px] text-[var(--color-text-1)]">{{ t('apiTestDebug.openGlobalPostCondition') }}</div>
<a-tooltip :content="t('apiTestDebug.openGlobalPostConditionTip')" position="left">
@ -16,7 +16,7 @@
size="16"
/>
</a-tooltip>
</template> -->
</template>
</condition>
</template>
@ -25,23 +25,24 @@
import condition from '@/views/api-test/components/condition/index.vue';
import { useI18n } from '@/hooks/useI18n';
import { ExecuteConditionConfig, ExecuteConditionProcessor } from '@/models/apiTest/common';
import { RequestConditionProcessor } from '@/enums/apiEnum';
// import { useI18n } from '@/hooks/useI18n';
const props = defineProps<{
config: ExecuteConditionConfig;
secondBoxHeight?: number;
layout: 'horizontal' | 'vertical';
response?: string; //
isDefinition?: boolean; //
}>();
const emit = defineEmits<{
(e: 'update:params', params: ExecuteConditionProcessor[]): void;
(e: 'change'): void;
}>();
// const { t } = useI18n();
const { t } = useI18n();
const innerConfig = useVModel(props, 'config', emit);
const heightUsed = computed(() => {
if (props.layout === 'horizontal') {
@ -49,6 +50,13 @@
}
return 428 + (props.secondBoxHeight || 0);
});
const conditionTypes = computed(() => {
if (props.isDefinition) {
return [RequestConditionProcessor.SCRIPT, RequestConditionProcessor.SQL, RequestConditionProcessor.EXTRACT];
}
return [RequestConditionProcessor.SCRIPT];
});
</script>
<style lang="less" scoped></style>

View File

@ -1,12 +1,11 @@
<template>
<condition
v-model:list="innerConfig.processors"
:condition-types="[RequestConditionProcessor.SCRIPT, RequestConditionProcessor.TIME_WAITING]"
:condition-types="conditionTypes"
add-text="apiTestDebug.precondition"
@change="emit('change')"
/>
<!-- <template #titleRight>
>
<template v-if="props.isDefinition" #titleRight>
<a-switch v-model:model-value="innerConfig.enableGlobal" size="small" type="line"></a-switch>
<div class="ml-[8px] text-[var(--color-text-1)]">{{ t('apiTestDebug.openGlobalPrecondition') }}</div>
<a-tooltip :content="t('apiTestDebug.openGlobalPreconditionTip')" position="left">
@ -15,7 +14,8 @@
size="16"
/>
</a-tooltip>
</template> -->
</template>
</condition>
</template>
<script setup lang="ts">
@ -23,21 +23,29 @@
import condition from '@/views/api-test/components/condition/index.vue';
import { useI18n } from '@/hooks/useI18n';
import { ExecuteConditionConfig } from '@/models/apiTest/common';
import { RequestConditionProcessor } from '@/enums/apiEnum';
// import { useI18n } from '@/hooks/useI18n';
const props = defineProps<{
config: ExecuteConditionConfig;
isDefinition?: boolean; //
}>();
const emit = defineEmits<{
(e: 'update:config', params: ExecuteConditionConfig): void;
(e: 'change'): void;
}>();
// const { t } = useI18n();
const { t } = useI18n();
const innerConfig = useVModel(props, 'config', emit);
const conditionTypes = computed(() => {
if (props.isDefinition) {
return [RequestConditionProcessor.SCRIPT, RequestConditionProcessor.SQL, RequestConditionProcessor.TIME_WAITING];
}
return [RequestConditionProcessor.SCRIPT, RequestConditionProcessor.TIME_WAITING];
});
</script>
<style lang="less" scoped></style>

View File

@ -109,7 +109,6 @@
:show-theme-change="false"
:show-language-change="false"
:show-charset-change="false"
read-only
>
<template #rightTitle>
<a-button type="outline" class="arco-btn-outline--secondary p-[0_8px]" size="mini" @click="copyScript">
@ -171,7 +170,7 @@
const props = defineProps<{
responseDefinition: ResponseDefinition[];
uploadTempFileApi?: (...args) => Promise<any>; //
uploadTempFileApi?: (file: File) => Promise<any>; //
}>();
const emit = defineEmits<{
(e: 'change'): void;
@ -322,20 +321,17 @@
}
}
const fileList = ref<any[]>(
activeResponse.value.body.binaryBody && activeResponse.value.body.binaryBody.file
? [activeResponse.value.body.binaryBody.file]
: []
);
async function handleFileChange(files: MsFileItem[]) {
if (files.length === 0) {
activeResponse.value.body.binaryBody.file = undefined;
return;
const fileList = ref<MsFileItem[]>([]);
onBeforeMount(() => {
if (activeResponse.value.body.binaryBody && activeResponse.value.body.binaryBody.file) {
fileList.value = [activeResponse.value.body.binaryBody.file as unknown as MsFileItem];
}
});
async function handleFileChange() {
if (!props.uploadTempFileApi) return;
try {
if (fileList.value[0]?.local) {
if (fileList.value[0]?.local && fileList.value[0].file) {
appStore.showLoading();
const res = await props.uploadTempFileApi(fileList.value[0].file);
activeResponse.value.body.binaryBody.file = {

View File

@ -1,5 +1,5 @@
<template>
<div class="h-full rounded-[var(--border-radius-small)] border border-[var(--color-text-n8)] p-[16px]">
<div class="setting">
<a-form :model="settingForm" layout="vertical">
<div class="flex items-center gap-[32px]">
<a-form-item class="flex-1">
@ -99,4 +99,13 @@
}
</script>
<style lang="less" scoped></style>
<style lang="less" scoped>
.setting {
@apply h-full overflow-y-auto;
.ms-scroll-bar();
padding: 16px;
border: 1px solid var(--color-text-n8);
border-radius: var(--border-radius-small);
}
</style>

View File

@ -33,6 +33,7 @@
mode="add"
:all-names="rootModulesName"
parent-id="NONE"
:add-module-api="addDebugModule"
@add-finish="initModules"
>
<MsButton v-permission="['PROJECT_API_DEBUG:READ+ADD']" type="icon" class="!mr-0 p-[2px]">
@ -100,6 +101,7 @@
:field-config="{ field: renameFolderTitle }"
:all-names="(nodeData.children || []).map((e: ModuleTreeNode) => e.name || '')"
:node-type="nodeData.type"
:add-module-api="addDebugModule"
@close="resetFocusNodeKey"
@rename-finish="handleRenameFinish"
>
@ -111,6 +113,8 @@
mode="add"
:all-names="(nodeData.children || []).map((e: ModuleTreeNode) => e.name || '')"
:parent-id="nodeData.id"
:update-module-api="updateDebugModule"
:update-api-node-api="updateDebug"
@close="resetFocusNodeKey"
@add-finish="() => initModules()"
>
@ -137,12 +141,15 @@
import popConfirm from '@/views/api-test/components/popConfirm.vue';
import {
addDebugModule,
deleteDebug,
deleteDebugModule,
dragDebug,
getDebugModuleCount,
getDebugModules,
moveDebugModule,
updateDebug,
updateDebugModule,
} from '@/api/modules/api-test/debug';
import { dropPositionMap } from '@/config/common';
import { useI18n } from '@/hooks/useI18n';

View File

@ -2,6 +2,7 @@ export default {
'apiTestDebug.newApi': 'New request',
'apiTestDebug.importApi': 'Import request',
'apiTestDebug.urlPlaceholder': 'Please enter the full URL including http or https',
'apiTestDebug.definitionUrlPlaceholder': 'Enter the interface URL, starting with "/"',
'apiTestDebug.serverExec': 'Server execution',
'apiTestDebug.localExec': 'Local execution',
'apiTestDebug.noMatchModule': 'No matching module data yet',

View File

@ -2,6 +2,7 @@ export default {
'apiTestDebug.newApi': '新建请求',
'apiTestDebug.importApi': '导入请求',
'apiTestDebug.urlPlaceholder': '请输入包含 http 或 https 的完整URL',
'apiTestDebug.definitionUrlPlaceholder': '输入接口URL以“/”开始',
'apiTestDebug.serverExec': '服务端执行',
'apiTestDebug.localExec': '本地执行',
'apiTestDebug.noMatchModule': '暂无匹配的模块数据',

View File

@ -11,12 +11,14 @@
read-only
@init="(val) => (folderTree = val)"
@folder-node-select="handleNodeSelect"
@change-protocol="handleProtocolChange"
/>
<a-divider direction="vertical" :margin="16"></a-divider>
<apiTable
:active-module="activeModule"
:offspring-ids="offspringIds"
class="flex-1 overflow-hidden !pl-0 !pr-[16px]"
:protocol="protocol"
read-only
/>
</div>
@ -46,11 +48,16 @@
const folderTree = ref<ModuleTreeNode[]>([]);
const activeModule = ref<string>('all');
const offspringIds = ref<string[]>([]);
const protocol = ref('HTTP');
function handleNodeSelect(keys: string[], _offspringIds: string[]) {
[activeModule.value] = keys;
offspringIds.value = _offspringIds;
}
function handleProtocolChange(val: string) {
protocol.value = val;
}
</script>
<style lang="less" scoped></style>

View File

@ -52,10 +52,10 @@
trigger="click"
@popup-visible-change="handleFilterHidden"
>
<a-button type="text" class="arco-btn-text--secondary" @click="methodFilterVisible = true">
<MsButton type="text" class="arco-btn-text--secondary" @click="methodFilterVisible = true">
{{ t(columnConfig.title as string) }}
<icon-down :class="methodFilterVisible ? 'text-[rgb(var(--primary-5))]' : ''" />
</a-button>
</MsButton>
<template #content>
<div class="arco-table-filters-content">
<div class="flex items-center justify-center px-[6px] py-[2px]">
@ -75,16 +75,16 @@
trigger="click"
@popup-visible-change="handleFilterHidden"
>
<a-button type="text" class="arco-btn-text--secondary" @click="statusFilterVisible = true">
<MsButton type="text" class="arco-btn-text--secondary ml-[10px]" @click="statusFilterVisible = true">
{{ t(columnConfig.title as string) }}
<icon-down :class="statusFilterVisible ? 'text-[rgb(var(--primary-5))]' : ''" />
</a-button>
</MsButton>
<template #content>
<div class="arco-table-filters-content">
<div class="flex items-center justify-center px-[6px] py-[2px]">
<a-checkbox-group v-model:model-value="statusFilters" direction="vertical" size="small">
<a-checkbox v-for="key of RequestDefinitionStatus" :key="key" :value="key">
<apiStatus :status="key" />
<a-checkbox v-for="val of Object.values(RequestDefinitionStatus)" :key="val" :value="val">
<apiStatus :status="val" />
</a-checkbox>
</a-checkbox-group>
</div>
@ -108,7 +108,7 @@
<template #label>
<apiStatus :status="record.status" />
</template>
<a-option v-for="item of RequestDefinitionStatus" :key="item" :value="item">
<a-option v-for="item of Object.values(RequestDefinitionStatus)" :key="item" :value="item">
<apiStatus :status="item" />
</a-option>
</a-select>
@ -271,6 +271,7 @@
class?: string;
activeModule: string;
offspringIds: string[];
protocol: string; //
readOnly?: boolean; //
}>();
const emit = defineEmits<{
@ -346,12 +347,6 @@
titleSlotName: 'statusFilter',
width: 130,
},
{
title: 'apiTestManagement.responsiblePerson',
dataIndex: 'createUserName',
showTooltip: true,
width: 120,
},
{
title: 'apiTestManagement.path',
slotName: 'path',
@ -474,6 +469,7 @@
projectId: appStore.currentProjectId,
moduleIds: moduleIds.value,
env: checkedEnv.value,
protocol: props.protocol,
filter: { status: statusFilters.value, type: methodFilters.value },
};
setLoadListParams(params);
@ -495,6 +491,13 @@
}
);
watch(
() => props.protocol,
() => {
loadApiList();
}
);
function handleFilterHidden(val: boolean) {
if (!val) {
loadApiList();

View File

@ -9,7 +9,12 @@
</MsEditableTab>
</div>
<div v-show="activeApiTab.id === 'all'" class="flex-1">
<apiTable :active-module="props.activeModule" :offspring-ids="props.offspringIds" @open-api-tab="openApiTab" />
<apiTable
:active-module="props.activeModule"
:offspring-ids="props.offspringIds"
:protocol="props.protocol"
@open-api-tab="openApiTab"
/>
</div>
<div v-if="activeApiTab.id !== 'all'" class="flex-1 overflow-hidden">
<a-tabs default-active-key="definition" animation lazy-load class="ms-api-tab-nav">
@ -68,7 +73,7 @@
<template #label>
<apiStatus :status="activeApiTab.status" />
</template>
<a-option v-for="item of RequestDefinitionStatus" :key="item" :value="item">
<a-option v-for="item of Object.values(RequestDefinitionStatus)" :key="item" :value="item">
<apiStatus :status="item" />
</a-option>
</a-select>
@ -178,6 +183,7 @@
activeModule: string;
offspringIds: string[];
moduleTree: ModuleTreeNode[]; //
protocol: string;
}>();
const emit = defineEmits(['addDone']);

View File

@ -7,6 +7,7 @@
:active-module="props.activeModule"
:all-count="props.allCount"
:offspring-ids="props.offspringIds"
:protocol="protocol"
/>
</a-tab-pane>
<a-tab-pane key="case" title="CASE" class="ms-api-tab-pane"> </a-tab-pane>
@ -26,6 +27,7 @@
allCount: number;
activeModule: string;
offspringIds: string[];
protocol: string;
moduleTree: ModuleTreeNode[]; //
}>();

View File

@ -51,7 +51,13 @@
<a-doption value="addModule">{{ t('apiTestManagement.addSubModule') }}</a-doption>
</template>
</a-dropdown>
<popConfirm mode="add" :all-names="rootModulesName" parent-id="NONE" @add-finish="initModules">
<popConfirm
mode="add"
:all-names="rootModulesName"
parent-id="NONE"
:add-module-api="addModule"
@add-finish="initModules"
>
<span id="addModulePopSpan"></span>
</popConfirm>
</template>
@ -105,6 +111,7 @@
mode="add"
:all-names="(nodeData.children || []).map((e: ModuleTreeNode) => e.name || '')"
:parent-id="nodeData.id"
:add-module-api="addModule"
@close="resetFocusNodeKey"
@add-finish="() => initModules()"
>
@ -119,6 +126,8 @@
:node-id="nodeData.id"
:field-config="{ field: renameFolderTitle }"
:all-names="(nodeData.children || []).map((e: ModuleTreeNode) => e.name || '')"
:update-module-api="updateModule"
:update-api-node-api="updateModule"
@close="resetFocusNodeKey"
@rename-finish="initModules"
>
@ -144,11 +153,13 @@
import { getProtocolList } from '@/api/modules/api-test/common';
import {
addModule,
deleteModule,
getModuleCount,
getModuleTree,
getModuleTreeOnlyModules,
moveModule,
updateModule,
} from '@/api/modules/api-test/management';
import { useI18n } from '@/hooks/useI18n';
import useModal from '@/hooks/useModal';
@ -168,7 +179,7 @@
activeModule: 'all',
}
);
const emit = defineEmits(['init', 'newApi', 'import', 'folderNodeSelect', 'clickApiNode']);
const emit = defineEmits(['init', 'newApi', 'import', 'folderNodeSelect', 'clickApiNode', 'changeProtocol']);
const appStore = useAppStore();
const { t } = useI18n();
@ -345,7 +356,7 @@
if (isSetDefaultKey) {
selectedKeys.value = [folderTree.value[0].id];
}
emit('init', folderTree.value);
emit('init', folderTree.value, moduleProtocol.value);
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
@ -377,6 +388,7 @@
}
async function handleProtocolChange() {
emit('changeProtocol', moduleProtocol.value);
await initModules();
initModuleCount();
}

View File

@ -5,11 +5,12 @@
<div class="p-[24px]">
<moduleTree
:active-node-id="activeApi?.id"
@init="(val) => (folderTree = val)"
@init="handleModuleInit"
@new-api="newApi"
@import="importDrawerVisible = true"
@folder-node-select="handleNodeSelect"
@click-api-node="handleApiNodeClick"
@change-protocol="handleProtocolChange"
/>
</div>
<!-- <div class="b-0 absolute w-[88%]">
@ -42,6 +43,7 @@
:all-count="allCount"
:active-module="activeModule"
:offspring-ids="offspringIds"
:protocol="protocol"
/>
</div>
</template>
@ -66,9 +68,15 @@
const allCount = ref(0);
const importDrawerVisible = ref(false);
const offspringIds = ref<string[]>([]);
const protocol = ref('HTTP');
const activeApi = ref<RequestParam>();
const managementRef = ref<InstanceType<typeof management>>();
function handleModuleInit(tree, _protocol: string) {
folderTree.value = tree;
protocol.value = _protocol;
}
function newApi() {
managementRef.value?.newTab();
}
@ -86,6 +94,10 @@
activeApi.value = params;
}
function handleProtocolChange(val: string) {
protocol.value = val;
}
provide('setActiveApi', setActiveApi);
</script>