浏览代码

feat: 优化测试和更多操作

yanglzh 5 月之前
父节点
当前提交
deed703718

+ 481 - 502
src/views/apihub/apilist.vue

@@ -1,628 +1,607 @@
 <template>
-	<div class="page">
-		<div class="apihub-container">
-			<!-- 左侧分组树 -->
-			<div class="apihub-sidebar">
-				<el-card shadow="never" class="group-card">
-					<template #header>
-						<div class="card-header">
-							<span>API分组</span>
-							<div class="header-actions">
-								<el-button type="primary" size="small" @click="addGroup" v-auth="'add_group'">
-									<el-icon><ele-Plus /></el-icon>
-								</el-button>
-								<el-button type="primary" size="small" @click="refreshGroups">
-									<el-icon><ele-Refresh /></el-icon>
-								</el-button>
-							</div>
-						</div>
-					</template>
-					<div class="group-search">
-						<el-input
-							v-model="groupSearchKey"
-							placeholder="搜索分组"
-							clearable
-							prefix-icon="ele-Search"
-							@input="filterGroups" />
-					</div>
-					<div class="group-tree-container">
-						<el-tree
-							ref="groupTreeRef"
-							:data="groupTreeData"
-							:props="{ label: 'Name', children: 'Children' }"
-							node-key="Id"
-							highlight-current
-							:expand-on-click-node="true"
-							default-expand-all
-							@node-click="handleGroupClick">
-							<template #default="{ node, data }">
-								<div class="custom-tree-node">
-									<span>{{ node.label }}</span>
-									<span class="api-count" v-if="data.ApiCount">{{ data.ApiCount }}</span>
-									<div class="node-actions">
-										<el-dropdown @command="(command: string) => handleGroupCommand(command, data)" trigger="click">
-											<el-icon><ele-More /></el-icon>
-											<template #dropdown>
-												<el-dropdown-menu>
-													<el-dropdown-item command="edit" v-auth="'edit_group'">编辑</el-dropdown-item>
-													<el-dropdown-item command="add_child" v-auth="'add_group'">添加子分组</el-dropdown-item>
-													<el-dropdown-item command="delete" v-auth="'delete_group'">删除</el-dropdown-item>
-												</el-dropdown-menu>
-											</template>
-										</el-dropdown>
-									</div>
-								</div>
-							</template>
-						</el-tree>
-					</div>
-				</el-card>
-			</div>
-
-			<!-- 右侧API列表 -->
-			<div class="apihub-content">
-				<el-card shadow="never">
-					<div class="api-header">
-						<div class="current-group" v-if="currentGroup.name">
-							当前分组: <span class="group-name">{{ currentGroup.name }}</span>
-						</div>
-						<div class="current-group" v-else>
-							全部API
-						</div>
-					</div>
-					<el-form :model="params" inline ref="queryRef">
-						<el-form-item label="API名称" prop="keyWord">
-							<el-input v-model="params.keyWord" placeholder="请输入API名称" clearable style="width: 180px" @keyup.enter.native="getList(1)" />
-						</el-form-item>
-						<el-form-item label="数据源" prop="dataSourceId">
-							<el-select v-model="params.dataSourceId" placeholder="请选择数据源" clearable style="width: 180px">
-								<el-option v-for="item in dataSources" :key="item.id" :label="item.name" :value="item.id" />
-							</el-select>
-						</el-form-item>
-						<el-form-item label="状态" prop="status">
-							<el-select v-model="params.status" placeholder="请选择状态" clearable style="width: 120px">
-								<el-option label="全部" value="" />
-								<el-option label="草稿" value="Draft" />
-								<el-option label="已发布" value="Published" />
-								<el-option label="已废弃" value="Deprecated" />
-							</el-select>
-						</el-form-item>
-						<el-form-item label="日期范围" prop="dateRange">
-							<el-date-picker
-								v-model="params.dateRange"
-								type="daterange"
-								range-separator="至"
-								start-placeholder="开始日期"
-								end-placeholder="结束日期"
-								value-format="YYYY-MM-DD"
-								style="width: 240px" />
-						</el-form-item>
-						<el-form-item>
-							<el-button type="primary" class="ml10" @click="getList(1)">
-								<el-icon>
-									<ele-Search />
-								</el-icon>
-								查询
-							</el-button>
-							<el-button @click="resetQuery()">
-								<el-icon>
-									<ele-Refresh />
-								</el-icon>
-								重置
-							</el-button>
-							<el-button type="primary" @click="addOrEdit()" v-auth="'add'">
-								<el-icon>
-									<ele-FolderAdd />
-								</el-icon>
-								新增API
-							</el-button>
-						</el-form-item>
-					</el-form>
-					<el-table :data="tableData" style="width: 100%" v-loading="loading" row-key="id">
-						<el-table-column type="selection" width="55" align="center" />
-						<el-table-column prop="id" label="ID" width="80" align="center" />
-						<el-table-column prop="name" label="API名称" min-width="120" show-overflow-tooltip></el-table-column>
-						<el-table-column prop="path" label="API路径" min-width="150" show-overflow-tooltip></el-table-column>
-						<el-table-column prop="method" label="请求方法" width="100" align="center">
-							<template #default="scope">
-								<el-tag
-									:type="getMethodTagType(scope.row.method)"
-									size="small">
-									{{ scope.row.method }}
-								</el-tag>
-							</template>
-						</el-table-column>
-						<el-table-column prop="dataSourceName" label="数据源" width="120" show-overflow-tooltip></el-table-column>
-						<el-table-column prop="sqlType" label="SQL类型" width="100" align="center">
-							<template #default="scope">
-								<el-tag size="small" type="info" v-if="scope.row.sqlType === 'query'">查询</el-tag>
-								<el-tag size="small" type="warning" v-else-if="scope.row.sqlType === 'procedure'">存储过程</el-tag>
-								<span v-else>{{ scope.row.sqlType }}</span>
-							</template>
-						</el-table-column>
-						<el-table-column prop="version" label="版本" width="80" align="center"></el-table-column>
-						<el-table-column prop="status" label="状态" width="100" align="center">
-							<template #default="scope">
-								<el-tag size="small" v-if="scope.row.status === 'Draft'">草稿</el-tag>
-								<el-tag size="small" type="success" v-else-if="scope.row.status === 'Published'">已发布</el-tag>
-								<el-tag size="small" type="info" v-else-if="scope.row.status === 'Deprecated'">已废弃</el-tag>
-								<span v-else>{{ scope.row.status }}</span>
-							</template>
-						</el-table-column>
-						<el-table-column prop="createdAt" label="创建时间" width="160" align="center"></el-table-column>
-						<el-table-column label="操作" width="220" align="center">
-							<template #default="scope">
-								<el-button size="small" text type="primary" @click="viewDetail(scope.row)" v-auth="'view'">查看</el-button>
-								<el-button size="small" text type="warning" @click="addOrEdit(scope.row)" v-auth="'edit'">编辑</el-button>
-								<el-button size="small" text type="success" @click="testApi(scope.row)" v-auth="'test'">测试</el-button>
-								<el-dropdown @command="(command: string) => handleCommand(command, scope.row)">
-									<el-button size="small" text type="primary">
-										更多<el-icon class="el-icon--right"><arrow-down /></el-icon>
-									</el-button>
-									<template #dropdown>
-										<el-dropdown-menu>
-											<el-dropdown-item command="publish" v-if="scope.row.status === 'Draft'" v-auth="'publish'">发布</el-dropdown-item>
-											<el-dropdown-item command="deprecate" v-if="scope.row.status === 'Published'" v-auth="'deprecate'">废弃</el-dropdown-item>
-											<el-dropdown-item command="delete" v-auth="'delete'">删除</el-dropdown-item>
-										</el-dropdown-menu>
-									</template>
-								</el-dropdown>
-							</template>
-						</el-table-column>
-					</el-table>
-					<pagination v-if="params.total" :total="params.total" v-model:page="params.pageNum" v-model:limit="params.pageSize" @pagination="getList()" />
-				</el-card>
-			</div>
-		</div>
-
-		<!-- 组件 -->
-		<EditForm ref="editFormRef" @getList="getList(1)"></EditForm>
-		<ViewDetail ref="viewDetailRef"></ViewDetail>
-		<TestApi ref="testApiRef"></TestApi>
-		<GroupForm ref="groupFormRef" @refresh="refreshGroups"></GroupForm>
-	</div>
+  <div class="page">
+    <div class="apihub-container">
+      <!-- 左侧分组树 -->
+      <div class="apihub-sidebar">
+        <el-card shadow="never" class="group-card">
+          <template #header>
+            <div class="card-header">
+              <span>API分组</span>
+              <div class="header-actions">
+                <el-button type="primary" size="small" @click="addGroup" v-auth="'add_group'">
+                  <el-icon><ele-Plus /></el-icon>
+                </el-button>
+                <el-button type="primary" size="small" @click="refreshGroups">
+                  <el-icon><ele-Refresh /></el-icon>
+                </el-button>
+              </div>
+            </div>
+          </template>
+          <div class="group-search">
+            <el-input v-model="groupSearchKey" placeholder="搜索分组" clearable prefix-icon="ele-Search" @input="filterGroups" />
+          </div>
+          <div class="group-tree-container">
+            <el-tree ref="groupTreeRef" :data="groupTreeData" :props="{ label: 'Name', children: 'Children' }" node-key="Id" highlight-current :expand-on-click-node="true" default-expand-all @node-click="handleGroupClick">
+              <template #default="{ node, data }">
+                <div class="custom-tree-node">
+                  <span>{{ node.label }}</span>
+                  <span class="api-count" v-if="data.ApiCount">{{ data.ApiCount }}</span>
+                  <div class="node-actions">
+                    <el-dropdown @command="(command: string) => handleGroupCommand(command, data)" trigger="click">
+                      <el-icon><ele-More /></el-icon>
+                      <template #dropdown>
+                        <el-dropdown-menu>
+                          <el-dropdown-item command="edit" v-auth="'edit_group'">编辑</el-dropdown-item>
+                          <el-dropdown-item command="add_child" v-auth="'add_group'">添加子分组</el-dropdown-item>
+                          <el-dropdown-item command="delete" v-auth="'delete_group'">删除</el-dropdown-item>
+                        </el-dropdown-menu>
+                      </template>
+                    </el-dropdown>
+                  </div>
+                </div>
+              </template>
+            </el-tree>
+          </div>
+        </el-card>
+      </div>
+
+      <!-- 右侧API列表 -->
+      <div class="apihub-content">
+        <el-card shadow="never">
+          <div class="api-header">
+            <div class="current-group" v-if="currentGroup.name">
+              当前分组: <span class="group-name">{{ currentGroup.name }}</span>
+            </div>
+            <div class="current-group" v-else>全部API</div>
+          </div>
+          <el-form :model="params" inline ref="queryRef">
+            <el-form-item label="API名称" prop="keyWord">
+              <el-input v-model="params.keyWord" placeholder="请输入API名称" clearable style="width: 180px" @keyup.enter.native="getList(1)" />
+            </el-form-item>
+            <el-form-item label="数据源" prop="dataSourceId">
+              <el-select v-model="params.dataSourceId" placeholder="请选择数据源" clearable style="width: 180px">
+                <el-option v-for="item in dataSources" :key="item.id" :label="item.name" :value="item.id" />
+              </el-select>
+            </el-form-item>
+            <el-form-item label="状态" prop="status">
+              <el-select v-model="params.status" placeholder="请选择状态" clearable style="width: 120px">
+                <el-option label="全部" value="" />
+                <el-option label="草稿" value="Draft" />
+                <el-option label="已发布" value="Published" />
+                <el-option label="已废弃" value="Deprecated" />
+              </el-select>
+            </el-form-item>
+            <el-form-item label="日期范围" prop="dateRange">
+              <el-date-picker v-model="params.dateRange" type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" value-format="YYYY-MM-DD" style="width: 240px" />
+            </el-form-item>
+            <el-form-item>
+              <el-button type="primary" class="ml10" @click="getList(1)">
+                <el-icon>
+                  <ele-Search />
+                </el-icon>
+                查询
+              </el-button>
+              <el-button @click="resetQuery()">
+                <el-icon>
+                  <ele-Refresh />
+                </el-icon>
+                重置
+              </el-button>
+              <el-button type="primary" @click="addOrEdit()" v-auth="'add'">
+                <el-icon>
+                  <ele-FolderAdd />
+                </el-icon>
+                新增API
+              </el-button>
+            </el-form-item>
+          </el-form>
+          <el-table :data="tableData" style="width: 100%" v-loading="loading" row-key="id">
+            <el-table-column type="selection" width="55" align="center" />
+            <el-table-column prop="id" label="ID" width="80" align="center" />
+            <el-table-column prop="name" label="API名称" min-width="120" show-overflow-tooltip></el-table-column>
+            <el-table-column prop="path" label="API路径" min-width="150" show-overflow-tooltip></el-table-column>
+            <el-table-column prop="method" label="请求方法" width="100" align="center">
+              <template #default="scope">
+                <el-tag :type="getMethodTagType(scope.row.method)" size="small">
+                  {{ scope.row.method }}
+                </el-tag>
+              </template>
+            </el-table-column>
+            <el-table-column prop="dataSourceName" label="数据源" width="120" show-overflow-tooltip></el-table-column>
+            <el-table-column prop="sqlType" label="SQL类型" width="100" align="center">
+              <template #default="scope">
+                <el-tag size="small" type="info" v-if="scope.row.sqlType === 'query'">查询</el-tag>
+                <el-tag size="small" type="warning" v-else-if="scope.row.sqlType === 'procedure'">存储过程</el-tag>
+                <span v-else>{{ scope.row.sqlType }}</span>
+              </template>
+            </el-table-column>
+            <el-table-column prop="version" label="版本" width="80" align="center"></el-table-column>
+            <el-table-column prop="status" label="状态" width="100" align="center">
+              <template #default="scope">
+                <el-tag size="small" v-if="scope.row.status === 'Draft'">草稿</el-tag>
+                <el-tag size="small" type="success" v-else-if="scope.row.status === 'Published'">已发布</el-tag>
+                <el-tag size="small" type="info" v-else-if="scope.row.status === 'Deprecated'">已废弃</el-tag>
+                <span v-else>{{ scope.row.status }}</span>
+              </template>
+            </el-table-column>
+            <el-table-column prop="createdAt" label="创建时间" width="160" align="center"></el-table-column>
+            <el-table-column label="操作" width="190" align="center" fixed="right">
+              <template #default="scope">
+                <div class="flex-row">
+                  <el-button size="small" text type="primary" @click="viewDetail(scope.row)" v-auth="'view'">查看</el-button>
+                  <el-button size="small" text type="warning" @click="addOrEdit(scope.row)" v-auth="'edit'">编辑</el-button>
+                  <el-button size="small" text type="success" @click="testApi(scope.row)" v-auth="'test'">测试</el-button>
+                  <el-dropdown @command="(command: string) => handleCommand(command, scope.row)">
+                    <el-button size="small" text type="primary" style="margin-left: 12px; vertical-align: top">
+                      更多<el-icon class="el-icon--right" style="font-size: 12px !important"><arrow-down /></el-icon>
+                    </el-button>
+                    <template #dropdown>
+                      <el-dropdown-menu>
+                        <el-dropdown-item command="publish" v-if="scope.row.status === 'Draft'" v-auth="'publish'">发布</el-dropdown-item>
+                        <el-dropdown-item command="deprecate" v-if="scope.row.status === 'Published'" v-auth="'deprecate'">废弃</el-dropdown-item>
+                        <el-dropdown-item command="delete" v-auth="'delete'">删除</el-dropdown-item>
+                      </el-dropdown-menu>
+                    </template>
+                  </el-dropdown>
+                </div>
+              </template>
+            </el-table-column>
+          </el-table>
+          <pagination v-if="params.total" :total="params.total" v-model:page="params.pageNum" v-model:limit="params.pageSize" @pagination="getList()" />
+        </el-card>
+      </div>
+    </div>
+
+    <!-- 组件 -->
+    <EditForm ref="editFormRef" @getList="getList(1)"></EditForm>
+    <ViewDetail ref="viewDetailRef"></ViewDetail>
+    <TestApi ref="testApiRef"></TestApi>
+    <GroupForm ref="groupFormRef" @refresh="refreshGroups"></GroupForm>
+  </div>
 </template>
 
 <script lang="ts" setup>
