下载模块 Init
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -1,11 +1,27 @@
|
||||
import type { ModuleConfig } from '@/types/module'
|
||||
import type { SearchIndexItem } from '@/stores/searchIndex'
|
||||
import { invoke } from '@tauri-apps/api/core'
|
||||
|
||||
const searchItems: SearchIndexItem[] = [
|
||||
{
|
||||
title: '下载管理',
|
||||
description: '管理下载任务',
|
||||
keywords: ['下载', 'download', '文件', 'file']
|
||||
title: '下载任务',
|
||||
description: '查看与管理下载任务',
|
||||
keywords: ['下载', 'download', '任务', 'task', 'aria2']
|
||||
},
|
||||
{
|
||||
title: '添加下载',
|
||||
description: '添加 HTTP/HTTPS 直链下载',
|
||||
keywords: ['添加', '链接', 'url', 'add', '新建']
|
||||
},
|
||||
{
|
||||
title: '下载设置',
|
||||
description: '配置下载目录、速度限制与 RPC',
|
||||
keywords: ['设置', 'setting', 'rpc', '速度', '端口', '目录']
|
||||
},
|
||||
{
|
||||
title: '浏览器扩展',
|
||||
description: '安装 Thing Extension 接管浏览器下载',
|
||||
keywords: ['扩展', 'extension', '浏览器', 'chrome', 'edge']
|
||||
}
|
||||
]
|
||||
|
||||
@@ -13,18 +29,40 @@ export const moduleConfig: ModuleConfig = {
|
||||
id: 'downloader',
|
||||
name: '下载器',
|
||||
icon: 'downloader',
|
||||
description: 'HTTP下载、BT/磁力链接支持',
|
||||
description: '基于 aria2 的多线程 HTTP 下载管理',
|
||||
category: 'network',
|
||||
defaultEnabled: true,
|
||||
loader: () => import('./DownloaderModule.vue'),
|
||||
searchItems,
|
||||
// 进程由 Aria2Manager 通过 ProcessManager 统一管理(id='downloader'),
|
||||
// executable/args 在运行时由后端确定,此处仅声明 hasProcess 以便禁用时自动停止。
|
||||
process: {
|
||||
name: 'aria2c',
|
||||
executable: '',
|
||||
args: ['--enable-rpc', '--rpc-listen-port=6800'],
|
||||
autoStart: false,
|
||||
restartOnCrash: true,
|
||||
maxRestarts: 3
|
||||
},
|
||||
lifecycle: {
|
||||
// 启用模块时若用户开启了"自动启动",则随模块启用而运行 aria2
|
||||
onEnable: async () => {
|
||||
try {
|
||||
const s = await invoke<{ autoStart?: boolean }>('downloader_get_settings')
|
||||
if (s.autoStart) {
|
||||
await invoke('downloader_start')
|
||||
}
|
||||
} catch {
|
||||
/* 忽略:可能内核未安装 */
|
||||
}
|
||||
},
|
||||
// 禁用模块时停止 aria2 进程(cleanup_on_exit 会在应用退出时调用)
|
||||
onDisable: async () => {
|
||||
try {
|
||||
await invoke('downloader_stop')
|
||||
} catch {
|
||||
/* 忽略:可能进程未运行 */
|
||||
}
|
||||
}
|
||||
},
|
||||
order: 50
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import { invoke } from '@tauri-apps/api/core'
|
||||
import { appDataDir } from '@tauri-apps/api/path'
|
||||
import { revealItemInDir } from '@tauri-apps/plugin-opener'
|
||||
import { useProxyStore, type ProxyNode } from '@/stores/proxyStore'
|
||||
import { useModuleTabs } from '@/lib/useModuleTabs'
|
||||
import { createLogger } from '@/lib/logger'
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Button } from '@/components/ui/button'
|
||||
@@ -68,6 +69,13 @@ const onConfirmOpenChange = (open: boolean) => {
|
||||
}
|
||||
|
||||
const activeTab = ref('overview')
|
||||
// 浮动标签切换器:注册到 TitleBar,滚动遮挡时自动显示
|
||||
const tabsListRef = useModuleTabs(activeTab, [
|
||||
{ value: 'overview', label: '概览' },
|
||||
{ value: 'proxies', label: '节点' },
|
||||
{ value: 'profiles', label: '订阅' },
|
||||
{ value: 'settings', label: '设置' }
|
||||
])
|
||||
const starting = ref(false)
|
||||
const stopping = ref(false)
|
||||
const restarting = ref(false)
|
||||
@@ -613,10 +621,10 @@ const formatMB = (bytes: number) => `${(bytes / 1024 / 1024).toFixed(2)} MB`
|
||||
// 镜像源选择:'__direct' = GitHub 直连(value 不能用空串,reka-ui SelectItem 禁止空 value)
|
||||
// | 'ghproxy.net' 等预设 key | '__custom' = 自定义
|
||||
const MIRROR_PRESETS = [
|
||||
{ label: 'GitHub 直连', value: '__direct', hint: '需能访问 GitHub,速度最快' },
|
||||
{ label: 'GitHub 直连', value: '__direct', hint: '能访问 GitHub 时选择,最稳定' },
|
||||
{ label: 'gh-proxy.com', value: 'https://gh-proxy.com/', hint: '最推荐公益镜像' },
|
||||
{ label: 'ghproxy.net', value: 'https://ghproxy.net/', hint: '老牌公益镜像' },
|
||||
{ label: 'gh-proxy.com', value: 'https://gh-proxy.com/', hint: '公益镜像' },
|
||||
{ label: 'ghfast.top', value: 'https://ghfast.top/', hint: '较新镜像' },
|
||||
{ label: 'ghfast.top', value: 'https://ghfast.top/', hint: '较新镜像,备用' },
|
||||
{ label: '自定义', value: '__custom', hint: '手动输入镜像站前缀' },
|
||||
] as const
|
||||
|
||||
@@ -843,12 +851,14 @@ const saveSettingsForm = async () => {
|
||||
<template>
|
||||
<div class="h-full p-6">
|
||||
<Tabs v-model="activeTab" class="h-full flex flex-col">
|
||||
<TabsList class="grid w-full grid-cols-4 max-w-md !bg-transparent !p-0 !shadow-none">
|
||||
<TabsTrigger value="overview" class="gap-1.5"><Globe class="size-3.5" />概览</TabsTrigger>
|
||||
<TabsTrigger value="proxies" class="gap-1.5"><Server class="size-3.5" />节点</TabsTrigger>
|
||||
<TabsTrigger value="profiles" class="gap-1.5"><ListChecks class="size-3.5" />订阅</TabsTrigger>
|
||||
<TabsTrigger value="settings" class="gap-1.5"><SettingsIcon class="size-3.5" />设置</TabsTrigger>
|
||||
</TabsList>
|
||||
<div ref="tabsListRef">
|
||||
<TabsList class="grid w-full grid-cols-4 max-w-md !bg-transparent !p-0 !shadow-none">
|
||||
<TabsTrigger value="overview" class="gap-1.5"><Globe class="size-3.5" />概览</TabsTrigger>
|
||||
<TabsTrigger value="proxies" class="gap-1.5"><Server class="size-3.5" />节点</TabsTrigger>
|
||||
<TabsTrigger value="profiles" class="gap-1.5"><ListChecks class="size-3.5" />订阅</TabsTrigger>
|
||||
<TabsTrigger value="settings" class="gap-1.5"><SettingsIcon class="size-3.5" />设置</TabsTrigger>
|
||||
</TabsList>
|
||||
</div>
|
||||
|
||||
<!-- 概览 -->
|
||||
<TabsContent value="overview" class="flex-1 mt-4 tab-animate">
|
||||
@@ -873,16 +883,18 @@ const saveSettingsForm = async () => {
|
||||
<CardContent class="space-y-3 text-sm">
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="text-muted-foreground">状态</span>
|
||||
<span v-if="store.kernel?.exists" class="flex items-center gap-1 text-emerald-500">
|
||||
<Check class="size-3.5" />已安装
|
||||
</span>
|
||||
<span v-else class="flex items-center gap-1 text-red-500">
|
||||
<AlertCircle class="size-3.5" />未安装
|
||||
</span>
|
||||
<Badge v-if="store.kernel?.exists" variant="default" class="gap-1 bg-emerald-500 hover:bg-emerald-500">
|
||||
<Check class="size-3" />已安装
|
||||
</Badge>
|
||||
<Badge v-else variant="destructive" class="gap-1">
|
||||
<AlertCircle class="size-3" />未安装
|
||||
</Badge>
|
||||
</div>
|
||||
<div v-if="store.kernel?.exists" class="flex items-center justify-between">
|
||||
<span class="text-muted-foreground">当前版本</span>
|
||||
<span class="font-mono text-xs" :title="store.kernel?.version ?? ''">{{ versionShort }}</span>
|
||||
<Badge variant="secondary" class="font-mono text-xs" :title="store.kernel?.version ?? ''">
|
||||
{{ versionShort }}
|
||||
</Badge>
|
||||
</div>
|
||||
<div v-if="kernelUpdateInfo" class="flex items-center justify-between">
|
||||
<span class="text-muted-foreground">最新版本</span>
|
||||
@@ -1479,19 +1491,5 @@ const saveSettingsForm = async () => {
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/* Tab 内容切换动画 */
|
||||
.tab-animate {
|
||||
animation: tabFadeSlide 0.2s ease-out;
|
||||
}
|
||||
|
||||
@keyframes tabFadeSlide {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(4px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
/* Tab 内容切换动画已移至 src/style.css 全局 .tab-animate 类,所有模块共用 */
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user