feat(install): 实现安装管理器,支持安装、检查已安装状态和初步卸载功能

This commit is contained in:
Elysia
2026-01-26 00:12:01 +08:00
parent bdf51a1037
commit bf93059da1
5 changed files with 77 additions and 11 deletions

View File

@@ -11,8 +11,8 @@
<AppGrid :apps="filteredApps" :loading="loading" @open-detail="openDetail" />
</main>
<AppDetailModal :show="showModal" :app="currentApp" :screenshots="screenshots" @close="closeDetail"
@install="handleInstall" @open-preview="openScreenPreview" />
<AppDetailModal :show="showModal" :app="currentApp" :screenshots="screenshots" :isinstalled="currentAppIsInstalled" @close="closeDetail"
@install="handleInstall" @remove="handleRemove" @open-preview="openScreenPreview" />
<ScreenPreview :show="showPreview" :screenshots="screenshots" :current-screen-index="currentScreenIndex"
@close="closeScreenPreview" @prev="prevScreen" @next="nextScreen" />
@@ -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++) {

View File

@@ -15,9 +15,12 @@
</div>
</div>
<div class="modal-actions">
<button class="install-btn" @click="handleInstall">
<button v-if="!isinstalled" class="install-btn" @click="handleInstall">
<i class="fas fa-download"></i> 安装
</button>
<button v-else class="install-btn remove" @click="handelRemove">
<i class="fas fa-trash"></i> 卸载
</button>
<button class="close-modal" @click="closeModal" aria-label="关闭">×</button>
</div>
</div>
@@ -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) => {
<style scoped>
/* 该组件样式已在全局样式中定义 */
.install-btn.remove {
border-color: rgb(231, 76, 60);
box-shadow: 0 4px 12px rgb(231, 76, 60);
background: linear-gradient(90deg, rgb(231, 76, 60), rgb(231, 76, 60));
}
</style>

View File

@@ -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';
}
});
});