-import { ref, onMounted, reactive } from 'vue'
-import EditForm from './component/edit.vue'
-import ViewDetail from './component/view.vue'
-import TestApi from './component/test.vue'
-import GroupForm from './component/group.vue'
-import { useSearch } from '/@/hooks/useCommon'
-import { ElMessageBox, ElMessage } from 'element-plus'
-import { ArrowDown } from '@element-plus/icons-vue'
-import api from '/@/api/modules/apiHub'
+import { ref, onMounted, reactive } from "vue";
+import EditForm from "./component/edit.vue";
+import ViewDetail from "./component/view.vue";
+import TestApi from "./component/test.vue";
+import GroupForm from "./component/group.vue";
+import { useSearch } from "/@/hooks/useCommon";
+import { ElMessageBox, ElMessage } from "element-plus";
+import { ArrowDown } from "@element-plus/icons-vue";
+import api from "/@/api/modules/apiHub";
 
 // 定义API接口类型
 interface ApiDefinition {
-	id?: number
-	name: string
-	path: string
-	method: string
-	dataSourceId: number
-	dataSourceName?: string
-	sqlType: string
-	sqlContent: string
-	parameters?: any[]
-	returnFormat: string
-	version: string
-	status: string
-	plugins?: any[]
-	description?: string
-	createdAt?: string
-	updatedAt?: string
+  id?: number;
+  name: string;
+  path: string;
+  method: string;
+  dataSourceId: number;
+  dataSourceName?: string;
+  sqlType: string;
+  sqlContent: string;
+  parameters?: any[];
+  returnFormat: string;
+  version: string;
+  status: string;
+  plugins?: any[];
+  description?: string;
+  createdAt?: string;
+  updatedAt?: string;
 }
 
 // 引用组件
-const editFormRef = ref()
-const viewDetailRef = ref()
-const testApiRef = ref()
-const queryRef = ref()
-const groupFormRef = ref()
-const groupTreeRef = ref()
+const editFormRef = ref();
+const viewDetailRef = ref();
+const testApiRef = ref();
+const queryRef = ref();
+const groupFormRef = ref();
+const groupTreeRef = ref();
 
 // 分组相关状态
-const groupSearchKey = ref('')
-const groupTreeData = ref<any[]>([])
+const groupSearchKey = ref("");
+const groupTreeData = ref<any[]>([]);
 const currentGroup = reactive<any>({
-	Id: undefined,
-	GroupKey: '',
-	Name: '',
-	ParentId: 0,
-	Description: ''
-})
-const originalGroupTree = ref<any[]>([])
+  Id: undefined,
+  GroupKey: "",
+  Name: "",
+  ParentId: 0,
+  Description: "",
+});
+const originalGroupTree = ref<any[]>([]);
 
 // 数据源列表
-const dataSources = ref<any[]>([])
+const dataSources = ref<any[]>([]);
 
 // 使用通用搜索钩子
