feat(接口测试): 接口调试-模块树+ header 参数调整
This commit is contained in:
parent
82f8a60794
commit
a294a486a3
|
@ -4,6 +4,7 @@ import {
|
|||
AddDebugModuleUrl,
|
||||
DeleteDebugModuleUrl,
|
||||
ExecuteApiDebugUrl,
|
||||
GetApiDebugDetailUrl,
|
||||
GetDebugModuleCountUrl,
|
||||
GetDebugModulesUrl,
|
||||
MoveDebugModuleUrl,
|
||||
|
@ -13,6 +14,7 @@ import {
|
|||
|
||||
import {
|
||||
AddDebugModuleParams,
|
||||
DebugDetail,
|
||||
ExecuteRequestParams,
|
||||
SaveDebugParams,
|
||||
UpdateDebugModule,
|
||||
|
@ -64,3 +66,8 @@ export function addDebug(data: SaveDebugParams) {
|
|||
export function updateDebug(data: UpdateDebugParams) {
|
||||
return MSR.post({ url: UpdateApiDebugUrl, data });
|
||||
}
|
||||
|
||||
// 获取接口调试详情
|
||||
export function getDebugDetail(id: string) {
|
||||
return MSR.get<DebugDetail>({ url: GetApiDebugDetailUrl, params: id });
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
export const ExecuteApiDebugUrl = '/api/debug/debug'; // 执行调试
|
||||
export const AddApiDebugUrl = '/api/debug/add'; // 新增调试
|
||||
export const UpdateApiDebugUrl = '/api/debug/update'; // 更新调试
|
||||
export const GetApiDebugDetailUrl = '/api/debug/get'; // 获取接口调试详情
|
||||
export const UpdateDebugModuleUrl = '/api/debug/module/update'; // 更新模块
|
||||
export const MoveDebugModuleUrl = '/api/debug/module/move'; // 移动模块
|
||||
export const GetDebugModuleCountUrl = '/api/debug/module/count'; // 模块统计数量
|
||||
|
|
|
@ -146,8 +146,8 @@
|
|||
},
|
||||
{
|
||||
title: 'project.commonScript.description',
|
||||
slotName: 'desc',
|
||||
dataIndex: 'desc',
|
||||
slotName: 'description',
|
||||
dataIndex: 'description',
|
||||
},
|
||||
{
|
||||
title: 'project.commonScript.isRequired',
|
||||
|
|
|
@ -166,13 +166,6 @@ export const mockStringGroup: MockParamItem[] = [
|
|||
],
|
||||
},
|
||||
];
|
||||
// 字符串变量特殊处理
|
||||
export const specialStringVars = [
|
||||
'@character(pool)',
|
||||
"@character('lower')",
|
||||
"@character('upper')",
|
||||
"@character('symbol')",
|
||||
];
|
||||
// mock日期分组
|
||||
export const mockDateGroup: MockParamItem[] = [
|
||||
{
|
||||
|
@ -901,3 +894,15 @@ export const JMeterAllVars = [
|
|||
...JMeterInfoGroup,
|
||||
...JMeterStringGroup,
|
||||
];
|
||||
// 同名函数但参数不同,需要特殊处理
|
||||
export const sameFuncNameVars = [
|
||||
'@character(pool)',
|
||||
"@character('lower')",
|
||||
"@character('upper')",
|
||||
"@character('symbol')",
|
||||
'@idCard(birth)',
|
||||
'@natural(1,100)',
|
||||
'@integer(1,100)',
|
||||
];
|
||||
// 带形参的函数集合,指的是函数入参为形参,如果用户未填写实参则不需要填充到入参框中
|
||||
export const formalParameterVars = ['@character(pool)', '@idCard(birth)'];
|
||||
|
|
|
@ -238,12 +238,13 @@
|
|||
import { useI18n } from '@/hooks/useI18n';
|
||||
|
||||
import {
|
||||
formalParameterVars,
|
||||
JMeterAllGroup,
|
||||
JMeterAllVars,
|
||||
mockAllGroup,
|
||||
mockAllParams,
|
||||
mockFunctions,
|
||||
specialStringVars,
|
||||
sameFuncNameVars,
|
||||
} from './config';
|
||||
import type { MockParamInputGroupItem, MockParamItem } from './types';
|
||||
import type { AutoComplete, CascaderOption, FormInstance } from '@arco-design/web-vue';
|
||||
|
@ -375,7 +376,8 @@
|
|||
// 匹配@开头的函数名
|
||||
const regex = /@([a-zA-Z]+)(\([^)]*\))?/;
|
||||
const currentParamType = mockAllParams.find((e) => {
|
||||
if (specialStringVars.includes(val)) {
|
||||
if (sameFuncNameVars.includes(val)) {
|
||||
// 如果是同名函数,但可能是不同的变量,所以需要全等匹配
|
||||
return e.value === val;
|
||||
}
|
||||
if (e.value.match(regex)?.[1] === val.match(regex)?.[1]) {
|
||||
|
@ -428,6 +430,7 @@
|
|||
*/
|
||||
function openParamSetting() {
|
||||
if (/^\$/.test(innerValue.value)) {
|
||||
// 如果是 JMeter 变量
|
||||
paramSettingType.value = 'jmeter';
|
||||
if (JMeterAllVars.findIndex((e) => e.value === innerValue.value) !== -1) {
|
||||
paramForm.value.JMeterType = innerValue.value;
|
||||
|
@ -435,6 +438,7 @@
|
|||
paramForm.value.JMeterType = '';
|
||||
}
|
||||
} else if (/^@/.test(innerValue.value)) {
|
||||
// 如果是 Mock 变量
|
||||
const valueArr = innerValue.value.split('|'); // 分割 mock变量和函数
|
||||
if (valueArr[0]) {
|
||||
// 匹配@开头的变量名
|
||||
|
@ -444,16 +448,23 @@
|
|||
if (variableMatch) {
|
||||
const variableName = variableMatch[1];
|
||||
const variableParams = variableMatch[2]?.split(',').map((param) => param.trim());
|
||||
const formalParameterVar = formalParameterVars.find((e) => e.includes(variableName)); // 匹配带形参的函数,监测输入的变量是否是带形参的函数
|
||||
|
||||
if (variableName === 'character') {
|
||||
handleParamTypeChange(`@${variableName}(${variableParams})`); // character变量特殊处理
|
||||
if (formalParameterVar && variableParams.length > 0) {
|
||||
// 如果是带形参的函数,则需要使用 原函数名(形式参数) 的变量
|
||||
handleParamTypeChange(formalParameterVar);
|
||||
} else if (sameFuncNameVars.includes(valueArr[0])) {
|
||||
// 如果是同名函数,但可能是不同的变量,所以需要全等匹配
|
||||
handleParamTypeChange(valueArr[0]);
|
||||
} else {
|
||||
handleParamTypeChange(`@${variableName}`); // 设置匹配的变量参数输入框组
|
||||
}
|
||||
if (variableName !== 'character' || (variableName === 'character' && variableParams?.[0] !== 'pool')) {
|
||||
// 字符串变量@character(pool)特殊处理,不需要填入 pool
|
||||
if (!formalParameterVars.includes(valueArr[0])) {
|
||||
// 如果是带形式参数的函数,且未填实参,则不需要填入参数
|
||||
// 如果是带形式参数的函数,但已填写实参,则需要填入参数
|
||||
// 如果是非形式参数的函数,则需要填入参数
|
||||
(variableParams || []).forEach((e, i) => {
|
||||
// 设置变量入参
|
||||
// 根据入参顺序,设置变量入参
|
||||
paramForm.value[`param${i + 1}`] = Number.isNaN(Number(e)) ? e : Number(e);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -470,9 +470,12 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.arco-tree-node-disabled-selectable {
|
||||
@apply !cursor-default;
|
||||
.arco-tree-node-disabled-selectable {
|
||||
@apply cursor-default;
|
||||
.arco-tree-node-title {
|
||||
@apply cursor-default;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
<div
|
||||
:class="`ms-split-box ${props.direction === 'horizontal' ? 'ms-split-box--left' : 'ms-split-box--top'} ${
|
||||
props.disabled && props.direction === 'horizontal' ? 'border-r border-[var(--color-text-n8)]' : ''
|
||||
} ${props.firstContainerClass}`"
|
||||
} ${props.firstContainerClass || ''}`"
|
||||
>
|
||||
<div
|
||||
v-if="props.direction === 'horizontal' && props.expandDirection === 'right' && !props.disabled"
|
||||
|
@ -171,6 +171,11 @@
|
|||
.ms-split-box {
|
||||
@apply relative h-full overflow-auto;
|
||||
.ms-scroll-bar();
|
||||
:deep(.arco-split-vertical) {
|
||||
.arco-split-pane-first {
|
||||
padding-bottom: 6px; // 为了避免滚动条遮挡,垂直布局下第二个盒子顶部有6px的阴影
|
||||
}
|
||||
}
|
||||
}
|
||||
.ms-split-box--left {
|
||||
width: calc(v-bind(innerSize) - 2px);
|
||||
|
|
|
@ -69,9 +69,9 @@
|
|||
:title="item.slotName"
|
||||
>
|
||||
<template #title>
|
||||
<div :class="{ 'flex w-full flex-row flex-nowrap items-center': !item.align }">
|
||||
<div :class="{ 'flex w-full flex-row flex-nowrap items-center gap-[16px]': !item.align }">
|
||||
<slot :name="item.titleSlotName" :column-config="item">
|
||||
<div class="text-[var(--color-text-3)]">{{ t(item.title as string) }}</div>
|
||||
<div v-if="item.title" class="text-[var(--color-text-3)]">{{ t(item.title as string) }}</div>
|
||||
</slot>
|
||||
<columnSelectorIcon
|
||||
v-if="
|
||||
|
|
|
@ -109,7 +109,6 @@
|
|||
|
||||
<style lang="scss" scoped>
|
||||
.setting-icon {
|
||||
margin-left: 16px;
|
||||
color: var(--color-text-4);
|
||||
background-color: var(--color-text-10);
|
||||
cursor: pointer;
|
||||
|
|
|
@ -8,6 +8,8 @@ export enum TableModuleEnum {
|
|||
|
||||
export enum TableKeyEnum {
|
||||
API_TEST = 'apiTest',
|
||||
API_TEST_DEBUG_FORM_DATA = 'apiTestDebugFormData',
|
||||
API_TEST_DEBUG_FORM_URL_ENCODE = 'apiTestDebugFormUrlEncoded',
|
||||
SYSTEM_USER = 'systemUser',
|
||||
SYSTEM_RESOURCEPOOL = 'systemResourcePool',
|
||||
SYSTEM_AUTH = 'systemAuth',
|
||||
|
|
|
@ -20,11 +20,11 @@ import {
|
|||
} from '@/enums/apiEnum';
|
||||
|
||||
// 条件操作类型
|
||||
export type ConditionType = keyof typeof RequestConditionProcessor;
|
||||
export type ConditionType = RequestConditionProcessor;
|
||||
// 断言-匹配条件规则
|
||||
export type RequestAssertionConditionType = keyof typeof RequestAssertionCondition;
|
||||
export type RequestAssertionConditionType = RequestAssertionCondition;
|
||||
// 前后置条件-脚本语言类型
|
||||
export type RequestConditionScriptLanguageType = keyof typeof RequestConditionScriptLanguage;
|
||||
export type RequestConditionScriptLanguageType = RequestConditionScriptLanguage;
|
||||
// 响应时间信息
|
||||
export interface ResponseTiming {
|
||||
dnsLookupTime: number;
|
||||
|
@ -41,12 +41,17 @@ export interface KeyValueParam {
|
|||
key: string;
|
||||
value: string;
|
||||
}
|
||||
// 接口请求-带开启关闭的参数集合信息
|
||||
export interface EnableKeyValueParam extends KeyValueParam {
|
||||
description: string;
|
||||
enable: boolean; // 参数是否启用
|
||||
}
|
||||
// 接口请求公共参数集合信息
|
||||
export interface ExecuteRequestCommonParam {
|
||||
export interface ExecuteRequestCommonParam extends EnableKeyValueParam {
|
||||
encode: boolean; // 是否编码
|
||||
maxLength: number;
|
||||
minLength: number;
|
||||
paramType: keyof typeof RequestParamsType; // 参数类型
|
||||
paramType: RequestParamsType; // 参数类型
|
||||
required: boolean;
|
||||
description: string;
|
||||
enable: boolean; // 参数是否启用
|
||||
|
@ -57,7 +62,7 @@ export type ExecuteRequestFormBodyFormValue = ExecuteRequestCommonParam & {
|
|||
fileId: string;
|
||||
fileName: string;
|
||||
}[];
|
||||
contentType?: keyof typeof RequestContentTypeEnum & string;
|
||||
contentType?: RequestContentTypeEnum & string;
|
||||
};
|
||||
export interface ExecuteRequestFormBody {
|
||||
formValues: ExecuteRequestFormBodyFormValue[];
|
||||
|
@ -77,11 +82,6 @@ export interface ExecuteJsonBody {
|
|||
jsonSchema?: string;
|
||||
jsonValue: string;
|
||||
}
|
||||
// 接口请求-带开启关闭的参数集合信息
|
||||
export interface EnableKeyValueParam extends KeyValueParam {
|
||||
description: string;
|
||||
enable: boolean; // 参数是否启用
|
||||
}
|
||||
// 执行请求配置
|
||||
export interface ExecuteOtherConfig {
|
||||
autoRedirects: boolean; // 是否自动重定向 默认 false
|
||||
|
@ -94,12 +94,12 @@ export interface ExecuteOtherConfig {
|
|||
export interface ResponseAssertionCommon {
|
||||
name: string; // 断言名称
|
||||
enable: boolean; // 是否启用断言
|
||||
assertionType: keyof typeof ResponseAssertionType; // 断言类型
|
||||
assertionType: ResponseAssertionType; // 断言类型
|
||||
}
|
||||
// 断言-断言列表泛型
|
||||
export interface ResponseAssertionGenerics<T> {
|
||||
assertions: T[];
|
||||
responseFormat?: keyof typeof ResponseBodyXPathAssertionFormat;
|
||||
responseFormat?: ResponseBodyXPathAssertionFormat;
|
||||
}
|
||||
// 断言-响应头断言子项
|
||||
export interface ResponseHeaderAssertionItem {
|
||||
|
@ -119,13 +119,13 @@ export interface ResponseDocumentAssertionElement {
|
|||
expectedResult: Record<string, any>; // 匹配值 即预期结果
|
||||
include: boolean; // 是否必含
|
||||
paramName: string; // 参数名
|
||||
type: keyof typeof ResponseBodyDocumentAssertionType; // 断言类型
|
||||
type: ResponseBodyDocumentAssertionType; // 断言类型
|
||||
typeVerification: boolean; // 是否类型验证
|
||||
}
|
||||
// 断言-文档断言
|
||||
export interface ResponseDocumentAssertion {
|
||||
enable: boolean; // 是否启用
|
||||
documentType: keyof typeof ResponseBodyAssertionDocumentType; // 文档类型
|
||||
documentType: ResponseBodyAssertionDocumentType; // 文档类型
|
||||
followApiId: string; // 跟随定义的apiId 传空为不跟随接口定义
|
||||
jsonAssertion: ResponseDocumentAssertionElement;
|
||||
xmlAssertion: ResponseDocumentAssertionElement;
|
||||
|
@ -153,7 +153,7 @@ export interface ScriptCommonConfig {
|
|||
}
|
||||
// 断言-响应体断言
|
||||
export interface ResponseBodyAssertion {
|
||||
assertionBodyType: keyof typeof ResponseBodyAssertionType; // 断言类型
|
||||
assertionBodyType: ResponseBodyAssertionType; // 断言类型
|
||||
documentAssertion: ResponseDocumentAssertion; // 文档断言
|
||||
jsonPathAssertion: ResponseAssertionGenerics<ResponseJSONPathAssertionItem>; // JSONPath断言
|
||||
regexAssertion: ResponseAssertionGenerics<ResponseRegexAssertionItem>; // 正则断言
|
||||
|
@ -171,7 +171,7 @@ export interface ResponseVariableAssertion {
|
|||
export interface ExecuteConditionProcessorCommon {
|
||||
enable: boolean; // 是否启用
|
||||
name: string; // 请求名称
|
||||
processorType: keyof typeof RequestConditionProcessor;
|
||||
processorType: RequestConditionProcessor;
|
||||
}
|
||||
// 执行请求-前后置条件-脚本处理器
|
||||
export type ScriptProcessor = ScriptCommonConfig;
|
||||
|
@ -191,27 +191,27 @@ export interface TimeWaitingProcessor {
|
|||
delay: number; // 等待时间 单位:毫秒
|
||||
}
|
||||
// 表达式类型
|
||||
export type ExpressionType = keyof typeof RequestExtractExpressionEnum;
|
||||
export type ExpressionType = RequestExtractExpressionEnum;
|
||||
// 表达式配置
|
||||
export interface ExpressionCommonConfig {
|
||||
enable: boolean; // 是否启用
|
||||
expression: string;
|
||||
extractType: ExpressionType; // 表达式类型
|
||||
variableName: string;
|
||||
variableType: keyof typeof RequestExtractEnvType;
|
||||
resultMatchingRule: keyof typeof RequestExtractResultMatchingRule; // 结果匹配规则
|
||||
variableType: RequestExtractEnvType;
|
||||
resultMatchingRule: RequestExtractResultMatchingRule; // 结果匹配规则
|
||||
resultMatchingRuleNum: number; // 匹配第几条结果
|
||||
}
|
||||
// 正则提取配置
|
||||
export interface RegexExtract extends ExpressionCommonConfig {
|
||||
expressionMatchingRule: keyof typeof RequestExtractExpressionRuleType; // 正则表达式匹配规则
|
||||
extractScope: keyof typeof RequestExtractScope; // 正则提取范围
|
||||
expressionMatchingRule: RequestExtractExpressionRuleType; // 正则表达式匹配规则
|
||||
extractScope: RequestExtractScope; // 正则提取范围
|
||||
}
|
||||
// JSONPath提取配置
|
||||
export type JSONPathExtract = ExpressionCommonConfig;
|
||||
// XPath提取配置
|
||||
export interface XPathExtract extends ExpressionCommonConfig {
|
||||
responseFormat: keyof typeof ResponseBodyXPathAssertionFormat; // 响应格式
|
||||
responseFormat: ResponseBodyXPathAssertionFormat; // 响应格式
|
||||
}
|
||||
// 执行请求-前后置条件-参数提取处理器
|
||||
export interface ExtractProcessor {
|
||||
|
@ -246,7 +246,7 @@ export interface ExecuteCommonChild {
|
|||
}
|
||||
// 执行请求-认证配置
|
||||
export interface ExecuteAuthConfig {
|
||||
authType: keyof typeof RequestAuthType;
|
||||
authType: RequestAuthType;
|
||||
password: string;
|
||||
username: string;
|
||||
}
|
||||
|
@ -256,7 +256,7 @@ export interface ExecuteValueBody {
|
|||
}
|
||||
// 执行请求- body 配置
|
||||
export interface ExecuteBody {
|
||||
bodyType: keyof typeof RequestBodyFormat;
|
||||
bodyType: RequestBodyFormat;
|
||||
binaryBody: ExecuteBinaryBody;
|
||||
formDataBody: ExecuteRequestFormBody;
|
||||
jsonBody: ExecuteJsonBody;
|
||||
|
@ -269,7 +269,7 @@ export interface ExecuteHTTPRequestFullParams {
|
|||
authConfig: ExecuteAuthConfig;
|
||||
body: ExecuteBody;
|
||||
headers: EnableKeyValueParam[];
|
||||
method: keyof typeof RequestMethods;
|
||||
method: RequestMethods;
|
||||
otherConfig: ExecuteOtherConfig;
|
||||
path: string;
|
||||
query: ExecuteRequestCommonParam[];
|
||||
|
@ -297,7 +297,7 @@ export interface ExecuteRequestParams {
|
|||
export interface SaveDebugParams {
|
||||
name: string;
|
||||
protocol: string;
|
||||
method: keyof typeof RequestMethods;
|
||||
method: RequestMethods;
|
||||
path: string;
|
||||
projectId: string;
|
||||
moduleId: string;
|
||||
|
@ -322,3 +322,31 @@ export interface AddDebugModuleParams {
|
|||
name: string;
|
||||
parentId: string;
|
||||
}
|
||||
// 接口调试详情-请求参数
|
||||
export interface DebugDetailRequest {
|
||||
stepId: string;
|
||||
resourceId: string;
|
||||
projectId: string;
|
||||
name: string;
|
||||
enable: boolean;
|
||||
children: string[];
|
||||
parent: string;
|
||||
polymorphicName: string;
|
||||
}
|
||||
// 接口调试详情
|
||||
export interface DebugDetail {
|
||||
id: string;
|
||||
name: string;
|
||||
protocol: string;
|
||||
method: string;
|
||||
path: string;
|
||||
projectId: string;
|
||||
moduleId: string;
|
||||
createTime: number;
|
||||
createUser: string;
|
||||
updateTime: number;
|
||||
updateUser: string;
|
||||
pos: number;
|
||||
request: DebugDetailRequest & (ExecuteHTTPRequestFullParams | ExecutePluginRequestParams);
|
||||
response: string;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import { RequestMethods } from '@/enums/apiEnum';
|
||||
|
||||
// 请求返回结构
|
||||
export default interface CommonResponse<T> {
|
||||
code: number;
|
||||
|
@ -53,9 +55,12 @@ export interface MoveModules {
|
|||
export interface ModuleTreeNode {
|
||||
id: string;
|
||||
name: string;
|
||||
type: string;
|
||||
type: 'MODULE' | 'API';
|
||||
children: ModuleTreeNode[];
|
||||
attachInfo: Record<string, any>; // 附加信息
|
||||
attachInfo: {
|
||||
method?: keyof typeof RequestMethods;
|
||||
protocol: string;
|
||||
}; // 附加信息
|
||||
count: 0;
|
||||
parentId: string;
|
||||
path: string;
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { findKey } from 'lodash-es';
|
||||
import JSEncrypt from 'jsencrypt';
|
||||
|
||||
import { BatchActionQueryParams, MsTableColumn, MsTableColumnData } from '@/components/pure/ms-table/type';
|
||||
import { BatchActionQueryParams, MsTableColumnData } from '@/components/pure/ms-table/type';
|
||||
|
||||
import { CustomFieldItem } from '@/models/bug-management';
|
||||
|
||||
|
@ -216,6 +215,38 @@ export function mapTree<T>(
|
|||
.filter(Boolean);
|
||||
}
|
||||
|
||||
/**
|
||||
* 过滤树形数组或树
|
||||
* @param tree 树形数组或树
|
||||
* @param customNodeFn 自定义节点函数
|
||||
* @param customChildrenKey 自定义子节点的key
|
||||
* @returns 遍历后的树形数组
|
||||
*/
|
||||
export function filterTree<T>(
|
||||
tree: TreeNode<T> | TreeNode<T>[] | T | T[],
|
||||
filterFn: (node: TreeNode<T>) => boolean,
|
||||
customChildrenKey = 'children'
|
||||
): TreeNode<T>[] {
|
||||
if (!Array.isArray(tree)) {
|
||||
tree = [tree];
|
||||
}
|
||||
const filteredTree: TreeNode<T>[] = [];
|
||||
for (let i = 0; i < tree.length; i++) {
|
||||
const node = tree[i];
|
||||
// 如果节点满足过滤条件,则保留该节点,并递归过滤子节点
|
||||
if (filterFn(node)) {
|
||||
const newNode: TreeNode<T> = { ...node };
|
||||
if (node[customChildrenKey] && node[customChildrenKey].length > 0) {
|
||||
// 递归过滤子节点,并将过滤后的子节点添加到当前节点中
|
||||
newNode[customChildrenKey] = filterTree(node[customChildrenKey], filterFn, customChildrenKey);
|
||||
} else {
|
||||
newNode[customChildrenKey] = [];
|
||||
}
|
||||
filteredTree.push(newNode);
|
||||
}
|
||||
}
|
||||
return filteredTree;
|
||||
}
|
||||
/**
|
||||
* 根据属性 key 查找树形数组中匹配的某个节点
|
||||
* @param trees 属性数组
|
||||
|
|
|
@ -47,7 +47,7 @@
|
|||
|
||||
const methodColor = computed(() => {
|
||||
const colorMap = colorMaps.find((item) => item.includes.includes(props.method));
|
||||
return colorMap?.color;
|
||||
return colorMap?.color || 'rgb(var(--link-7))'; // 方法映射内找不到对应的 key 说明是插件,所有的插件协议颜色都是一样的
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
@ -42,15 +42,16 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { isEmpty } from 'lodash-es';
|
||||
|
||||
import MsCodeEditor from '@/components/pure/ms-code-editor/index.vue';
|
||||
import MsDrawer from '@/components/pure/ms-drawer/index.vue';
|
||||
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
|
||||
import { RequestContentTypeEnum, RequestParamsType } from '@/enums/apiEnum';
|
||||
|
||||
const props = defineProps<{
|
||||
params: Record<string, any>[];
|
||||
defaultParamItem?: Record<string, any>; // 默认参数项
|
||||
}>();
|
||||
const emit = defineEmits<{
|
||||
(e: 'apply', resultArr: (Record<string, any> | null)[]): void;
|
||||
|
@ -66,8 +67,8 @@
|
|||
(val) => {
|
||||
if (val) {
|
||||
batchParamsCode.value = props.params
|
||||
.filter((e) => e && (e.name !== '' || e.value !== ''))
|
||||
.map((item) => `${item.name}:${item.value}`)
|
||||
.filter((e) => e && (!isEmpty(e.key) || !isEmpty(e.value)))
|
||||
.map((item) => `${item.key}:${item.value}`)
|
||||
.join('\n');
|
||||
}
|
||||
},
|
||||
|
@ -83,19 +84,13 @@
|
|||
const arr = batchParamsCode.value.replaceAll('\r', '\n').split('\n'); // 先将回车符替换成换行符,避免粘贴的代码是以回车符分割的,然后以换行符分割
|
||||
const tempObj: Record<string, any> = {}; // 同名参数去重,保留最新的
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
const [name, value] = arr[i].split(':');
|
||||
if (name) {
|
||||
tempObj[name.trim()] = {
|
||||
const [key, value] = arr[i].split(':');
|
||||
if (key) {
|
||||
tempObj[key.trim()] = {
|
||||
id: new Date().getTime() + i,
|
||||
name: name.trim(),
|
||||
...props.defaultParamItem,
|
||||
key: key.trim(),
|
||||
value: value?.trim(),
|
||||
required: false,
|
||||
type: RequestParamsType.STRING,
|
||||
min: undefined,
|
||||
max: undefined,
|
||||
contentType: RequestContentTypeEnum.TEXT,
|
||||
desc: '',
|
||||
encode: false,
|
||||
};
|
||||
}
|
||||
}
|
|
@ -222,7 +222,7 @@
|
|||
:selectable="false"
|
||||
:scroll="{ x: '700px' }"
|
||||
:response="props.response"
|
||||
:height-used="(props.heightUsed || 0) + 62"
|
||||
:height-used="(props.heightUsed || 0) + 68"
|
||||
@change="handleExtractParamTableChange"
|
||||
@more-action-select="(e,r)=> handleExtractParamMoreActionSelect(e,r as ExpressionConfig)"
|
||||
>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<MsBaseTable v-bind="propsRes" :hoverable="false" no-disable v-on="propsEvent">
|
||||
<MsBaseTable v-bind="propsRes" :hoverable="false" no-disable is-simple-setting v-on="propsEvent">
|
||||
<!-- 表格头 slot -->
|
||||
<template #encodeTitle>
|
||||
<div class="flex items-center text-[var(--color-text-3)]">
|
||||
|
@ -28,8 +28,8 @@
|
|||
</div>
|
||||
</template>
|
||||
<!-- 表格列 slot -->
|
||||
<template #name="{ record, columnConfig }">
|
||||
<a-popover position="tl" :disabled="!record.name || record.name.trim() === ''" class="ms-params-input-popover">
|
||||
<template #key="{ record, columnConfig }">
|
||||
<a-popover position="tl" :disabled="!record.key || record.key.trim() === ''" class="ms-params-input-popover">
|
||||
<template #content>
|
||||
<div class="param-popover-title">
|
||||
{{ t('apiTestDebug.paramName') }}
|
||||
|
@ -43,7 +43,7 @@
|
|||
:placeholder="t('apiTestDebug.paramNamePlaceholder')"
|
||||
class="param-input"
|
||||
:max-length="255"
|
||||
@input="(val) => addTableLine(val, 'name')"
|
||||
@input="(val) => addTableLine(val, 'key')"
|
||||
/>
|
||||
</a-popover>
|
||||
</template>
|
||||
|
@ -125,15 +125,17 @@
|
|||
<a-input-number
|
||||
v-model:model-value="record.min"
|
||||
:placeholder="t('apiTestDebug.paramMin')"
|
||||
:min="0"
|
||||
class="param-input param-input-number"
|
||||
@input="(val) => addTableLine(val || '', 'min')"
|
||||
@change="(val) => addTableLine(val || '', 'min')"
|
||||
/>
|
||||
<div class="mx-[4px]">~</div>
|
||||
<a-input-number
|
||||
v-model:model-value="record.max"
|
||||
:placeholder="t('apiTestDebug.paramMax')"
|
||||
:min="0"
|
||||
class="param-input"
|
||||
@input="(val) => addTableLine(val || '', 'max')"
|
||||
@change="(val) => addTableLine(val || '', 'max')"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -159,10 +161,10 @@
|
|||
/>
|
||||
</a-popover>
|
||||
</template>
|
||||
<template #desc="{ record, columnConfig }">
|
||||
<template #description="{ record, columnConfig }">
|
||||
<paramDescInput
|
||||
v-model:desc="record[columnConfig.dataIndex as string]"
|
||||
@input="(val) => addTableLine(val, 'desc')"
|
||||
@input="(val) => addTableLine(val, 'description')"
|
||||
@dblclick="quickInputDesc(record)"
|
||||
@change="handleDescChange"
|
||||
/>
|
||||
|
@ -201,11 +203,11 @@
|
|||
</template>
|
||||
</a-trigger>
|
||||
<a-switch
|
||||
v-if="columnConfig.hasEnable"
|
||||
v-model:model-value="record.enable"
|
||||
v-if="columnConfig.hasDisable"
|
||||
v-model:model-value="record.disable"
|
||||
size="small"
|
||||
type="line"
|
||||
@change="(val) => addTableLine(val, 'enable')"
|
||||
@change="(val) => addTableLine(val, 'disable')"
|
||||
/>
|
||||
<icon-minus-circle
|
||||
v-if="paramsLength > 1 && rowIndex !== paramsLength - 1"
|
||||
|
@ -292,7 +294,6 @@
|
|||
</template>
|
||||
|
||||
<script async setup lang="ts">
|
||||
import { useVModel } from '@vueuse/core';
|
||||
import { isEqual } from 'lodash-es';
|
||||
|
||||
import MsButton from '@/components/pure/ms-button/index.vue';
|
||||
|
@ -311,18 +312,18 @@
|
|||
import useTableStore from '@/hooks/useTableStore';
|
||||
|
||||
import { RequestBodyFormat, RequestContentTypeEnum, RequestParamsType } from '@/enums/apiEnum';
|
||||
import { TableKeyEnum } from '@/enums/tableEnum';
|
||||
import { SelectAllEnum, TableKeyEnum } from '@/enums/tableEnum';
|
||||
|
||||
interface Param {
|
||||
id: number;
|
||||
required: boolean;
|
||||
name: string;
|
||||
key: string;
|
||||
type: string;
|
||||
value: string;
|
||||
min: number | undefined;
|
||||
max: number | undefined;
|
||||
contentType: RequestContentTypeEnum;
|
||||
desc: string;
|
||||
description: string;
|
||||
encode: boolean;
|
||||
tag: string[];
|
||||
enable: boolean;
|
||||
|
@ -335,14 +336,13 @@
|
|||
hasRequired?: boolean; // 用于 type 列区分是否有 required 星号
|
||||
typeOptions?: { label: string; value: string }[]; // 用于 type 列选择器选项
|
||||
typeTitleTooltip?: string; // 用于 type 表头列展示的 tooltip
|
||||
hasEnable?: boolean; // 用于 operation 列区分是否有 enable 开关
|
||||
hasDisable?: boolean; // 用于 operation 列区分是否有 enable 开关
|
||||
moreAction?: ActionsItem[]; // 用于 operation 列更多操作按钮配置
|
||||
format?: RequestBodyFormat; // 用于 operation 列区分是否有请求体格式选择器
|
||||
};
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
selectedKeys?: string[];
|
||||
params: any[];
|
||||
defaultParamItem?: Partial<Param>; // 默认参数项,用于添加新行时的默认值
|
||||
columns: ParamTableColumn[];
|
||||
|
@ -369,22 +369,21 @@
|
|||
isSimpleSetting: true,
|
||||
defaultParamItem: () => ({
|
||||
required: false,
|
||||
name: '',
|
||||
key: '',
|
||||
type: RequestParamsType.STRING,
|
||||
value: '',
|
||||
min: undefined,
|
||||
max: undefined,
|
||||
contentType: RequestContentTypeEnum.TEXT,
|
||||
tag: [],
|
||||
desc: '',
|
||||
description: '',
|
||||
encode: false,
|
||||
enable: false,
|
||||
disable: false,
|
||||
mustContain: false,
|
||||
}),
|
||||
}
|
||||
);
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:selectedKeys', value: string[]): void;
|
||||
(e: 'change', data: any[], isInit?: boolean): void; // 都触发这个事件以通知父组件参数数组被更改
|
||||
(e: 'moreActionSelect', event: ActionsItem, record: Record<string, any>): void;
|
||||
(e: 'projectChange', projectId: string): void;
|
||||
|
@ -392,8 +391,6 @@
|
|||
|
||||
const { t } = useI18n();
|
||||
|
||||
const innerSelectedKeys = useVModel(props, 'selectedKeys', emit);
|
||||
|
||||
const tableStore = useTableStore();
|
||||
async function initColumns() {
|
||||
if (props.showSetting && props.tableKey) {
|
||||
|
@ -417,10 +414,28 @@
|
|||
showPagination: false,
|
||||
});
|
||||
|
||||
const selectedKeys = computed(() => propsRes.value.data.filter((e) => e.enable).map((e) => e.id));
|
||||
propsEvent.value.rowSelectChange = (key: string) => {
|
||||
propsRes.value.data = propsRes.value.data.map((e) => {
|
||||
if (e.id === key) {
|
||||
e.enable = !e.enable;
|
||||
}
|
||||
return e;
|
||||
});
|
||||
emit('change', propsRes.value.data);
|
||||
};
|
||||
propsEvent.value.selectAllChange = (v: SelectAllEnum) => {
|
||||
propsRes.value.data = propsRes.value.data.map((e) => {
|
||||
e.enable = v !== SelectAllEnum.NONE;
|
||||
return e;
|
||||
});
|
||||
emit('change', propsRes.value.data);
|
||||
};
|
||||
|
||||
watch(
|
||||
() => propsRes.value.selectedKeys,
|
||||
(val) => {
|
||||
innerSelectedKeys.value = Array.from(val);
|
||||
() => selectedKeys.value,
|
||||
(arr) => {
|
||||
propsRes.value.selectedKeys = new Set(arr);
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -450,7 +465,8 @@
|
|||
isForce?: boolean
|
||||
) {
|
||||
const lastData = { ...propsRes.value.data[propsRes.value.data.length - 1] };
|
||||
delete lastData.id;
|
||||
delete lastData.id; // 删除 id 属性,避免影响判断是否有变化
|
||||
lastData.enable = props.defaultParamItem.enable; // enable 是用于判断表格行是否勾选的属性,不参与判断是否有变化
|
||||
// 当不传入输入值或对应列的 key 时,遍历整个数据对象判断是否有变化;当传入输入值或对应列的 key 时,判断对应列的值是否有变化
|
||||
const isNotChange =
|
||||
val === undefined || key === undefined
|
||||
|
@ -461,8 +477,8 @@
|
|||
propsRes.value.data.push({
|
||||
id,
|
||||
...props.defaultParamItem,
|
||||
enable: true, // 是否勾选
|
||||
} as any);
|
||||
propsRes.value.selectedKeys.add(id);
|
||||
emit('change', propsRes.value.data);
|
||||
}
|
||||
}
|
||||
|
@ -473,6 +489,7 @@
|
|||
if (val.length > 0) {
|
||||
const lastData = { ...val[val.length - 1] };
|
||||
delete lastData.id; // 删除 id 属性,避免影响判断是否有变化
|
||||
delete lastData.enable; // 删除 enable 属性,避免影响判断是否有变化
|
||||
const isNotChange = isEqual(lastData, props.defaultParamItem);
|
||||
propsRes.value.data = val;
|
||||
if (!isNotChange) {
|
||||
|
@ -484,9 +501,9 @@
|
|||
{
|
||||
id, // 默认给时间戳 id,若 props.defaultParamItem 有 id,则覆盖
|
||||
...props.defaultParamItem,
|
||||
enable: true, // 是否勾选
|
||||
},
|
||||
] as any[];
|
||||
propsRes.value.selectedKeys.add(id);
|
||||
emit('change', propsRes.value.data, true);
|
||||
}
|
||||
},
|
||||
|
@ -532,7 +549,7 @@
|
|||
function quickInputDesc(record: any) {
|
||||
activeQuickInputRecord.value = record;
|
||||
showQuickInputDesc.value = true;
|
||||
quickInputDescValue.value = record.desc;
|
||||
quickInputDescValue.value = record.description;
|
||||
}
|
||||
|
||||
function clearQuickInputDesc() {
|
||||
|
@ -541,7 +558,7 @@
|
|||
}
|
||||
|
||||
function applyQuickInputDesc() {
|
||||
activeQuickInputRecord.value.desc = quickInputDescValue.value;
|
||||
activeQuickInputRecord.value.description = quickInputDescValue.value;
|
||||
showQuickInputDesc.value = false;
|
||||
clearQuickInputDesc();
|
||||
emit('change', propsRes.value.data);
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
<template>
|
||||
<div class="mb-[8px] font-medium">{{ t('apiTestDebug.auth') }}</div>
|
||||
<div class="rounded-[var(--border-radius-small)] border border-[var(--color-text-n8)] p-[16px]">
|
||||
<div class="h-full rounded-[var(--border-radius-small)] border border-[var(--color-text-n8)] p-[16px]">
|
||||
<div class="mb-[8px]">{{ t('apiTestDebug.authType') }}</div>
|
||||
<a-radio-group v-model:model-value="authForm.authType" class="mb-[16px]" @change="authTypeChange">
|
||||
<a-radio :value="RequestAuthType.NONE">No Auth</a-radio>
|
||||
|
|
|
@ -1,33 +1,42 @@
|
|||
<template>
|
||||
<div class="mb-[8px] flex items-center justify-between">
|
||||
<batchAddKeyVal v-if="showParamTable" :params="currentTableParams" @apply="handleBatchParamApply" />
|
||||
<a-radio-group v-model:model-value="bodyType" type="button" size="small" @change="formatChange">
|
||||
<a-radio-group
|
||||
v-model:model-value="innerParams.bodyType"
|
||||
type="button"
|
||||
size="small"
|
||||
@change="(val) => changeBodyFormat(val as string)"
|
||||
>
|
||||
<a-radio v-for="item of RequestBodyFormat" :key="item" :value="item">{{ requestBodyTypeMap[item] }}</a-radio>
|
||||
</a-radio-group>
|
||||
<batchAddKeyVal v-if="showParamTable" :params="currentTableParams" @apply="handleBatchParamApply" />
|
||||
</div>
|
||||
<div
|
||||
v-if="bodyType === RequestBodyFormat.NONE"
|
||||
v-if="innerParams.bodyType === RequestBodyFormat.NONE"
|
||||
class="flex h-[100px] items-center justify-center rounded-[var(--border-radius-small)] bg-[var(--color-text-n9)] text-[var(--color-text-4)]"
|
||||
>
|
||||
{{ t('apiTestDebug.noneBody') }}
|
||||
</div>
|
||||
<paramTable
|
||||
v-else-if="bodyType === RequestBodyFormat.FORM_DATA"
|
||||
v-else-if="innerParams.bodyType === RequestBodyFormat.FORM_DATA"
|
||||
v-model:params="currentTableParams"
|
||||
:scroll="{ minWidth: 1160 }"
|
||||
:columns="columns"
|
||||
:height-used="heightUsed"
|
||||
:show-setting="true"
|
||||
:table-key="TableKeyEnum.API_TEST_DEBUG_FORM_DATA"
|
||||
@change="handleParamTableChange"
|
||||
/>
|
||||
<paramTable
|
||||
v-else-if="bodyType === RequestBodyFormat.WWW_FORM"
|
||||
v-else-if="innerParams.bodyType === RequestBodyFormat.WWW_FORM"
|
||||
v-model:params="currentTableParams"
|
||||
:scroll="{ minWidth: 1160 }"
|
||||
:columns="columns"
|
||||
:height-used="heightUsed"
|
||||
:show-setting="true"
|
||||
:table-key="TableKeyEnum.API_TEST_DEBUG_FORM_URL_ENCODE"
|
||||
@change="handleParamTableChange"
|
||||
/>
|
||||
<div v-else-if="bodyType === RequestBodyFormat.BINARY">
|
||||
<div v-else-if="innerParams.bodyType === RequestBodyFormat.BINARY">
|
||||
<div class="mb-[16px] flex justify-between gap-[8px] bg-[var(--color-text-n9)] p-[12px]">
|
||||
<a-input
|
||||
v-model:model-value="innerParams.binaryBody.description"
|
||||
|
@ -78,14 +87,15 @@
|
|||
|
||||
import MsCodeEditor from '@/components/pure/ms-code-editor/index.vue';
|
||||
import { LanguageEnum } from '@/components/pure/ms-code-editor/types';
|
||||
import paramTable, { type ParamTableColumn } from '../../../components/paramTable.vue';
|
||||
import batchAddKeyVal from './batchAddKeyVal.vue';
|
||||
import batchAddKeyVal from '@/views/api-test/components/batchAddKeyVal.vue';
|
||||
import paramTable, { type ParamTableColumn } from '@/views/api-test/components/paramTable.vue';
|
||||
|
||||
import { requestBodyTypeMap } from '@/config/apiTest';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
|
||||
import { ExecuteBody } from '@/models/apiTest/debug';
|
||||
import { RequestBodyFormat, RequestParamsType } from '@/enums/apiEnum';
|
||||
import { TableKeyEnum } from '@/enums/tableEnum';
|
||||
|
||||
const props = defineProps<{
|
||||
params: ExecuteBody;
|
||||
|
@ -100,7 +110,6 @@
|
|||
const { t } = useI18n();
|
||||
|
||||
const innerParams = useVModel(props, 'params', emit);
|
||||
const bodyType = ref(RequestBodyFormat.NONE);
|
||||
|
||||
const columns = computed<ParamTableColumn[]>(() => [
|
||||
{
|
||||
|
@ -134,8 +143,8 @@
|
|||
},
|
||||
{
|
||||
title: 'apiTestDebug.desc',
|
||||
dataIndex: 'desc',
|
||||
slotName: 'desc',
|
||||
dataIndex: 'description',
|
||||
slotName: 'description',
|
||||
},
|
||||
{
|
||||
title: 'apiTestDebug.encode',
|
||||
|
@ -147,8 +156,8 @@
|
|||
title: '',
|
||||
slotName: 'operation',
|
||||
fixed: 'right',
|
||||
format: bodyType.value,
|
||||
width: bodyType.value === RequestBodyFormat.FORM_DATA ? 90 : 50,
|
||||
format: innerParams.value.bodyType,
|
||||
width: innerParams.value.bodyType === RequestBodyFormat.FORM_DATA ? 90 : 50,
|
||||
},
|
||||
]);
|
||||
|
||||
|
@ -157,7 +166,7 @@
|
|||
watch(
|
||||
() => props.layout,
|
||||
(val) => {
|
||||
heightUsed.value = val === 'horizontal' ? 422 : 422 + props.secondBoxHeight;
|
||||
heightUsed.value = val === 'horizontal' ? 428 : 428 + props.secondBoxHeight;
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
|
@ -168,7 +177,7 @@
|
|||
() => props.secondBoxHeight,
|
||||
(val) => {
|
||||
if (props.layout === 'vertical') {
|
||||
heightUsed.value = 422 + val;
|
||||
heightUsed.value = 428 + val;
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -178,18 +187,18 @@
|
|||
|
||||
const showParamTable = computed(() => {
|
||||
// 仅当格式为FORM_DATA或X_WWW_FORM_URLENCODED时,显示参数表格
|
||||
return [RequestBodyFormat.FORM_DATA, RequestBodyFormat.WWW_FORM].includes(bodyType.value);
|
||||
return [RequestBodyFormat.FORM_DATA, RequestBodyFormat.WWW_FORM].includes(innerParams.value.bodyType);
|
||||
});
|
||||
// 当前显示的参数表格数据
|
||||
const currentTableParams = computed({
|
||||
get() {
|
||||
if (bodyType.value === RequestBodyFormat.FORM_DATA) {
|
||||
if (innerParams.value.bodyType === RequestBodyFormat.FORM_DATA) {
|
||||
return innerParams.value.formDataBody.formValues;
|
||||
}
|
||||
return innerParams.value.wwwFormBody.formValues;
|
||||
},
|
||||
set(val) {
|
||||
if (bodyType.value === RequestBodyFormat.FORM_DATA) {
|
||||
if (innerParams.value.bodyType === RequestBodyFormat.FORM_DATA) {
|
||||
innerParams.value.formDataBody.formValues = val;
|
||||
} else {
|
||||
innerParams.value.wwwFormBody.formValues = val;
|
||||
|
@ -199,18 +208,18 @@
|
|||
// 当前显示的代码
|
||||
const currentBodyCode = computed({
|
||||
get() {
|
||||
if (bodyType.value === RequestBodyFormat.JSON) {
|
||||
if (innerParams.value.bodyType === RequestBodyFormat.JSON) {
|
||||
return innerParams.value.jsonBody.jsonValue;
|
||||
}
|
||||
if (bodyType.value === RequestBodyFormat.XML) {
|
||||
if (innerParams.value.bodyType === RequestBodyFormat.XML) {
|
||||
return innerParams.value.xmlBody.value;
|
||||
}
|
||||
return innerParams.value.rawBody.value;
|
||||
},
|
||||
set(val) {
|
||||
if (bodyType.value === RequestBodyFormat.JSON) {
|
||||
if (innerParams.value.bodyType === RequestBodyFormat.JSON) {
|
||||
innerParams.value.jsonBody.jsonValue = val;
|
||||
} else if (bodyType.value === RequestBodyFormat.XML) {
|
||||
} else if (innerParams.value.bodyType === RequestBodyFormat.XML) {
|
||||
innerParams.value.xmlBody.value = val;
|
||||
} else {
|
||||
innerParams.value.rawBody.value = val;
|
||||
|
@ -219,19 +228,15 @@
|
|||
});
|
||||
// 当前代码编辑器的语言
|
||||
const currentCodeLanguage = computed(() => {
|
||||
if (bodyType.value === RequestBodyFormat.JSON) {
|
||||
if (innerParams.value.bodyType === RequestBodyFormat.JSON) {
|
||||
return LanguageEnum.JSON;
|
||||
}
|
||||
if (bodyType.value === RequestBodyFormat.XML) {
|
||||
if (innerParams.value.bodyType === RequestBodyFormat.XML) {
|
||||
return LanguageEnum.XML;
|
||||
}
|
||||
return LanguageEnum.PLAINTEXT;
|
||||
});
|
||||
|
||||
function formatChange() {
|
||||
console.log('formatChange', bodyType.value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量参数代码转换为参数表格数据
|
||||
*/
|
||||
|
@ -248,6 +253,11 @@
|
|||
currentTableParams.value = [...resultArr];
|
||||
emit('change');
|
||||
}
|
||||
|
||||
function changeBodyFormat(val: string) {
|
||||
innerParams.value.bodyType = val as RequestBodyFormat;
|
||||
emit('change');
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped></style>
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
<template>
|
||||
<div class="mb-[8px] flex items-center justify-between">
|
||||
<div class="font-medium">{{ t('apiTestDebug.header') }}</div>
|
||||
<batchAddKeyVal :params="innerParams" @apply="handleBatchParamApply" />
|
||||
<batchAddKeyVal :params="innerParams" :default-param-item="defaultParamItem" @apply="handleBatchParamApply" />
|
||||
</div>
|
||||
<paramTable
|
||||
v-model:params="innerParams"
|
||||
v-model:selected-keys="selectedKeys"
|
||||
:columns="columns"
|
||||
:height-used="heightUsed"
|
||||
:scroll="scroll"
|
||||
:default-param-item="defaultParamItem"
|
||||
draggable
|
||||
@change="handleParamTableChange"
|
||||
/>
|
||||
|
@ -17,15 +17,14 @@
|
|||
<script setup lang="ts">
|
||||
import { useVModel } from '@vueuse/core';
|
||||
|
||||
import paramTable, { ParamTableColumn } from '../../../components/paramTable.vue';
|
||||
import batchAddKeyVal from './batchAddKeyVal.vue';
|
||||
import batchAddKeyVal from '@/views/api-test/components/batchAddKeyVal.vue';
|
||||
import paramTable, { ParamTableColumn } from '@/views/api-test/components/paramTable.vue';
|
||||
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
|
||||
import { EnableKeyValueParam } from '@/models/apiTest/debug';
|
||||
|
||||
const props = defineProps<{
|
||||
selectedKeys?: string[];
|
||||
params: EnableKeyValueParam[];
|
||||
layout: 'horizontal' | 'vertical';
|
||||
secondBoxHeight: number;
|
||||
|
@ -39,13 +38,18 @@
|
|||
const { t } = useI18n();
|
||||
|
||||
const innerParams = useVModel(props, 'params', emit);
|
||||
const selectedKeys = useVModel(props, 'selectedKeys', emit);
|
||||
const defaultParamItem = ref<EnableKeyValueParam>({
|
||||
key: '',
|
||||
value: '',
|
||||
description: '',
|
||||
enable: true,
|
||||
});
|
||||
|
||||
const columns: ParamTableColumn[] = [
|
||||
{
|
||||
title: 'apiTestDebug.paramName',
|
||||
dataIndex: 'name',
|
||||
slotName: 'name',
|
||||
dataIndex: 'key',
|
||||
slotName: 'key',
|
||||
},
|
||||
{
|
||||
title: 'apiTestDebug.paramValue',
|
||||
|
@ -54,8 +58,8 @@
|
|||
},
|
||||
{
|
||||
title: 'apiTestDebug.desc',
|
||||
dataIndex: 'desc',
|
||||
slotName: 'desc',
|
||||
dataIndex: 'description',
|
||||
slotName: 'description',
|
||||
},
|
||||
{
|
||||
title: '',
|
||||
|
@ -66,9 +70,9 @@
|
|||
|
||||
const heightUsed = computed(() => {
|
||||
if (props.layout === 'horizontal') {
|
||||
return 422;
|
||||
return 428;
|
||||
}
|
||||
return 422 + props.secondBoxHeight;
|
||||
return 428 + props.secondBoxHeight;
|
||||
});
|
||||
const scroll = computed(() => (props.layout === 'horizontal' ? { x: '700px' } : { x: '100%' }));
|
||||
|
||||
|
|
|
@ -189,7 +189,7 @@
|
|||
<a-form-item :label="t('apiTestDebug.requestModule')" class="mb-0">
|
||||
<a-tree-select
|
||||
v-model:modelValue="saveModalForm.moduleId"
|
||||
:data="props.moduleTree"
|
||||
:data="selectTree"
|
||||
:field-names="{ title: 'name', key: 'id', children: 'children' }"
|
||||
allow-search
|
||||
/>
|
||||
|
@ -215,18 +215,24 @@
|
|||
import apiMethodName from '@/views/api-test/components/apiMethodName.vue';
|
||||
import apiMethodSelect from '@/views/api-test/components/apiMethodSelect.vue';
|
||||
|
||||
import { addDebug, executeDebug } from '@/api/modules/api-test/debug';
|
||||
import { addDebug, executeDebug, getDebugDetail } from '@/api/modules/api-test/debug';
|
||||
import { getPluginScript, getProtocolList } from '@/api/modules/api-test/management';
|
||||
import { getSocket } from '@/api/modules/project-management/commonScript';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import { useAppStore } from '@/store';
|
||||
import { getGenerateId } from '@/utils';
|
||||
import { filterTree, getGenerateId } from '@/utils';
|
||||
import { scrollIntoView } from '@/utils/dom';
|
||||
import { registerCatchSaveShortcut, removeCatchSaveShortcut } from '@/utils/event';
|
||||
|
||||
import { ExecuteBody, ExecuteHTTPRequestFullParams } from '@/models/apiTest/debug';
|
||||
import { ModuleTreeNode } from '@/models/common';
|
||||
import { RequestBodyFormat, RequestComposition, RequestMethods, ResponseComposition } from '@/enums/apiEnum';
|
||||
import {
|
||||
RequestAuthType,
|
||||
RequestBodyFormat,
|
||||
RequestComposition,
|
||||
RequestMethods,
|
||||
ResponseComposition,
|
||||
} from '@/enums/apiEnum';
|
||||
|
||||
// 懒加载Http协议组件
|
||||
const debugHeader = defineAsyncComponent(() => import('./header.vue'));
|
||||
|
@ -237,7 +243,6 @@
|
|||
export type DebugTabParam = ExecuteHTTPRequestFullParams & TabItem & Record<string, any>;
|
||||
|
||||
const props = defineProps<{
|
||||
module: string; // 当前激活的接口模块
|
||||
moduleTree: ModuleTreeNode[]; // 接口模块树
|
||||
}>();
|
||||
|
||||
|
@ -264,6 +269,28 @@
|
|||
},
|
||||
rawBody: { value: '' },
|
||||
};
|
||||
const defaultResponse = {
|
||||
requestResults: [
|
||||
{
|
||||
body: '',
|
||||
responseResult: {
|
||||
body: '',
|
||||
contentType: '',
|
||||
headers: '',
|
||||
dnsLookupTime: 0,
|
||||
downloadTime: 0,
|
||||
latency: 0,
|
||||
responseCode: 0,
|
||||
responseTime: 0,
|
||||
responseSize: 0,
|
||||
socketInitTime: 0,
|
||||
tcpHandshakeTime: 0,
|
||||
transferStartTime: 0,
|
||||
},
|
||||
},
|
||||
],
|
||||
console: '',
|
||||
}; // 调试返回的响应内容
|
||||
const defaultDebugParams: DebugTabParam = {
|
||||
id: initDefaultId,
|
||||
moduleId: 'root',
|
||||
|
@ -285,7 +312,7 @@
|
|||
uploadFileIds: [],
|
||||
linkFileIds: [],
|
||||
authConfig: {
|
||||
authType: 'NONE',
|
||||
authType: RequestAuthType.NONE,
|
||||
username: '',
|
||||
password: '',
|
||||
},
|
||||
|
@ -310,32 +337,11 @@
|
|||
connectTimeout: 60000,
|
||||
responseTimeout: 60000,
|
||||
certificateAlias: '',
|
||||
followRedirects: false,
|
||||
followRedirects: true,
|
||||
autoRedirects: false,
|
||||
},
|
||||
responseActiveTab: ResponseComposition.BODY,
|
||||
response: {
|
||||
requestResults: [
|
||||
{
|
||||
body: '',
|
||||
responseResult: {
|
||||
body: '',
|
||||
contentType: '',
|
||||
headers: '',
|
||||
dnsLookupTime: 0,
|
||||
downloadTime: 0,
|
||||
latency: 0,
|
||||
responseCode: 0,
|
||||
responseTime: 0,
|
||||
responseSize: 0,
|
||||
socketInitTime: 0,
|
||||
tcpHandshakeTime: 0,
|
||||
transferStartTime: 0,
|
||||
},
|
||||
},
|
||||
],
|
||||
console: '',
|
||||
}, // 调试返回的响应内容
|
||||
response: cloneDeep(defaultResponse),
|
||||
};
|
||||
const debugTabs = ref<DebugTabParam[]>([cloneDeep(defaultDebugParams)]);
|
||||
const activeDebug = ref<DebugTabParam>(debugTabs.value[0]);
|
||||
|
@ -366,13 +372,12 @@
|
|||
const id = `debug-${Date.now()}`;
|
||||
debugTabs.value.push({
|
||||
...cloneDeep(defaultDebugParams),
|
||||
moduleId: props.module,
|
||||
id,
|
||||
...defaultProps,
|
||||
});
|
||||
activeRequestTab.value = id;
|
||||
activeRequestTab.value = defaultProps?.id || id;
|
||||
nextTick(() => {
|
||||
if (defaultProps) {
|
||||
if (defaultProps && !defaultProps.id) {
|
||||
handleActiveDebugChange();
|
||||
}
|
||||
});
|
||||
|
@ -584,14 +589,6 @@
|
|||
executeLoading.value = false;
|
||||
}
|
||||
});
|
||||
|
||||
websocket.value.addEventListener('close', (event) => {
|
||||
console.log('关闭:', event);
|
||||
});
|
||||
|
||||
websocket.value.addEventListener('error', (event) => {
|
||||
console.error('错误:', event);
|
||||
});
|
||||
}
|
||||
|
||||
function makeRequestParams() {
|
||||
|
@ -685,10 +682,16 @@
|
|||
const saveModalForm = ref({
|
||||
name: '',
|
||||
path: activeDebug.value.url || '',
|
||||
moduleId: activeDebug.value.module,
|
||||
moduleId: 'root',
|
||||
});
|
||||
const saveModalFormRef = ref<FormInstance>();
|
||||
const saveLoading = ref(false);
|
||||
const selectTree = computed(() =>
|
||||
filterTree(props.moduleTree, (e) => {
|
||||
e.draggable = false;
|
||||
return e.type === 'MODULE';
|
||||
})
|
||||
);
|
||||
|
||||
watch(
|
||||
() => saveModalVisible.value,
|
||||
|
@ -708,7 +711,7 @@
|
|||
saveModalForm.value = {
|
||||
name: '',
|
||||
path: activeDebug.value.url || '',
|
||||
moduleId: activeDebug.value.module,
|
||||
moduleId: 'root',
|
||||
};
|
||||
saveModalVisible.value = true;
|
||||
} catch (error) {
|
||||
|
@ -752,6 +755,26 @@
|
|||
done(false);
|
||||
}
|
||||
|
||||
const apiDetailLoading = ref(false);
|
||||
async function openApiTab(apiInfo: ModuleTreeNode) {
|
||||
try {
|
||||
apiDetailLoading.value = true;
|
||||
const res = await getDebugDetail(apiInfo.id);
|
||||
addDebugTab({
|
||||
label: apiInfo.name,
|
||||
...res,
|
||||
response: cloneDeep(defaultResponse),
|
||||
...res.request,
|
||||
url: res.path,
|
||||
});
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
} finally {
|
||||
apiDetailLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
onBeforeMount(() => {
|
||||
initProtocolList();
|
||||
});
|
||||
|
@ -766,6 +789,7 @@
|
|||
|
||||
defineExpose({
|
||||
addDebugTab,
|
||||
openApiTab,
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<condition
|
||||
v-model:list="postConditions"
|
||||
:condition-types="['SCRIPT']"
|
||||
:condition-types="[RequestConditionProcessor.SCRIPT]"
|
||||
add-text="apiTestDebug.postCondition"
|
||||
:response="props.response"
|
||||
:height-used="heightUsed"
|
||||
|
@ -26,6 +26,7 @@
|
|||
import condition from '@/views/api-test/components/condition/index.vue';
|
||||
|
||||
import { ExecuteConditionProcessor } from '@/models/apiTest/debug';
|
||||
import { RequestConditionProcessor } from '@/enums/apiEnum';
|
||||
|
||||
// import { useI18n } from '@/hooks/useI18n';
|
||||
|
||||
|
@ -46,9 +47,9 @@
|
|||
const postConditions = useVModel(props, 'params', emit);
|
||||
const heightUsed = computed(() => {
|
||||
if (props.layout === 'horizontal') {
|
||||
return 422;
|
||||
return 428;
|
||||
}
|
||||
return 422 + (props.secondBoxHeight || 0);
|
||||
return 428 + (props.secondBoxHeight || 0);
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
<script setup lang="ts">
|
||||
import { useVModel } from '@vueuse/core';
|
||||
|
||||
import batchAddKeyVal from './batchAddKeyVal.vue';
|
||||
import batchAddKeyVal from '@/views/api-test/components/batchAddKeyVal.vue';
|
||||
import paramTable, { type ParamTableColumn } from '@/views/api-test/components/paramTable.vue';
|
||||
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
|
@ -84,8 +84,8 @@
|
|||
},
|
||||
{
|
||||
title: 'apiTestDebug.desc',
|
||||
dataIndex: 'desc',
|
||||
slotName: 'desc',
|
||||
dataIndex: 'description',
|
||||
slotName: 'description',
|
||||
},
|
||||
{
|
||||
title: '',
|
||||
|
@ -100,7 +100,7 @@
|
|||
watch(
|
||||
() => props.layout,
|
||||
(val) => {
|
||||
heightUsed.value = val === 'horizontal' ? 422 : 422 + props.secondBoxHeight;
|
||||
heightUsed.value = val === 'horizontal' ? 428 : 428 + props.secondBoxHeight;
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
|
@ -111,7 +111,7 @@
|
|||
() => props.secondBoxHeight,
|
||||
(val) => {
|
||||
if (props.layout === 'vertical') {
|
||||
heightUsed.value = 422 + val;
|
||||
heightUsed.value = 428 + val;
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
<script setup lang="ts">
|
||||
import { useVModel } from '@vueuse/core';
|
||||
|
||||
import batchAddKeyVal from './batchAddKeyVal.vue';
|
||||
import batchAddKeyVal from '@/views/api-test/components/batchAddKeyVal.vue';
|
||||
import paramTable, { type ParamTableColumn } from '@/views/api-test/components/paramTable.vue';
|
||||
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
|
@ -84,8 +84,8 @@
|
|||
},
|
||||
{
|
||||
title: 'apiTestDebug.desc',
|
||||
dataIndex: 'desc',
|
||||
slotName: 'desc',
|
||||
dataIndex: 'description',
|
||||
slotName: 'description',
|
||||
},
|
||||
{
|
||||
title: '',
|
||||
|
@ -100,7 +100,7 @@
|
|||
watch(
|
||||
() => props.layout,
|
||||
(val) => {
|
||||
heightUsed.value = val === 'horizontal' ? 422 : 422 + props.secondBoxHeight;
|
||||
heightUsed.value = val === 'horizontal' ? 428 : 428 + props.secondBoxHeight;
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
|
@ -111,7 +111,7 @@
|
|||
() => props.secondBoxHeight,
|
||||
(val) => {
|
||||
if (props.layout === 'vertical') {
|
||||
heightUsed.value = 422 + val;
|
||||
heightUsed.value = 428 + val;
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
<template>
|
||||
<div class="pb-[24px]">
|
||||
<div class="mb-[8px] font-medium">{{ t('apiTestDebug.auth') }}</div>
|
||||
<div class="rounded-[var(--border-radius-small)] border border-[var(--color-text-n8)] p-[16px]">
|
||||
<div class="mb-[8px]">{{ t('apiTestDebug.setting') }}</div>
|
||||
<a-form :model="settingForm" layout="vertical">
|
||||
<a-form-item>
|
||||
<div class="h-full rounded-[var(--border-radius-small)] border border-[var(--color-text-n8)] p-[16px]">
|
||||
<a-form :model="settingForm" layout="vertical">
|
||||
<div class="flex items-center gap-[32px]">
|
||||
<a-form-item class="flex-1">
|
||||
<template #label>
|
||||
<div class="flex items-center">
|
||||
{{ t('apiTestDebug.connectTimeout') }}
|
||||
|
@ -34,22 +32,22 @@
|
|||
class="w-[160px]"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item :label="t('apiTestDebug.certificateAlias')">
|
||||
<a-input
|
||||
v-model:model-value="settingForm.certificateAlias"
|
||||
:max-length="255"
|
||||
:placeholder="t('apiTestDebug.commonPlaceholder')"
|
||||
class="w-[450px]"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item :label="t('apiTestDebug.redirect')">
|
||||
<a-radio-group v-model:model-value="settingForm.autoRedirects">
|
||||
<a-radio value="follow">{{ t('apiTestDebug.follow') }}</a-radio>
|
||||
<a-radio value="auto">{{ t('apiTestDebug.auto') }}</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</div>
|
||||
</div>
|
||||
<a-form-item :label="t('apiTestDebug.certificateAlias')">
|
||||
<a-input
|
||||
v-model:model-value="settingForm.certificateAlias"
|
||||
:max-length="255"
|
||||
:placeholder="t('apiTestDebug.commonPlaceholder')"
|
||||
class="w-[450px]"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item :label="t('apiTestDebug.redirect')">
|
||||
<a-radio v-model:model-value="settingForm.followRedirects">{{ t('apiTestDebug.follow') }}</a-radio>
|
||||
<a-radio v-model:model-value="settingForm.autoRedirects" class="ml-[24px]">
|
||||
{{ t('apiTestDebug.auto') }}
|
||||
</a-radio>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -37,7 +37,6 @@
|
|||
<a-spin class="min-h-[400px] w-full" :loading="loading">
|
||||
<MsTree
|
||||
v-model:focus-node-key="focusNodeKey"
|
||||
v-model:selected-keys="selectedKeys"
|
||||
:data="folderTree"
|
||||
:keyword="moduleKeyword"
|
||||
:node-more-actions="folderMoreActions"
|
||||
|
@ -60,7 +59,15 @@
|
|||
@drop="handleDrop"
|
||||
>
|
||||
<template #title="nodeData">
|
||||
<div class="inline-flex w-full">
|
||||
<div
|
||||
v-if="nodeData.type === 'API'"
|
||||
class="inline-flex w-full cursor-pointer gap-[4px]"
|
||||
@click="emit('clickApiNode', nodeData)"
|
||||
>
|
||||
<apiMethodName :method="nodeData.attachInfo?.method || nodeData.attachInfo?.protocol" />
|
||||
<div class="one-line-text w-[calc(100%-32px)] text-[var(--color-text-1)]">{{ nodeData.name }}</div>
|
||||
</div>
|
||||
<div v-else class="inline-flex w-full">
|
||||
<div class="one-line-text w-[calc(100%-32px)] text-[var(--color-text-1)]">{{ nodeData.name }}</div>
|
||||
<div class="ml-[4px] text-[var(--color-text-4)]">({{ nodeData.count || 0 }})</div>
|
||||
</div>
|
||||
|
@ -106,6 +113,7 @@
|
|||
import type { ActionsItem } from '@/components/pure/ms-table-more-action/types';
|
||||
import MsTree from '@/components/business/ms-tree/index.vue';
|
||||
import type { MsTreeNodeData } from '@/components/business/ms-tree/types';
|
||||
import apiMethodName from '@/views/api-test/components/apiMethodName.vue';
|
||||
import popConfirm from '@/views/api-test/components/popConfirm.vue';
|
||||
|
||||
import {
|
||||
|
@ -113,7 +121,6 @@
|
|||
getDebugModuleCount,
|
||||
getDebugModules,
|
||||
moveDebugModule,
|
||||
updateDebugModule,
|
||||
} from '@/api/modules/api-test/debug';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import useModal from '@/hooks/useModal';
|
||||
|
@ -124,7 +131,7 @@
|
|||
const props = defineProps<{
|
||||
isExpandAll?: boolean; // 是否展开所有节点
|
||||
}>();
|
||||
const emit = defineEmits(['init', 'change', 'newApi', 'import']);
|
||||
const emit = defineEmits(['init', 'clickApiNode', 'newApi', 'import']);
|
||||
|
||||
const { t } = useI18n();
|
||||
const { openModal } = useModal();
|
||||
|
@ -145,11 +152,10 @@
|
|||
|
||||
const virtualListProps = computed(() => {
|
||||
return {
|
||||
height: 'calc(100vh - 325px)',
|
||||
height: 'calc(100% - 180px)',
|
||||
};
|
||||
});
|
||||
|
||||
const activeFolder = ref<string>('all');
|
||||
const isExpandAll = ref(props.isExpandAll);
|
||||
const rootModulesName = ref<string[]>([]); // 根模块名称列表
|
||||
|
||||
|
@ -167,16 +173,8 @@
|
|||
const moduleKeyword = ref('');
|
||||
const folderTree = ref<ModuleTreeNode[]>([]);
|
||||
const focusNodeKey = ref<string | number>('');
|
||||
const selectedKeys = ref<string[]>([]);
|
||||
const loading = ref(false);
|
||||
|
||||
watch(
|
||||
() => selectedKeys.value,
|
||||
(arr) => {
|
||||
emit('change', arr[0]);
|
||||
}
|
||||
);
|
||||
|
||||
function setFocusNodeKey(node: MsTreeNodeData) {
|
||||
focusNodeKey.value = node.id || '';
|
||||
}
|
||||
|
@ -207,7 +205,6 @@
|
|||
...e,
|
||||
hideMoreAction: e.id === 'root',
|
||||
draggable: e.id !== 'root',
|
||||
disabled: e.id === activeFolder.value,
|
||||
};
|
||||
});
|
||||
emit('init', folderTree.value);
|
||||
|
|
|
@ -6,14 +6,14 @@
|
|||
<moduleTree
|
||||
@init="(val) => (folderTree = val)"
|
||||
@new-api="newApi"
|
||||
@change="(val) => (activeModule = val)"
|
||||
@click-api-node="handleApiNodeClick"
|
||||
@import="importDrawerVisible = true"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<template #second>
|
||||
<div class="flex h-full flex-col">
|
||||
<debug ref="debugRef" :module="activeModule" :module-tree="folderTree" />
|
||||
<debug ref="debugRef" :module-tree="folderTree" />
|
||||
</div>
|
||||
</template>
|
||||
</MsSplitBox>
|
||||
|
@ -67,7 +67,6 @@
|
|||
const { t } = useI18n();
|
||||
|
||||
const debugRef = ref<InstanceType<typeof debug>>();
|
||||
const activeModule = ref<string>('root');
|
||||
const folderTree = ref<ModuleTreeNode[]>([]);
|
||||
const importDrawerVisible = ref(false);
|
||||
const curlCode = ref('');
|
||||
|
@ -111,6 +110,10 @@
|
|||
curlCode.value = '';
|
||||
importDrawerVisible.value = false;
|
||||
}
|
||||
|
||||
function handleApiNodeClick(node: ModuleTreeNode) {
|
||||
debugRef.value?.openApiTab(node);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped></style>
|
||||
|
|
|
@ -157,7 +157,7 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { defineModel, ref } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
import { debounce } from 'lodash-es';
|
||||
|
|
|
@ -98,8 +98,8 @@
|
|||
},
|
||||
{
|
||||
title: 'project.environmental.desc',
|
||||
dataIndex: 'desc',
|
||||
slotName: 'desc',
|
||||
dataIndex: 'description',
|
||||
slotName: 'description',
|
||||
},
|
||||
{
|
||||
title: '',
|
||||
|
|
|
@ -30,8 +30,8 @@
|
|||
<script setup lang="ts">
|
||||
import { useVModel } from '@vueuse/core';
|
||||
|
||||
import batchAddKeyVal from '@/views/api-test/components/batchAddKeyVal.vue';
|
||||
import paramsTable, { type ParamTableColumn } from '@/views/api-test/components/paramTable.vue';
|
||||
import batchAddKeyVal from '@/views/api-test/debug/components/debug/batchAddKeyVal.vue';
|
||||
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
|
||||
|
@ -121,7 +121,7 @@
|
|||
{
|
||||
title: 'project.environmental.desc',
|
||||
dataIndex: 'description',
|
||||
slotName: 'desc',
|
||||
slotName: 'description',
|
||||
showInTable: true,
|
||||
showDrag: true,
|
||||
},
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onBeforeMount, ref } from 'vue';
|
||||
import { defineModel, onBeforeMount, ref } from 'vue';
|
||||
|
||||
import MsButton from '@/components/pure/ms-button/index.vue';
|
||||
import MsDrawer from '@/components/pure/ms-drawer/index.vue';
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
<script setup lang="ts">
|
||||
import { useVModel } from '@vueuse/core';
|
||||
|
||||
import batchAddKeyVal from '@/views/api-test/components/batchAddKeyVal.vue';
|
||||
import paramsTable, { type ParamTableColumn } from '@/views/api-test/components/paramTable.vue';
|
||||
import batchAddKeyVal from '@/views/api-test/debug/components/debug/batchAddKeyVal.vue';
|
||||
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
|
||||
|
@ -59,7 +59,7 @@
|
|||
{
|
||||
title: 'apiTestDebug.desc',
|
||||
dataIndex: 'description',
|
||||
slotName: 'desc',
|
||||
slotName: 'description',
|
||||
},
|
||||
{
|
||||
title: '',
|
||||
|
|
Loading…
Reference in New Issue