mirror of
https://gitee.com/spark-store-project/spark-store
synced 2026-09-21 14:10:11 +08:00
fix: 标签策略启动初始化加固 + 已安装应用过滤/统计性能优化
- 标签优先显示策略 initTagPriorityStrategy 提到 App.vue onMounted 统一调用, 消除组件挂载顺序导致先读内存默认 auto 的竞态窗口(AppDetailModal 移除重复调用) - InstalledAppsModal 过滤逻辑合并为单次遍历(保留 hasOrigin 双来源语义) - 统计 apm/spark/total 改为基于 searchFilteredApps 单次遍历 - canOpenDetail 补充业务含义注释与可读性变量 - 将 App.vue 重构的 9 个 composable 纳入版本管理 - 验证:vue-tsc 0 / eslint 0 / dpkg-buildpackage 打包 spark-store_5.2.1.24-test 成功
This commit is contained in:
@@ -578,7 +578,6 @@ import {
|
||||
} from "../global/storeConfig";
|
||||
import {
|
||||
tagPriorityStrategyRef,
|
||||
initTagPriorityStrategy,
|
||||
type TagPriorityStrategy,
|
||||
} from "../global/tagPriority";
|
||||
// 评论功能暂时关闭
|
||||
@@ -690,7 +689,7 @@ const computeDefaultViewingOrigin = (
|
||||
// - 策略变化(在设置页实时切换)→ 已打开且未手动切标签的详情页立即更新默认标签
|
||||
// 手动点标签页(selectOrigin)只是临时预览:置 manualOverride 标记,本次会话内保持,
|
||||
// 既不被策略变化强行拉回,也不跨重开持久(重开后会按设置重算)。
|
||||
initTagPriorityStrategy();
|
||||
// 注:initTagPriorityStrategy() 已在 App.vue 启动时统一调用,此处不再重复。
|
||||
const manualOverride = ref(false); // 用户是否手动切换过标签(本次会话)
|
||||
let lastPkgname: string | null = null; // 上一次计算对应的应用 pkgname,用于识别"新打开/重开"
|
||||
watch(
|
||||
|
||||
@@ -424,13 +424,19 @@ const getIconUrl = (app: App) => {
|
||||
return `${APM_STORE_BASE_URL}/${finalArch}/${app.category}/${app.pkgname}/icon.png`;
|
||||
};
|
||||
|
||||
const canOpenDetail = (app: App) => {
|
||||
// 判断应用是否可打开详情页。满足以下任一条件即可展示"查看详情":
|
||||
// - 有明确分类(非 unknown):详情页可按分类加载元数据
|
||||
// - 有详细描述(more) / 官网(website) / 作者(author) 任一字段:详情有内容可展示
|
||||
// - 有截图(img_urls):详情页可渲染预览图
|
||||
// 这些字段缺失时详情页信息过空,故隐藏入口仅保留打开/卸载。
|
||||
const canOpenDetail = (app: App): boolean => {
|
||||
const hasCategory = app.category !== "unknown";
|
||||
const hasDescription = Boolean(app.more);
|
||||
const hasWebsite = Boolean(app.website);
|
||||
const hasAuthor = Boolean(app.author);
|
||||
const hasScreenshots = (app.img_urls?.length ?? 0) > 0;
|
||||
return (
|
||||
app.category !== "unknown" ||
|
||||
Boolean(app.more) ||
|
||||
Boolean(app.website) ||
|
||||
Boolean(app.author) ||
|
||||
(app.img_urls?.length ?? 0) > 0
|
||||
hasCategory || hasDescription || hasWebsite || hasAuthor || hasScreenshots
|
||||
);
|
||||
};
|
||||
|
||||
@@ -461,12 +467,19 @@ const searchFilteredApps = computed(() => {
|
||||
);
|
||||
});
|
||||
|
||||
const apmCount = computed(
|
||||
() => searchFilteredApps.value.filter((a) => hasOrigin(a, "apm")).length,
|
||||
);
|
||||
const sparkCount = computed(
|
||||
() => searchFilteredApps.value.filter((a) => hasOrigin(a, "spark")).length,
|
||||
);
|
||||
// 对搜索过滤后的列表做单次遍历,同时统计 APM / Spark 各自安装数。
|
||||
// 同一 pkgname 同时装两种来源时各计一次(双来源计数),避免对 props.apps 多次 filter 遍历。
|
||||
const appStats = computed(() => {
|
||||
let apm = 0;
|
||||
let spark = 0;
|
||||
for (const a of searchFilteredApps.value) {
|
||||
if (hasOrigin(a, "apm")) apm++;
|
||||
if (hasOrigin(a, "spark")) spark++;
|
||||
}
|
||||
return { apm, spark };
|
||||
});
|
||||
const apmCount = computed(() => appStats.value.apm);
|
||||
const sparkCount = computed(() => appStats.value.spark);
|
||||
// 总数 = APM 包数 + Spark 包数(不同来源视为不同包,单独计数)
|
||||
const totalCount = computed(() => apmCount.value + sparkCount.value);
|
||||
|
||||
@@ -476,26 +489,23 @@ const filterOrigin = ref<"all" | "apm" | "spark">("all");
|
||||
// 搜索关键词(按名称/包名不区分大小写过滤已安装应用)
|
||||
const searchQuery = ref("");
|
||||
const filteredApps = computed(() => {
|
||||
// 1. 先按搜索关键词过滤
|
||||
// 单次遍历完成「搜索过滤 + 来源筛选」,避免多次 .filter() 创建中间数组。
|
||||
// 来源判定统一用 hasOrigin(兼顾双来源 origins 数组,不能退化为 a.origin)。
|
||||
const q = searchQuery.value.trim().toLowerCase();
|
||||
let list = props.apps;
|
||||
if (q) {
|
||||
list = list.filter(
|
||||
(a) =>
|
||||
a.name.toLowerCase().includes(q) || a.pkgname.toLowerCase().includes(q),
|
||||
);
|
||||
}
|
||||
const originFilter = filterOrigin.value;
|
||||
const matched = props.apps.filter((a) => {
|
||||
if (originFilter === "apm" && !hasOrigin(a, "apm")) return false;
|
||||
if (originFilter === "spark" && !hasOrigin(a, "spark")) return false;
|
||||
if (q) {
|
||||
const nameLower = a.name.toLowerCase();
|
||||
const pkgLower = a.pkgname.toLowerCase();
|
||||
if (!nameLower.includes(q) && !pkgLower.includes(q)) return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
// 2. 再按来源筛选(默认 all = 不过滤)
|
||||
if (filterOrigin.value === "apm") {
|
||||
list = list.filter((a) => hasOrigin(a, "apm"));
|
||||
} else if (filterOrigin.value === "spark") {
|
||||
list = list.filter((a) => hasOrigin(a, "spark"));
|
||||
}
|
||||
|
||||
// 3. 排序:APM 应用始终排在前面(默认全部视图也遵守此规则)
|
||||
// 返回新数组,避免修改原始 props.apps
|
||||
return [...list].sort((a, b) => {
|
||||
// 返回新数组排序:APM 应用始终排在前面(默认全部视图也遵守此规则)
|
||||
return [...matched].sort((a, b) => {
|
||||
const aApm = hasOrigin(a, "apm") ? 0 : 1;
|
||||
const bApm = hasOrigin(b, "apm") ? 0 : 1;
|
||||
if (aApm !== bApm) return aApm - bApm;
|
||||
|
||||
Reference in New Issue
Block a user