-const { params, tableData, getList, loading } = useSearch<ApiDefinition[]>(api.list, 'Data', {
-	keyWord: '',
-	dataSourceId: '',
-	status: '',
-	dateRange: [],
-	orderBy: '',
-	pageNum: 1,
-	pageSize: 10
-})
+const { params, tableData, getList, loading } = useSearch<ApiDefinition[]>(api.list, "Data", {
+  keyWord: "",
+  dataSourceId: "",
+  status: "",
+  dateRange: [],
+  orderBy: "",
+  pageNum: 1,
+  pageSize: 10,
+});
 
 // 加载数据源列表
 const loadDataSources = () => {
-	api.dataSource.list().then((res: any) => {
-		dataSources.value = res.list
-	})
-}
+  api.dataSource.list().then((res: any) => {
+    dataSources.value = res.list;
+  });
+};
 
 // 页面加载时获取列表数据
 onMounted(() => {
-	// 获取API列表
-	getList(1)
+  // 获取API列表
+  getList(1);
 
-	// 加载数据源列表
-	loadDataSources()
+  // 加载数据源列表
+  loadDataSources();
 
-	// 加载分组树
-	refreshGroups()
-})
+  // 加载分组树
+  refreshGroups();
+});
 
 // 将扁平数组转换为树形结构
 const convertToTree = (flatData: any[]) => {
-	// 创建一个映射表,用于快速查找节点
-	const map: any = {}
-	const result: any[] = []
-
-	// 首先创建所有节点的映射
-	flatData.forEach(item => {
-		// 确保每个节点都有Children属性
-		map[item.Id] = { ...item, Children: [] }
-	})
-
-	// 然后建立父子关系
-	flatData.forEach(item => {
-		const node = map[item.Id]
-
-		if (item.ParentId === 0 || !map[item.ParentId]) {
-			// 如果ParentId为0或者父节点不存在,则为顶级节点
-			result.push(node)
-		} else {
-			// 否则将该节点添加到父节点的Children中
-			map[item.ParentId].Children.push(node)
-		}
-	})
-
-	// 清理空的Children数组
-	flatData.forEach(item => {
-		if (map[item.Id].Children.length === 0) {
-			map[item.Id].Children = null
-		}
-	})
-
-	return result
-}
+  // 创建一个映射表,用于快速查找节点
+  const map: any = {};
+  const result: any[] = [];
+
+  // 首先创建所有节点的映射
+  flatData.forEach((item) => {
+    // 确保每个节点都有Children属性
+    map[item.Id] = { ...item, Children: [] };
+  });
+
+  // 然后建立父子关系
+  flatData.forEach((item) => {
+    const node = map[item.Id];
+
+    if (item.ParentId === 0 || !map[item.ParentId]) {
+      // 如果ParentId为0或者父节点不存在,则为顶级节点
+      result.push(node);
+    } else {
+      // 否则将该节点添加到父节点的Children中
+      map[item.ParentId].Children.push(node);
+    }
+  });
+
+  // 清理空的Children数组
+  flatData.forEach((item) => {
+    if (map[item.Id].Children.length === 0) {
+      map[item.Id].Children = null;
+    }
+  });
+
+  return result;
+};
 
 // 检查数据是否已经是树形结构
 const isTreeStructure = (data: any[]) => {
-	// 检查数据中是否有包含非空的Children字段的项
-	return data.some(item => item.Children && Array.isArray(item.Children) && item.Children.length > 0)
-}
+  // 检查数据中是否有包含非空的Children字段的项
+  return data.some((item) => item.Children && Array.isArray(item.Children) && item.Children.length > 0);
+};
 
 // 刷新分组树
 const refreshGroups = async () => {
-	try {
-		// 调用API获取分组树
-		const res: any = await api.group.tree()
-
-		// 使用API返回的数据
-		if (res?.list) {
-			// 获取原始数据
-			const apiData = res.list || []
-
-			// 检查数据是否已经是树形结构
-			const hasTreeStructure = isTreeStructure(apiData)
-
-			let treeData: any[]
-			if (hasTreeStructure) {
-				// 如果已经是树形结构,直接使用
-				treeData = apiData
-			} else {
-				// 如果是扁平结构,通过ParentId构建树形结构
-				treeData = convertToTree(apiData)
-			}
-
-
-			// 设置到组件中
-			groupTreeData.value = treeData
-			originalGroupTree.value = JSON.parse(JSON.stringify(treeData))
-			return
-		}
-
-		// 如果没有数据,初始化为空数组
-		groupTreeData.value = []
-		originalGroupTree.value = []
-	} catch (error) {
-		ElMessage.error('获取分组数据失败')
-	}
-}
+  try {
+    // 调用API获取分组树
+    const res: any = await api.group.tree();
+
+    // 使用API返回的数据
+    if (res?.list) {
+      // 获取原始数据
+      const apiData = res.list || [];
+
+      // 检查数据是否已经是树形结构
+      const hasTreeStructure = isTreeStructure(apiData);
+
+      let treeData: any[];
+      if (hasTreeStructure) {
+        // 如果已经是树形结构,直接使用
+        treeData = apiData;
+      } else {
+        // 如果是扁平结构,通过ParentId构建树形结构
+        treeData = convertToTree(apiData);
+      }
+
+      // 设置到组件中
+      groupTreeData.value = treeData;
+      originalGroupTree.value = JSON.parse(JSON.stringify(treeData));
+      return;
+    }
+
+    // 如果没有数据,初始化为空数组
+    groupTreeData.value = [];
+    originalGroupTree.value = [];
+  } catch (error) {
+    ElMessage.error("获取分组数据失败");
+  }
+};
 
 // 搜索过滤分组
 const filterGroups = () => {
-	if (!groupSearchKey.value) {
-		// 如果搜索关键字为空,恢复原始数据
-		groupTreeData.value = JSON.parse(JSON.stringify(originalGroupTree.value))
-		return
-	}
-
-	// 递归搜索函数
-	const searchTree = (nodes: any[]) => {
-		return nodes.filter(node => {
-			// 当前节点名称匹配
-			const matchesName = node.Name.toLowerCase().includes(groupSearchKey.value.toLowerCase())
-
-			// 递归搜索子节点
-			if (node.Children && node.Children.length) {
-				node.Children = searchTree(node.Children)
-				// 如果子节点有匹配项,则保留父节点
-				return matchesName || node.Children.length > 0
-			}
-
-			return matchesName
-		})
-	}
-
-	groupTreeData.value = searchTree(JSON.parse(JSON.stringify(originalGroupTree.value)))
-}
+  if (!groupSearchKey.value) {
+    // 如果搜索关键字为空,恢复原始数据
+    groupTreeData.value = JSON.parse(JSON.stringify(originalGroupTree.value));
+    return;
+  }
+
+  // 递归搜索函数
+  const searchTree = (nodes: any[]) => {
+    return nodes.filter((node) => {
+      // 当前节点名称匹配
+      const matchesName = node.Name.toLowerCase().includes(groupSearchKey.value.toLowerCase());
+
+      // 递归搜索子节点
+      if (node.Children && node.Children.length) {
+        node.Children = searchTree(node.Children);
+        // 如果子节点有匹配项,则保留父节点
+        return matchesName || node.Children.length > 0;
+      }
+
+      return matchesName;
+    });
+  };
+
+  groupTreeData.value = searchTree(JSON.parse(JSON.stringify(originalGroupTree.value)));
+};
 
 // 点击分组节点
 const handleGroupClick = (data: any) => {
-	console.log(data)
-	// 设置当前选中分组
-	Object.assign(currentGroup, data)
+  console.log(data);
+  // 设置当前选中分组
+  Object.assign(currentGroup, data);
 
-	// 更新查询参数,加入分组条件
-	params.groupKey = data.GroupKey
+  // 更新查询参数,加入分组条件
+  params.groupKey = data.GroupKey;
 
-	// 重新获取列表
-	getList(1)
-}
+  // 重新获取列表
+  getList(1);
+};
 
 // 添加分组
 const addGroup = () => {
-	groupFormRef.value.open()
-}
+  groupFormRef.value.open();
+};
 
 // 处理分组操作
 const handleGroupCommand = (command: string, data: any) => {
-	switch (command) {
-		case 'edit':
-			groupFormRef.value.open(data)
-			break
-		case 'add_child':
-			groupFormRef.value.open(null, data.GroupKey)
-			break
-		case 'delete':
-			deleteGroupConfirm(data)
-			break
-	}
-}
+  switch (command) {
+    case "edit":
+      groupFormRef.value.open(data);
+      break;
+    case "add_child":
+      groupFormRef.value.open(null, data.GroupKey);
+      break;
+    case "delete":
+      deleteGroupConfirm(data);
+      break;
+  }
+};
 
 // 删除分组确认
 const deleteGroupConfirm = (data: any) => {
-	ElMessageBox.confirm(`确定要删除分组「${data.Name}」吗?如果包含子分组或API,将一并删除。`, '警告', {
-		confirmButtonText: '确定',
-		cancelButtonText: '取消',
-		type: 'warning'
-	}).then(async () => {
-		try {
-			// 实际使用时调用API
-			await api.group.delete([data.Id])
-			ElMessage.success('删除成功')
-
-			// 如果当前选中的是要删除的分组,则清空当前分组
-			if (currentGroup.GroupKey === data.GroupKey) {
-				Object.assign(currentGroup, {
-					Id: undefined,
-					GroupKey: '',
-					Name: '',
-					ParentId: 0,
-					Description: ''
-				})
-				params.groupKey = ''
-				getList(1)
-			}
-
-			// 刷新分组树
-			refreshGroups()
-		} catch (error) {
-			ElMessage.error('删除失败')
-		}
-	}).catch(() => { })
-}
+  ElMessageBox.confirm(`确定要删除分组「${data.Name}」吗?如果包含子分组或API,将一并删除。`, "警告", {
+    confirmButtonText: "确定",
+    cancelButtonText: "取消",
+    type: "warning",
+  })
+    .then(async () => {
+      try {
+        // 实际使用时调用API
+        await api.group.delete([data.Id]);
+        ElMessage.success("删除成功");
+
+        // 如果当前选中的是要删除的分组,则清空当前分组
+        if (currentGroup.GroupKey === data.GroupKey) {
+          Object.assign(currentGroup, {
+            Id: undefined,
+            GroupKey: "",
+            Name: "",
+            ParentId: 0,
+            Description: "",
+          });
+          params.groupKey = "";
+          getList(1);
+        }
+
+        // 刷新分组树
+        refreshGroups();
+      } catch (error) {
+        ElMessage.error("删除失败");
+      }
+    })
+    .catch(() => {});
+};
 
 // 根据请求方法返回不同的标签类型
 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'
