From 8a5f8d154ff6ecd7485a327c7b9982054af8cf68 Mon Sep 17 00:00:00 2001 From: shenmo Date: Tue, 12 May 2026 21:54:47 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0APM=E5=AE=89=E8=A3=85?= =?UTF-8?q?=E7=A1=AE=E8=AE=A4=E5=BC=B9=E7=AA=97=E5=B9=B6=E9=87=8D=E6=9E=84?= =?UTF-8?q?APM=E6=A3=80=E6=9F=A5=E6=B5=81=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 新增全局状态控制APM安装弹窗显示 2. 新建ApmInstallConfirmModal弹窗组件 3. 将主进程的APM安装弹窗逻辑迁移到前端Vue组件 4. 更新package.json版本到5.1.0 5. 简化安装和升级流程中的APM检查逻辑 --- electron/main/backend/install-manager.ts | 131 +++------------------- package.json | 2 +- src/App.vue | 28 +++++ src/components/ApmInstallConfirmModal.vue | 81 +++++++++++++ src/global/storeConfig.ts | 1 + src/modules/processInstall.ts | 21 +--- 6 files changed, 133 insertions(+), 131 deletions(-) create mode 100644 src/components/ApmInstallConfirmModal.vue diff --git a/electron/main/backend/install-manager.ts b/electron/main/backend/install-manager.ts index a12351e6..a087bb03 100644 --- a/electron/main/backend/install-manager.ts +++ b/electron/main/backend/install-manager.ts @@ -1,4 +1,4 @@ -import { BrowserWindow, dialog, ipcMain, WebContents } from "electron"; +import { ipcMain, WebContents } from "electron"; import { spawn, ChildProcess, exec } from "node:child_process"; import { promisify } from "node:util"; import fs from "node:fs"; @@ -114,25 +114,6 @@ const checkSparkAvailable = async (): Promise => { return found; }; -/** 提权执行 shell-caller aptss install apm 安装 APM,安装后需用户重启电脑 */ -const runInstallApm = async (superUserCmd: string): Promise => { - const execCommand = superUserCmd || SHELL_CALLER_PATH; - const execParams = superUserCmd - ? [SHELL_CALLER_PATH, "aptss", "install", "apm"] - : [SHELL_CALLER_PATH, "aptss", "install", "apm"]; - logger.info(`执行安装 APM: ${execCommand} ${execParams.join(" ")}`); - const { code, stdout, stderr } = await runCommandCapture( - execCommand, - execParams, - ); - if (code !== 0) { - logger.error({ code, stdout, stderr }, "安装 APM 失败"); - return false; - } - logger.info("安装 APM 完成"); - return true; -}; - const parseUpgradableList = (output: string) => { const apps: Array<{ pkgname: string; @@ -215,61 +196,23 @@ ipcMain.on("queue-install", async (event, download_json) => { const execParams = []; const downloadDir = `/tmp/spark-store/download/${pkgname}`; - // APM 应用:若本机没有 apm 命令,弹窗提示并可选提权安装 APM(安装后需重启电脑) + // APM 应用:若本机没有 apm 命令,通知前端弹窗引导安装 APM if (origin === "apm") { const hasApm = await checkApmAvailable(); if (!hasApm) { - const win = BrowserWindow.fromWebContents(webContents); - const { response } = await dialog.showMessageBox(win ?? undefined, { - type: "question", - title: "需要安装 APM", - message: "此应用需要使用 APM 安装。", - detail: - "APM是星火应用商店的容器包管理器,安装APM后方可安装此应用,是否确认安装?", - buttons: ["确认", "取消"], - defaultId: 0, - cancelId: 1, + webContents.send("trigger-apm-install-dialog"); + webContents.send("install-complete", { + id, + success: false, + time: Date.now(), + exitCode: -1, + message: JSON.stringify({ + message: "未安装 APM,无法继续安装此应用", + stdout: "", + stderr: "", + }), }); - if (response !== 0) { - webContents.send("install-complete", { - id, - success: false, - time: Date.now(), - exitCode: -1, - message: JSON.stringify({ - message: "用户取消安装 APM,无法继续安装此应用", - stdout: "", - stderr: "", - }), - }); - return; - } - const installApmOk = await runInstallApm(superUserCmd); - if (!installApmOk) { - webContents.send("install-complete", { - id, - success: false, - time: Date.now(), - exitCode: -1, - message: JSON.stringify({ - message: "安装 APM 失败,请检查网络或权限后重试", - stdout: "", - stderr: "", - }), - }); - return; - } else { - // 安装APM成功,提示用户已安装成功,需要重启后方可展示应用 - await dialog.showMessageBox(win ?? undefined, { - type: "info", - title: "APM 安装成功", - message: "恭喜您,APM 已成功安装", - detail: - "恭喜您,APM 已成功安装!您的应用已在安装中~\n首次安装APM后,需要重启电脑后方可在启动器展示应用。您可在应用安装完毕后择机重启电脑\n若您需要立即使用应用,可在应用安装后先在应用商店中打开您的应用。", - buttons: ["确定"], - defaultId: 0, - }); - } + return; } } @@ -1089,51 +1032,11 @@ ipcMain.handle("check-spark-available", async () => { }); // 显示 APM 安装对话框(在点击安装按钮时提前检查) +// 前端已改为 Vue 弹窗,此后端处理仅作为兜底 ipcMain.handle("show-apm-install-dialog", async (event) => { const webContents = event.sender; - const win = BrowserWindow.fromWebContents(webContents); - const superUserCmd = await checkSuperUserCommand(); - - const { response } = await dialog.showMessageBox(win ?? undefined, { - type: "question", - title: "需要安装 APM", - message: "此应用需要使用 APM 安装。", - detail: - "APM 是星火应用商店的软件包兼容工具,此应用使用星火 APM 提供支持,安装APM后方可安装此应用,是否确认安装?", - buttons: ["确认", "取消"], - defaultId: 0, - cancelId: 1, - }); - - if (response !== 0) { - return { success: false, cancelled: true }; - } - - const installApmOk = await runInstallApm(superUserCmd); - if (!installApmOk) { - await dialog.showMessageBox(win ?? undefined, { - type: "error", - title: "安装失败", - message: "安装 APM 失败", - detail: "请检查网络或权限后重试", - buttons: ["确定"], - defaultId: 0, - }); - return { success: false, cancelled: false }; - } - - // 安装APM成功,提示用户已安装成功,需要重启后方可展示应用 - await dialog.showMessageBox(win ?? undefined, { - type: "info", - title: "APM 安装成功", - message: "恭喜您,APM 已成功安装", - detail: - "恭喜您,APM 已成功安装!\n首次安装APM后,需要重启电脑后方可使用全部功能。您可在应用安装完毕后择机重启电脑。", - buttons: ["确定"], - defaultId: 0, - }); - - return { success: true, cancelled: false }; + webContents.send("trigger-apm-install-dialog"); + return { success: false, cancelled: true }; }); // eslint-disable-next-line @typescript-eslint/no-explicit-any diff --git a/package.json b/package.json index b9ab7712..bb47f6be 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "spark-store", - "version": "5.0.1", + "version": "5.1.0", "main": "dist-electron/main/index.js", "description": "Client for Spark App Store", "author": "elysia-best ", diff --git a/src/App.vue b/src/App.vue index 879c2ea8..14286c2a 100644 --- a/src/App.vue +++ b/src/App.vue @@ -152,6 +152,12 @@ @success="onUninstallSuccess" /> + + @@ -173,6 +179,7 @@ import DownloadDetail from "./components/DownloadDetail.vue"; import InstalledAppsModal from "./components/InstalledAppsModal.vue"; import UpdateCenterModal from "./components/UpdateCenterModal.vue"; import UninstallConfirmModal from "./components/UninstallConfirmModal.vue"; +import ApmInstallConfirmModal from "./components/ApmInstallConfirmModal.vue"; import AboutModal from "./components/AboutModal.vue"; import SettingsModal from "./components/SettingsModal.vue"; import { @@ -181,6 +188,7 @@ import { currentAppSparkInstalled, currentAppApmInstalled, currentStoreMode, + showApmInstallDialog, getHybridDefaultOrigin, loadPriorityConfig, } from "./global/storeConfig"; @@ -964,6 +972,22 @@ const onUninstallSuccess = () => { } }; +const closeApmInstallDialog = () => { + showApmInstallDialog.value = false; +}; + +const confirmApmInstall = async () => { + showApmInstallDialog.value = false; + closeDetail(); + await nextTick(); + const apmApp = apps.value.find((a) => a.pkgname === "apm"); + if (apmApp) { + openDetail(apmApp); + } else { + searchQuery.value = "apm"; + } +}; + const installCompleteCallback = (pkgname?: string) => { if (currentApp.value && (!pkgname || currentApp.value.pkgname === pkgname)) { checkAppInstalled(currentApp.value); @@ -1276,6 +1300,10 @@ onMounted(async () => { } }); + window.ipcRenderer.on("trigger-apm-install-dialog", () => { + showApmInstallDialog.value = true; + }); + window.ipcRenderer.on( "deep-link-install", (_event: IpcRendererEvent, pkgname: string) => { diff --git a/src/components/ApmInstallConfirmModal.vue b/src/components/ApmInstallConfirmModal.vue new file mode 100644 index 00000000..aa327ea0 --- /dev/null +++ b/src/components/ApmInstallConfirmModal.vue @@ -0,0 +1,81 @@ + + + diff --git a/src/global/storeConfig.ts b/src/global/storeConfig.ts index 87bb0f87..8113a59f 100644 --- a/src/global/storeConfig.ts +++ b/src/global/storeConfig.ts @@ -11,6 +11,7 @@ export const APM_STORE_STATS_BASE_URL: string = export const currentApp = ref(null); export const currentAppSparkInstalled = ref(false); export const currentAppApmInstalled = ref(false); +export const showApmInstallDialog = ref(false); export const currentStoreMode = ref("hybrid"); diff --git a/src/modules/processInstall.ts b/src/modules/processInstall.ts index 350661e1..67c77c61 100644 --- a/src/modules/processInstall.ts +++ b/src/modules/processInstall.ts @@ -5,6 +5,7 @@ import { currentApp, currentAppSparkInstalled, currentAppApmInstalled, + showApmInstallDialog, } from "../global/storeConfig"; import { APM_STORE_BASE_URL } from "../global/storeConfig"; import { downloads, getNextDownloadId } from "../global/downloadStatus"; @@ -28,14 +29,8 @@ export const handleInstall = async (appObj?: App) => { if (targetApp.origin === "apm") { const hasApm = await window.ipcRenderer.invoke("check-apm-available"); if (!hasApm) { - // 发送事件到主进程显示 APM 安装对话框 - const { success, cancelled } = await window.ipcRenderer.invoke( - "show-apm-install-dialog", - ); - if (!success || cancelled) { - // 用户取消或未安装成功,不继续安装应用 - return; - } + showApmInstallDialog.value = true; + return; } } @@ -119,14 +114,8 @@ export const handleUpgrade = async (app: App) => { if (app.origin === "apm") { const hasApm = await window.ipcRenderer.invoke("check-apm-available"); if (!hasApm) { - // 发送事件到主进程显示 APM 安装对话框 - const { success, cancelled } = await window.ipcRenderer.invoke( - "show-apm-install-dialog", - ); - if (!success || cancelled) { - // 用户取消或未安装成功,不继续更新应用 - return; - } + showApmInstallDialog.value = true; + return; } }