mirror of
https://gitee.com/spark-store-project/spark-store
synced 2026-04-26 01:10:16 +08:00
feat: enhance application type definitions and improve app management logic
This commit is contained in:
@@ -4,6 +4,8 @@ import readline from 'node:readline';
|
|||||||
import { promisify } from 'node:util';
|
import { promisify } from 'node:util';
|
||||||
import pino from 'pino';
|
import pino from 'pino';
|
||||||
|
|
||||||
|
import { InstalledAppInfo } from '../../typedefinition';
|
||||||
|
|
||||||
const logger = pino({ 'name': 'install-manager' });
|
const logger = pino({ 'name': 'install-manager' });
|
||||||
|
|
||||||
type InstallTask = {
|
type InstallTask = {
|
||||||
@@ -66,7 +68,7 @@ const runCommandCapture = async (execCommand: string, execParams: string[]) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const parseInstalledList = (output: 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');
|
const lines = output.split('\n');
|
||||||
for (const line of lines) {
|
for (const line of lines) {
|
||||||
const trimmed = line.trim();
|
const trimmed = line.trim();
|
||||||
|
|||||||
7
electron/typedefinition.ts
Normal file
7
electron/typedefinition.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
export interface InstalledAppInfo {
|
||||||
|
pkgname: string;
|
||||||
|
version: string;
|
||||||
|
arch: string;
|
||||||
|
flags: string;
|
||||||
|
raw: string;
|
||||||
|
}
|
||||||
@@ -23,6 +23,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite --mode debug | pino-pretty",
|
"dev": "vite --mode debug | pino-pretty",
|
||||||
"build": "vue-tsc --noEmit && vite build --mode production && electron-builder --config electron-builder.yml",
|
"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: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",
|
"build:deb": "vue-tsc --noEmit && vite build --mode production && electron-builder --config electron-builder.yml --linux deb",
|
||||||
"preview": "vite preview --mode debug"
|
"preview": "vite preview --mode debug"
|
||||||
|
|||||||
76
src/App.vue
76
src/App.vue
@@ -59,7 +59,7 @@ import UninstallConfirmModal from './components/UninstallConfirmModal.vue';
|
|||||||
import { APM_STORE_BASE_URL, currentApp, currentAppIsInstalled } from './global/storeConfig';
|
import { APM_STORE_BASE_URL, currentApp, currentAppIsInstalled } from './global/storeConfig';
|
||||||
import { downloads } from './global/downloadStatus';
|
import { downloads } from './global/downloadStatus';
|
||||||
import { handleInstall, handleRetry, handleUpgrade } from './modeuls/processInstall';
|
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';
|
import type { Ref } from 'vue';
|
||||||
|
|
||||||
const logger = pino();
|
const logger = pino();
|
||||||
@@ -267,7 +267,7 @@ const toggleAllUpgrades = () => {
|
|||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
const upgradeSingleApp = (app: App) => {
|
const upgradeSingleApp = (app: UpdateAppItem) => {
|
||||||
if (!app?.pkgname) return;
|
if (!app?.pkgname) return;
|
||||||
const target = apps.value.find(a => a.pkgname === app.pkgname);
|
const target = apps.value.find(a => a.pkgname === app.pkgname);
|
||||||
if (target) {
|
if (target) {
|
||||||
@@ -275,7 +275,25 @@ const upgradeSingleApp = (app: App) => {
|
|||||||
} else {
|
} else {
|
||||||
// If we can't find it in the list (e.g. category not loaded?), use the info we have
|
// 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
|
// 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;
|
return;
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
installedApps.value = (result.apps || []).map((app: any) => ({
|
installedApps.value = []
|
||||||
...app,
|
for (const app of result.apps) {
|
||||||
// Normalize if Main process returns different casing
|
let appInfo = apps.value.find(a => a.pkgname === app.pkgname);
|
||||||
name: app.name || app.Name || app.pkgname,
|
if (appInfo) {
|
||||||
pkgname: app.pkgname || app.Pkgname,
|
appInfo.flags = app.flags;
|
||||||
version: app.version || app.Version,
|
appInfo.arch = app.arch;
|
||||||
removing: false
|
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) {
|
} catch (error: any) {
|
||||||
installedApps.value = [];
|
installedApps.value = [];
|
||||||
installedError.value = error?.message || '读取已安装应用失败';
|
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;
|
let target = null;
|
||||||
if (!('pkgname' in app) && 'Pkgname' in app) {
|
target = apps.value.find(a => a.pkgname === app.pkgname) || 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) {
|
if (target) {
|
||||||
uninstallTargetApp.value = target as App;
|
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,
|
img_urls: typeof appJson.img_urls === 'string' ? JSON.parse(appJson.img_urls) : appJson.img_urls,
|
||||||
icons: appJson.icons,
|
icons: appJson.icons,
|
||||||
category: category,
|
category: category,
|
||||||
|
currentStatus: 'not-installed',
|
||||||
};
|
};
|
||||||
apps.value.push(normalizedApp);
|
apps.value.push(normalizedApp);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import AppCard from './AppCard.vue';
|
import AppCard from './AppCard.vue';
|
||||||
import type { App } from '@/global/typedefinition';
|
import type { App } from '../global/typedefinition';
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
apps: App[];
|
apps: App[];
|
||||||
|
|||||||
@@ -61,9 +61,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<button type="button"
|
<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"
|
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>
|
<i class="fas fa-trash"></i>
|
||||||
{{ app.removing ? '卸载中…' : '卸载' }}
|
卸载
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -74,7 +74,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { App } from '@/global/typedefinition';
|
import { App } from '../global/typedefinition';
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
show: boolean;
|
show: boolean;
|
||||||
|
|||||||
@@ -58,7 +58,7 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref, watch, nextTick, onUnmounted } from 'vue';
|
import { computed, ref, watch, nextTick, onUnmounted } from 'vue';
|
||||||
import type { App } from '@/global/typedefinition';
|
import type { App } from '../global/typedefinition';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
show: boolean;
|
show: boolean;
|
||||||
|
|||||||
@@ -80,7 +80,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { UpdateAppItem } from '@/global/typedefinition';
|
import type { UpdateAppItem } from '../global/typedefinition';
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
show: boolean;
|
show: boolean;
|
||||||
|
|||||||
@@ -86,6 +86,9 @@ export interface App {
|
|||||||
icons: string;
|
icons: string;
|
||||||
category: string; // Frontend added
|
category: string; // Frontend added
|
||||||
installed?: boolean; // Frontend state
|
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 {
|
export interface UpdateAppItem {
|
||||||
@@ -95,3 +98,13 @@ export interface UpdateAppItem {
|
|||||||
selected?: boolean;
|
selected?: boolean;
|
||||||
upgrading?: boolean;
|
upgrading?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************Below are type from main process ********************/
|
||||||
|
export interface InstalledAppInfo {
|
||||||
|
pkgname: string;
|
||||||
|
version: string;
|
||||||
|
arch: string;
|
||||||
|
flags: string;
|
||||||
|
raw: string;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user