-	}
-}
+  switch (method.toUpperCase()) {
+    case "GET":
+      return "success";
+    case "POST":
+      return "primary";
+    case "PUT":
+      return "warning";
+    case "DELETE":
+      return "danger";
+    default:
+      return "info";
+  }
+};
 
 // 重置查询表单
 const resetQuery = () => {
-	queryRef.value.resetFields()
-	getList(1)
-}
+  queryRef.value.resetFields();
+  getList(1);
+};
 
 // 新增或编辑API
 const addOrEdit = (row?: ApiDefinition) => {
-	if (row) {
-		// 编辑现有API
-		// 实际使用时,可能需要先获取详情
-		editFormRef.value.open(row)
-	} else {
-		// 新增API,如果有选中分组,则传递分组标识
-		editFormRef.value.open(null, currentGroup.GroupKey)
-	}
-}
+  if (row) {
+    // 编辑现有API
+    // 实际使用时,可能需要先获取详情
+    editFormRef.value.open(row);
+  } else {
+    // 新增API,如果有选中分组,则传递分组标识
+    editFormRef.value.open(null, currentGroup.GroupKey);
+  }
+};
 
 // 查看API详情
 const viewDetail = (row: ApiDefinition) => {
-	viewDetailRef.value.open(row)
-}
+  viewDetailRef.value.open(row);
+};
 
 // 测试API
 const testApi = (row: ApiDefinition) => {
-	testApiRef.value.open(row)
-}
+  testApiRef.value.open(row);
+};
 
 // 处理下拉菜单命令
 const handleCommand = (command: string, row: ApiDefinition) => {
-	switch (command) {
-		case 'publish':
-			publishApi(row)
-			break
-		case 'deprecate':
-			deprecateApi(row)
-			break
-		case 'delete':
-			deleteApi(row)
-			break
-	}
-}
+  switch (command) {
+    case "publish":
+      publishApi(row);
+      break;
+    case "deprecate":
+      deprecateApi(row);
+      break;
+    case "delete":
+      deleteApi(row);
+      break;
+  }
+};
 
 // 发布API
 const publishApi = (row: ApiDefinition) => {
-	ElMessageBox.confirm(`确定要发布API「${row.name}」吗?`, '提示', {
-		confirmButtonText: '确定',
-		cancelButtonText: '取消',
-		type: 'warning'
-	}).then(async () => {
-		await api.publish(row.id!)
-		ElMessage.success('发布成功')
-		getList()
-	})
-}
+  ElMessageBox.confirm(`确定要发布API「${row.name}」吗?`, "提示", {
+    confirmButtonText: "确定",
+    cancelButtonText: "取消",
+    type: "warning",
+  }).then(async () => {
+    await api.publish(row.id!);
+    ElMessage.success("发布成功");
+    getList();
+  });
+};
 
 // 废弃API
 const deprecateApi = (row: ApiDefinition) => {
-	ElMessageBox.confirm(`确定要废弃API「${row.name}」吗?`, '提示', {
-		confirmButtonText: '确定',
-		cancelButtonText: '取消',
-		type: 'warning'
-	}).then(async () => {
-		await api.deprecate(row.id!)
-		ElMessage.success('废弃成功')
-		getList()
-	})
-}
+  ElMessageBox.confirm(`确定要废弃API「${row.name}」吗?`, "提示", {
+    confirmButtonText: "确定",
+    cancelButtonText: "取消",
+    type: "warning",
+  }).then(async () => {
+    await api.deprecate(row.id!);
+    ElMessage.success("废弃成功");
+    getList();
+  });
+};
 
 // 删除API
 const deleteApi = (row: ApiDefinition) => {
-	ElMessageBox.confirm(`确定要删除API「${row.name}」吗?此操作不可恢复!`, '警告', {
-		confirmButtonText: '确定',
-		cancelButtonText: '取消',
-		type: 'error'
-	}).then(async () => {
-		await api.delete([row.id!])
-		ElMessage.success('删除成功')
-		getList()
-	})
-}
+  ElMessageBox.confirm(`确定要删除API「${row.name}」吗?此操作不可恢复!`, "警告", {
+    confirmButtonText: "确定",
+    cancelButtonText: "取消",
+    type: "error",
+  }).then(async () => {
+    await api.delete([row.id!]);
+    ElMessage.success("删除成功");
+    getList();
+  });
+};
 </script>
 
 <style scoped>
 .ml10 {
-	margin-left: 10px;
+  margin-left: 10px;
 }
 
 .apihub-container {
-	display: flex;
-	height: calc(100vh - 160px);
+  display: flex;
+  height: calc(100vh - 160px);
 }
 
 .apihub-sidebar {
-	width: 280px;
-	margin-right: 16px;
-	overflow: hidden;
+  width: 280px;
+  margin-right: 16px;
+  overflow: hidden;
 }
 
 .apihub-content {
-	flex: 1;
-	overflow: hidden;
+  flex: 1;
+  overflow: hidden;
 }
 
 .group-card {
-	height: 100%;
-	display: flex;
-	flex-direction: column;
+  height: 100%;
+  display: flex;
+  flex-direction: column;
 }
 
 .card-header {
-	display: flex;
-	justify-content: space-between;
-	align-items: center;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
 }
 
 .header-actions {
-	display: flex;
-	gap: 8px;
+  display: flex;
+  gap: 8px;
 }
 
 .group-search {
-	margin-bottom: 12px;
+  margin-bottom: 12px;
 }
 
 .group-tree-container {
-	overflow-y: auto;
-	flex: 1;
-	min-height: 200px;
-	/* 确保容器有最小高度 */
-	border: 1px solid #ebeef5;
-	/* 添加边框以便于调试 */
-	padding: 10px;
+  overflow-y: auto;
+  flex: 1;
+  min-height: 200px;
+  /* 确保容器有最小高度 */
+  border: 1px solid #ebeef5;
+  /* 添加边框以便于调试 */
+  padding: 10px;
 }
 
 .custom-tree-node {
-	flex: 1;
-	display: flex;
-	align-items: center;
-	justify-content: space-between;
-	font-size: 14px;
-	padding-right: 8px;
+  flex: 1;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  font-size: 14px;
+  padding-right: 8px;
 }
 
 .api-count {
-	padding: 0 6px;
-	background-color: #f0f0f0;
-	border-radius: 10px;
-	font-size: 12px;
-	color: #606266;
+  padding: 0 6px;
+  background-color: #f0f0f0;
+  border-radius: 10px;
+  font-size: 12px;
+  color: #606266;
 }
 
 .node-actions {
-	margin-left: 8px;
-	visibility: hidden;
+  margin-left: 8px;
+  visibility: hidden;
 }
 
 .custom-tree-node:hover .node-actions {
-	visibility: visible;
+  visibility: visible;
 }
 
 .api-header {
-	margin-bottom: 16px;
-	font-size: 16px;
+  margin-bottom: 16px;
+  font-size: 16px;
 }
 
 .group-name {
-	font-weight: bold;
-	color: #409EFF;
+  font-weight: bold;
+  color: #409eff;
 }
 </style>

+ 206 - 218
src/views/apihub/component/edit.vue

@@ -1,272 +1,260 @@
 <template>
-	<el-dialog
-		class="api-edit"
-		v-model="showDialog"
-		:title="`${formData.id ? '编辑API' : '新增API'}`"
-		width="800px"
-		:close-on-click-modal="false"
-		:close-on-press-escape="false">
-		<el-form ref="formRef" :model="formData" :rules="ruleForm" label-width="100px" @keyup.enter="onSubmit">
-			<el-form-item label="API名称" prop="name">
-				<el-input v-model="formData.name" placeholder="请输入API名称" />
-			</el-form-item>
-			<el-form-item label="API路径" prop="path">
-				<el-input v-model="formData.path" placeholder="请输入API路径,如/api/v1/users" />
-			</el-form-item>
-			<el-form-item label="请求方法" prop="method">
-				<el-select v-model="formData.method" placeholder="请选择请求方法">
-					<el-option label="GET" value="GET"></el-option>
-					<el-option label="POST" value="POST"></el-option>
-					<el-option label="PUT" value="PUT"></el-option>
-					<el-option label="DELETE" value="DELETE"></el-option>
-				</el-select>
-			</el-form-item>
-			<el-form-item label="数据源" prop="dataSourceId">
-				<el-select v-model="formData.dataSourceId" placeholder="请选择数据源" filterable>
-					<el-option v-for="item in dataSources" :key="item.id" :label="item.name" :value="item.id"></el-option>
-				</el-select>
-			</el-form-item>
-			<el-form-item label="SQL类型" prop="sqlType">
-				<el-radio-group v-model="formData.sqlType">
-					<el-radio label="query">查询</el-radio>
-					<el-radio label="procedure">存储过程</el-radio>
-				</el-radio-group>
-			</el-form-item>
-			<el-form-item label="所属分组" prop="groupKey">
-				<el-cascader
-					v-model="formData.groupKey"
-					:options="groupOptions"
-					:props="{ checkStrictly: true, emitPath: false, value: 'GroupKey', label: 'Name', children: 'Children' }"
-					placeholder="请选择所属分组"
-					clearable
-					style="width: 100%" />
-			</el-form-item>
-			<el-form-item label="SQL内容" prop="sqlContent">
-				<el-input v-model="formData.sqlContent" type="textarea" :rows="4" placeholder="请输入SQL语句或存储过程名称" />
-			</el-form-item>
+  <el-dialog class="api-edit" v-model="showDialog" :title="`${formData.id ? '编辑API' : '新增API'}`" width="800px" :close-on-click-modal="false" :close-on-press-escape="false">
+    <el-form ref="formRef" :model="formData" :rules="ruleForm" label-width="100px" @keyup.enter="onSubmit">
+      <el-form-item label="API名称" prop="name">
+        <el-input v-model="formData.name" placeholder="请输入API名称" />
+      </el-form-item>
+      <el-form-item label="API路径" prop="path">
+        <el-input v-model="formData.path" placeholder="请输入API路径,如/api/v1/users" />
+      </el-form-item>
+      <el-form-item label="请求方法" prop="method">
+        <el-select v-model="formData.method" placeholder="请选择请求方法">
+          <el-option label="GET" value="GET"></el-option>
+          <el-option label="POST" value="POST"></el-option>
+          <el-option label="PUT" value="PUT"></el-option>
+          <el-option label="DELETE" value="DELETE"></el-option>
+        </el-select>
+      </el-form-item>
+      <el-form-item label="数据源" prop="dataSourceId">
+        <el-select v-model="formData.dataSourceId" placeholder="请选择数据源" filterable>
+          <el-option v-for="item in dataSources" :key="item.id" :label="item.name" :value="item.id"></el-option>
+        </el-select>
+      </el-form-item>
+      <el-form-item label="SQL类型" prop="sqlType">
+        <el-radio-group v-model="formData.sqlType">
+          <el-radio label="query">查询</el-radio>
+          <el-radio label="procedure">存储过程</el-radio>
+        </el-radio-group>
+      </el-form-item>
+      <el-form-item label="所属分组" prop="groupKey">
+        <el-cascader v-model="formData.groupKey" :options="groupOptions" :props="{ checkStrictly: true, emitPath: false, value: 'GroupKey', label: 'Name', children: 'Children' }" placeholder="请选择所属分组" clearable style="width: 100%" />
+      </el-form-item>
+      <el-form-item label="SQL内容" prop="sqlContent">
+        <el-input v-model="formData.sqlContent" type="textarea" :rows="4" placeholder="请输入SQL语句或存储过程名称" />
+      </el-form-item>
 
