优化,接口文档支持通过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-label">接口</div>
<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>
<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 filterGroup = ref("");
const filterScene = ref("");
const showExportDrawer = ref(false);
const exportDrawerSize = computed(() => {
if (isMobile.value) return "100%";
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) => {
if (newVal && newVal.trim()) {
@@ -878,13 +918,54 @@ const initDB = () => {
};
// 从 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 () => {
try {
const db = await initDB();
return new Promise((resolve, reject) => {
const transaction = db.transaction([STORE_NAME], "readonly");
const store = transaction.objectStore(STORE_NAME);
const request = store.get("api-list");
const request = store.get(getApiListCacheKey());
request.onsuccess = () => resolve(request.result || null);
request.onerror = () => reject(request.error);
});
@@ -901,7 +982,7 @@ const saveApiListToCache = async (apiList) => {
return new Promise((resolve, reject) => {
const transaction = db.transaction([STORE_NAME], "readwrite");
const store = transaction.objectStore(STORE_NAME);
const request = store.put(apiList, "api-list");
const request = store.put(apiList, getApiListCacheKey());
request.onsuccess = () => resolve();
request.onerror = () => reject(request.error);
});
@@ -956,10 +1037,22 @@ const processApiList = (apiData) => {
onMounted(async () => {
checkDeviceType();
window.addEventListener("resize", checkDeviceType);
readFiltersFromUrl();
loading.value = true; // 先显示加载状态,避免显示空白页面
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;
try {
@@ -969,16 +1062,7 @@ onMounted(async () => {
apis.value = processApiList(cachedApiList);
// 有缓存数据后,立即隐藏加载状态,让用户看到内容
loading.value = false;
// 检查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);
}
}
openApiFromUrl();
}
// 如果没有缓存数据,继续显示加载状态,等待请求完成
} catch (error) {
@@ -989,9 +1073,7 @@ onMounted(async () => {
// 然后请求最新的接口列表(后台无感刷新)
try {
const response = await axios.get(hostUrl.value, {
params: {
action: "getActionList",
},
params: buildActionListQueryParams(),
});
// 处理并保存最新的接口列表
@@ -1001,15 +1083,7 @@ onMounted(async () => {
// 保存到缓存
await saveApiListToCache(response.data.data);
// 检查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);
}
}
openApiFromUrl();
} catch (error) {
console.error("Failed to fetch API list:", error);
// 如果请求失败且没有缓存数据,显示错误
@@ -1024,7 +1098,6 @@ onMounted(async () => {
// 确保最终隐藏加载状态(如果没有缓存数据,这里会隐藏;如果有缓存数据,已经在上面隐藏了)
loading.value = false;
}
});
// 根据id查找API
@@ -1032,9 +1105,10 @@ const findApiById = (id) => {
return apis.value.find((a) => a.uniqueId === id || a.originalId === id) || null;
};
// 更新URL参数
// 更新URL参数(保留 group / scene 过滤参数)
const updateUrlParameter = (id) => {
const url = new URL(window.location);
syncFiltersToUrl(url);
if (id) {
url.searchParams.set("id", id);
} else {
@@ -1914,6 +1988,18 @@ const generateSingleApiMarkdown = (api, detail) => {
// 接口详情
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.paramModel) {
@@ -2746,6 +2832,34 @@ const handleExportSelected = async () => {
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 {
margin-bottom: 16px;
}