优化
This commit is contained in:
@@ -1,2 +1,2 @@
|
|||||||
# 开发环境配置
|
# 开发环境配置
|
||||||
VITE_API_URL=http://miniweb.iwxerp.com/api
|
VITE_API_URL=https://vet.iqudoo.com/api
|
||||||
|
|||||||
11
package-lock.json
generated
11
package-lock.json
generated
@@ -1,13 +1,14 @@
|
|||||||
{
|
{
|
||||||
"name": "api-docs",
|
"name": "iqudoo-miniweb-backend-docs",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "api-docs",
|
"name": "iqudoo-miniweb-backend-docs",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@element-plus/icons-vue": "^2.3.2",
|
||||||
"axios": "^1.8.3",
|
"axios": "^1.8.3",
|
||||||
"element-plus": "^2.9.6",
|
"element-plus": "^2.9.6",
|
||||||
"vue": "^3.5.13",
|
"vue": "^3.5.13",
|
||||||
@@ -74,9 +75,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@element-plus/icons-vue": {
|
"node_modules/@element-plus/icons-vue": {
|
||||||
"version": "2.3.1",
|
"version": "2.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/@element-plus/icons-vue/-/icons-vue-2.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/@element-plus/icons-vue/-/icons-vue-2.3.2.tgz",
|
||||||
"integrity": "sha512-XxVUZv48RZAd87ucGS48jPf6pKu0yV5UCg9f4FFwtrYxXOwWuVJo6wOvSLKEoMQKjv8GsX/mhP6UsC1lRwbUWg==",
|
"integrity": "sha512-OzIuTaIfC8QXEPmJvB4Y4kw34rSXdCJzxcD1kFStBvr8bK6X1zQAYDo0CNMjojnfTqRQCJ0I7prlErcoRiET2A==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"vue": "^3.2.0"
|
"vue": "^3.2.0"
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@element-plus/icons-vue": "^2.3.2",
|
||||||
"axios": "^1.8.3",
|
"axios": "^1.8.3",
|
||||||
"element-plus": "^2.9.6",
|
"element-plus": "^2.9.6",
|
||||||
"vue": "^3.5.13",
|
"vue": "^3.5.13",
|
||||||
|
|||||||
1241
pnpm-lock.yaml
generated
Normal file
1241
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
284
src/App.vue
284
src/App.vue
@@ -679,16 +679,14 @@
|
|||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
|
|
||||||
<!-- 调试模块tab -->
|
<!-- 调试模块tab -->
|
||||||
<el-tab-pane label="调试工具" name="debug">
|
<!-- <el-tab-pane label="调试工具" name="debug">
|
||||||
<div class="debug-module">
|
<div class="debug-module">
|
||||||
<div class="debug-layout">
|
<div class="debug-layout">
|
||||||
<!-- 左侧:请求配置区域 -->
|
|
||||||
<div class="debug-request-panel">
|
<div class="debug-request-panel">
|
||||||
<div class="debug-panel-header">
|
<div class="debug-panel-header">
|
||||||
<span class="panel-title">请求</span>
|
<span class="panel-title">请求</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="debug-panel-content">
|
<div class="debug-panel-content">
|
||||||
<!-- URL配置 -->
|
|
||||||
<div class="debug-url-section">
|
<div class="debug-url-section">
|
||||||
<div class="debug-label">接口地址</div>
|
<div class="debug-label">接口地址</div>
|
||||||
<el-input
|
<el-input
|
||||||
@@ -704,7 +702,6 @@
|
|||||||
</el-input>
|
</el-input>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 接口选择 -->
|
|
||||||
<div class="debug-form-section">
|
<div class="debug-form-section">
|
||||||
<el-form :model="debugForm" ref="debugFormRef" label-position="top">
|
<el-form :model="debugForm" ref="debugFormRef" label-position="top">
|
||||||
<el-form-item label="调试接口">
|
<el-form-item label="调试接口">
|
||||||
@@ -727,7 +724,6 @@
|
|||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<!-- 变量管理(可折叠) -->
|
|
||||||
<el-collapse
|
<el-collapse
|
||||||
v-model="variableCollapseActive"
|
v-model="variableCollapseActive"
|
||||||
class="debug-collapse"
|
class="debug-collapse"
|
||||||
@@ -774,7 +770,6 @@
|
|||||||
</el-collapse-item>
|
</el-collapse-item>
|
||||||
</el-collapse>
|
</el-collapse>
|
||||||
|
|
||||||
<!-- 参数配置 -->
|
|
||||||
<el-collapse
|
<el-collapse
|
||||||
v-model="paramsCollapseActive"
|
v-model="paramsCollapseActive"
|
||||||
class="debug-collapse"
|
class="debug-collapse"
|
||||||
@@ -795,7 +790,6 @@
|
|||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- 表单模式 -->
|
|
||||||
<template v-if="paramInputMode === 'form'">
|
<template v-if="paramInputMode === 'form'">
|
||||||
<el-form-item
|
<el-form-item
|
||||||
v-for="param in debugFormParams"
|
v-for="param in debugFormParams"
|
||||||
@@ -852,7 +846,6 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- JSON模式 -->
|
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<div class="json-mode-container">
|
<div class="json-mode-container">
|
||||||
<div class="json-mode-header">
|
<div class="json-mode-header">
|
||||||
@@ -909,7 +902,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 右侧:响应结果区域 -->
|
|
||||||
<div class="debug-response-panel">
|
<div class="debug-response-panel">
|
||||||
<div class="debug-panel-header">
|
<div class="debug-panel-header">
|
||||||
<span class="panel-title">响应</span>
|
<span class="panel-title">响应</span>
|
||||||
@@ -958,7 +950,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</el-tab-pane>
|
</el-tab-pane> -->
|
||||||
|
|
||||||
<el-tab-pane label="导出文档" name="export">
|
<el-tab-pane label="导出文档" name="export">
|
||||||
<!-- 导出模块 -->
|
<!-- 导出模块 -->
|
||||||
@@ -3102,10 +3094,73 @@ const getActionDisplayName = (action, parentPath) => {
|
|||||||
return action;
|
return action;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 构建树形结构(支持无限级)
|
// 解析点分路径,构建目录/文件树(最后一段为接口,其余为目录)
|
||||||
const buildApiTree = (apiList) => {
|
const buildTree = (paths) => {
|
||||||
const tree = {};
|
const root = {};
|
||||||
|
|
||||||
|
paths.forEach((path) => {
|
||||||
|
const parts = path.split(".");
|
||||||
|
let current = root;
|
||||||
|
|
||||||
|
parts.forEach((part, index) => {
|
||||||
|
const isFile = index === parts.length - 1;
|
||||||
|
|
||||||
|
if (!current[part]) {
|
||||||
|
current[part] = isFile
|
||||||
|
? { type: "file" }
|
||||||
|
: { type: "dir", children: {} };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isFile && current[part].type !== "dir") {
|
||||||
|
throw new Error(`冲突: "${part}" 不能同时是文件和目录`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isFile) {
|
||||||
|
current = current[part].children;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return root;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 将 buildTree 结果转为侧边栏节点,并挂载 API 对象
|
||||||
|
const convertTreeToUiNodes = (treeObj, parentPath, apiMap) => {
|
||||||
|
return Object.entries(treeObj)
|
||||||
|
.filter(([, node]) => node.type === "dir")
|
||||||
|
.map(([name, node]) => {
|
||||||
|
const fullPath = parentPath ? `${parentPath}.${name}` : name;
|
||||||
|
const children = [];
|
||||||
|
const apis = [];
|
||||||
|
|
||||||
|
Object.entries(node.children).forEach(([part, child]) => {
|
||||||
|
if (child.type === "file") {
|
||||||
|
const api = apiMap.get(`${fullPath}.${part}`);
|
||||||
|
if (api) apis.push(api);
|
||||||
|
} else if (child.type === "dir") {
|
||||||
|
children.push(...convertTreeToUiNodes({ [part]: child }, fullPath, apiMap));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
key: fullPath,
|
||||||
|
displayName: name,
|
||||||
|
fullPath,
|
||||||
|
children,
|
||||||
|
apis,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.sort((a, b) => {
|
||||||
|
if (a.children.length > 0 && b.children.length === 0) return -1;
|
||||||
|
if (a.children.length === 0 && b.children.length > 0) return 1;
|
||||||
|
return a.displayName.localeCompare(b.displayName);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 按点分路径分类接口(有层级 / 未分组)
|
||||||
|
const classifyApisByPath = (apiList) => {
|
||||||
const ungrouped = [];
|
const ungrouped = [];
|
||||||
|
const groupedApis = [];
|
||||||
|
|
||||||
apiList.forEach((api) => {
|
apiList.forEach((api) => {
|
||||||
if (!api.action || typeof api.action !== "string") {
|
if (!api.action || typeof api.action !== "string") {
|
||||||
@@ -3114,65 +3169,117 @@ const buildApiTree = (apiList) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const parts = api.action.split(".");
|
const parts = api.action.split(".");
|
||||||
// 如果只有一个部分(没有点分割),作为未分组API
|
if (parts.length <= 1 || parts[0] === "") {
|
||||||
if (parts.length === 1) {
|
|
||||||
// 没有包名,直接显示
|
|
||||||
ungrouped.push(api);
|
|
||||||
} else if (parts.length === 0 || parts[0] === "") {
|
|
||||||
// 空字符串,也作为未分组API
|
|
||||||
ungrouped.push(api);
|
ungrouped.push(api);
|
||||||
} else {
|
} else {
|
||||||
// 最后一部分是action名称,其余都是包名
|
groupedApis.push(api);
|
||||||
const actionName = parts[parts.length - 1];
|
|
||||||
const packageParts = parts.slice(0, -1);
|
|
||||||
|
|
||||||
let current = tree;
|
|
||||||
let fullPath = "";
|
|
||||||
|
|
||||||
// 构建无限级分组
|
|
||||||
let lastNode = null;
|
|
||||||
packageParts.forEach((part) => {
|
|
||||||
fullPath = fullPath ? `${fullPath}.${part}` : part;
|
|
||||||
if (!current[part]) {
|
|
||||||
current[part] = {
|
|
||||||
key: fullPath,
|
|
||||||
displayName: part,
|
|
||||||
fullPath: fullPath,
|
|
||||||
children: {},
|
|
||||||
apis: [],
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
lastNode = current[part];
|
|
||||||
current = current[part].children;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// 添加API到最后一级节点
|
return { groupedApis, ungrouped };
|
||||||
if (lastNode) {
|
};
|
||||||
lastNode.apis.push(api);
|
|
||||||
|
// 导出树节点排序:目录在前,接口在后,同类型按名称排序
|
||||||
|
const sortExportNodes = (nodes) => {
|
||||||
|
return nodes.sort((a, b) => {
|
||||||
|
if (!a.isApi && b.isApi) return -1;
|
||||||
|
if (a.isApi && !b.isApi) return 1;
|
||||||
|
return a.label.localeCompare(b.label);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 将 buildTree 结果转为导出树节点(el-tree 格式)
|
||||||
|
const convertTreeToExportNodes = (treeObj, parentPath, apiMap) => {
|
||||||
|
const result = [];
|
||||||
|
|
||||||
|
Object.entries(treeObj).forEach(([name, node]) => {
|
||||||
|
if (node.type === "dir") {
|
||||||
|
const fullPath = parentPath ? `${parentPath}.${name}` : name;
|
||||||
|
const children = [];
|
||||||
|
|
||||||
|
Object.entries(node.children).forEach(([part, child]) => {
|
||||||
|
if (child.type === "file") {
|
||||||
|
const api = apiMap.get(`${fullPath}.${part}`);
|
||||||
|
if (api) {
|
||||||
|
children.push({
|
||||||
|
id: api.uniqueId,
|
||||||
|
label: getActionDisplayName(api.action, fullPath),
|
||||||
|
isApi: true,
|
||||||
|
api,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else if (child.type === "dir") {
|
||||||
|
const subNodes = convertTreeToExportNodes({ [part]: child }, fullPath, apiMap);
|
||||||
|
children.push(...subNodes);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
result.push({
|
||||||
|
id: fullPath,
|
||||||
|
label: name,
|
||||||
|
isApi: false,
|
||||||
|
children: sortExportNodes(children),
|
||||||
|
});
|
||||||
|
} else if (node.type === "file") {
|
||||||
|
const actionPath = parentPath ? `${parentPath}.${name}` : name;
|
||||||
|
const api = apiMap.get(actionPath);
|
||||||
|
if (api) {
|
||||||
|
result.push({
|
||||||
|
id: api.uniqueId,
|
||||||
|
label: api.action,
|
||||||
|
isApi: true,
|
||||||
|
api,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 转换为数组并排序
|
return sortExportNodes(result);
|
||||||
const convertToArray = (obj) => {
|
|
||||||
return Object.values(obj)
|
|
||||||
.map((node) => {
|
|
||||||
const children =
|
|
||||||
Object.keys(node.children).length > 0 ? convertToArray(node.children) : [];
|
|
||||||
return {
|
|
||||||
...node,
|
|
||||||
children: children,
|
|
||||||
};
|
|
||||||
})
|
|
||||||
.sort((a, b) => {
|
|
||||||
// 先按是否有children排序(有children的在前),然后按displayName排序
|
|
||||||
if (a.children.length > 0 && b.children.length === 0) return -1;
|
|
||||||
if (a.children.length === 0 && b.children.length > 0) return 1;
|
|
||||||
return a.displayName.localeCompare(b.displayName);
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const treeArray = convertToArray(tree);
|
// 构建导出树(支持无限级)
|
||||||
|
const buildExportTree = (apiList) => {
|
||||||
|
const { groupedApis, ungrouped } = classifyApisByPath(apiList);
|
||||||
|
const result = [];
|
||||||
|
|
||||||
|
if (groupedApis.length > 0) {
|
||||||
|
try {
|
||||||
|
const tree = buildTree(groupedApis.map((api) => api.action));
|
||||||
|
const apiMap = new Map(groupedApis.map((api) => [api.action, api]));
|
||||||
|
result.push(...convertTreeToExportNodes(tree, "", apiMap));
|
||||||
|
} catch (error) {
|
||||||
|
console.error("导出树路径解析冲突:", error);
|
||||||
|
groupedApis.forEach((api) => ungrouped.push(api));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ungrouped.forEach((api) => {
|
||||||
|
result.push({
|
||||||
|
id: api.uniqueId,
|
||||||
|
label: api.action,
|
||||||
|
isApi: true,
|
||||||
|
api,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return sortExportNodes(result);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 构建树形结构(支持无限级)
|
||||||
|
const buildApiTree = (apiList) => {
|
||||||
|
const { groupedApis, ungrouped } = classifyApisByPath(apiList);
|
||||||
|
|
||||||
|
let treeArray = [];
|
||||||
|
if (groupedApis.length > 0) {
|
||||||
|
try {
|
||||||
|
const tree = buildTree(groupedApis.map((api) => api.action));
|
||||||
|
const apiMap = new Map(groupedApis.map((api) => [api.action, api]));
|
||||||
|
treeArray = convertTreeToUiNodes(tree, "", apiMap);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("接口路径解析冲突:", error);
|
||||||
|
groupedApis.forEach((api) => ungrouped.push(api));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 未分组的API也添加到结果中
|
// 未分组的API也添加到结果中
|
||||||
ungrouped.forEach((api) => {
|
ungrouped.forEach((api) => {
|
||||||
@@ -3671,57 +3778,6 @@ const copyApiMarkdown = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 将API树转换为导出树结构
|
|
||||||
const convertToExportTree = (nodes, parentPath = "") => {
|
|
||||||
const result = [];
|
|
||||||
|
|
||||||
nodes.forEach((node) => {
|
|
||||||
const nodePath = parentPath ? `${parentPath}.${node.displayName}` : node.displayName;
|
|
||||||
const hasChildren = node.children && node.children.length > 0;
|
|
||||||
const hasApis = node.apis && node.apis.length > 0;
|
|
||||||
|
|
||||||
// 如果节点有子节点,需要创建目录节点
|
|
||||||
if (hasChildren) {
|
|
||||||
const children = [];
|
|
||||||
|
|
||||||
// 添加子节点
|
|
||||||
children.push(...convertToExportTree(node.children, nodePath));
|
|
||||||
|
|
||||||
// 添加API节点
|
|
||||||
if (hasApis) {
|
|
||||||
node.apis.forEach((api) => {
|
|
||||||
children.push({
|
|
||||||
id: api.uniqueId,
|
|
||||||
label: getActionDisplayName(api.action, node.fullPath || parentPath),
|
|
||||||
isApi: true,
|
|
||||||
api: api,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
result.push({
|
|
||||||
id: node.key,
|
|
||||||
label: node.displayName,
|
|
||||||
isApi: false,
|
|
||||||
children: children,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// 如果节点只有API而没有子节点,直接显示接口,不创建目录
|
|
||||||
else if (hasApis) {
|
|
||||||
node.apis.forEach((api) => {
|
|
||||||
result.push({
|
|
||||||
id: api.uniqueId,
|
|
||||||
label: getActionDisplayName(api.action, node.fullPath || parentPath),
|
|
||||||
isApi: true,
|
|
||||||
api: api,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
// 导出树数据
|
// 导出树数据
|
||||||
const exportTreeData = computed(() => {
|
const exportTreeData = computed(() => {
|
||||||
const apiList = exportSearchQuery.value
|
const apiList = exportSearchQuery.value
|
||||||
@@ -3730,8 +3786,7 @@ const exportTreeData = computed(() => {
|
|||||||
return api.action.toLowerCase().includes(exportSearchQuery.value.toLowerCase());
|
return api.action.toLowerCase().includes(exportSearchQuery.value.toLowerCase());
|
||||||
})
|
})
|
||||||
: apis.value;
|
: apis.value;
|
||||||
const tree = buildApiTree(apiList);
|
return buildExportTree(apiList);
|
||||||
return convertToExportTree(tree);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// 计算总API数量
|
// 计算总API数量
|
||||||
@@ -4139,7 +4194,6 @@ const handleExportSelected = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.api-detail {
|
.api-detail {
|
||||||
max-width: 1200px;
|
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
animation: fadeIn 0.5s ease;
|
animation: fadeIn 0.5s ease;
|
||||||
|
|||||||
Reference in New Issue
Block a user