-			<el-form-item label="参数定义">
-				<el-button type="primary" size="small" @click="addParameter">
-					<el-icon><ele-Plus /></el-icon>添加参数
-				</el-button>
-				<el-table :data="formData.parameters" style="width: 100%; margin-top: 10px;" border>
-					<el-table-column label="参数名" width="150">
-						<template #default="scope">
-							<el-input v-model="scope.row.name" placeholder="参数名"></el-input>
-						</template>
-					</el-table-column>
-					<el-table-column label="类型" width="120">
-						<template #default="scope">
-							<el-select v-model="scope.row.type" placeholder="类型">
-								<el-option label="string" value="string"></el-option>
-								<el-option label="int" value="int"></el-option>
-								<el-option label="float" value="float"></el-option>
-								<el-option label="bool" value="bool"></el-option>
-							</el-select>
-						</template>
-					</el-table-column>
-					<el-table-column label="必填" width="80">
-						<template #default="scope">
-							<el-checkbox v-model="scope.row.required"></el-checkbox>
-						</template>
-					</el-table-column>
-					<el-table-column label="默认值" width="120">
-						<template #default="scope">
-							<el-input v-model="scope.row.defaultValue" placeholder="默认值"></el-input>
-						</template>
-					</el-table-column>
-					<el-table-column label="描述">
-						<template #default="scope">
-							<el-input v-model="scope.row.description" placeholder="参数描述"></el-input>
-						</template>
-					</el-table-column>
-					<el-table-column label="操作" width="80">
-						<template #default="scope">
-							<el-button type="danger" size="small" @click="removeParameter(scope.$index)" text>
-								<el-icon><ele-Delete /></el-icon>
-							</el-button>
-						</template>
-					</el-table-column>
-				</el-table>
-			</el-form-item>
+      <el-form-item label="参数定义">
+        <el-button type="primary" size="small" @click="addParameter">
+          <el-icon><ele-Plus /></el-icon>添加参数
+        </el-button>
+        <el-table :data="formData.parameters" style="width: 100%; margin-top: 10px" border>
+          <el-table-column label="参数名" width="150">
+            <template #default="scope">
+              <el-input v-model="scope.row.name" placeholder="参数名"></el-input>
+            </template>
+          </el-table-column>
+          <el-table-column label="类型" width="120">
+            <template #default="scope">
+              <el-select v-model="scope.row.type" placeholder="类型">
+                <el-option label="string" value="string"></el-option>
+                <el-option label="int" value="int"></el-option>
+                <el-option label="float" value="float"></el-option>
+                <el-option label="bool" value="bool"></el-option>
+              </el-select>
+            </template>
+          </el-table-column>
+          <el-table-column label="必填" width="80">
+            <template #default="scope">
+              <el-checkbox v-model="scope.row.required"></el-checkbox>
+            </template>
+          </el-table-column>
+          <el-table-column label="默认值" width="120">
+            <template #default="scope">
+              <el-input v-model="scope.row.defaultValue" placeholder="默认值"></el-input>
+            </template>
+          </el-table-column>
+          <el-table-column label="描述">
+            <template #default="scope">
+              <el-input v-model="scope.row.description" placeholder="参数描述"></el-input>
+            </template>
+          </el-table-column>
+          <el-table-column label="操作" width="80">
+            <template #default="scope">
+              <el-button type="danger" size="small" @click="removeParameter(scope.$index)" text>
+                <el-icon><ele-Delete /></el-icon>
+              </el-button>
+            </template>
+          </el-table-column>
+        </el-table>
+      </el-form-item>
 
-			<el-form-item label="返回格式" prop="returnFormat">
-				<el-select v-model="formData.returnFormat" placeholder="请选择返回格式">
-					<el-option label="JSON" value="JSON"></el-option>
-					<el-option label="XML" value="XML"></el-option>
-				</el-select>
-			</el-form-item>
-			<el-form-item label="版本" prop="version">
-				<el-input v-model="formData.version" placeholder="请输入版本号,如1.0" />
-			</el-form-item>
-			<el-form-item label="状态" prop="status">
-				<el-select v-model="formData.status" placeholder="请选择状态">
-					<el-option label="草稿" value="Draft"></el-option>
-					<el-option label="已发布" value="Published"></el-option>
-					<el-option label="已废弃" value="Deprecated"></el-option>
-				</el-select>
-			</el-form-item>
-			<el-form-item label="描述" prop="description">
-				<el-input v-model="formData.description" type="textarea" :rows="3" placeholder="请输入API描述" />
-			</el-form-item>
-		</el-form>
-		<template #footer>
-			<div class="dialog-footer">
-				<el-button @click="cancel">取消</el-button>
-				<el-button type="primary" @click="onSubmit">确定</el-button>
-			</div>
-		</template>
-	</el-dialog>
+      <el-form-item label="返回格式" prop="returnFormat">
+        <el-select v-model="formData.returnFormat" placeholder="请选择返回格式">
+          <el-option label="JSON" value="JSON"></el-option>
+          <el-option label="XML" value="XML"></el-option>
+        </el-select>
+      </el-form-item>
+      <el-form-item label="版本" prop="version">
+        <el-input v-model="formData.version" placeholder="请输入版本号,如1.0" />
+      </el-form-item>
+      <el-form-item label="状态" prop="status">
+        <el-select v-model="formData.status" placeholder="请选择状态">
+          <el-option label="草稿" value="Draft"></el-option>
+          <el-option label="已发布" value="Published"></el-option>
+          <el-option label="已废弃" value="Deprecated"></el-option>
+        </el-select>
+      </el-form-item>
+      <el-form-item label="描述" prop="description">
+        <el-input v-model="formData.description" type="textarea" :rows="3" placeholder="请输入API描述" />
+      </el-form-item>
+    </el-form>
+    <template #footer>
+      <div class="dialog-footer">
+        <el-button @click="cancel">取消</el-button>
+        <el-button type="primary" @click="onSubmit">确定</el-button>
+      </div>
+    </template>
+  </el-dialog>
 </template>
 
 <script lang="ts" setup>
-import { ref, reactive, nextTick } from 'vue'
-import { ElMessage } from 'element-plus'
-import { ruleRequired } from '/@/utils/validator'
-import api from '/@/api/modules/apiHub'
+import { ref, reactive, nextTick } from "vue";
+import { ElMessage } from "element-plus";
+import { ruleRequired } from "/@/utils/validator";
+import api from "/@/api/modules/apiHub";
 
-const emit = defineEmits(['getList'])
+const emit = defineEmits(["getList"]);
 
-const showDialog = ref(false)
-const formRef = ref()
-const dataSources = ref<any[]>([])
+const showDialog = ref(false);
+const formRef = ref();
+const dataSources = ref<any[]>([]);
 
 api.dataSource.list().then((res: any) => {
-	dataSources.value = res.list
-})
+  dataSources.value = res.list;
+});
 
 // 分组选项
-const groupOptions = ref([])
+const groupOptions = ref([]);
 
 // 定义基础表单数据
 const baseForm = {
-	id: null,
-	name: '',
-	path: '',
-	method: 'GET',
-	dataSourceId: null,
-	sqlType: 'query',
-	sqlContent: '',
-	parameters: [],
-	returnFormat: 'JSON',
-	version: '1.0',
-	status: 'Draft',
-	plugins: [],
-	groupKey: '',
-	description: ''
-}
+  id: null,
+  name: "",
+  path: "",
+  method: "GET",
+  dataSourceId: null,
+  sqlType: "query",
+  sqlContent: "",
+  parameters: [],
+  returnFormat: "JSON",
+  version: "1.0",
+  status: "Draft",
+  plugins: [],
+  groupKey: "",
+  description: "",
+};
 
