From ae572315779c993f6f9bb8b92a98714f73c56021 Mon Sep 17 00:00:00 2001 From: lanyeeee Date: Sat, 9 Aug 2025 06:27:33 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=8E=A7=E5=88=B6=E5=92=8C=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E4=B8=8B=E8=BD=BD=E4=BB=BB=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components.d.ts | 3 + package.json | 1 + pnpm-lock.yaml | 10 + src-tauri/Cargo.lock | 132 ++++++++- src-tauri/Cargo.toml | 2 + src-tauri/capabilities/default.json | 3 +- src-tauri/src/lib.rs | 1 + src/panes/DownloadPane/DownloadPane.vue | 211 ++++++++++++++- .../components/CompletedProgresses.vue | 192 +++++++++++++ .../components/DownloadDirInput.vue | 46 ++++ .../components/DownloadProgress.vue | 237 ++++++++++++++++ .../components/UncompletedProgresses.vue | 255 ++++++++++++++++++ src/panes/SearchPane/SearchPane.vue | 2 +- src/store.ts | 54 +++- 14 files changed, 1142 insertions(+), 7 deletions(-) create mode 100644 src/panes/DownloadPane/components/CompletedProgresses.vue create mode 100644 src/panes/DownloadPane/components/DownloadDirInput.vue create mode 100644 src/panes/DownloadPane/components/DownloadProgress.vue create mode 100644 src/panes/DownloadPane/components/UncompletedProgresses.vue diff --git a/components.d.ts b/components.d.ts index 672a1e1..6e9eb42 100644 --- a/components.d.ts +++ b/components.d.ts @@ -11,6 +11,7 @@ declare module 'vue' { ColorfulTag: typeof import('./src/components/ColorfulTag.vue')['default'] FloatLabelInput: typeof import('./src/components/FloatLabelInput.vue')['default'] NA: typeof import('naive-ui')['NA'] + NBadge: typeof import('naive-ui')['NBadge'] NButton: typeof import('naive-ui')['NButton'] NCheckbox: typeof import('naive-ui')['NCheckbox'] NCollapseTransition: typeof import('naive-ui')['NCollapseTransition'] @@ -28,6 +29,8 @@ declare module 'vue' { NModal: typeof import('naive-ui')['NModal'] NModalProvider: typeof import('naive-ui')['NModalProvider'] NNotificationProvider: typeof import('naive-ui')['NNotificationProvider'] + NPagination: typeof import('naive-ui')['NPagination'] + NProgress: typeof import('naive-ui')['NProgress'] NQrCode: typeof import('naive-ui')['NQrCode'] NRadioButton: typeof import('naive-ui')['NRadioButton'] NRadioGroup: typeof import('naive-ui')['NRadioGroup'] diff --git a/package.json b/package.json index c4a513e..07f7ded 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "dependencies": { "@phosphor-icons/vue": "^2.2.1", "@tauri-apps/api": "^2", + "@tauri-apps/plugin-dialog": "^2.3.0", "@tauri-apps/plugin-opener": "^2", "@tauri-apps/plugin-os": "^2.3.0", "@viselect/vue": "^3.9.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 925e1da..043e0e4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,6 +14,9 @@ importers: '@tauri-apps/api': specifier: ^2 version: 2.6.0 + '@tauri-apps/plugin-dialog': + specifier: ^2.3.0 + version: 2.3.0 '@tauri-apps/plugin-opener': specifier: ^2 version: 2.4.0 @@ -711,6 +714,9 @@ packages: engines: {node: '>= 10'} hasBin: true + '@tauri-apps/plugin-dialog@2.3.0': + resolution: {integrity: sha512-ylSBvYYShpGlKKh732ZuaHyJ5Ie1JR71QCXewCtsRLqGdc8Is4xWdz6t43rzXyvkItM9syNPMvFVcvjgEy+/GA==} + '@tauri-apps/plugin-opener@2.4.0': resolution: {integrity: sha512-43VyN8JJtvKWJY72WI/KNZszTpDpzHULFxQs0CJBIYUdCRowQ6Q1feWTDb979N7nldqSuDOaBupZ6wz2nvuWwQ==} @@ -2546,6 +2552,10 @@ snapshots: '@tauri-apps/cli-win32-ia32-msvc': 2.6.2 '@tauri-apps/cli-win32-x64-msvc': 2.6.2 + '@tauri-apps/plugin-dialog@2.3.0': + dependencies: + '@tauri-apps/api': 2.6.0 + '@tauri-apps/plugin-opener@2.4.0': dependencies: '@tauri-apps/api': 2.6.0 diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index bacacc4..6fbbecf 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -68,6 +68,24 @@ version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" +[[package]] +name = "ashpd" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cbdf310d77fd3aaee6ea2093db7011dc2d35d2eb3481e5607f1f8d942ed99df" +dependencies = [ + "enumflags2", + "futures-channel", + "futures-util", + "rand 0.9.1", + "raw-window-handle", + "serde", + "serde_repr", + "tokio", + "url", + "zbus", +] + [[package]] name = "async-broadcast" version = "0.7.2" @@ -289,6 +307,7 @@ dependencies = [ "strfmt", "tauri", "tauri-build", + "tauri-plugin-dialog", "tauri-plugin-opener", "tauri-plugin-os", "tauri-specta", @@ -793,6 +812,18 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" +[[package]] +name = "dispatch2" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a0d569e003ff27784e0e14e4a594048698e0c0f0b66cabcb51511be55a7caa0" +dependencies = [ + "bitflags 2.9.1", + "block2 0.6.1", + "libc", + "objc2 0.6.1", +] + [[package]] name = "dispatch2" version = "0.3.0" @@ -2481,7 +2512,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166" dependencies = [ "bitflags 2.9.1", - "dispatch2", + "dispatch2 0.3.0", "objc2 0.6.1", ] @@ -2492,7 +2523,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "989c6c68c13021b5c2d6b71456ebb0f9dc78d752e86a98da7c716f4f9470f5a4" dependencies = [ "bitflags 2.9.1", - "dispatch2", + "dispatch2 0.3.0", "objc2 0.6.1", "objc2-core-foundation", "objc2-io-surface", @@ -3196,6 +3227,16 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "rand" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.3", +] + [[package]] name = "rand_chacha" version = "0.2.2" @@ -3216,6 +3257,16 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.3", +] + [[package]] name = "rand_core" version = "0.5.1" @@ -3234,6 +3285,15 @@ dependencies = [ "getrandom 0.2.16", ] +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.3", +] + [[package]] name = "rand_hc" version = "0.2.0" @@ -3421,6 +3481,31 @@ dependencies = [ "rand 0.8.5", ] +[[package]] +name = "rfd" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80c844748fdc82aae252ee4594a89b6e7ebef1063de7951545564cbc4e57075d" +dependencies = [ + "ashpd", + "block2 0.6.1", + "dispatch2 0.2.0", + "glib-sys", + "gobject-sys", + "gtk-sys", + "js-sys", + "log", + "objc2 0.6.1", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation 0.3.1", + "raw-window-handle", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows-sys 0.59.0", +] + [[package]] name = "rustc-demangle" version = "0.1.25" @@ -4257,6 +4342,46 @@ dependencies = [ "walkdir", ] +[[package]] +name = "tauri-plugin-dialog" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aefb14219b492afb30b12647b5b1247cadd2c0603467310c36e0f7ae1698c28" +dependencies = [ + "log", + "raw-window-handle", + "rfd", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "tauri-plugin-fs", + "thiserror 2.0.12", + "url", +] + +[[package]] +name = "tauri-plugin-fs" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c341290d31991dbca38b31d412c73dfbdb070bb11536784f19dd2211d13b778f" +dependencies = [ + "anyhow", + "dunce", + "glob", + "percent-encoding", + "schemars 0.8.22", + "serde", + "serde_json", + "serde_repr", + "tauri", + "tauri-plugin", + "tauri-utils", + "thiserror 2.0.12", + "toml", + "url", +] + [[package]] name = "tauri-plugin-opener" version = "2.4.0" @@ -4556,6 +4681,7 @@ dependencies = [ "slab", "socket2", "tokio-macros", + "tracing", "windows-sys 0.52.0", ] @@ -5796,6 +5922,7 @@ dependencies = [ "ordered-stream", "serde", "serde_repr", + "tokio", "tracing", "uds_windows", "windows-sys 0.59.0", @@ -5921,6 +6048,7 @@ dependencies = [ "endi", "enumflags2", "serde", + "url", "winnow 0.7.11", "zvariant_derive", "zvariant_utils", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 738ad44..9ebe500 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -21,6 +21,8 @@ tauri-build = { version = "2", features = [] } tauri = { version = "2", features = [] } tauri-plugin-opener = "2" tauri-plugin-os = "2" +tauri-plugin-dialog = "2" + serde = { version = "1", features = ["derive"] } serde_json = "1" diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json index 1b1b962..f6bd0e9 100644 --- a/src-tauri/capabilities/default.json +++ b/src-tauri/capabilities/default.json @@ -12,6 +12,7 @@ "core:window:allow-start-dragging", "core:window:allow-minimize", "core:window:allow-toggle-maximize", - "core:window:allow-close" + "core:window:allow-close", + "dialog:default" ] } \ No newline at end of file diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 2e15eeb..e7a291c 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -75,6 +75,7 @@ pub fn run() { .expect("Failed to export typescript bindings"); tauri::Builder::default() + .plugin(tauri_plugin_dialog::init()) .plugin(tauri_plugin_os::init()) .plugin(tauri_plugin_opener::init()) .invoke_handler(builder.invoke_handler()) diff --git a/src/panes/DownloadPane/DownloadPane.vue b/src/panes/DownloadPane/DownloadPane.vue index 98ce566..972eef6 100644 --- a/src/panes/DownloadPane/DownloadPane.vue +++ b/src/panes/DownloadPane/DownloadPane.vue @@ -1,3 +1,212 @@ + + + diff --git a/src/panes/DownloadPane/components/CompletedProgresses.vue b/src/panes/DownloadPane/components/CompletedProgresses.vue new file mode 100644 index 0000000..50066dc --- /dev/null +++ b/src/panes/DownloadPane/components/CompletedProgresses.vue @@ -0,0 +1,192 @@ + + + + + diff --git a/src/panes/DownloadPane/components/DownloadDirInput.vue b/src/panes/DownloadPane/components/DownloadDirInput.vue new file mode 100644 index 0000000..f67202a --- /dev/null +++ b/src/panes/DownloadPane/components/DownloadDirInput.vue @@ -0,0 +1,46 @@ + + + diff --git a/src/panes/DownloadPane/components/DownloadProgress.vue b/src/panes/DownloadPane/components/DownloadProgress.vue new file mode 100644 index 0000000..a0c90ce --- /dev/null +++ b/src/panes/DownloadPane/components/DownloadProgress.vue @@ -0,0 +1,237 @@ + + + diff --git a/src/panes/DownloadPane/components/UncompletedProgresses.vue b/src/panes/DownloadPane/components/UncompletedProgresses.vue new file mode 100644 index 0000000..6c62386 --- /dev/null +++ b/src/panes/DownloadPane/components/UncompletedProgresses.vue @@ -0,0 +1,255 @@ + + + + + diff --git a/src/panes/SearchPane/SearchPane.vue b/src/panes/SearchPane/SearchPane.vue index 03e1c9a..a2a82be 100644 --- a/src/panes/SearchPane/SearchPane.vue +++ b/src/panes/SearchPane/SearchPane.vue @@ -9,7 +9,7 @@ import NormalSinglePanel from './components/NormalSinglePanel.vue' import { extractBvid, extractAid } from '../../utils.tsx' import { useStore } from '../../store.ts' -export type SearchType = 'Auto' | 'Normal' +export type SearchType = 'Auto' | 'Normal' | 'Bangumi' | 'Cheese' const searchTypeOptions: SelectProps['options'] = [ { label: '自动', value: 'Auto' }, diff --git a/src/store.ts b/src/store.ts index d6f0bf7..e2ff68e 100644 --- a/src/store.ts +++ b/src/store.ts @@ -1,12 +1,62 @@ import { defineStore } from 'pinia' -import { ref } from 'vue' +import { ref, computed, readonly } from 'vue' import { Config, UserInfo } from './bindings.ts' import { CurrentNavName } from './AppContent.vue' +import { ProgressData } from './panes/DownloadPane/DownloadPane.vue' export const useStore = defineStore('store', () => { const config = ref() const userInfo = ref() const currentNavName = ref('search') + const downloadSpeed = ref('') - return { config, userInfo, currentNavName } + const { progresses, updateProgresses } = useProgresses() + const uncompletedProgressesCount = computed(() => { + return Array.from(progresses.value.values()).filter(({ state }) => state !== 'Completed').length + }) + + return { + config, + currentNavName, + userInfo, + progresses, + updateProgresses, + uncompletedProgressesCount, + downloadSpeed, + } }) + +function useProgresses() { + // 内部的高频更新状态 + const _progresses = new Map() + // 对外暴露的响应式状态 + const progresses = ref>(new Map()) + + // 用于确保在同一渲染帧内只安排一次UI更新 + let isUpdateScheduled = false + + // 将 `_progresses` 的内容更新到 `progresses` 中,并触发重新渲染 + const updateProgressesOnFrame = () => { + const newProgressesMap = new Map() + for (const [key, value] of _progresses.entries()) { + newProgressesMap.set(key, { ...value }) + } + progresses.value = newProgressesMap + + isUpdateScheduled = false + } + + const updateProgresses = (updateFn: (progresses: Map) => void) => { + // 使用传入的更新函数来修改 `_progresses` + updateFn(_progresses) + + if (!isUpdateScheduled) { + // 如果没有安排过UI更新,则安排一次 + isUpdateScheduled = true + // 使用 `requestAnimationFrame` 调度 UI 更新 + requestAnimationFrame(updateProgressesOnFrame) + } + } + + return { progresses: readonly(progresses), updateProgresses } +}