123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369 |
- <template>
- <el-dialog class="api-test" v-model="showDialog" title="测试API" width="800px" :close-on-click-modal="false" :close-on-press-escape="false">
- <el-descriptions :column="2" border>
- <el-descriptions-item label="API名称" :span="2">{{ apiData.name }}</el-descriptions-item>
- <el-descriptions-item label="API路径" :span="2">
- <div class="api-path-container">
- <span class="domain">{{ originUrl }}/apihub/</span>
- <span class="path">{{ apiData.path }}</span>
- <el-tooltip content="复制API完整路径" placement="top">
- <el-icon class="copy-icon" @click="copyApiPath"><CopyDocument /></el-icon>
- </el-tooltip>
- </div>
- </el-descriptions-item>
- <el-descriptions-item label="请求方法">
- <el-tag :type="getMethodTagType(apiData.method)" size="small">{{ apiData.method }}</el-tag>
- </el-descriptions-item>
- <el-descriptions-item label="数据源">{{ apiData.dataSourceName }}</el-descriptions-item>
- </el-descriptions>
- <div class="section-title">参数设置</div>
- <el-form :model="testParams" label-width="120px" v-if="apiData.parameters && apiData.parameters.length">
- <el-form-item v-for="param in apiData.parameters" :key="param.name" :label="param.name" :required="param.required">
- <el-input v-if="param.type === 'string'" v-model="testParams[param.name]" :placeholder="getParamPlaceholder(param)"></el-input>
- <el-input-number v-else-if="param.type === 'int' || param.type === 'float'" v-model="testParams[param.name]" :placeholder="getParamPlaceholder(param)"></el-input-number>
- <el-switch v-else-if="param.type === 'bool'" v-model="testParams[param.name]" :active-value="true" :inactive-value="false"></el-switch>
- <el-input v-else v-model="testParams[param.name]" :placeholder="getParamPlaceholder(param)"></el-input>
- <div class="param-desc" v-if="param.description">{{ param.description }}</div>
- </el-form-item>
- </el-form>
- <el-empty description="该API没有定义参数" v-else></el-empty>
- <div class="section-title">测试结果</div>
- <div v-if="!testResult.success && !loading" class="test-placeholder">点击下方"执行测试"按钮开始测试</div>
- <div v-else>
- <el-alert :title="testResult.message" :type="testResult.success ? 'success' : 'error'" :closable="false" show-icon></el-alert>
- <div v-if="testResult.success && testResult.data" class="result-container">
- <el-tabs v-model="activeTab">
- <el-tab-pane label="结果数据" name="data">
- <pre class="result-json">{{ formatJson(testResult.data) }}</pre>
- </el-tab-pane>
- <el-tab-pane label="原始响应" name="raw">
- <pre class="result-json">{{ formatJson(testResult) }}</pre>
- </el-tab-pane>
- </el-tabs>
- </div>
- </div>
- <template #footer>
- <div class="dialog-footer">
- <el-button @click="closeDialog">关闭</el-button>
- <el-button type="primary" @click="runTest" :loading="loading">执行测试</el-button>
- </div>
- </template>
- </el-dialog>
- </template>
- <script lang="ts" setup>
- import { ref, reactive, watch } from "vue";
- import apiHub from "/@/api/modules/apiHub";
- import { ElMessage } from "element-plus";
- import { CopyDocument } from "@element-plus/icons-vue";
- import getOrigin from "/@/utils/origin";
- const showDialog = ref(false);
- const loading = ref(false);
- const activeTab = ref("data");
- const originUrl: string = getOrigin("");
- // API数据
- const apiData = reactive<any>({
- id: null,
- name: "",
- path: "",
- method: "",
- dataSourceId: null,
- dataSourceName: "",
- sqlType: "",
- sqlContent: "",
- parameters: [],
- returnFormat: "",
- version: "",
- status: "",
- plugins: [],
- description: "",
- createdAt: "",
- updatedAt: "",
- });
- // 测试参数
- const testParams = reactive<any>({});
- // 测试结果
- const testResult = reactive<any>({
- success: false,
- message: "",
- data: null,
- });
- // 监听API数据变化,初始化测试参数
- watch(
- () => apiData.parameters,
- (newParams) => {
- // 清空测试参数
- Object.keys(testParams).forEach((key) => {
- delete testParams[key];
- });
- // 根据参数定义初始化测试参数
- if (newParams && newParams.length) {
- newParams.forEach((param: any) => {
- // 如果有默认值,使用默认值
- if (param.defaultValue !== undefined && param.defaultValue !== "") {
- // 根据类型转换默认值
- if (param.type === "int") {
- testParams[param.name] = parseInt(param.defaultValue);
- } else if (param.type === "float") {
- testParams[param.name] = parseFloat(param.defaultValue);
- } else if (param.type === "bool") {
- testParams[param.name] = param.defaultValue === "true";
- } else {
- testParams[param.name] = param.defaultValue;
- }
- } else {
- // 否则设置为空值
- if (param.type === "int" || param.type === "float") {
- testParams[param.name] = null;
- } else if (param.type === "bool") {
- testParams[param.name] = false;
- } else {
- testParams[param.name] = "";
- }
- }
- });
- }
- },
- { deep: true }
- );
- // 根据请求方法返回不同的标签类型
- const getMethodTagType = (method: string) => {
- switch (method?.toUpperCase()) {
- case "GET":
- return "success";
- case "POST":
- return "primary";
- case "PUT":
- return "warning";
- case "DELETE":
- return "danger";
- default:
- return "info";
- }
- };
- // 获取参数占位符文本
- const getParamPlaceholder = (param: any) => {
- if (param.required) {
- return `请输入${param.description || param.name}(必填)`;
- }
- return `请输入${param.description || param.name}(选填)`;
- };
- // 格式化JSON
- const formatJson = (json: any) => {
- try {
- return JSON.stringify(json, null, 2);
- } catch (e) {
- return json;
- }
- };
- // 关闭对话框
- const closeDialog = () => {
- showDialog.value = false;
- };
- // 执行测试
- const runTest = async () => {
- // 重置测试结果
- testResult.success = false;
- testResult.message = "";
- testResult.data = null;
- loading.value = true;
- apiHub
- .test({
- id: apiData.id,
- parameters: testParams,
- })
- .then((res: any) => {
- testResult.success = true;
- testResult.message = "测试成功";
- testResult.data = res;
- })
- .catch((error: any) => {
- testResult.success = false;
- testResult.message = error?.message || "测试失败";
- })
- .finally(() => {
- loading.value = false;
- });
- };
- // 复制API完整路径
- const copyApiPath = () => {
- // 实现复制功能
- const fullPath = `${originUrl}/apihub/${apiData.path}`;
- // 使用Clipboard API复制到剪贴板
- navigator.clipboard.writeText(fullPath)
- .then(() => {
- ElMessage.success('已复制API完整路径到剪贴板');
- })
- .catch(() => {
- ElMessage.error('复制失败,请手动复制');
- });
- };
- // 打开对话框
- const open = async (row: any) => {
- // 清空数据
- Object.keys(apiData).forEach((key) => {
- apiData[key] = undefined;
- });
- // 重置测试结果
- testResult.success = false;
- testResult.message = "";
- testResult.data = null;
- showDialog.value = true;
- // 实际使用时,应该调用API获取详细信息
- // const res = await apiHub.get(row.id)
- // Object.assign(apiData, res)
- // 这里模拟直接使用传入的行数据
- Object.assign(apiData, row);
- };
- defineExpose({ open });
- </script>
- <style scoped>
- .dialog-footer {
- display: flex;
- justify-content: flex-end;
- }
- .section-title {
- font-weight: bold;
- margin: 20px 0 10px;
- font-size: 16px;
- border-left: 4px solid #409eff;
- padding-left: 10px;
- }
- .param-desc {
- font-size: 12px;
- color: #909399;
- margin-top: 5px;
- }
- .test-placeholder {
- text-align: center;
- color: #909399;
- padding: 20px;
- background-color: #f5f7fa;
- border-radius: 4px;
- }
- /* 深色主题下的样式 */
- [data-theme='dark'] .test-placeholder {
- text-align: center;
- color: #e4e3e3;
- padding: 20px;
- background-color: #424040;
- border-radius: 4px;
- }
- .result-container {
- margin-top: 15px;
- border: 1px solid #e4e7ed;
- border-radius: 4px;
- }
- /* 深色主题下的样式 */
- [data-theme='dark'] .result-container {
- margin-top: 15px;
- border: 1px solid #575656;
- border-radius: 4px;
- }
- .result-json {
- background-color: #f5f7fa;
- padding: 10px;
- font-family: monospace;
- white-space: pre-wrap;
- word-break: break-all;
- margin: 0;
- max-height: 300px;
- overflow: auto;
- }
- /* 深色主题下的样式 */
- [data-theme='dark'] .result-json {
- background-color: #3c3b3b;
- color: #ffffff;
- padding: 10px;
- font-family: monospace;
- white-space: pre-wrap;
- word-break: break-all;
- margin: 0;
- max-height: 300px;
- overflow: auto;
- }
- .el-empty{
- height: 120px;
- }
- .api-path-container {
- display: inline-block;
- text-decoration: none;
- color: black; /* 设置整体文字颜色 */
- align-items: center; /* 垂直居中对齐 */
- }
- /* 深色主题下的样式 */
- [data-theme='dark'] .api-path-container {
- display: inline-block;
- text-decoration: none;
- color: #fff; /* 设置整体文字颜色 */
- }
- .api-path-container .domain {
- background-color: #eee; /* 设置域名部分的背景颜色 */
- color: black; /* 设置域名部分的文字颜色 */
- padding: 2px 4px; /* 添加一些内边距 */
- display: inline-block;
- border-radius: 15px; /* 增加圆角 */
- }
- /* 深色主题下的样式 */
- [data-theme='dark'] .api-path-container .domain{
- background-color: #3c3b3b; /* 设置域名部分的背景颜色 */
- color: white; /* 设置域名部分的文字颜色 */
- padding: 2px 4px; /* 添加一些内边距 */
- display: inline-block;
- border-radius: 15px; /* 增加圆角 */
- }
- .api-path-container .path {
- display: inline-block;
- }
- .copy-icon {
- cursor: pointer;
- color: var(--el-color-primary);
- transition: all 0.2s;
- font-size: 24px;
- margin-left: 8px;
- padding: 4px;
- border-radius: 4px;
- }
- .copy-icon:hover {
- transform: scale(1.1);
- color: var(--el-color-primary-dark-2);
- background-color: var(--el-color-primary-light-9);
- }
- </style>
|