-const formData = reactive(JSON.parse(JSON.stringify(baseForm)))
+const formData = reactive(JSON.parse(JSON.stringify(baseForm)));
 
 // 表单验证规则
 const ruleForm = {
-	name: [ruleRequired('API名称不能为空', 'change')],
-	path: [ruleRequired('API路径不能为空', 'change')],
-	method: [ruleRequired('请求方法不能为空', 'change')],
-	dataSourceId: [ruleRequired('数据源不能为空', 'change')],
-	sqlType: [ruleRequired('SQL类型不能为空', 'change')],
-	sqlContent: [ruleRequired('SQL内容不能为空', 'change')],
-	returnFormat: [ruleRequired('返回格式不能为空', 'change')],
-	version: [ruleRequired('版本不能为空', 'change')],
-	status: [ruleRequired('状态不能为空', 'change')]
-}
+  name: [ruleRequired("API名称不能为空", "change")],
+  path: [ruleRequired("API路径不能为空", "change")],
+  method: [ruleRequired("请求方法不能为空", "change")],
+  dataSourceId: [ruleRequired("数据源不能为空", "change")],
+  sqlType: [ruleRequired("SQL类型不能为空", "change")],
+  sqlContent: [ruleRequired("SQL内容不能为空", "change")],
+  returnFormat: [ruleRequired("返回格式不能为空", "change")],
+  version: [ruleRequired("版本不能为空", "change")],
+  status: [ruleRequired("状态不能为空", "change")],
+};
 
 // 添加参数
 const addParameter = () => {
-	if (!formData.parameters) {
-		formData.parameters = []
-	}
-	formData.parameters.push({
-		name: '',
-		type: 'string',
-		required: false,
-		defaultValue: '',
-		description: ''
-	})
-}
+  if (!formData.parameters) {
+    formData.parameters = [];
+  }
+  formData.parameters.push({
+    name: "",
+    type: "string",
+    required: false,
+    defaultValue: "",
+    description: "",
+  });
+};
 
 // 移除参数
 const removeParameter = (index: number) => {
-	formData.parameters.splice(index, 1)
-}
+  formData.parameters.splice(index, 1);
+};
 
 // 提交表单
 const onSubmit = async () => {
-	// 表单验证
-	await formRef.value.validate()
+  // 表单验证
+  await formRef.value.validate();
 
-	// 准备提交数据
-	const submitData = JSON.parse(JSON.stringify(formData))
+  // 准备提交数据
+  const submitData = JSON.parse(JSON.stringify(formData));
 
-	// 调用API
-	if (submitData.id) {
-		// 编辑模式
-		await api.edit(submitData)
-	} else {
-		// 新增模式
-		await api.add(submitData)
-	}
+  // 调用API
+  if (submitData.id) {
+    // 编辑模式
+    await api.edit(submitData);
+  } else {
+    // 新增模式
+    await api.add(submitData);
+  }
 
-	ElMessage.success('操作成功')
-	resetForm()
-	showDialog.value = false
-	emit('getList')
-}
+  ElMessage.success("操作成功");
+  resetForm();
+  showDialog.value = false;
+  emit("getList");
+};
 
 // 重置表单
 const resetForm = () => {
-	Object.assign(formData, JSON.parse(JSON.stringify(baseForm)))
-	formRef.value && formRef.value.resetFields()
-}
+  Object.assign(formData, JSON.parse(JSON.stringify(baseForm)));
+  formRef.value && formRef.value.resetFields();
+};
 
 // 取消操作
 const cancel = () => {
-	resetForm()
-	showDialog.value = false
-}
+  resetForm();
+  showDialog.value = false;
+};
 
 // 加载分组选项
 const loadGroupOptions = async () => {
-	api.group.tree().then((res: any) => {
-		groupOptions.value = res.list || []
-	})
-}
+  api.group.tree().then((res: any) => {
+    groupOptions.value = res.list || [];
+  });
+};
 
 // 打开对话框
 const open = async (row?: any, defaultGroupKey?: string) => {
-	resetForm()
-	showDialog.value = true
+  resetForm();
+  showDialog.value = true;
 
-	// 加载分组选项
-	await loadGroupOptions()
+  // 加载分组选项
+  await loadGroupOptions();
 
-	if (row) {
-		// 编辑模式,填充表单数据
-		nextTick(() => {
-			// 如果是编辑模式,需要先获取详情
-			// 实际使用时,可能需要先调用API获取完整数据
-			// 这里模拟直接使用传入的行数据
+  if (row) {
+    // 编辑模式,填充表单数据
+    nextTick(() => {
+      // 如果是编辑模式,需要先获取详情
+      // 实际使用时,可能需要先调用API获取完整数据
+      // 这里模拟直接使用传入的行数据
 
-			Object.assign(formData, JSON.parse(JSON.stringify(row)))
+      Object.assign(formData, JSON.parse(JSON.stringify(row)));
 
-			// 确保参数数组存在
-			if (!formData.parameters) {
-				formData.parameters = []
-			}
-		})
-	}
-	if (defaultGroupKey) {
-		// 新增模式,设置默认分组
-		formData.groupKey = defaultGroupKey
-	}
-}
+      // 确保参数数组存在
+      if (!formData.parameters) {
+        formData.parameters = [];
+      }
+    });
+  }
+  if (defaultGroupKey) {
+    // 新增模式,设置默认分组
+    formData.groupKey = defaultGroupKey;
+  }
+};
 
-defineExpose({ open })
+defineExpose({ open });
 </script>
 
 <style scoped>
 .dialog-footer {
-	display: flex;
-	justify-content: flex-end;
+  display: flex;
+  justify-content: flex-end;
 }
 </style>

+ 126 - 138
src/views/apihub/component/group.vue

@@ -1,175 +1,163 @@
 <template>
-	<el-dialog
-		class="group-edit"
-		v-model="showDialog"
-		:title="`${formData.id ? '编辑分组' : '新增分组'}`"
-		width="500px"
-		:close-on-click-modal="false"
-		:close-on-press-escape="false">
-		<el-form ref="formRef" :model="formData" :rules="ruleForm" label-width="100px" @keyup.enter="onSubmit">
-			<el-form-item label="分组名称" prop="name">
-				<el-input v-model="formData.name" placeholder="请输入分组名称" />
-			</el-form-item>
-			<el-form-item label="分组标识" prop="groupKey">
-				<el-input v-model="formData.groupKey" placeholder="请输入分组唯一标识,留空则自动生成" />
-			</el-form-item>
-			<el-form-item label="父级分组" prop="parentId">
-				<el-cascader
-					v-model="formData.parentId"
-					:options="groupOptions"
-					:props="{ checkStrictly: true, emitPath: false, value: 'Id', label: 'Name', children: 'Children' }"
-					placeholder="请选择父级分组,不选择则为顶级分组"
-					clearable
-					style="width: 100%" />
-			</el-form-item>
-			<el-form-item label="排序号" prop="sort">
-				<el-input-number v-model="formData.sort" :min="0" :max="999" />
-			</el-form-item>
-			<el-form-item label="分组描述" prop="description">
-				<el-input v-model="formData.description" type="textarea" :rows="3" placeholder="请输入分组描述" />
-			</el-form-item>
-		</el-form>
-		<template #footer>
-			<div class="dialog-footer">
-				<el-button @click="cancel">取消</el-button>
-				<el-button type="primary" @click="onSubmit">确定</el-button>
-			</div>
-		</template>
-	</el-dialog>
+  <el-dialog class="group-edit" v-model="showDialog" :title="`${formData.id ? '编辑分组' : '新增分组'}`" width="500px" :close-on-click-modal="false" :close-on-press-escape="false">
+    <el-form ref="formRef" :model="formData" :rules="ruleForm" label-width="100px" @keyup.enter="onSubmit">
+      <el-form-item label="分组名称" prop="name">
+        <el-input v-model="formData.name" placeholder="请输入分组名称" />
+      </el-form-item>
+      <el-form-item label="分组标识" prop="groupKey">
+        <el-input v-model="formData.groupKey" placeholder="请输入分组唯一标识,留空则自动生成" />
+      </el-form-item>
+      <el-form-item label="父级分组" prop="parentId">
+        <el-cascader v-model="formData.parentId" :options="groupOptions" :props="{ checkStrictly: true, emitPath: false, value: 'Id', label: 'Name', children: 'Children' }" placeholder="请选择父级分组,不选择则为顶级分组" clearable style="width: 100%" />
+      </el-form-item>
+      <el-form-item label="排序号" prop="sort">
+        <el-input-number v-model="formData.sort" :min="0" :max="999" />
+      </el-form-item>
+      <el-form-item label="分组描述" prop="description">
+        <el-input v-model="formData.description" type="textarea" :rows="3" placeholder="请输入分组描述" />
+      </el-form-item>
+    </el-form>
+    <template #footer>
+      <div class="dialog-footer">
+        <el-button @click="cancel">取消</el-button>
+        <el-button type="primary" @click="onSubmit">确定</el-button>
+      </div>
+    </template>
+  </el-dialog>
 </template>
 
 <script lang="ts" setup>
-import { ref, reactive, nextTick } from 'vue'
-import { ElMessage } from 'element-plus'
-import { ruleRequired } from '/@/utils/validator'
-import api from '/@/api/modules/apiHub'
+import { ref, reactive, nextTick } from "vue";
+import { ElMessage } from "element-plus";
+import { ruleRequired } from "/@/utils/validator";
+import api from "/@/api/modules/apiHub";
 
-const emit = defineEmits(['refresh'])
+const emit = defineEmits(["refresh"]);
 
-const showDialog = ref(false)
-const formRef = ref()
-const groupOptions = ref<any[]>([])
-let treeData: any[] = []
+const showDialog = ref(false);
+const formRef = ref();
+const groupOptions = ref<any[]>([]);
+let treeData: any[] = [];
 
 // 定义基础表单数据
 const baseForm = {
-	id: undefined,
-	groupKey: '',
-	name: '',
-	parentId: '',
-	sort: 0,
-	description: ''
-}
+  id: undefined,
+  groupKey: "",
+  name: "",
+  parentId: "",
+  sort: 0,
+  description: "",
+};
 
