diff --git a/src/App.vue b/src/App.vue index 771da690..6b3da917 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1467,13 +1467,15 @@ const loadHomeListEntries = async () => { { name: string; urls: { spark?: string; apm?: string } } >(); - for (const mode of modes) { - const finalArch = mode === "spark" ? `${arch}-store` : `${arch}-apm`; - const base = `${APM_STORE_BASE_URL}/${finalArch}/home`; - - try { - const res = await fetch(`${base}/homelist.json`); - if (res.ok) { + // 并发拉取各来源的 homelist.json(spark/apm),缩短首页入口加载耗时; + // 各来源独立解析后合并到局部 byName,不逐个触发响应式更新。 + await Promise.all( + modes.map(async (mode) => { + const finalArch = mode === "spark" ? `${arch}-store` : `${arch}-apm`; + const base = `${APM_STORE_BASE_URL}/${finalArch}/home`; + try { + const res = await fetch(`${base}/homelist.json`); + if (!res.ok) return; const lists = await res.json(); lists.forEach( (item: { name?: string; type?: string; jsonUrl?: string }) => { @@ -1494,11 +1496,11 @@ const loadHomeListEntries = async () => { } }, ); + } catch (e) { + console.warn(`Failed to load ${mode} homelist.json`, e); } - } catch (e) { - console.warn(`Failed to load ${mode} homelist.json`, e); - } - } + }), + ); const entries: SidebarEntry[] = []; const urlsMap: Record = {}; @@ -3486,6 +3488,8 @@ onMounted(async () => { onUnmounted(() => { rootAbortController.abort(); updateCenterStore.unbind(); + // 清理窗口尺寸防抖定时器,防止组件销毁后仍触发 IPC 保存调用 + if (saveBoundsTimer) clearTimeout(saveBoundsTimer); // 停止仍挂起的"等待 loading 完成后执行一次"的 watcher,避免泄漏 for (const stop of pendingWatchers.splice(0)) stop(); // 移除 window / document 监听 diff --git a/src/components/AppDetailModal.vue b/src/components/AppDetailModal.vue index 0c457b4a..5a011c21 100644 --- a/src/components/AppDetailModal.vue +++ b/src/components/AppDetailModal.vue @@ -783,8 +783,10 @@ const installBtnText = computed(() => { const iconPath = computed(() => { if (!displayApp.value) return ""; const arch = window.apm_store.arch || "amd64"; + // 切换来源标签时(viewingOrigin)需动态重算架构路径,确保图标/资源跟随当前查看来源, + // 而非固定使用合并应用原始 origin,避免切换源时图标与数据错位。 const finalArch = - displayApp.value.origin === "spark" ? `${arch}-store` : `${arch}-apm`; + viewingOrigin.value === "spark" ? `${arch}-store` : `${arch}-apm`; return `${APM_STORE_BASE_URL}/${finalArch}/${displayApp.value.category}/${displayApp.value.pkgname}/icon.png`; }); diff --git a/src/components/InstalledAppsModal.vue b/src/components/InstalledAppsModal.vue index e16ca2eb..93e62559 100644 --- a/src/components/InstalledAppsModal.vue +++ b/src/components/InstalledAppsModal.vue @@ -450,11 +450,22 @@ const hasOrigin = (app: App, origin: "spark" | "apm"): boolean => app.origins?.includes(origin) ?? app.origin === origin; // APM / Spark 分别统计实际安装的包数量(同一 pkgname 同时装两种来源时各计一次) +// 搜索过滤后的全量(不叠加来源筛选),用于顶部统计徽章实时同步搜索结果, +// 避免搜索时列表缩减而徽章数字仍显示全量造成误导。 +const searchFilteredApps = computed(() => { + const q = searchQuery.value.trim().toLowerCase(); + if (!q) return props.apps; + return props.apps.filter( + (a) => + a.name.toLowerCase().includes(q) || a.pkgname.toLowerCase().includes(q), + ); +}); + const apmCount = computed( - () => props.apps.filter((a) => hasOrigin(a, "apm")).length, + () => searchFilteredApps.value.filter((a) => hasOrigin(a, "apm")).length, ); const sparkCount = computed( - () => props.apps.filter((a) => hasOrigin(a, "spark")).length, + () => searchFilteredApps.value.filter((a) => hasOrigin(a, "spark")).length, ); // 总数 = APM 包数 + Spark 包数(不同来源视为不同包,单独计数) const totalCount = computed(() => apmCount.value + sparkCount.value);