import { defineStore } from 'pinia' import { ref } from 'vue' import { invoke } from '@tauri-apps/api/core' import { listen, type UnlistenFn } from '@tauri-apps/api/event' import { createLogger } from '@/lib/logger' const logger = createLogger('downloader') // ===== 与 Rust 端对应的数据结构(camelCase) ===== export type TaskStatus = 'queued' | 'active' | 'paused' | 'complete' | 'error' export interface Segment { index: number start: number end: number completed: number } export interface DownloadTask { id: string url: string filename: string dir: string status: TaskStatus totalSize: number completedSize: number speed: number supportsResume: boolean segments: Segment[] error: string | null createdAt: number headers: Record } export interface DownloaderSettings { downloadDir: string maxConcurrent: number maxConnections: number continueDownload: boolean globalSpeedLimit: number extensionPort: number extensionSecret: string deleteFilesOnRemove: boolean } export interface DownloaderStatus { running: boolean } export interface ExtensionInfo { url: string port: number secret: string hasSecret: boolean } /** 下载进度事件载荷 */ interface ProgressPayload { id: string completedSize: number totalSize: number speed: number status: TaskStatus } /** 下载完成事件载荷 */ interface CompletePayload { id: string filename: string status: TaskStatus error: string | null } export const useDownloaderStore = defineStore('downloader', () => { const tasks = ref([]) const status = ref({ running: false }) const settings = ref(null) const extensionInfo = ref(null) // 事件监听器 let progressUnlisten: UnlistenFn | null = null let completeUnlisten: UnlistenFn | null = null let addedUnlisten: UnlistenFn | null = null // ===== 任务列表 ===== const refreshTasks = async () => { try { tasks.value = await invoke('downloader_get_tasks') } catch (e) { logger.error('获取任务列表失败: ' + e) } return tasks.value } /** 根据进度事件更新单个任务(不触发完整刷新) */ const updateTaskProgress = (payload: ProgressPayload) => { const task = tasks.value.find((t) => t.id === payload.id) if (task) { task.completedSize = payload.completedSize task.totalSize = payload.totalSize task.speed = payload.speed task.status = payload.status } } /** 下载完成事件处理 */ const handleTaskComplete = async (_payload: CompletePayload) => { // 刷新任务列表以获取最终状态 await refreshTasks() } // ===== 任务操作 ===== const addTask = async ( url: string, filename?: string, dir?: string, headers?: Record ): Promise => { const id = await invoke('downloader_add_task', { url, filename: filename || null, dir: dir || null, headers: headers || null }) await refreshTasks() return id } const pauseTask = async (id: string) => { await invoke('downloader_pause_task', { id }) await refreshTasks() } const resumeTask = async (id: string) => { await invoke('downloader_resume_task', { id }) await refreshTasks() } const removeTask = async (id: string, deleteFiles = false) => { await invoke('downloader_remove_task', { id, deleteFiles }) await refreshTasks() } // ===== 设置 ===== const loadSettings = async () => { settings.value = await invoke('downloader_get_settings') return settings.value } const saveSettings = async (s: DownloaderSettings) => { await invoke('downloader_save_settings', { settings: s }) settings.value = s } // ===== 状态 ===== const refreshStatus = async () => { try { status.value = await invoke('downloader_status') } catch (e) { logger.error('获取状态失败: ' + e) } return status.value } // ===== 扩展信息 ===== const loadExtensionInfo = async () => { extensionInfo.value = await invoke('downloader_get_extension_info') return extensionInfo.value } // ===== 事件监听 ===== const startEventListeners = async () => { if (progressUnlisten && completeUnlisten && addedUnlisten) return if (!progressUnlisten) { progressUnlisten = await listen('download-progress', (e) => { updateTaskProgress(e.payload) }) } if (!completeUnlisten) { completeUnlisten = await listen('download-complete', (e) => { handleTaskComplete(e.payload) }) } if (!addedUnlisten) { addedUnlisten = await listen<{ id: string }>('download-added', () => { // 新任务加入(可能是扩展通过 HTTP API 添加的),刷新任务列表 refreshTasks() }) } } const stopEventListeners = () => { if (progressUnlisten) { progressUnlisten() progressUnlisten = null } if (completeUnlisten) { completeUnlisten() completeUnlisten = null } if (addedUnlisten) { addedUnlisten() addedUnlisten = null } } // ===== 初始化 ===== const init = async () => { await Promise.all([refreshStatus(), loadSettings(), loadExtensionInfo(), refreshTasks()]) await startEventListeners() } // ===== 工具函数 ===== const openDir = (path: string) => invoke('downloader_open_dir', { path }) return { // state tasks, status, settings, extensionInfo, // tasks refreshTasks, addTask, pauseTask, resumeTask, removeTask, // settings loadSettings, saveSettings, // status refreshStatus, // extension loadExtensionInfo, // events startEventListeners, stopEventListeners, // init init, // utils openDir } })