import { defineStore } from 'pinia' import { invoke } from '@tauri-apps/api/core' import { listen } from '@tauri-apps/api/event' const sortByName = (a: any, b: any) => a.name.localeCompare(b.name, 'zh-CN', { sensitivity: 'accent' }); export interface LogEntry { id: string; // 日志唯一标识 timestamp: string; command: string; output: string; status: 'info' | 'success' | 'error'; } export const useSoftwareStore = defineStore('software', { state: () => ({ essentials: [] as any[], essentialsVersion: '', updates: [] as any[], allSoftware: [] as any[], selectedEssentialIds: [] as string[], selectedUpdateIds: [] as string[], logs: [] as LogEntry[], settings: { repo_url: 'https://karlblue.github.io/winget-repo' }, loading: false, isInitialized: false, initStatus: '正在检查系统环境...', lastFetched: 0 }), getters: { // ... (mergedEssentials, sortedUpdates, sortedAllSoftware, isBusy getters stay the same) mergedEssentials: (state) => { return state.essentials.map(item => { const installedInfo = state.allSoftware.find(s => s.id.toLowerCase() === item.id.toLowerCase()); const isInstalled = !!installedInfo; const hasUpdate = state.updates.some(s => s.id.toLowerCase() === item.id.toLowerCase()); let displayStatus = item.status; let actionLabel = '安装'; let currentVersion = item.version; // 默认使用清单中的推荐版本 if (isInstalled) { currentVersion = installedInfo.version; // 如果已安装,显示本地真实版本 if (hasUpdate) { actionLabel = '更新'; } else if (displayStatus === 'idle') { displayStatus = 'installed'; actionLabel = '已安装'; } } return { ...item, version: currentVersion, recommended_version: item.version, // 额外保存一个原始推荐版本字段供前端判断 status: displayStatus, actionLabel }; }); }, sortedUpdates: (state) => [...state.updates].sort(sortByName), sortedAllSoftware: (state) => [...state.allSoftware].sort(sortByName), isBusy: (state) => { const allItems = [...state.essentials, ...state.updates, ...state.allSoftware]; return allItems.some(item => item.status === 'pending' || item.status === 'installing'); } }, actions: { async initializeApp() { if (this.isInitialized) return; this.initStatus = '正在加载应用配置...'; try { this.settings = await invoke('get_settings'); this.initStatus = '正在同步 Winget 模块...'; await invoke('initialize_app'); this.isInitialized = true; } catch (err) { this.initStatus = '环境配置失败,请检查运行日志'; setTimeout(() => { this.isInitialized = true; }, 2000); } }, async saveSettings(newSettings: any) { await invoke('save_settings', { settings: newSettings }); this.settings = newSettings; }, async syncEssentials() { this.loading = true; try { await invoke('sync_essentials'); await this.fetchEssentials(); } finally { this.loading = false; } }, // ... (Selection methods stay the same) toggleSelection(id: string, type: 'essential' | 'update') { if (this.isBusy) return; const list = type === 'essential' ? this.selectedEssentialIds : this.selectedUpdateIds; const index = list.indexOf(id); if (index === -1) list.push(id); else list.splice(index, 1); }, selectAll(type: 'essential' | 'update') { if (type === 'essential') { const selectable = this.mergedEssentials.filter(s => s.status !== 'installed'); this.selectedEssentialIds = selectable.map(s => s.id); } else { this.selectedUpdateIds = this.updates.map(s => s.id); } }, deselectAll(type: 'essential' | 'update') { if (type === 'essential') this.selectedEssentialIds = []; else this.selectedUpdateIds = []; }, invertSelection(type: 'essential' | 'update') { if (type === 'essential') { const selectable = this.mergedEssentials.filter(s => s.status !== 'installed').map(s => s.id); this.selectedEssentialIds = selectable.filter(id => !this.selectedEssentialIds.includes(id)); } else { const selectable = this.updates.map(s => s.id); this.selectedUpdateIds = selectable.filter(id => !this.selectedUpdateIds.includes(id)); } }, async fetchEssentials() { let repo = await invoke('get_essentials') as any; // 如果本地没有文件,则尝试联网获取一次 if (!repo) { try { await invoke('sync_essentials'); repo = await invoke('get_essentials') as any; } catch (err) { console.error('Initial sync failed:', err); } } if (repo) { this.essentials = repo.essentials; this.essentialsVersion = repo.version; } else { this.essentials = []; this.essentialsVersion = ''; } }, async fetchUpdates() { if (this.isBusy) return; this.loading = true try { const res = await invoke('get_updates') this.updates = res as any[] if (this.selectedUpdateIds.length === 0) this.selectAll('update'); } finally { this.loading = false } }, async fetchAll() { if (this.isBusy) return; this.loading = true try { const res = await invoke('get_all_software') this.allSoftware = res as any[] } finally { this.loading = false } }, async syncDataIfNeeded(force = false) { if (this.isBusy) return; const now = Date.now(); const CACHE_TIMEOUT = 5 * 60 * 1000; if (!force && this.allSoftware.length > 0 && (now - this.lastFetched < CACHE_TIMEOUT)) { if (this.essentials.length === 0) await this.fetchEssentials(); return; } await this.fetchAllData(); }, async fetchAllData() { this.loading = true; try { // 先确保加载了必备清单(内部处理本地缺失逻辑) await this.fetchEssentials(); // 然后同步本地软件安装/更新状态,不再强制联网下载 JSON const [all, updates] = await Promise.all([ invoke('get_all_software'), invoke('get_updates') ]); this.allSoftware = all as any[]; this.updates = updates as any[]; this.lastFetched = Date.now(); if (this.selectedEssentialIds.length === 0) this.selectAll('essential'); } finally { this.loading = false; } }, async install(id: string) { const software = this.findSoftware(id) if (software) { software.status = 'pending'; await invoke('install_software', { id, version: software.version }) } }, findSoftware(id: string) { return this.essentials.find(s => s.id === id) || this.updates.find(s => s.id === id) || this.allSoftware.find(s => s.id === id) }, initListener() { if ((window as any).__tauri_listener_init) return; (window as any).__tauri_listener_init = true; listen('install-status', (event: any) => { const { id, status, progress } = event.payload const software = this.findSoftware(id) if (software) { software.status = status software.progress = progress } if (status === 'success') { this.lastFetched = 0; this.selectedEssentialIds = this.selectedEssentialIds.filter(i => i !== id); this.selectedUpdateIds = this.selectedUpdateIds.filter(i => i !== id); } }) // 日志监听:根据 ID 追加内容 listen('log-event', (event: any) => { const payload = event.payload as LogEntry; const existingLog = this.logs.find(l => l.id === payload.id); if (existingLog) { // 如果是增量更新 if (payload.output) { existingLog.output += '\n' + payload.output; } if (payload.status !== 'info') { existingLog.status = payload.status; } } else { // 如果是新日志 this.logs.unshift(payload); if (this.logs.length > 100) this.logs.pop(); } }) } } })