fix: PR 审查真实缺陷修复(图标错位/定时器清理/并发/统计同步)

- AppDetailModal: iconPath 改用 viewingOrigin 动态计算,修复合并源切换时图标与数据错位
- App.vue: onUnmounted 清理 saveBoundsTimer 防抖定时器,避免组件销毁后悬空 IPC 调用
- App.vue: loadHomeListEntries 串行 for-of 改 Promise.all 并发拉取,缩短首页入口加载
- InstalledAppsModal: 顶部统计徽章基于搜索过滤结果实时计算,修复搜索时数字不同步

(审查误报项已核实未改: fetchAppFromStore 已有 encodeURIComponent + AbortSignal)
This commit is contained in:
xiyidaiwa
2026-08-11 00:20:19 +08:00
parent 44fa822146
commit 8888afd4f5
3 changed files with 31 additions and 14 deletions
+9 -5
View File
@@ -1467,13 +1467,15 @@ const loadHomeListEntries = async () => {
{ name: string; urls: { spark?: string; apm?: string } }
>();
for (const mode of modes) {
// 并发拉取各来源的 homelist.jsonspark/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) {
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);
}
}
}),
);
const entries: SidebarEntry[] = [];
const urlsMap: Record<string, { spark?: string; apm?: string }> = {};
@@ -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 监听
+3 -1
View File
@@ -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`;
});
+13 -2
View File
@@ -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);