From bf93059da177c2403c2c6f5b31b8855220d032b2 Mon Sep 17 00:00:00 2001 From: Elysia Date: Mon, 26 Jan 2026 00:12:01 +0800 Subject: [PATCH] =?UTF-8?q?feat(install):=20=E5=AE=9E=E7=8E=B0=E5=AE=89?= =?UTF-8?q?=E8=A3=85=E7=AE=A1=E7=90=86=E5=99=A8=EF=BC=8C=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E5=AE=89=E8=A3=85=E3=80=81=E6=A3=80=E6=9F=A5=E5=B7=B2=E5=AE=89?= =?UTF-8?q?=E8=A3=85=E7=8A=B6=E6=80=81=E5=92=8C=E5=88=9D=E6=AD=A5=E5=8D=B8?= =?UTF-8?q?=E8=BD=BD=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...download-manager.ts => install-manager.ts} | 42 +++++++++++++++++-- electron/main/index.ts | 2 +- src/App.vue | 17 ++++++-- src/components/AppDetailModal.vue | 20 ++++++++- src/modeuls/processInstall.ts | 7 +++- 5 files changed, 77 insertions(+), 11 deletions(-) rename electron/main/backend/{download-manager.ts => install-manager.ts} (82%) diff --git a/electron/main/backend/download-manager.ts b/electron/main/backend/install-manager.ts similarity index 82% rename from electron/main/backend/download-manager.ts rename to electron/main/backend/install-manager.ts index 3badfe31..356b393f 100644 --- a/electron/main/backend/download-manager.ts +++ b/electron/main/backend/install-manager.ts @@ -6,7 +6,7 @@ import pino from 'pino'; const logger = pino({ 'name': 'download-manager' }); -type DownloadTask = { +type InstallTask = { id: number; execCommand: string; execParams: string[]; @@ -14,7 +14,7 @@ type DownloadTask = { webContents: WebContents | null; }; -const tasks = new Map(); +const tasks = new Map(); let idle = true; // Indicates if the installation manager is idle @@ -82,7 +82,7 @@ ipcMain.on('queue-install', async (event, download_json) => { } execParams.push('install', '-y', pkgname); - const task: DownloadTask = { + const task: InstallTask = { id, execCommand, execParams, @@ -166,4 +166,38 @@ function processNextInQueue(index: number) { if (tasks.size > 0) processNextInQueue(0); }); -} \ No newline at end of file +} + +ipcMain.handle('check-installed', async (_event, pkgname: string) => { + if (!pkgname) { + logger.warn('check-installed missing pkgname'); + return false; + } + let isInstalled = false; + + logger.info(`检查应用是否已安装: ${pkgname}`); + + let child = spawn('/usr/bin/apm', ['list', '--installed', pkgname], { + shell: true, + env: process.env + }); + + let output = ''; + + child.stdout.on('data', (data) => { + output += data.toString(); + }); + + await new Promise((resolve) => { + child.on('close', (code) => { + if (code === 0 && output.includes(pkgname)) { + isInstalled = true; + logger.info(`应用已安装: ${pkgname}`); + } else { + logger.info(`应用未安装: ${pkgname}`); + } + resolve(); + }); + }); + return isInstalled; +}); \ No newline at end of file diff --git a/electron/main/index.ts b/electron/main/index.ts index 0be3d0bb..b73d028d 100644 --- a/electron/main/index.ts +++ b/electron/main/index.ts @@ -10,7 +10,7 @@ if (!app.requestSingleInstanceLock()) { } import './handle-url-scheme.js' -import './backend/download-manager.js' +import './backend/install-manager.js' const require = createRequire(import.meta.url) const __dirname = path.dirname(fileURLToPath(import.meta.url)) diff --git a/src/App.vue b/src/App.vue index b64d1b81..66d1ac36 100644 --- a/src/App.vue +++ b/src/App.vue @@ -11,8 +11,8 @@ - + @@ -40,7 +40,7 @@ import DownloadQueue from './components/DownloadQueue.vue'; import DownloadDetail from './components/DownloadDetail.vue'; import { APM_STORE_ARCHITECTURE, APM_STORE_BASE_URL, currentApp } from './global/storeConfig'; import { downloads } from './global/downloadStatus'; -import { handleInstall, handleRetry } from './modeuls/processInstall'; +import { handleInstall, handleRetry, handleRemove } from './modeuls/processInstall'; const logger = pino(); @@ -60,6 +60,7 @@ const showModal = ref(false); const showPreview = ref(false); const currentScreenIndex = ref(0); const screenshots = ref([]); +const currentAppIsInstalled = ref(false); const loading = ref(true); const showDownloadDetailModal = ref(false); const currentDownload = ref(null); @@ -127,6 +128,10 @@ const openDetail = (app) => { loadScreenshots(app); showModal.value = true; + // 检测本地是否已经安装了该应用 + currentAppIsInstalled.value = false; + checkAppInstalled(app); + // 确保模态框显示后滚动到顶部 nextTick(() => { const modal = document.querySelector('.modal'); @@ -134,6 +139,12 @@ const openDetail = (app) => { }); }; +const checkAppInstalled = (app) => { + window.ipcRenderer.invoke('check-installed', app.Pkgname).then((isInstalled) => { + currentAppIsInstalled.value = isInstalled; + }); +}; + const loadScreenshots = (app) => { screenshots.value = []; for (let i = 1; i <= 5; i++) { diff --git a/src/components/AppDetailModal.vue b/src/components/AppDetailModal.vue index d36efdb4..98b66b9e 100644 --- a/src/components/AppDetailModal.vue +++ b/src/components/AppDetailModal.vue @@ -15,9 +15,12 @@ @@ -95,10 +98,14 @@ const props = defineProps({ screenshots: { type: Array, required: true + }, + isinstalled: { + type: Boolean, + required: true } }); -const emit = defineEmits(['close', 'install', 'open-preview']); +const emit = defineEmits(['close', 'install', 'remove', 'open-preview']); const iconPath = computed(() => { return props.app ? `${APM_STORE_BASE_URL}/${APM_STORE_ARCHITECTURE}/${props.app._category}/${props.app.Pkgname}/icon.png` : ''; @@ -112,6 +119,10 @@ const handleInstall = () => { emit('install'); }; +const handelRemove = () => { + emit('remove'); +}; + const openPreview = (index) => { emit('open-preview', index); }; @@ -119,4 +130,9 @@ const openPreview = (index) => { diff --git a/src/modeuls/processInstall.ts b/src/modeuls/processInstall.ts index adb16b78..d4fa4004 100644 --- a/src/modeuls/processInstall.ts +++ b/src/modeuls/processInstall.ts @@ -53,6 +53,11 @@ export const handleRetry = (download_: DownloadItem) => { window.ipcRenderer.send('queue-install', JSON.stringify(download_)); }; +export const handleRemove = (download_: DownloadItem) => { + if (!currentApp.value?.Pkgname) return; + console.log('请求卸载: ', currentApp.value.Pkgname); +} + window.ipcRenderer.on('install-status', (_event, log: InstallLog) => { const downloadObj: any = downloads.value.find(d => d.id === log.id); downloadObj.status = log.message; @@ -72,4 +77,4 @@ window.ipcRenderer.on('install-complete', (_event, log: DownloadResult) => { } else { downloadObj.status = 'failed'; } -}); \ No newline at end of file +});