-const formData = reactive({ ...baseForm })
+const formData = reactive({ ...baseForm });
 
 // 表单验证规则
 const ruleForm = {
-	name: [ruleRequired('分组名称不能为空', 'change')]
-}
+  name: [ruleRequired("分组名称不能为空", "change")],
+};
 
 // 获取分组树形结构
 api.group.tree().then((res: any) => {
-	treeData = res.list || []
-	groupOptions.value = res.list || []
-})
+  treeData = res.list || [];
+  groupOptions.value = res.list || [];
+});
 
 // 提交表单
 const onSubmit = async () => {
-	await formRef.value.validate()
-
-	// 准备提交数据
-	const submitData = JSON.parse(JSON.stringify(formData))
-
-	// 如果没有设置groupKey,生成一个基于名称的唯一标识
-	if (!submitData.groupKey) {
-		submitData.groupKey = 'group_' + Date.now() + '_' + Math.floor(Math.random() * 1000)
-	}
-
-	// 处理空的parent_key,将空字符串设置为null
-	if (submitData.parentId === '') {
-		submitData.parentId = null
-	}
-
-	// 调用API
-	if (submitData.id) {
-		// 编辑模式
-		await api.group.edit(submitData)
-	} else {
-		// 新增模式
-		await api.group.add(submitData)
-	}
-
-	ElMessage.success('操作成功')
-	resetForm()
-	showDialog.value = false
-	emit('refresh')
-}
+  await formRef.value.validate();
+
+  // 准备提交数据
+  const submitData = JSON.parse(JSON.stringify(formData));
+
+  // 如果没有设置groupKey,生成一个基于名称的唯一标识
+  if (!submitData.groupKey) {
+    submitData.groupKey = "group_" + Date.now() + "_" + Math.floor(Math.random() * 1000);
+  }
+
+  // 处理空的parent_key,将空字符串设置为null
+  if (submitData.parentId === "") {
+    submitData.parentId = null;
+  }
+
+  // 调用API
+  if (submitData.id) {
+    // 编辑模式
+    await api.group.edit(submitData);
+  } else {
+    // 新增模式
+    await api.group.add(submitData);
+  }
+
+  ElMessage.success("操作成功");
+  resetForm();
+  showDialog.value = false;
+  emit("refresh");
+};
 
 // 重置表单
 const resetForm = () => {
-	Object.assign(formData, { ...baseForm })
-	formRef.value && formRef.value.resetFields()
-}
+  Object.assign(formData, { ...baseForm });
+  formRef.value && formRef.value.resetFields();
+};
 
 // 取消操作
 const cancel = () => {
-	resetForm()
-	showDialog.value = false
-}
+  resetForm();
+  showDialog.value = false;
+};
 
 // 加载分组选项
 const loadGroupOptions = async (excludeId?: string) => {
-	// 如果是编辑模式,需要排除当前分组及其子分组
-	if (excludeId) {
-		const filterTree = (nodes: any[]) => {
-			return nodes.filter(node => {
-				if (node.Id === excludeId) {
-					return false
-				}
-				if (node.Children && node.Children.length) {
-					node.Children = filterTree(node.Children)
-				}
-				return true
-			})
-		}
-
-		groupOptions.value = filterTree(treeData)
-	} else {
-		groupOptions.value = treeData
-	}
-}
+  // 如果是编辑模式,需要排除当前分组及其子分组
+  if (excludeId) {
+    const filterTree = (nodes: any[]) => {
+      return nodes.filter((node) => {
+        if (node.Id === excludeId) {
+          return false;
+        }
+        if (node.Children && node.Children.length) {
+          node.Children = filterTree(node.Children);
+        }
+        return true;
+      });
+    };
+
+    groupOptions.value = filterTree(treeData);
+  } else {
+    groupOptions.value = treeData;
+  }
+};
 
 // 打开对话框
 const open = async (row?: any, parentId?: string) => {
-	resetForm()
-	showDialog.value = true
-
-	// 加载分组选项
-	await loadGroupOptions(row?.Id)
-
-	if (row) {
-		// 编辑模式,填充表单数据
-		nextTick(() => {
-			formData.id = row.Id
-			formData.groupKey = row.GroupKey
-			formData.name = row.Name
-			formData.parentId = row.ParentId
-			formData.sort = row.Sort
-			formData.description = row.Description
-		})
-	} else if (parentId) {
-		// 新增子分组模式
-		formData.parentId = parentId
-	}
-}
-
-defineExpose({ open })
+  resetForm();
+  showDialog.value = true;
+
+  // 加载分组选项
+  await loadGroupOptions(row?.Id);
+
+  if (row) {
+    // 编辑模式,填充表单数据
+    nextTick(() => {
+      formData.id = row.Id;
+      formData.groupKey = row.GroupKey;
+      formData.name = row.Name;
+      formData.parentId = row.ParentId;
+      formData.sort = row.Sort;
+      formData.description = row.Description;
+    });
+  } else if (parentId) {
+    // 新增子分组模式
+    formData.parentId = parentId;
+  }
+};
+
+defineExpose({ open });
 </script>
 
 <style scoped>
 .dialog-footer {
-	display: flex;
-	justify-content: flex-end;
+  display: flex;
+  justify-content: flex-end;
 }
 </style>

+ 65 - 88
src/views/apihub/component/test.vue

@@ -1,11 +1,5 @@
 <template>
-	<el-dialog
-		class="api-test"
-		v-model="showDialog"
-		title="测试API"
-		width="800px"
-		:close-on-click-modal="false"
-		:close-on-press-escape="false">
+	<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">{{ apiData.path }}</el-descriptions-item>
@@ -17,26 +11,14 @@
 
 		<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-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>
+				<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>
@@ -45,12 +27,7 @@
 		<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>
-
+			<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">
@@ -74,6 +51,7 @@
 
 <script lang="ts" setup>
 import { ref, reactive, watch } from 'vue'
+import apiHub from '/@/api/modules/apiHub'
 
 const showDialog = ref(false)
 const loading = ref(false)
