fix(frontend): 菜单点击偶发无响应三处根因——①路由懒加载chunk失败被静默吞掉(新增onError自动整页重载+保险丝防循环) ②首页入口被权限接口阻塞(改乐观显示+缓存TTL10分钟+超时30s→8s) ③菜单hover预取目标页chunk(工具卡片/工作流步骤/顶部导航)

This commit is contained in:
2026-09-09 18:53:34 +08:00
parent 83cb3ff84e
commit 0cf20527a4
6 changed files with 139 additions and 14 deletions
@@ -37,6 +37,40 @@ function getAppPermissionCacheKey(uid: number) {
return `app_column_permissions:${String(uid)}`
}
/** 权限缓存有效期:10 分钟内直接用本地缓存渲染菜单/入口,避免接口慢时入口消失 */
const PERMISSION_CACHE_TTL_MS = 10 * 60 * 1000
interface CachedAppPermissions {
ts: number
items: PermissionMenuItem[]
}
/**
* 读取本地缓存的权限菜单项(兼容旧版纯数组格式:无时间戳视为长期有效)。
* 接口失败/超时时由调用方用它兜底,保证菜单不因权限接口抖动而消失。
*/
export function readCachedAppColumnPermissions(uid: number): PermissionMenuItem[] | null {
try {
const raw = window.localStorage.getItem(getAppPermissionCacheKey(uid)) || ''
if (!raw) return null
const parsed: unknown = JSON.parse(raw)
if (Array.isArray(parsed)) {
return parsed as PermissionMenuItem[]
}
if (
parsed &&
typeof parsed === 'object' &&
Array.isArray((parsed as CachedAppPermissions).items) &&
Date.now() - Number((parsed as CachedAppPermissions).ts || 0) < PERMISSION_CACHE_TTL_MS
) {
return (parsed as CachedAppPermissions).items
}
return null
} catch {
return null
}
}
function getAuthToken() {
return typeof window === 'undefined' ? '' : window.localStorage.getItem('aiimage_auth_token') || ''
}
@@ -70,6 +104,8 @@ async function fetchAppColumnPermissions() {
{
params: { menuType: 'app' },
headers,
// 权限接口只决定菜单显隐:8s 未返回即失败走缓存兜底,避免入口区长时间空白
timeout: 8000,
},
)
@@ -79,7 +115,7 @@ async function fetchAppColumnPermissions() {
const items = res.data || res.items || []
try {
window.localStorage.setItem(cacheKey, JSON.stringify(items))
window.localStorage.setItem(cacheKey, JSON.stringify({ ts: Date.now(), items }))
} catch (_error) {}
return items
+34
View File
@@ -0,0 +1,34 @@
import type { Router } from 'vue-router'
/**
* 路由懒加载 chunk 预取
*
* 页面均为动态 import(每页独立 chunk,见 src/router/index.ts),首次进入某页
* 需现场从线上拉取 chunk。菜单 hover 时提前触发 import()(结果被 router 缓存,
* 点击时直接命中缓存),弱网下显著减少"点了没反应/跳转慢"的等待时间。
*/
/** 触发目标路由的懒加载 chunk 预取;href 为空或预取失败时静默(点击时会走 onError 兜底) */
export function prefetchRouteChunk(router: Router, href?: string): void {
if (!href) return
let target = href.trim()
const hashIndex = target.indexOf('#')
if (hashIndex >= 0) {
target = target.slice(0, hashIndex)
}
try {
const resolved = router.resolve(target)
const component = resolved.matched[0]?.components?.default as unknown
// 懒加载路由的 default 是 () => import(...) 加载器:调用即触发 chunk 下载(结果被 vue-router 缓存)
if (typeof component === 'function') {
const result = (component as () => unknown)()
if (result && typeof (result as Promise<unknown>).then === 'function') {
void (result as Promise<unknown>).catch(() => {
/* 预取失败静默,点击时统一由 router.onError 兜底 */
})
}
}
} catch {
/* 路径无法解析时忽略 */
}
}