😎增加接口压测菜单和页面
This commit is contained in:
parent
3e19165557
commit
d99bd99c0d
@ -208,6 +208,7 @@ public class SysMenuSeedData : ISqlSugarEntitySeedData<SysMenu>
|
||||
new SysMenu{ Id=1310000000621, Pid=1310000000601, Title="代码生成", Path="/develop/codeGen", Name="sysCodeGen", Component="/system/codeGen/index", Icon="ele-Crop", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=110 },
|
||||
new SysMenu{ Id=1310000000631, Pid=1310000000601, Title="表单设计", Path="/develop/formDes", Name="sysFormDes", Component="/system/formDes/index", Icon="ele-Edit", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=120 },
|
||||
new SysMenu{ Id=1310000000641, Pid=1310000000601, Title="系统接口", Path="/develop/api", Name="sysApi", Component="layout/routerView/iframe", IsIframe=true, OutLink="http://localhost:5005", Icon="ele-Help", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=130 },
|
||||
new SysMenu{ Id=1310000000651, Pid=1310000000601, Title="接口压测", Path="/develop/stressTest", Name="sysStressTest", Component="/system/stressTest/index", Icon="ele-Compass", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2024-12-28 00:00:00"), OrderNo=140 },
|
||||
|
||||
new SysMenu{ Id=1310000000701, Pid=0, Title="帮助文档", Path="/doc", Name="doc", Component="Layout", Icon="ele-Notebook", Type=MenuTypeEnum.Dir, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=14000 },
|
||||
new SysMenu{ Id=1310000000711, Pid=1310000000701, Title="框架教程", Path="/doc/admin", Name="sysAdmin", Component="layout/routerView/link", IsIframe=false, IsKeepAlive=false, OutLink="http://101.43.53.74:5050/", Icon="ele-Sunny", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
|
||||
|
||||
@ -9,6 +9,7 @@ namespace Admin.NET.Plugin.ApprovalFlow;
|
||||
/// <summary>
|
||||
/// 流程类型枚举
|
||||
/// </summary>
|
||||
[Description("流程类型枚举")]
|
||||
public enum FlowTypeEnum
|
||||
{
|
||||
}
|
||||
203
Web/src/views/system/stressTest/component/editStressTest.vue
Normal file
203
Web/src/views/system/stressTest/component/editStressTest.vue
Normal file
@ -0,0 +1,203 @@
|
||||
<template>
|
||||
<div class="sys-stress-test">
|
||||
<el-dialog v-model="state.isShowDialog" draggable :close-on-click-modal="false" width="40vw">
|
||||
<template #header>
|
||||
<div style="color: #fff">
|
||||
<el-icon size="16" style="margin-right: 3px; display: inline; vertical-align: middle"> <ele-Odometer /> </el-icon>
|
||||
<span> 接口压测参数 </span>
|
||||
</div>
|
||||
</template>
|
||||
<el-form :model="state.ruleForm" ref="ruleFormRef" label-width="auto" v-loading="state.loading">
|
||||
<el-row :gutter="35">
|
||||
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
|
||||
<el-form-item label="请求地址" :rules="[{ required: true, message: '请求地址不能为空', trigger: 'blur' }]">
|
||||
<el-input v-model="state.ruleForm.requestUri" placeholder="请求地址" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="请求方式" :rules="[{ required: true, message: '请求方式不能为空', trigger: 'blur' }]">
|
||||
<el-select v-model="state.ruleForm.requestMethod" placeholder="请求方式">
|
||||
<el-option :value="'GET'">GET</el-option>
|
||||
<el-option :value="'PUT'">PUT</el-option>
|
||||
<el-option :value="'POST'">POST</el-option>
|
||||
<el-option :value="'DELETE'">DELETE</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="压测轮数" :rules="[{ required: true, message: '压测轮数不能为空', trigger: 'blur' }]">
|
||||
<el-input-number v-model="state.ruleForm.numberOfRounds" placeholder="压测轮数" class="w100" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="每轮请求数" :rules="[{ required: true, message: '每轮请求数不能为空', trigger: 'blur' }]">
|
||||
<el-input-number v-model="state.ruleForm.numberOfRequests" :step="100" placeholder="每轮请求数" class="w100" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="最大并发量">
|
||||
<el-input-number v-model="state.ruleForm.maxDegreeOfParallelism" :step="5" placeholder="最大并发量" class="w100" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
|
||||
<el-tabs v-model="state.activeName" addable @tab-add="addParams()">
|
||||
<el-tab-pane label="请求头(Headers)" name="1">
|
||||
<el-row v-for="(item, index) in state.ruleForm.headers" :key="index" :gutter="25" class="w100">
|
||||
<el-col :xs="24" :sm="2" :md="2" :lg="2" :xl="2" class="mb10">
|
||||
<el-button type="danger" size="small" icon="ele-Delete" text @click="() => state.ruleForm.headers.splice(index, 1)" />
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="4" :md="4" :lg="4" :xl="4" class="mb10">
|
||||
<el-input v-model="item[0]" placeholder="参数名" clearable />
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="18" :md="18" :lg="18" :xl="18" class="mb10">
|
||||
<el-input v-model="item[1]" placeholder="参数值" clearable />
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="请求体(Body)" name="2">
|
||||
<el-row v-for="(item, index) in state.ruleForm.requestParameters" :key="index" :gutter="25" class="w100">
|
||||
<el-col :xs="24" :sm="2" :md="2" :lg="2" :xl="2" class="mb10">
|
||||
<el-button type="danger" size="small" icon="ele-Delete" text @click="() => state.ruleForm.requestParameters.splice(index, 1)" />
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="4" :md="4" :lg="4" :xl="4" class="mb10">
|
||||
<el-input v-model="item[0]" placeholder="参数名" clearable />
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="18" :md="18" :lg="18" :xl="18" class="mb10">
|
||||
<el-input v-model="item[1]" placeholder="参数值" clearable />
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="路径参数(Path)" name="3">
|
||||
<el-row v-for="(item, index) in state.ruleForm.pathParameters" :key="index" :gutter="25" class="w100">
|
||||
<el-col :xs="24" :sm="2" :md="2" :lg="2" :xl="2" class="mb10">
|
||||
<el-button type="danger" size="small" icon="ele-Delete" text @click="() => state.ruleForm.pathParameters.splice(index, 1)" />
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="4" :md="4" :lg="4" :xl="4" class="mb10">
|
||||
<el-input v-model="item[0]" placeholder="参数名" clearable />
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="18" :md="18" :lg="18" :xl="18" class="mb10">
|
||||
<el-input v-model="item[1]" placeholder="参数值" clearable />
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="查询参数(Query)" name="4">
|
||||
<el-row v-for="(item, index) in state.ruleForm.queryParameters" :key="index" :gutter="25" class="w100">
|
||||
<el-col :xs="24" :sm="2" :md="2" :lg="2" :xl="2" class="mb10">
|
||||
<el-button type="danger" size="small" icon="ele-Delete" text @click="() => state.ruleForm.queryParameters.splice(index, 1)" />
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="4" :md="4" :lg="4" :xl="4" class="mb10">
|
||||
<el-input v-model="item[0]" placeholder="参数名" clearable />
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="18" :md="18" :lg="18" :xl="18" class="mb10">
|
||||
<el-input v-model="item[1]" placeholder="参数值" clearable />
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer" v-loading="state.loading">
|
||||
<el-button @click="() => (state.isShowDialog = false)">取 消</el-button>
|
||||
<el-button type="primary" @click="submit" v-reclick="1000">确 定</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup name="sysStressTest">
|
||||
import { reactive, ref } from 'vue';
|
||||
import { SysCommonApi } from '/@/api-services';
|
||||
import { getAPI } from '/@/utils/axios-utils';
|
||||
|
||||
const emits = defineEmits(['refreshData']);
|
||||
const ruleFormRef = ref();
|
||||
const state = reactive({
|
||||
isShowDialog: false,
|
||||
activeName: '1',
|
||||
loading: false,
|
||||
ruleForm: {
|
||||
requestUri: '',
|
||||
requestMethod: 'GET',
|
||||
numberOfRounds: 1,
|
||||
numberOfRequests: 100,
|
||||
maxDegreeOfParallelism: 500,
|
||||
requestParameters: [[]] as [[]] | {},
|
||||
queryParameters: [[]] as [[]] | {},
|
||||
pathParameters: [[]] as [[]] | {},
|
||||
headers: [[]] as [[]] | {},
|
||||
} as any,
|
||||
});
|
||||
|
||||
// 格式化参数
|
||||
const formatParameter = (params: any[] | {}) => {
|
||||
if (Array.isArray(params)) {
|
||||
return Object.fromEntries(params.filter((e) => e.length === 2));
|
||||
} else if (typeof params === 'object' && params !== null) {
|
||||
return Object.entries(params);
|
||||
}
|
||||
return {};
|
||||
};
|
||||
|
||||
// 打开弹窗
|
||||
const openDialog = (row: any) => {
|
||||
const newRow = { ...state.ruleForm, ...row }; // 合并默认值和新值
|
||||
state.ruleForm = {
|
||||
...newRow,
|
||||
requestMethod: row.requestMethod?.toUpperCase() ?? 'GET',
|
||||
};
|
||||
state.isShowDialog = true;
|
||||
ruleFormRef.value?.resetFields();
|
||||
};
|
||||
|
||||
// 提交
|
||||
const submit = () => {
|
||||
ruleFormRef.value.validate(async (valid: boolean) => {
|
||||
if (!valid) return;
|
||||
try {
|
||||
state.loading = true;
|
||||
|
||||
// 创建一个新的对象来保存格式化后的数据
|
||||
const formattedRuleForm = {
|
||||
...state.ruleForm,
|
||||
headers: formatParameter(state.ruleForm.headers),
|
||||
pathParameters: formatParameter(state.ruleForm.pathParameters),
|
||||
queryParameters: formatParameter(state.ruleForm.queryParameters),
|
||||
requestParameters: formatParameter(state.ruleForm.requestParameters),
|
||||
} as any;
|
||||
|
||||
// 确保所有可能是空对象的参数被正确设置为 undefined
|
||||
['headers', 'pathParameters', 'queryParameters', 'requestParameters'].forEach((paramKey) => {
|
||||
if (Object.keys(formattedRuleForm[paramKey] || {}).length === 0) {
|
||||
formattedRuleForm[paramKey] = undefined;
|
||||
}
|
||||
});
|
||||
|
||||
emits(
|
||||
'refreshData',
|
||||
await getAPI(SysCommonApi)
|
||||
.apiSysCommonStressTestPost(formattedRuleForm, { timeout: 0 })
|
||||
.then((res) => res.data.result)
|
||||
);
|
||||
state.isShowDialog = false;
|
||||
} finally {
|
||||
state.loading = false;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 添加参数事件
|
||||
const addParams = () => {
|
||||
const paramType = ['headers', 'requestParameters', 'pathParameters', 'queryParameters'][+state.activeName - 1];
|
||||
if (Array.isArray(state.ruleForm[paramType])) {
|
||||
state.ruleForm[paramType].push([null, null]);
|
||||
} else if (typeof state.ruleForm[paramType] === 'object') {
|
||||
state.ruleForm[paramType] = [[null, null]];
|
||||
}
|
||||
};
|
||||
|
||||
// 导出对象
|
||||
defineExpose({ openDialog });
|
||||
</script>
|
||||
284
Web/src/views/system/stressTest/index.vue
Normal file
284
Web/src/views/system/stressTest/index.vue
Normal file
@ -0,0 +1,284 @@
|
||||
<template>
|
||||
<div class="sys-stress-test h100 overlay-none">
|
||||
<div>
|
||||
<NoticeBar text="接口压测会占用服务器大量的系统资源(内存、带宽),请慎重操作!!!" style="margin: 4px" />
|
||||
</div>
|
||||
|
||||
<splitpanes class="default-theme overlay-hidden">
|
||||
<pane size="20" class="vh100">
|
||||
<el-card class="vh80" shadow="hover" header="" v-loading="state.loading">
|
||||
<el-row :gutter="35">
|
||||
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb10">
|
||||
<el-select v-model="state.swaggerUrl" placeholder="接口分组">
|
||||
<el-option v-for="(item, index) in state.apiGroupList" :key="index" :label="item.name" :value="item.url" />
|
||||
</el-select>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb10">
|
||||
<el-input v-model="state.keywords" placeholder="接口名称" clearable>
|
||||
<template #append>
|
||||
<el-button icon="ele-Search" v-reclick="100" @click="queryTreeNode()" />
|
||||
</template>
|
||||
</el-input>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-tree
|
||||
ref="treeRef"
|
||||
class="filter-tree overlay-y vh68"
|
||||
style="padding-bottom: 60px"
|
||||
:data="state.apiList"
|
||||
:props="{ children: 'children', label: 'summary' }"
|
||||
:filter-node-method="filterNode"
|
||||
node-key="id"
|
||||
highlight-current
|
||||
check-strictly
|
||||
>
|
||||
<template #default="{ node }">
|
||||
<el-icon v-if="node.level == 1" size="16" style="margin-right: 3px; display: inline; vertical-align: middle"><ele-Menu /></el-icon>
|
||||
<el-icon v-else size="16" style="margin-right: 3px; display: inline; vertical-align: middle"><ele-Link /></el-icon>
|
||||
{{ node.label }}
|
||||
<span class="node-button" v-if="!node.data.children">
|
||||
<el-button type="warning" plain icon="ele-Position" @click="treeNodeTest(node.data)" />
|
||||
</span>
|
||||
</template>
|
||||
</el-tree>
|
||||
</el-card>
|
||||
</pane>
|
||||
|
||||
<pane size="80" class="vh100">
|
||||
<el-card class="main-container vh80" shadow="hover" header="" v-loading="state.loading" body-style="height:100vh; overflow:auto">
|
||||
<template #header>
|
||||
<el-button type="primary" icon="ele-Odometer" @click="showDialog(undefined)">接口压测</el-button>
|
||||
</template>
|
||||
<el-descriptions title="⚡压测参数" label-width="180px" :column="1" class="mb20" border>
|
||||
<el-descriptions-item label="请求地址" label-align="left" align="left">
|
||||
{{ state.ruleForm.requestUri }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="请求方式" label-align="left" align="left">
|
||||
<el-tag>{{ state.ruleForm.requestMethod?.toUpperCase() }}</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="压测轮数" label-align="left" align="left">
|
||||
{{ state.ruleForm.numberOfRounds ?? 1 }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="每轮请求数" label-align="left" align="left">
|
||||
{{ state.ruleForm.numberOfRequests ?? 1 }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="最大并发量" label-align="left" align="left">
|
||||
{{ state.ruleForm.maxDegreeOfParallelism ?? 1 }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
|
||||
<el-descriptions title="⚡压测结果" label-width="180px" :column="4" border>
|
||||
<el-descriptions-item label="总用时(秒)" label-align="left" align="left">
|
||||
<el-tag>{{ (state.result.totalTimeInSeconds ?? 0).toFixed(2) }}</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="成功请求次数" label-align="left" align="left">
|
||||
<el-tag type="success">{{ state.result.successfulRequests ?? 0 }}</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="失败请求次数" label-align="left" align="left">
|
||||
<el-tag type="danger">{{ state.result.failedRequests ?? 0 }}</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="每秒查询率(QPS)" label-align="left" align="left">
|
||||
<el-tag>{{ (state.result.queriesPerSecond ?? 0).toFixed(2) }}</el-tag>
|
||||
</el-descriptions-item>
|
||||
|
||||
<el-descriptions-item label="最小响应时间(毫秒)" label-align="left" align="left">
|
||||
{{ (state.result.minResponseTime ?? 0).toFixed(2) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="最大响应时间(毫秒)" label-align="left" align="left">
|
||||
{{ (state.result.maxResponseTime ?? 0).toFixed(2) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="平均响应时间(毫秒)" span="3" label-align="left" align="left">
|
||||
{{ (state.result.averageResponseTime ?? 0).toFixed(2) }}
|
||||
</el-descriptions-item>
|
||||
|
||||
<el-descriptions-item label="P10 响应时间(毫秒)" label-align="left" align="left">
|
||||
{{ (state.result.percentile10ResponseTime ?? 0).toFixed(2) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="P25 响应时间(毫秒)" label-align="left" align="left">
|
||||
{{ (state.result.percentile25ResponseTime ?? 0).toFixed(2) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="P50 响应时间(毫秒)" label-align="left" align="left">
|
||||
{{ (state.result.percentile50ResponseTime ?? 0).toFixed(2) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="P75 响应时间(毫秒)" label-align="left" align="left">
|
||||
{{ (state.result.percentile75ResponseTime ?? 0).toFixed(2) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="P90 响应时间(毫秒)" label-align="left" align="left">
|
||||
{{ (state.result.percentile90ResponseTime ?? 0).toFixed(2) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="P99 响应时间(毫秒)" label-align="left" align="left">
|
||||
{{ (state.result.percentile99ResponseTime ?? 0).toFixed(2) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="P999 响应时间(毫秒)" label-align="left" align="left">
|
||||
{{ (state.result.percentile9999ResponseTime ?? 0).toFixed(2) }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-card>
|
||||
</pane>
|
||||
</splitpanes>
|
||||
|
||||
<EditStressTest ref="editStressTestRef" @refreshData="refreshData" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup name="sysStressTest">
|
||||
import { onMounted, reactive, ref } from 'vue';
|
||||
import { ElTree } from 'element-plus';
|
||||
import { Splitpanes, Pane } from 'splitpanes';
|
||||
import 'splitpanes/dist/splitpanes.css';
|
||||
import 'vue-json-pretty/lib/styles.css';
|
||||
|
||||
import EditStressTest from './component/editStressTest.vue';
|
||||
import NoticeBar from '/@/components/noticeBar/index.vue';
|
||||
|
||||
import request, { getToken } from '/@/utils/request';
|
||||
import { StressTestHarnessResult } from '/@/api-services';
|
||||
|
||||
const editStressTestRef = ref();
|
||||
const treeRef = ref<InstanceType<typeof ElTree>>();
|
||||
const state = reactive({
|
||||
loading: false,
|
||||
activeName: '',
|
||||
ruleForm: {
|
||||
requestUri: '',
|
||||
requestMethod: 'GET',
|
||||
numberOfRounds: 1,
|
||||
numberOfRequests: 100,
|
||||
maxDegreeOfParallelism: 500,
|
||||
requestParameters: [[]],
|
||||
queryParameters: [[]],
|
||||
pathParameters: [[]],
|
||||
headers: [[]],
|
||||
},
|
||||
keywords: undefined,
|
||||
swaggerUrl: '/swagger/Default/swagger.json',
|
||||
result: {} as StressTestHarnessResult,
|
||||
apiList: [] as Array<any>,
|
||||
apiGroupList: [] as any,
|
||||
});
|
||||
|
||||
// 页面初始化
|
||||
onMounted(async () => {
|
||||
state.apiGroupList = await getApiGroupList();
|
||||
state.apiList = await getApiList('');
|
||||
});
|
||||
|
||||
// 获取接口分组列表
|
||||
const getApiGroupList = async () => {
|
||||
try {
|
||||
const html = await request(`/index.html`, { method: 'get' }).then(({ data }) => data);
|
||||
const prefixText = "var configObject = JSON.parse('";
|
||||
const jsonStr = html
|
||||
.substring(html.indexOf(prefixText) + prefixText.length, html.indexOf('var oauthConfigObject = JSON.parse('))
|
||||
?.trim()
|
||||
.replace("');", '');
|
||||
return JSON.parse(jsonStr).urls;
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
// 获取所有接口列表
|
||||
const getApiList = (keywords: string | undefined) => {
|
||||
const emojiPattern =
|
||||
/[\u{1F600}-\u{1F64F}\u{1F300}-\u{1F5FF}\u{1F680}-\u{1F6FF}\u{1F700}-\u{1F77F}\u{1F780}-\u{1F7FF}\u{1F800}-\u{1F8FF}\u{1F900}-\u{1F9FF}\u{1FA00}-\u{1FA6F}\u{1FA70}-\u{1FAFF}\u{2600}-\u{26FF}\u{2700}-\u{27BF}]/gu;
|
||||
return request(state.swaggerUrl, { method: 'get' }).then(({ data }) => {
|
||||
const pathMap = data.paths;
|
||||
const result = data.tags.map((e: any) => ({ path: e.name, summary: e.description.replaceAll(emojiPattern, ''), children: [] }));
|
||||
Object.keys(pathMap).map((path) => {
|
||||
const method = Object.keys(pathMap[path])[0];
|
||||
const apiInfo = pathMap[path][method];
|
||||
if (keywords && apiInfo.summary?.indexOf(keywords) === -1) return;
|
||||
result
|
||||
.find((u: any) => u.path === apiInfo.tags[0])
|
||||
.children.push({
|
||||
path: path,
|
||||
method: method,
|
||||
summary: apiInfo.summary?.replaceAll(emojiPattern, '') ?? path,
|
||||
parameters: apiInfo.parameters,
|
||||
requestBody: apiInfo.requestBody,
|
||||
data: apiInfo,
|
||||
});
|
||||
});
|
||||
return result.filter((u: any) => u.children.length > 0);
|
||||
});
|
||||
};
|
||||
|
||||
// 刷新数据
|
||||
const refreshData = (data: StressTestHarnessResult) => {
|
||||
state.result = data;
|
||||
};
|
||||
|
||||
// 打开接口参数设置页面
|
||||
const showDialog = async (row: any) => {
|
||||
const newRow = row ?? { ...state.ruleForm };
|
||||
const convertToKeyValuePairs = (params: any) => {
|
||||
if (Array.isArray(params) && params.every((item) => Array.isArray(item) && item.length === 2)) {
|
||||
return params;
|
||||
} else if (typeof params === 'object' && params !== null) {
|
||||
return Object.entries(params);
|
||||
}
|
||||
return [];
|
||||
};
|
||||
|
||||
state.ruleForm = {
|
||||
...newRow,
|
||||
requestParameters: convertToKeyValuePairs(newRow.requestParameters),
|
||||
queryParameters: convertToKeyValuePairs(newRow.queryParameters),
|
||||
pathParameters: convertToKeyValuePairs(newRow.pathParameters),
|
||||
headers: convertToKeyValuePairs(newRow.headers),
|
||||
};
|
||||
editStressTestRef.value.openDialog(state.ruleForm);
|
||||
};
|
||||
|
||||
// 接口树节点按钮事件
|
||||
const treeNodeTest = async (node: any) => {
|
||||
if (node.id == 0) return;
|
||||
state.ruleForm = {
|
||||
requestUri: location.origin + node.path,
|
||||
requestMethod: node.method,
|
||||
numberOfRounds: 1,
|
||||
numberOfRequests: 100,
|
||||
maxDegreeOfParallelism: 500,
|
||||
requestParameters: [],
|
||||
queryParameters: [],
|
||||
pathParameters: [],
|
||||
headers: [['Authorization', 'Bearer ' + getToken()]] as any,
|
||||
};
|
||||
showDialog(state.ruleForm);
|
||||
};
|
||||
|
||||
// 查询树节点
|
||||
const queryTreeNode = async () => {
|
||||
state.apiList = await getApiList(state.keywords);
|
||||
};
|
||||
|
||||
const filterNode = (value: string, data: any) => {
|
||||
if (!value) return true;
|
||||
return data.name.includes(value);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.card-header {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
:deep(.el-collapse-item) {
|
||||
.el-collapse-item__arrow {
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
:deep(.main-container) {
|
||||
.el-card__header {
|
||||
padding: 8px;
|
||||
}
|
||||
}
|
||||
.node-button {
|
||||
position: absolute;
|
||||
scale: 0.7;
|
||||
right: 0;
|
||||
}
|
||||
</style>
|
||||
Loading…
Reference in New Issue
Block a user