优化
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",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "api-docs",
|
||||
"name": "iqudoo-miniweb-backend-docs",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"@element-plus/icons-vue": "^2.3.2",
|
||||
"axios": "^1.8.3",
|
||||
"element-plus": "^2.9.6",
|
||||
"vue": "^3.5.13",
|
||||
@@ -74,9 +75,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@element-plus/icons-vue": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@element-plus/icons-vue/-/icons-vue-2.3.1.tgz",
|
||||
"integrity": "sha512-XxVUZv48RZAd87ucGS48jPf6pKu0yV5UCg9f4FFwtrYxXOwWuVJo6wOvSLKEoMQKjv8GsX/mhP6UsC1lRwbUWg==",
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@element-plus/icons-vue/-/icons-vue-2.3.2.tgz",
|
||||
"integrity": "sha512-OzIuTaIfC8QXEPmJvB4Y4kw34rSXdCJzxcD1kFStBvr8bK6X1zQAYDo0CNMjojnfTqRQCJ0I7prlErcoRiET2A==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"vue": "^3.2.0"
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@element-plus/icons-vue": "^2.3.2",
|
||||
"axios": "^1.8.3",
|
||||
"element-plus": "^2.9.6",
|
||||
"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
282
src/App.vue
282
src/App.vue
@@ -679,16 +679,14 @@
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- 调试模块tab -->
|
||||
<el-tab-pane label="调试工具" name="debug">
|
||||
<!-- <el-tab-pane label="调试工具" name="debug">
|
||||
<div class="debug-module">
|
||||
<div class="debug-layout">
|
||||
<!-- 左侧:请求配置区域 -->
|
||||
<div class="debug-request-panel">
|
||||
<div class="debug-panel-header">
|
||||
<span class="panel-title">请求</span>
|
||||
</div>
|
||||
<div class="debug-panel-content">
|
||||
<!-- URL配置 -->
|
||||
<div class="debug-url-section">
|
||||
<div class="debug-label">接口地址</div>
|
||||
<el-input
|
||||
@@ -704,7 +702,6 @@
|
||||
</el-input>
|
||||
</div>
|
||||
|
||||
<!-- 接口选择 -->
|
||||
<div class="debug-form-section">
|
||||
<el-form :model="debugForm" ref="debugFormRef" label-position="top">
|
||||
<el-form-item label="调试接口">
|
||||
@@ -727,7 +724,6 @@
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 变量管理(可折叠) -->
|
||||
<el-collapse
|
||||
v-model="variableCollapseActive"
|
||||
class="debug-collapse"
|
||||
@@ -774,7 +770,6 @@
|
||||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
|
||||
<!-- 参数配置 -->
|
||||
<el-collapse
|
||||
v-model="paramsCollapseActive"
|
||||
class="debug-collapse"
|
||||
@@ -795,7 +790,6 @@
|
||||
</el-radio-group>
|
||||
</template>
|
||||
|
||||
<!-- 表单模式 -->
|
||||
<template v-if="paramInputMode === 'form'">
|
||||
<el-form-item
|
||||
v-for="param in debugFormParams"
|
||||
@@ -852,7 +846,6 @@
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<!-- JSON模式 -->
|
||||
<template v-else>
|
||||
<div class="json-mode-container">
|
||||
<div class="json-mode-header">
|
||||
@@ -909,7 +902,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 右侧:响应结果区域 -->
|
||||
<div class="debug-response-panel">
|
||||
<div class="debug-panel-header">
|
||||
<span class="panel-title">响应</span>
|
||||
@@ -958,7 +950,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tab-pane> -->
|
||||
|
||||
<el-tab-pane label="导出文档" name="export">
|
||||
<!-- 导出模块 -->
|
||||
@@ -3102,10 +3094,73 @@ const getActionDisplayName = (action, parentPath) => {
|
||||
return action;
|
||||
};
|
||||
|
||||
// 构建树形结构(支持无限级)
|
||||
const buildApiTree = (apiList) => {
|
||||
const tree = {};
|
||||
// 解析点分路径,构建目录/文件树(最后一段为接口,其余为目录)
|
||||
const buildTree = (paths) => {
|
||||
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 groupedApis = [];
|
||||
|
||||
apiList.forEach((api) => {
|
||||
if (!api.action || typeof api.action !== "string") {
|
||||
@@ -3114,65 +3169,117 @@ const buildApiTree = (apiList) => {
|
||||
}
|
||||
|
||||
const parts = api.action.split(".");
|
||||
// 如果只有一个部分(没有点分割),作为未分组API
|
||||
if (parts.length === 1) {
|
||||
// 没有包名,直接显示
|
||||
ungrouped.push(api);
|
||||
} else if (parts.length === 0 || parts[0] === "") {
|
||||
// 空字符串,也作为未分组API
|
||||
if (parts.length <= 1 || parts[0] === "") {
|
||||
ungrouped.push(api);
|
||||
} else {
|
||||
// 最后一部分是action名称,其余都是包名
|
||||
const actionName = parts[parts.length - 1];
|
||||
const packageParts = parts.slice(0, -1);
|
||||
groupedApis.push(api);
|
||||
}
|
||||
});
|
||||
|
||||
let current = tree;
|
||||
let fullPath = "";
|
||||
return { groupedApis, ungrouped };
|
||||
};
|
||||
|
||||
// 构建无限级分组
|
||||
let lastNode = null;
|
||||
packageParts.forEach((part) => {
|
||||
fullPath = fullPath ? `${fullPath}.${part}` : part;
|
||||
if (!current[part]) {
|
||||
current[part] = {
|
||||
key: fullPath,
|
||||
displayName: part,
|
||||
fullPath: fullPath,
|
||||
children: {},
|
||||
apis: [],
|
||||
};
|
||||
// 导出树节点排序:目录在前,接口在后,同类型按名称排序
|
||||
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);
|
||||
}
|
||||
lastNode = current[part];
|
||||
current = current[part].children;
|
||||
});
|
||||
|
||||
// 添加API到最后一级节点
|
||||
if (lastNode) {
|
||||
lastNode.apis.push(api);
|
||||
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,
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 转换为数组并排序
|
||||
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);
|
||||
});
|
||||
};
|
||||
return sortExportNodes(result);
|
||||
};
|
||||
|
||||
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也添加到结果中
|
||||
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 apiList = exportSearchQuery.value
|
||||
@@ -3730,8 +3786,7 @@ const exportTreeData = computed(() => {
|
||||
return api.action.toLowerCase().includes(exportSearchQuery.value.toLowerCase());
|
||||
})
|
||||
: apis.value;
|
||||
const tree = buildApiTree(apiList);
|
||||
return convertToExportTree(tree);
|
||||
return buildExportTree(apiList);
|
||||
});
|
||||
|
||||
// 计算总API数量
|
||||
@@ -4139,7 +4194,6 @@ const handleExportSelected = async () => {
|
||||
}
|
||||
|
||||
.api-detail {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
animation: fadeIn 0.5s ease;
|
||||
|
||||
Reference in New Issue
Block a user