优化,接口文档支持通过url过滤显示

This commit is contained in:
iqudoo
2026-06-04 11:56:09 +08:00
parent 4ebe673885
commit ad1d760653

View File

@@ -234,6 +234,34 @@
<div class="page-header-text"> <div class="page-header-text">
<div class="page-header-label">接口</div> <div class="page-header-label">接口</div>
<h1 class="api-action-name">{{ currentApi.action }}</h1> <h1 class="api-action-name">{{ currentApi.action }}</h1>
<div
v-if="displayApiGroups.length || displayApiScenes.length"
class="api-meta-row"
>
<div v-if="displayApiGroups.length" class="api-meta-item">
<span class="api-meta-label">用户组</span>
<el-tag
v-for="item in displayApiGroups"
:key="'group-' + item"
size="small"
type="info"
class="api-meta-tag"
>
{{ item }}
</el-tag>
</div>
<div v-if="displayApiScenes.length" class="api-meta-item">
<span class="api-meta-label">场景组</span>
<el-tag
v-for="item in displayApiScenes"
:key="'scene-' + item"
size="small"
class="api-meta-tag"
>
{{ item }}
</el-tag>
</div>
</div>
</div> </div>
</div> </div>
<el-button type="primary" plain size="small" :icon="DocumentCopy" <el-button type="primary" plain size="small" :icon="DocumentCopy"
@@ -815,12 +843,24 @@ const exportTreeProps = {
}; };
const hostUrl = ref(import.meta.env.VITE_API_URL || "/api"); const hostUrl = ref(import.meta.env.VITE_API_URL || "/api");
const filterGroup = ref("");
const filterScene = ref("");
const showExportDrawer = ref(false); const showExportDrawer = ref(false);
const exportDrawerSize = computed(() => { const exportDrawerSize = computed(() => {
if (isMobile.value) return "100%"; if (isMobile.value) return "100%";
return `${Math.min(800, windowWidth.value)}px`; return `${Math.min(800, windowWidth.value)}px`;
}); });
const displayApiGroups = computed(() => {
const source = apiDetail.value || currentApi.value;
return normalizeStringList(source?.groups);
});
const displayApiScenes = computed(() => {
const source = apiDetail.value || currentApi.value;
return normalizeStringList(source?.scenes);
});
// 监听搜索,如果有搜索则自动返回根级别显示所有匹配结果 // 监听搜索,如果有搜索则自动返回根级别显示所有匹配结果
watch(searchQuery, (newVal) => { watch(searchQuery, (newVal) => {
if (newVal && newVal.trim()) { if (newVal && newVal.trim()) {
@@ -878,13 +918,54 @@ const initDB = () => {
}; };
// 从 IndexedDB 获取缓存的接口列表 // 从 IndexedDB 获取缓存的接口列表
const getApiListCacheKey = () => {
const group = filterGroup.value || "";
const scene = filterScene.value || "";
return `api-list|${group}|${scene}`;
};
const normalizeStringList = (value) => {
if (!value) return [];
return Array.isArray(value) ? value.filter((item) => item != null && item !== "") : [String(value)];
};
const readFiltersFromUrl = () => {
const urlParams = new URLSearchParams(window.location.search);
filterGroup.value = urlParams.get("group") || "";
filterScene.value = urlParams.get("scene") || "";
};
const syncFiltersToUrl = (url) => {
if (filterGroup.value) {
url.searchParams.set("group", filterGroup.value);
} else {
url.searchParams.delete("group");
}
if (filterScene.value) {
url.searchParams.set("scene", filterScene.value);
} else {
url.searchParams.delete("scene");
}
};
const buildActionListQueryParams = () => {
const query = { action: "getActionList" };
if (filterGroup.value) {
query.group = filterGroup.value;
}
if (filterScene.value) {
query.scene = filterScene.value;
}
return query;
};
const getCachedApiList = async () => { const getCachedApiList = async () => {
try { try {
const db = await initDB(); const db = await initDB();
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const transaction = db.transaction([STORE_NAME], "readonly"); const transaction = db.transaction([STORE_NAME], "readonly");
const store = transaction.objectStore(STORE_NAME); const store = transaction.objectStore(STORE_NAME);
const request = store.get("api-list"); const request = store.get(getApiListCacheKey());
request.onsuccess = () => resolve(request.result || null); request.onsuccess = () => resolve(request.result || null);
request.onerror = () => reject(request.error); request.onerror = () => reject(request.error);
}); });
@@ -901,7 +982,7 @@ const saveApiListToCache = async (apiList) => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const transaction = db.transaction([STORE_NAME], "readwrite"); const transaction = db.transaction([STORE_NAME], "readwrite");
const store = transaction.objectStore(STORE_NAME); const store = transaction.objectStore(STORE_NAME);
const request = store.put(apiList, "api-list"); const request = store.put(apiList, getApiListCacheKey());
request.onsuccess = () => resolve(); request.onsuccess = () => resolve();
request.onerror = () => reject(request.error); request.onerror = () => reject(request.error);
}); });
@@ -956,10 +1037,22 @@ const processApiList = (apiData) => {
onMounted(async () => { onMounted(async () => {
checkDeviceType(); checkDeviceType();
window.addEventListener("resize", checkDeviceType); window.addEventListener("resize", checkDeviceType);
readFiltersFromUrl();
loading.value = true; // 先显示加载状态,避免显示空白页面 loading.value = true; // 先显示加载状态,避免显示空白页面
loadError.value = null; loadError.value = null;
const openApiFromUrl = () => {
const urlParams = new URLSearchParams(window.location.search);
const id = urlParams.get("id");
if (id) {
const apiToShow = findApiById(id);
if (apiToShow) {
goToApi(apiToShow);
}
}
};
// 先从缓存获取接口列表 // 先从缓存获取接口列表
let cachedApiList = null; let cachedApiList = null;
try { try {
@@ -969,16 +1062,7 @@ onMounted(async () => {
apis.value = processApiList(cachedApiList); apis.value = processApiList(cachedApiList);
// 有缓存数据后,立即隐藏加载状态,让用户看到内容 // 有缓存数据后,立即隐藏加载状态,让用户看到内容
loading.value = false; loading.value = false;
openApiFromUrl();
// 检查URL是否包含API id参数
const urlParams = new URLSearchParams(window.location.search);
const id = urlParams.get("id");
if (id) {
const apiToShow = findApiById(id);
if (apiToShow) {
goToApi(apiToShow);
}
}
} }
// 如果没有缓存数据,继续显示加载状态,等待请求完成 // 如果没有缓存数据,继续显示加载状态,等待请求完成
} catch (error) { } catch (error) {
@@ -989,9 +1073,7 @@ onMounted(async () => {
// 然后请求最新的接口列表(后台无感刷新) // 然后请求最新的接口列表(后台无感刷新)
try { try {
const response = await axios.get(hostUrl.value, { const response = await axios.get(hostUrl.value, {
params: { params: buildActionListQueryParams(),
action: "getActionList",
},
}); });
// 处理并保存最新的接口列表 // 处理并保存最新的接口列表
@@ -1001,15 +1083,7 @@ onMounted(async () => {
// 保存到缓存 // 保存到缓存
await saveApiListToCache(response.data.data); await saveApiListToCache(response.data.data);
// 检查URL是否包含API id参数 openApiFromUrl();
const urlParams = new URLSearchParams(window.location.search);
const id = urlParams.get("id");
if (id) {
const apiToShow = findApiById(id);
if (apiToShow) {
goToApi(apiToShow);
}
}
} catch (error) { } catch (error) {
console.error("Failed to fetch API list:", error); console.error("Failed to fetch API list:", error);
// 如果请求失败且没有缓存数据,显示错误 // 如果请求失败且没有缓存数据,显示错误
@@ -1024,7 +1098,6 @@ onMounted(async () => {
// 确保最终隐藏加载状态(如果没有缓存数据,这里会隐藏;如果有缓存数据,已经在上面隐藏了) // 确保最终隐藏加载状态(如果没有缓存数据,这里会隐藏;如果有缓存数据,已经在上面隐藏了)
loading.value = false; loading.value = false;
} }
}); });
// 根据id查找API // 根据id查找API
@@ -1032,9 +1105,10 @@ const findApiById = (id) => {
return apis.value.find((a) => a.uniqueId === id || a.originalId === id) || null; return apis.value.find((a) => a.uniqueId === id || a.originalId === id) || null;
}; };
// 更新URL参数 // 更新URL参数(保留 group / scene 过滤参数)
const updateUrlParameter = (id) => { const updateUrlParameter = (id) => {
const url = new URL(window.location); const url = new URL(window.location);
syncFiltersToUrl(url);
if (id) { if (id) {
url.searchParams.set("id", id); url.searchParams.set("id", id);
} else { } else {
@@ -1914,6 +1988,18 @@ const generateSingleApiMarkdown = (api, detail) => {
// 接口详情 // 接口详情
markdown += `## ${api.action}\n\n`; markdown += `## ${api.action}\n\n`;
const groups = normalizeStringList(detail?.groups ?? api.groups);
const scenes = normalizeStringList(detail?.scenes ?? api.scenes);
if (groups.length) {
markdown += `- 用户组:${groups.join("、")}\n`;
}
if (scenes.length) {
markdown += `- 场景组:${scenes.join("、")}\n`;
}
if (groups.length || scenes.length) {
markdown += "\n";
}
if (detail) { if (detail) {
// 请求参数 // 请求参数
if (detail.paramModel) { if (detail.paramModel) {
@@ -2746,6 +2832,34 @@ const handleExportSelected = async () => {
font-family: "SF Mono", "Menlo", "Monaco", "Consolas", monospace; font-family: "SF Mono", "Menlo", "Monaco", "Consolas", monospace;
} }
.api-meta-row {
display: flex;
flex-direction: column;
gap: 8px;
margin-top: 10px;
}
.api-meta-item {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 6px;
}
.api-meta-label {
font-size: 12px;
color: #8f959e;
flex-shrink: 0;
}
.api-meta-tag {
font-family: "SF Mono", "Menlo", "Monaco", "Consolas", monospace;
}
.brand-filter-hint {
color: #8f959e;
}
.api-detail-alert { .api-detail-alert {
margin-bottom: 16px; margin-bottom: 16px;
} }