优化,接口文档支持通过url过滤显示
This commit is contained in:
166
src/App.vue
166
src/App.vue
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user