@@ -81,11 +59,11 @@ const activeTab = ref('data')
 
 // API数据
 const apiData = reactive<any>({
-	id: undefined,
+	id: null,
 	name: '',
 	path: '',
 	method: '',
-	dataSourceId: undefined,
+	dataSourceId: null,
 	dataSourceName: '',
 	sqlType: '',
 	sqlContent: '',
@@ -96,54 +74,58 @@ const apiData = reactive<any>({
 	plugins: [],
 	description: '',
 	createdAt: '',
-	updatedAt: ''
+	updatedAt: '',
 })
 
 // 测试参数
-const testParams = reactive({})
+const testParams = reactive<any>({})
 
 // 测试结果
-const testResult = reactive({
+const testResult = reactive<any>({
 	success: false,
 	message: '',
-	data: null
+	data: null,
 })
 
 // 监听API数据变化,初始化测试参数
-watch(() => apiData.parameters, (newParams) => {
-	// 清空测试参数
-	Object.keys(testParams).forEach(key => {
-		delete testParams[key]
-	})
+watch(
+	() => apiData.parameters,
+	(newParams) => {
+		// 清空测试参数
+		Object.keys(testParams).forEach((key) => {
+			delete testParams[key]
+		})
 
-	// 根据参数定义初始化测试参数
-	if (newParams && newParams.length) {
-		newParams.forEach(param => {
-			// 如果有默认值,使用默认值
-			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'
+		// 根据参数定义初始化测试参数
+		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 {
-					testParams[param.name] = param.defaultValue
+					// 否则设置为空值
+					if (param.type === 'int' || param.type === 'float') {
+						testParams[param.name] = null
+					} else if (param.type === 'bool') {
+						testParams[param.name] = false
+					} else {
+						testParams[param.name] = ''
+					}
 				}
-			} else {
-				// 否则设置为空值
-				if (param.type === 'int' || param.type === 'float') {
-					testParams[param.name] = undefined
-				} else if (param.type === 'bool') {
-					testParams[param.name] = false
-				} else {
-					testParams[param.name] = ''
-				}
-			}
-		})
-	}
-}, { deep: true })
+			})
+		}
+	},
+	{ deep: true }
+)
 
 // 根据请求方法返回不同的标签类型
 const getMethodTagType = (method: string) => {
@@ -162,7 +144,7 @@ const getMethodTagType = (method: string) => {
 }
 
 // 获取参数占位符文本
-const getParamPlaceholder = (param) => {
+const getParamPlaceholder = (param: any) => {
 	if (param.required) {
 		return `请输入${param.description || param.name}(必填)`
 	}
@@ -170,7 +152,7 @@ const getParamPlaceholder = (param) => {
 }
 
 // 格式化JSON
-const formatJson = (json) => {
+const formatJson = (json: any) => {
 	try {
 		return JSON.stringify(json, null, 2)
 	} catch (e) {
@@ -192,16 +174,10 @@ const runTest = async () => {
 
 	loading.value = true
 
-	try {
-		// 实际使用时,应该调用API进行测试
-		// const res = await api.apihub.test({ 
-		//   id: apiData.id,
-		//   parameters: testParams
-		// })
-
-		// 模拟API调用
-		await new Promise(resolve => setTimeout(resolve, 1000))
-
+	apiHub.test({
+		id: apiData.id,
+		parameters: testParams
+	}).then((res: any) => {
 		// 模拟测试结果
 		testResult.success = true
 		testResult.message = '测试成功'
@@ -213,34 +189,35 @@ const runTest = async () => {
 					id: 1,
 					username: 'admin',
 					email: 'admin@example.com',
-					created_at: '2025-05-01 10:00:00'
+					created_at: '2025-05-01 10:00:00',
 				}
 			} else {
 				testResult.data = {
 					id: 10,
 					username: testParams.username || 'newuser',
 					email: testParams.email || 'newuser@example.com',
-					created_at: '2025-05-06 11:00:00'
+					created_at: '2025-05-06 11:00:00',
 				}
 			}
 		} else {
 			testResult.data = {
 				result: '测试数据',
-				timestamp: new Date().toISOString()
+				timestamp: new Date().toISOString(),
 			}
 		}
-	} catch (error) {
+	}).catch((error: any) => {
 		testResult.success = false
-		testResult.message = error.message || '测试失败'
-	} finally {
+		testResult.message = error?.message || '测试失败'
+	}).finally(() => {
 		loading.value = false
-	}
+	})
+
 }
 
 // 打开对话框
 const open = async (row: any) => {
 	// 清空数据
-	Object.keys(apiData).forEach(key => {
+	Object.keys(apiData).forEach((key) => {
 		apiData[key] = undefined
 	})
 
@@ -252,7 +229,7 @@ const open = async (row: any) => {
 	showDialog.value = true
 
 	// 实际使用时,应该调用API获取详细信息
-	// const res = await api.apihub.getDetail({ id: row.id })
+	// const res = await apiHub.get(row.id)
 	// Object.assign(apiData, res)
 
 	// 这里模拟直接使用传入的行数据
@@ -272,7 +249,7 @@ defineExpose({ open })
 	font-weight: bold;
 	margin: 20px 0 10px;
 	font-size: 16px;
-	border-left: 4px solid #409EFF;
+	border-left: 4px solid #409eff;
 	padding-left: 10px;
 }
 

+ 124 - 131
src/views/apihub/component/view.vue

@@ -1,162 +1,155 @@
 <template>
-	<el-dialog
-		class="api-view"
-		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">{{ apiData.path }}</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="状态">
-				<el-tag size="small" v-if="apiData.status === 'Draft'">草稿</el-tag>
-				<el-tag size="small" type="success" v-else-if="apiData.status === 'Published'">已发布</el-tag>
-				<el-tag size="small" type="info" v-else-if="apiData.status === 'Deprecated'">已废弃</el-tag>
-				<span v-else>{{ apiData.status }}</span>
-			</el-descriptions-item>
-			<el-descriptions-item label="数据源">{{ apiData.dataSourceName }}</el-descriptions-item>
-			<el-descriptions-item label="版本">{{ apiData.version }}</el-descriptions-item>
-			<el-descriptions-item label="SQL类型">
-				<el-tag size="small" type="info" v-if="apiData.sqlType === 'query'">查询</el-tag>
-				<el-tag size="small" type="warning" v-else-if="apiData.sqlType === 'procedure'">存储过程</el-tag>
-				<span v-else>{{ apiData.sqlType }}</span>
-			</el-descriptions-item>
-			<el-descriptions-item label="返回格式">{{ apiData.returnFormat }}</el-descriptions-item>
-			<el-descriptions-item label="SQL内容" :span="2">
-				<div class="code-block">{{ apiData.sqlContent }}</div>
-			</el-descriptions-item>
-			<el-descriptions-item label="描述" :span="2">{{ apiData.description || '暂无描述' }}</el-descriptions-item>
-			<el-descriptions-item label="创建时间">{{ apiData.createdAt }}</el-descriptions-item>
-			<el-descriptions-item label="更新时间">{{ apiData.updatedAt }}</el-descriptions-item>
-		</el-descriptions>
-		
-		<div class="section-title">参数定义</div>
-		<el-table :data="apiData.parameters || []" style="width: 100%" border v-if="apiData.parameters && apiData.parameters.length">
-			<el-table-column prop="name" label="参数名" width="150"></el-table-column>
-			<el-table-column prop="type" label="类型" width="100"></el-table-column>
-			<el-table-column label="必填" width="80">
-				<template #default="scope">
-					<el-tag size="small" type="success" v-if="scope.row.required">是</el-tag>
-					<el-tag size="small" type="info" v-else>否</el-tag>
-				</template>
-			</el-table-column>
-			<el-table-column prop="defaultValue" label="默认值" width="120"></el-table-column>
-			<el-table-column prop="description" label="描述"></el-table-column>
-		</el-table>
-		<el-empty description="暂无参数" v-else></el-empty>
-		
-		<div class="section-title" v-if="apiData.plugins && apiData.plugins.length">关联插件</div>
-		<el-table :data="apiData.plugins || []" style="width: 100%" border v-if="apiData.plugins && apiData.plugins.length">
-			<el-table-column prop="pluginId" label="插件ID" width="100"></el-table-column>
-			<el-table-column prop="config" label="插件配置"></el-table-column>
-		</el-table>
-		
-		<template #footer>
-			<div class="dialog-footer">
-				<el-button @click="closeDialog">关闭</el-button>
-				<el-button type="primary" @click="testApi">测试API</el-button>
-			</div>
-		</template>
-	</el-dialog>
+  <el-dialog class="api-view" 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">{{ apiData.path }}</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="状态">
+        <el-tag size="small" v-if="apiData.status === 'Draft'">草稿</el-tag>
+        <el-tag size="small" type="success" v-else-if="apiData.status === 'Published'">已发布</el-tag>
+        <el-tag size="small" type="info" v-else-if="apiData.status === 'Deprecated'">已废弃</el-tag>
+        <span v-else>{{ apiData.status }}</span>
+      </el-descriptions-item>
+      <el-descriptions-item label="数据源">{{ apiData.dataSourceName }}</el-descriptions-item>
+      <el-descriptions-item label="版本">{{ apiData.version }}</el-descriptions-item>
+      <el-descriptions-item label="SQL类型">
+        <el-tag size="small" type="info" v-if="apiData.sqlType === 'query'">查询</el-tag>
+        <el-tag size="small" type="warning" v-else-if="apiData.sqlType === 'procedure'">存储过程</el-tag>
+        <span v-else>{{ apiData.sqlType }}</span>
+      </el-descriptions-item>
+      <el-descriptions-item label="返回格式">{{ apiData.returnFormat }}</el-descriptions-item>
+      <el-descriptions-item label="SQL内容" :span="2">
+        <div class="code-block">{{ apiData.sqlContent }}</div>
+      </el-descriptions-item>
+      <el-descriptions-item label="描述" :span="2">{{ apiData.description || "暂无描述" }}</el-descriptions-item>
+      <el-descriptions-item label="创建时间">{{ apiData.createdAt }}</el-descriptions-item>
+      <el-descriptions-item label="更新时间">{{ apiData.updatedAt }}</el-descriptions-item>
+    </el-descriptions>
+
+    <div class="section-title">参数定义</div>
+    <el-table :data="apiData.parameters || []" style="width: 100%" border v-if="apiData.parameters && apiData.parameters.length">
+      <el-table-column prop="name" label="参数名" width="150"></el-table-column>
+      <el-table-column prop="type" label="类型" width="100"></el-table-column>
+      <el-table-column label="必填" width="80">
+        <template #default="scope">
+          <el-tag size="small" type="success" v-if="scope.row.required">是</el-tag>
+          <el-tag size="small" type="info" v-else>否</el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column prop="defaultValue" label="默认值" width="120"></el-table-column>
+      <el-table-column prop="description" label="描述"></el-table-column>
+    </el-table>
+    <el-empty description="暂无参数" v-else></el-empty>
+
+    <div class="section-title" v-if="apiData.plugins && apiData.plugins.length">关联插件</div>
+    <el-table :data="apiData.plugins || []" style="width: 100%" border v-if="apiData.plugins && apiData.plugins.length">
+      <el-table-column prop="pluginId" label="插件ID" width="100"></el-table-column>
+      <el-table-column prop="config" label="插件配置"></el-table-column>
+    </el-table>
+
+    <template #footer>
+      <div class="dialog-footer">
+        <el-button @click="closeDialog">关闭</el-button>
+        <el-button type="primary" @click="testApi">测试API</el-button>
+      </div>
+    </template>
+  </el-dialog>
 </template>
 
 <script lang="ts" setup>
-import { ref, reactive } from 'vue'
+import { ref, reactive } from "vue";
 
-const emit = defineEmits(['test'])
-const showDialog = ref(false)
+const emit = defineEmits(["test"]);
+const showDialog = ref(false);
 const apiData = reactive({
-	id: undefined,
-	name: '',
-	path: '',
-	method: '',
-	dataSourceId: undefined,
-	dataSourceName: '',
-	sqlType: '',
-	sqlContent: '',
-	parameters: [],
-	returnFormat: '',
-	version: '',
-	status: '',
-	plugins: [],
-	description: '',
-	createdAt: '',
-	updatedAt: ''
-})
+  id: undefined,
+  name: "",
+  path: "",
+  method: "",
+  dataSourceId: undefined,
+  dataSourceName: "",
+  sqlType: "",
+  sqlContent: "",
+  parameters: [],
+  returnFormat: "",
+  version: "",
+  status: "",
+  plugins: [],
+  description: "",
+  createdAt: "",
+  updatedAt: "",
+});
 
 // 根据请求方法返回不同的标签类型
 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'
-	}
-}
+  switch (method?.toUpperCase()) {
+    case "GET":
+      return "success";
+    case "POST":
+      return "primary";
+    case "PUT":
+      return "warning";
+    case "DELETE":
+      return "danger";
+    default:
+      return "info";
+  }
+};
 
 // 关闭对话框
 const closeDialog = () => {
-	showDialog.value = false
-}
+  showDialog.value = false;
+};
 
 // 测试API
 const testApi = () => {
-	closeDialog()
-	emit('test', apiData)
-}
+  closeDialog();
+  emit("test", apiData);
+};
 
 // 打开对话框
 const open = async (row: any) => {
-	// 清空数据
-	Object.keys(apiData).forEach(key => {
-		apiData[key] = undefined
-	})
-	
-	showDialog.value = true
-	
-	// 实际使用时,应该调用API获取详细信息
-	// const res = await api.apihub.getDetail({ id: row.id })
-	// Object.assign(apiData, res)
-	
-	// 这里模拟直接使用传入的行数据
-	Object.assign(apiData, row)
-}
+  // 清空数据
+  Object.keys(apiData).forEach((key) => {
+    apiData[key] = undefined;
+  });
+
+  showDialog.value = true;
+
+  // 实际使用时,应该调用API获取详细信息
+  // const res = await api.apihub.getDetail({ id: row.id })
+  // Object.assign(apiData, res)
+
+  // 这里模拟直接使用传入的行数据
+  Object.assign(apiData, row);
+};
 
-defineExpose({ open })
+defineExpose({ open });
 </script>
 
 <style scoped>
 .dialog-footer {
-	display: flex;
-	justify-content: flex-end;
+  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;
+  font-weight: bold;
+  margin: 20px 0 10px;
+  font-size: 16px;
+  border-left: 4px solid #409eff;
+  padding-left: 10px;
 }
 
 .code-block {
-	background-color: #f5f7fa;
-	border: 1px solid #e4e7ed;
-	border-radius: 4px;
-	padding: 10px;
-	font-family: monospace;
-	white-space: pre-wrap;
-	word-break: break-all;
+  background-color: #f5f7fa;
+  border: 1px solid #e4e7ed;
+  border-radius: 4px;
+  padding: 10px;
+  font-family: monospace;
+  white-space: pre-wrap;
+  word-break: break-all;
 }
 </style>