feat: enhance application type definitions and improve app management logic

This commit is contained in:
Elysia
2026-01-31 17:48:06 +08:00
parent 3221cb6d5e
commit 39e40ff946
9 changed files with 86 additions and 27 deletions

View File

@@ -4,6 +4,8 @@ import readline from 'node:readline';
import { promisify } from 'node:util';
import pino from 'pino';
import { InstalledAppInfo } from '../../typedefinition';
const logger = pino({ 'name': 'install-manager' });
type InstallTask = {
@@ -66,7 +68,7 @@ const runCommandCapture = async (execCommand: string, execParams: string[]) => {
};
const parseInstalledList = (output: string) => {
const apps: Array<{ pkgname: string; version: string; arch: string; flags: string; raw: string }> = [];
const apps: Array<InstalledAppInfo> = [];
const lines = output.split('\n');
for (const line of lines) {
const trimmed = line.trim();

View File

@@ -0,0 +1,7 @@
export interface InstalledAppInfo {
pkgname: string;
version: string;
arch: string;
flags: string;
raw: string;
}

View File

@@ -23,6 +23,7 @@
"scripts": {
"dev": "vite --mode debug | pino-pretty",
"build": "vue-tsc --noEmit && vite build --mode production && electron-builder --config electron-builder.yml",
"build:vite": "vue-tsc --noEmit && vite build --mode production",
"build:rpm": "vue-tsc --noEmit && vite build --mode production && electron-builder --config electron-builder.yml --linux rpm",
"build:deb": "vue-tsc --noEmit && vite build --mode production && electron-builder --config electron-builder.yml --linux deb",
"preview": "vite preview --mode debug"

View File

@@ -59,7 +59,7 @@ import UninstallConfirmModal from './components/UninstallConfirmModal.vue';
import { APM_STORE_BASE_URL, currentApp, currentAppIsInstalled } from './global/storeConfig';
import { downloads } from './global/downloadStatus';
import { handleInstall, handleRetry, handleUpgrade } from './modeuls/processInstall';
import type { App, AppJson, DownloadItem } from './global/typedefinition';
import type { App, AppJson, DownloadItem, UpdateAppItem, InstalledAppInfo } from './global/typedefinition';
import type { Ref } from 'vue';
const logger = pino();
@@ -267,7 +267,7 @@ const toggleAllUpgrades = () => {
}));
};
const upgradeSingleApp = (app: App) => {
const upgradeSingleApp = (app: UpdateAppItem) => {
if (!app?.pkgname) return;
const target = apps.value.find(a => a.pkgname === app.pkgname);
if (target) {
@@ -275,7 +275,25 @@ const upgradeSingleApp = (app: App) => {
} else {
// If we can't find it in the list (e.g. category not loaded?), use the info we have
// But handleUpgrade expects App. Let's try to construct minimal App
handleUpgrade(app);
let minimalApp: App = {
name: app.pkgname,
pkgname: app.pkgname,
version: app.newVersion || '',
category: 'unknown',
tags: '',
more: '',
filename: '',
torrent_address: '',
author: '',
contributor: '',
website: '',
update: '',
size: '',
img_urls: [],
icons: '',
currentStatus: 'installed'
}
handleUpgrade(minimalApp);
}
};
@@ -306,14 +324,38 @@ const refreshInstalledApps = async () => {
return;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
installedApps.value = (result.apps || []).map((app: any) => ({
...app,
// Normalize if Main process returns different casing
name: app.name || app.Name || app.pkgname,
pkgname: app.pkgname || app.Pkgname,
version: app.version || app.Version,
removing: false
}));
installedApps.value = []
for (const app of result.apps) {
let appInfo = apps.value.find(a => a.pkgname === app.pkgname);
if (appInfo) {
appInfo.flags = app.flags;
appInfo.arch = app.arch;
appInfo.currentStatus = 'installed';
} else {
// 如果在当前应用列表中找不到该应用,创建一个最小的 App 对象
appInfo = {
name: app.name || app.pkgname,
pkgname: app.pkgname,
version: app.version,
category: 'unknown',
tags: '',
more: '',
filename: '',
torrent_address: '',
author: '',
contributor: '',
website: '',
update: '',
size: '',
img_urls: [],
icons: '',
currentStatus: 'installed',
arch: app.arch,
flags: app.flags
};
}
installedApps.value.push(appInfo);
}
} catch (error: any) {
installedApps.value = [];
installedError.value = error?.message || '读取已安装应用失败';
@@ -322,16 +364,9 @@ const refreshInstalledApps = async () => {
}
};
const requestUninstall = (app: App | {pkgname: string, [key:string]: any}) => {
const requestUninstall = (app: App) => {
let target = null;
if (!('pkgname' in app) && 'Pkgname' in app) {
// @ts-ignore
target = apps.value.find(a => a.pkgname === app.Pkgname);
} else if (!app?.pkgname) {
// try to find by some other way or failed
} else {
target = apps.value.find(a => a.pkgname === app.pkgname) || app;
}
if (target) {
uninstallTargetApp.value = target as App;
@@ -553,6 +588,7 @@ const loadApps = async () => {
img_urls: typeof appJson.img_urls === 'string' ? JSON.parse(appJson.img_urls) : appJson.img_urls,
icons: appJson.icons,
category: category,
currentStatus: 'not-installed',
};
apps.value.push(normalizedApp);
});

View File

@@ -16,7 +16,7 @@
<script setup lang="ts">
import AppCard from './AppCard.vue';
import type { App } from '@/global/typedefinition';
import type { App } from '../global/typedefinition';
defineProps<{
apps: App[];

View File

@@ -61,9 +61,9 @@
</div>
<button type="button"
class="inline-flex items-center gap-2 rounded-2xl border border-rose-300/60 px-4 py-2 text-sm font-semibold text-rose-600 transition hover:bg-rose-50 disabled:opacity-50"
:disabled="app.removing" @click="$emit('uninstall', app)">
:disabled="app.currentStatus === 'not-installed'" @click="$emit('uninstall', app)">
<i class="fas fa-trash"></i>
{{ app.removing ? '卸载中…' : '卸载' }}
卸载
</button>
</div>
</div>
@@ -74,7 +74,7 @@
</template>
<script setup lang="ts">
import type { App } from '@/global/typedefinition';
import { App } from '../global/typedefinition';
defineProps<{
show: boolean;

View File

@@ -58,7 +58,7 @@
<script setup lang="ts">
import { computed, ref, watch, nextTick, onUnmounted } from 'vue';
import type { App } from '@/global/typedefinition';
import type { App } from '../global/typedefinition';
const props = defineProps<{
show: boolean;

View File

@@ -80,7 +80,7 @@
</template>
<script setup lang="ts">
import type { UpdateAppItem } from '@/global/typedefinition';
import type { UpdateAppItem } from '../global/typedefinition';
defineProps<{
show: boolean;

View File

@@ -86,6 +86,9 @@ export interface App {
icons: string;
category: string; // Frontend added
installed?: boolean; // Frontend state
flags?: string; // Tags in apm packages manager, e.g. "automatic" for dependencies
arch?: string; // Architecture, e.g. "amd64", "arm64"
currentStatus: 'not-installed' | 'installed'; // Current installation status
}
export interface UpdateAppItem {
@@ -95,3 +98,13 @@ export interface UpdateAppItem {
selected?: boolean;
upgrading?: boolean;
}
/**************Below are type from main process ********************/
export interface InstalledAppInfo {
pkgname: string;
version: string;
arch: string;
flags: string;
raw: string;
}