mirror of
https://github.com/VirtualHotBar/NetMount.git
synced 2026-06-06 12:49:37 +08:00
拉坨大的
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "netmount-gui",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"version": "0.1",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
1
src-tauri/.gitignore
vendored
1
src-tauri/.gitignore
vendored
@@ -1,3 +1,4 @@
|
||||
# Generated by Cargo
|
||||
# will have compiled files and executables
|
||||
/target/
|
||||
/res/
|
||||
581
src-tauri/Cargo.lock
generated
581
src-tauri/Cargo.lock
generated
@@ -66,10 +66,13 @@ checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247"
|
||||
name = "app"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"futures-util",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tauri",
|
||||
"tauri-build",
|
||||
"tokio",
|
||||
"window-shadows",
|
||||
]
|
||||
|
||||
@@ -654,7 +657,7 @@ dependencies = [
|
||||
"rustc_version",
|
||||
"toml 0.8.12",
|
||||
"vswhom",
|
||||
"winreg",
|
||||
"winreg 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -845,6 +848,12 @@ dependencies = [
|
||||
"syn 2.0.53",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5"
|
||||
|
||||
[[package]]
|
||||
name = "futures-task"
|
||||
version = "0.3.30"
|
||||
@@ -858,8 +867,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"futures-macro",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"memchr",
|
||||
"pin-project-lite",
|
||||
"pin-utils",
|
||||
"slab",
|
||||
@@ -1171,6 +1183,25 @@ dependencies = [
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "h2"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"fnv",
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"futures-util",
|
||||
"http",
|
||||
"indexmap 2.2.5",
|
||||
"slab",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.12.3"
|
||||
@@ -1241,12 +1272,72 @@ dependencies = [
|
||||
"itoa 1.0.10",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http-body"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"http",
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http-range"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21dec9db110f5f872ed9699c3ecf50cf16f423502706ba5c72462e28d3157573"
|
||||
|
||||
[[package]]
|
||||
name = "httparse"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904"
|
||||
|
||||
[[package]]
|
||||
name = "httpdate"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
|
||||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "0.14.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"h2",
|
||||
"http",
|
||||
"http-body",
|
||||
"httparse",
|
||||
"httpdate",
|
||||
"itoa 1.0.10",
|
||||
"pin-project-lite",
|
||||
"socket2",
|
||||
"tokio",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
"want",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-tls"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"hyper",
|
||||
"native-tls",
|
||||
"tokio",
|
||||
"tokio-native-tls",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone"
|
||||
version = "0.1.60"
|
||||
@@ -1364,6 +1455,12 @@ dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ipnet"
|
||||
version = "2.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3"
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "0.4.8"
|
||||
@@ -1615,6 +1712,12 @@ dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mime"
|
||||
version = "0.3.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.7.2"
|
||||
@@ -1625,6 +1728,35 @@ dependencies = [
|
||||
"simd-adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "0.8.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "native-tls"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"log",
|
||||
"openssl",
|
||||
"openssl-probe",
|
||||
"openssl-sys",
|
||||
"schannel",
|
||||
"security-framework",
|
||||
"security-framework-sys",
|
||||
"tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ndk"
|
||||
version = "0.6.0"
|
||||
@@ -1764,6 +1896,81 @@ version = "1.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||
|
||||
[[package]]
|
||||
name = "open"
|
||||
version = "3.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2078c0039e6a54a0c42c28faa984e115fb4c2d5bf2208f77d1961002df8576f8"
|
||||
dependencies = [
|
||||
"pathdiff",
|
||||
"windows-sys 0.42.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl"
|
||||
version = "0.10.64"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f"
|
||||
dependencies = [
|
||||
"bitflags 2.5.0",
|
||||
"cfg-if",
|
||||
"foreign-types 0.3.2",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"openssl-macros",
|
||||
"openssl-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-macros"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.53",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-probe"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.102"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "os_info"
|
||||
version = "3.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6cbb46d5d01695d7a1fb8be5f0d1968bd2b2b8ba1d1b3e7062ce2a0593e57af1"
|
||||
dependencies = [
|
||||
"log",
|
||||
"serde",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "os_pipe"
|
||||
version = "1.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57119c3b893986491ec9aa85056780d3a0f3cf4da7cc09dd3650dbd6c6738fb9"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "overload"
|
||||
version = "0.1.1"
|
||||
@@ -1818,6 +2025,12 @@ dependencies = [
|
||||
"windows-targets 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pathdiff"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd"
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.3.1"
|
||||
@@ -2239,6 +2452,48 @@ version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
|
||||
|
||||
[[package]]
|
||||
name = "reqwest"
|
||||
version = "0.11.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62"
|
||||
dependencies = [
|
||||
"base64 0.21.7",
|
||||
"bytes",
|
||||
"encoding_rs",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"h2",
|
||||
"http",
|
||||
"http-body",
|
||||
"hyper",
|
||||
"hyper-tls",
|
||||
"ipnet",
|
||||
"js-sys",
|
||||
"log",
|
||||
"mime",
|
||||
"native-tls",
|
||||
"once_cell",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"rustls-pemfile",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_urlencoded",
|
||||
"sync_wrapper",
|
||||
"system-configuration",
|
||||
"tokio",
|
||||
"tokio-native-tls",
|
||||
"tokio-util",
|
||||
"tower-service",
|
||||
"url",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"wasm-streams",
|
||||
"web-sys",
|
||||
"winreg 0.50.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.23"
|
||||
@@ -2267,6 +2522,15 @@ dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-pemfile"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c"
|
||||
dependencies = [
|
||||
"base64 0.21.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.14"
|
||||
@@ -2294,6 +2558,15 @@ dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "schannel"
|
||||
version = "0.1.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534"
|
||||
dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scoped-tls"
|
||||
version = "1.0.1"
|
||||
@@ -2306,6 +2579,29 @@ version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "security-framework"
|
||||
version = "2.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "770452e37cad93e0a50d5abc3990d2bc351c36d0328f86cefec2f2fb206eaef6"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"core-foundation",
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
"security-framework-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "security-framework-sys"
|
||||
version = "2.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41f3cc463c0ef97e11c3461a9d3787412d30e8e7eb907c79180c4a57bf7c04ef"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "selectors"
|
||||
version = "0.22.0"
|
||||
@@ -2387,6 +2683,18 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_urlencoded"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
|
||||
dependencies = [
|
||||
"form_urlencoded",
|
||||
"itoa 1.0.10",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_with"
|
||||
version = "3.7.0"
|
||||
@@ -2469,6 +2777,25 @@ dependencies = [
|
||||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shared_child"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b0d94659ad3c2137fef23ae75b03d5241d633f8acded53d672decfa0e6e0caef"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "simd-adler32"
|
||||
version = "0.3.7"
|
||||
@@ -2496,6 +2823,16 @@ version = "1.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7"
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.5.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "soup2"
|
||||
version = "0.2.1"
|
||||
@@ -2593,6 +2930,46 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sync_wrapper"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
|
||||
|
||||
[[package]]
|
||||
name = "sys-locale"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8a11bd9c338fdba09f7881ab41551932ad42e405f61d01e8406baea71c07aee"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"libc",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
"windows-sys 0.45.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "system-configuration"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"core-foundation",
|
||||
"system-configuration-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "system-configuration-sys"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "system-deps"
|
||||
version = "5.0.0"
|
||||
@@ -2718,15 +3095,21 @@ dependencies = [
|
||||
"ignore",
|
||||
"objc",
|
||||
"once_cell",
|
||||
"open",
|
||||
"os_info",
|
||||
"os_pipe",
|
||||
"percent-encoding",
|
||||
"rand 0.8.5",
|
||||
"raw-window-handle",
|
||||
"regex",
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_repr",
|
||||
"serialize-to-javascript",
|
||||
"shared_child",
|
||||
"state",
|
||||
"sys-locale",
|
||||
"tar",
|
||||
"tauri-macros",
|
||||
"tauri-runtime",
|
||||
@@ -2775,6 +3158,7 @@ dependencies = [
|
||||
"png",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"regex",
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -2994,8 +3378,50 @@ checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"bytes",
|
||||
"libc",
|
||||
"mio",
|
||||
"num_cpus",
|
||||
"parking_lot",
|
||||
"pin-project-lite",
|
||||
"signal-hook-registry",
|
||||
"socket2",
|
||||
"tokio-macros",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-macros"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.53",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-native-tls"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2"
|
||||
dependencies = [
|
||||
"native-tls",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-util"
|
||||
version = "0.7.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3066,6 +3492,12 @@ dependencies = [
|
||||
"winnow 0.6.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower-service"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52"
|
||||
|
||||
[[package]]
|
||||
name = "tracing"
|
||||
version = "0.1.40"
|
||||
@@ -3136,6 +3568,12 @@ dependencies = [
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "try-lock"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.17.0"
|
||||
@@ -3202,6 +3640,12 @@ version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
|
||||
|
||||
[[package]]
|
||||
name = "vcpkg"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
||||
|
||||
[[package]]
|
||||
name = "version-compare"
|
||||
version = "0.0.11"
|
||||
@@ -3250,6 +3694,15 @@ dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "want"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e"
|
||||
dependencies = [
|
||||
"try-lock",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.9.0+wasi-snapshot-preview1"
|
||||
@@ -3287,6 +3740,18 @@ dependencies = [
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-futures"
|
||||
version = "0.4.42"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.92"
|
||||
@@ -3316,6 +3781,29 @@ version = "0.2.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-streams"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129"
|
||||
dependencies = [
|
||||
"futures-util",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webkit2gtk"
|
||||
version = "0.18.2"
|
||||
@@ -3502,6 +3990,30 @@ version = "0.39.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ee5e275231f07c6e240d14f34e1b635bf1faa1c76c57cfd59a5cdb9848e4278"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.42.2",
|
||||
"windows_aarch64_msvc 0.42.2",
|
||||
"windows_i686_gnu 0.42.2",
|
||||
"windows_i686_msvc 0.42.2",
|
||||
"windows_x86_64_gnu 0.42.2",
|
||||
"windows_x86_64_gnullvm 0.42.2",
|
||||
"windows_x86_64_msvc 0.42.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.45.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
|
||||
dependencies = [
|
||||
"windows-targets 0.42.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.48.0"
|
||||
@@ -3520,6 +4032,21 @@ dependencies = [
|
||||
"windows-targets 0.52.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.42.2",
|
||||
"windows_aarch64_msvc 0.42.2",
|
||||
"windows_i686_gnu 0.42.2",
|
||||
"windows_i686_msvc 0.42.2",
|
||||
"windows_x86_64_gnu 0.42.2",
|
||||
"windows_x86_64_gnullvm 0.42.2",
|
||||
"windows_x86_64_msvc 0.42.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.48.5"
|
||||
@@ -3565,6 +4092,12 @@ dependencies = [
|
||||
"windows-targets 0.52.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.48.5"
|
||||
@@ -3583,6 +4116,12 @@ version = "0.39.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec7711666096bd4096ffa835238905bb33fb87267910e154b18b44eaabb340f2"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.48.5"
|
||||
@@ -3601,6 +4140,12 @@ version = "0.39.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "763fc57100a5f7042e3057e7e8d9bdd7860d330070251a73d003563a3bb49e1b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.48.5"
|
||||
@@ -3619,6 +4164,12 @@ version = "0.39.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7bc7cbfe58828921e10a9f446fcaaf649204dcfe6c1ddd712c5eebae6bda1106"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.48.5"
|
||||
@@ -3637,6 +4188,12 @@ version = "0.39.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6868c165637d653ae1e8dc4d82c25d4f97dd6605eaa8d784b5c6e0ab2a252b65"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.48.5"
|
||||
@@ -3649,6 +4206,12 @@ version = "0.52.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.48.5"
|
||||
@@ -3667,6 +4230,12 @@ version = "0.39.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e4d40883ae9cae962787ca76ba76390ffa29214667a111db9e0a1ad8377e809"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.48.5"
|
||||
@@ -3697,6 +4266,16 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winreg"
|
||||
version = "0.50.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winreg"
|
||||
version = "0.52.0"
|
||||
|
||||
@@ -17,9 +17,13 @@ tauri-build = { version = "1.5.1", features = [] }
|
||||
[dependencies]
|
||||
serde_json = "1.0"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
tauri = { version = "1.6.1", features = [ "process-all", "system-tray", "window-all"] }
|
||||
tauri = { version = "1.6.1", features = [ "shell-open", "fs-all", "os-all", "shell-execute", "process-all", "system-tray", "window-all"] }
|
||||
window-shadows = "0.2.2"
|
||||
|
||||
reqwest = { version = "0.11", features = ["json", "stream"] }
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
futures-util = "0.3"
|
||||
|
||||
[features]
|
||||
# this feature is used for production builds or when `devPath` points to the filesystem and the built-in dev server is disabled.
|
||||
# If you use cargo directly instead of tauri's cli you can use this feature flag to switch between tauri's `dev` and `build` modes.
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -1,64 +0,0 @@
|
||||
{
|
||||
"mount": {
|
||||
"lists": [
|
||||
{
|
||||
"storageName": "Webdav",
|
||||
"mountPath": "Z:",
|
||||
"parameters": {
|
||||
"mountOpt": {
|
||||
"VolumeName": "",
|
||||
"AllowNonEmpty": false,
|
||||
"AllowOther": false,
|
||||
"AllowRoot": false,
|
||||
"AsyncRead": true,
|
||||
"AttrTimeout": 1000000000,
|
||||
"Daemon": false,
|
||||
"DaemonTimeout": 0,
|
||||
"DebugFUSE": false,
|
||||
"DefaultPermissions": false,
|
||||
"ExtraFlags": [],
|
||||
"ExtraOptions": [],
|
||||
"MaxReadAhead": 131072,
|
||||
"NoAppleDouble": true,
|
||||
"NoAppleXattr": false,
|
||||
"WritebackCache": false,
|
||||
"DaemonWait": 0,
|
||||
"DeviceName": "",
|
||||
"NetworkMode": false
|
||||
},
|
||||
"vfsOpt": {
|
||||
"ReadOnly": false,
|
||||
"CacheMaxAge": 3600000000000,
|
||||
"CacheMaxSize": -1,
|
||||
"CacheMode": "minimal",
|
||||
"CachePollInterval": 60000000000,
|
||||
"CaseInsensitive": false,
|
||||
"ChunkSize": 134217728,
|
||||
"ChunkSizeLimit": -1,
|
||||
"DirCacheTime": 300000000000,
|
||||
"DirPerms": 511,
|
||||
"FilePerms": 438,
|
||||
"NoChecksum": false,
|
||||
"NoModTime": false,
|
||||
"NoSeek": false,
|
||||
"PollInterval": 60000000000,
|
||||
"ReadAhead": 0,
|
||||
"ReadWait": 20000000,
|
||||
"WriteBack": 5000000000,
|
||||
"WriteWait": 1000000000,
|
||||
"Refresh": false,
|
||||
"BlockNormDupes": false,
|
||||
"UsedIsSize": false,
|
||||
"FastFingerprint": false,
|
||||
"DiskSpaceTotalSize": -1,
|
||||
"UID": 4294967295,
|
||||
"GID": 4294967295,
|
||||
"Umask": 0
|
||||
}
|
||||
},
|
||||
"autoMount": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"task": {}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
rclone.exe rcd --rc-addr=:5572 --rc-user= --rc-pass= --rc-allow-origin=* --rc-no-auth
|
||||
@@ -3,21 +3,18 @@
|
||||
|
||||
use serde_json::{to_string_pretty, Value};
|
||||
use std::env;
|
||||
use std::error::Error;
|
||||
use std::fs;
|
||||
use std::process::Command;
|
||||
use tauri::Manager;
|
||||
|
||||
mod tray;
|
||||
mod utils;
|
||||
use crate::utils::set_window_shadow;
|
||||
use crate::utils::download_with_progress;
|
||||
use crate::utils::find_first_available_drive_letter;
|
||||
use crate::utils::set_window_shadow;
|
||||
|
||||
const CONFIG_PATH: &str = "config.json";
|
||||
|
||||
const CONFIG_PATH: &str = "res/config.json";
|
||||
|
||||
fn main() {
|
||||
|
||||
tauri::Builder::default()
|
||||
.setup(|app| {
|
||||
set_window_shadow(app);
|
||||
@@ -28,13 +25,17 @@ fn main() {
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
read_config_file,
|
||||
write_config_file,
|
||||
start_rclone,
|
||||
download_file,
|
||||
get_available_drive_letter
|
||||
])
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
}
|
||||
|
||||
/*
|
||||
use std::error::Error;
|
||||
use std::process::Command;
|
||||
|
||||
fn run_command(cmd: &str) -> Result<std::process::Child, Box<dyn Error>> {
|
||||
let cmd_str = if cfg!(target_os = "windows") {
|
||||
format!("{}", cmd.replace("/", "\\"))
|
||||
@@ -49,18 +50,20 @@ fn run_command(cmd: &str) -> Result<std::process::Child, Box<dyn Error>> {
|
||||
};
|
||||
|
||||
Ok(child)
|
||||
}
|
||||
} */
|
||||
|
||||
#[tauri::command]
|
||||
fn start_rclone(parameter: String) -> Result<(), String> {
|
||||
match run_command(&("res/bin/rclone.exe".to_owned() + ¶meter)) {
|
||||
Ok(child) => {
|
||||
println!("rclone.exe started with PID: {}", child.id());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Err(error) => Err(format!("Failed to start rclone: {}", error)),
|
||||
}
|
||||
fn download_file(url: String, out_path: String) -> Result<bool, usize> {
|
||||
download_with_progress(&url, &out_path, |total_size, downloaded| {
|
||||
println!(
|
||||
"下载进度: {}/{} {}%",
|
||||
total_size,
|
||||
downloaded,
|
||||
(100 * downloaded / total_size)
|
||||
);
|
||||
})
|
||||
.expect("下载失败");
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
@@ -68,7 +71,7 @@ fn get_available_drive_letter() -> Result<String, String> {
|
||||
match find_first_available_drive_letter() {
|
||||
Ok(Some(drive)) => Ok(drive),
|
||||
Ok(None) => Ok(String::from("")),
|
||||
Err(e) => Ok(format!("{}", e))
|
||||
Err(e) => Ok(format!("{}", e)),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
use tauri::{Manager, Runtime};
|
||||
|
||||
use window_shadows::set_shadow;
|
||||
|
||||
use std::fs;
|
||||
use std::io;
|
||||
use std::io::{self, Write};
|
||||
|
||||
pub fn set_window_shadow<R: Runtime>(app: &tauri::App<R>) {
|
||||
let window = app.get_window("main").unwrap();
|
||||
@@ -21,3 +22,35 @@ pub fn find_first_available_drive_letter() -> Result<Option<String>, io::Error>
|
||||
// 如果所有盘符都被占用,返回None
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
use reqwest::Client;
|
||||
use std::fs::File;
|
||||
use futures_util::stream::StreamExt; // 此处使用futures_util
|
||||
|
||||
#[tokio::main]
|
||||
pub async fn download_with_progress<F>(url: &str, output_path: &str, mut callback: F) -> io::Result<()>
|
||||
where
|
||||
F: FnMut(usize,usize),
|
||||
{
|
||||
let response = Client::new().get(url).send().await.map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?;
|
||||
|
||||
let total_size = response.content_length().unwrap_or(1) as usize;
|
||||
|
||||
if response.status().is_success() {
|
||||
let mut file = File::create(output_path)?;
|
||||
let mut downloaded: usize = 0;
|
||||
|
||||
let mut stream = response.bytes_stream();
|
||||
|
||||
while let Some(item) = stream.next().await {
|
||||
let chunk = item.map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?;
|
||||
file.write_all(&chunk)?;
|
||||
downloaded += chunk.len();
|
||||
callback(total_size,downloaded);
|
||||
}
|
||||
} else {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "请求失败"));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -8,11 +8,11 @@
|
||||
},
|
||||
"package": {
|
||||
"productName": "NetMount",
|
||||
"version": "0.1.0"
|
||||
"version": "0.1.0-240410"
|
||||
},
|
||||
"tauri": {
|
||||
"systemTray": {
|
||||
"iconPath": "icons/icon.png",
|
||||
"iconPath": "icons/icon.png",
|
||||
"iconAsTemplate": true
|
||||
},
|
||||
"allowlist": {
|
||||
@@ -22,9 +22,45 @@
|
||||
},
|
||||
"process": {
|
||||
"all": true
|
||||
},
|
||||
"fs": {
|
||||
"all": true,
|
||||
"scope": [
|
||||
"res/*"
|
||||
]
|
||||
},
|
||||
"os": {
|
||||
"all": true
|
||||
},
|
||||
"shell": {
|
||||
"all": false,
|
||||
"execute": true,
|
||||
"open": true,
|
||||
"scope": [
|
||||
{
|
||||
"name": "ria2c",
|
||||
"cmd": "res/bin/aria2c",
|
||||
"args": true
|
||||
},
|
||||
{
|
||||
"name": "rclone",
|
||||
"cmd": "res/bin/rclone",
|
||||
"args": true
|
||||
},
|
||||
{
|
||||
"name": "msiexec",
|
||||
"cmd": "msiexec.exe",
|
||||
"args": true
|
||||
},
|
||||
{
|
||||
"name": "curl",
|
||||
"cmd": "curl",
|
||||
"args": true
|
||||
}
|
||||
],
|
||||
"sidecar": false
|
||||
}
|
||||
},
|
||||
|
||||
"bundle": {
|
||||
"active": true,
|
||||
"category": "DeveloperTool",
|
||||
@@ -56,7 +92,6 @@
|
||||
"certificateThumbprint": null,
|
||||
"digestAlgorithm": "sha256",
|
||||
"timestampUrl": ""
|
||||
|
||||
}
|
||||
},
|
||||
"security": {
|
||||
@@ -74,7 +109,7 @@
|
||||
"width": 800,
|
||||
"minHeight": 450,
|
||||
"minWidth": 700,
|
||||
"transparent":true,
|
||||
"transparent": true,
|
||||
"decorations": false
|
||||
}
|
||||
]
|
||||
|
||||
13
src/app.tsx
13
src/app.tsx
@@ -18,6 +18,7 @@ import AddMount_page from './page/mount/add';
|
||||
import { IconClose, IconMinus } from '@arco-design/web-react/icon';
|
||||
import { windowsHide, windowsMini } from './controller/window';
|
||||
import { rcloneInfo } from './services/rclone';
|
||||
import { AddTask_page } from './page/task/add';
|
||||
|
||||
const { Item: MenuItem, SubMenu } = Menu;
|
||||
const { Sider, Header, Content, Footer } = Layout;
|
||||
@@ -50,7 +51,7 @@ function mapMenuItem(routes: Routers[]): JSX.Element {
|
||||
if (item.hide) {
|
||||
return <></>
|
||||
} else if (item.children && item.children.length > 0 && !item.hideChildren) {
|
||||
return (<SubMenu key={item.path} title={item.title} > {mapMenuItem(item.children)}</SubMenu>)
|
||||
return (<SubMenu key={item.path} title={item.title} >{mapMenuItem(item.children)}</SubMenu>)
|
||||
} else {
|
||||
return (<MenuItem key={item.path} > {item.title}</MenuItem>)
|
||||
}
|
||||
@@ -181,6 +182,15 @@ function App() {
|
||||
title: t('task'),
|
||||
path: '/task',
|
||||
component: <Task_page />,
|
||||
hideChildren: true,
|
||||
children: [
|
||||
{
|
||||
title: t('add'),
|
||||
path: '/task/add',
|
||||
key: '/task',//因为父菜单隐藏了子菜单项,在此页面时设置父菜单key以选择父菜单项
|
||||
component: <AddTask_page />,
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: t('setting'),
|
||||
@@ -241,6 +251,7 @@ function App() {
|
||||
style={{ height: '100%' }}
|
||||
onClickMenuItem={(path) => {
|
||||
if (path != location.pathname) {
|
||||
location.pathname.includes('add')&&Message.warning(t('prompt_for_leaving_the_add_or_edit_page'))
|
||||
navigate(path)
|
||||
}
|
||||
}}
|
||||
|
||||
BIN
src/assets/font/HarmonyOS_Sans_Bold.ttf
Normal file
BIN
src/assets/font/HarmonyOS_Sans_Bold.ttf
Normal file
Binary file not shown.
BIN
src/assets/font/HarmonyOS_Sans_Regular.ttf
Normal file
BIN
src/assets/font/HarmonyOS_Sans_Regular.ttf
Normal file
Binary file not shown.
BIN
src/assets/font/seguiemj.woff2
Normal file
BIN
src/assets/font/seguiemj.woff2
Normal file
Binary file not shown.
@@ -7,14 +7,19 @@ import { reupMount } from "./storage/mount/mount"
|
||||
import { reupStorage } from "./storage/storage"
|
||||
import { listenWindow } from "./window"
|
||||
import { NMConfig } from "../type/config"
|
||||
import { randomString } from "../utils/rclone/utils"
|
||||
import { randomString } from "../utils/utils"
|
||||
import { t } from "i18next"
|
||||
import { startRclone, stopRclone } from "../utils/rclone/process"
|
||||
import { getOsInfo } from "../utils/tauri/osInfo"
|
||||
import { startTaskScheduler } from "./task/task"
|
||||
|
||||
async function init(setStartStr: Function) {
|
||||
setStartStr(t('init'))
|
||||
|
||||
listenWindow()
|
||||
|
||||
await getOsInfo()
|
||||
|
||||
setStartStr(t('read_config'))
|
||||
|
||||
await invoke('read_config_file').then(configData => {
|
||||
@@ -23,32 +28,26 @@ async function init(setStartStr: Function) {
|
||||
console.log(err);
|
||||
})
|
||||
|
||||
/* const darkThemeMq = window.matchMedia("(prefers-color-scheme: dark)");
|
||||
|
||||
darkThemeMq.addListener(e => {
|
||||
if (e.matches) {
|
||||
document.body.setAttribute('arco-theme', 'dark');
|
||||
} else {
|
||||
document.body.removeAttribute('arco-theme');
|
||||
}
|
||||
}); */
|
||||
await startRclone()
|
||||
/* const darkThemeMq = window.matchMedia("(prefers-color-scheme: dark)");
|
||||
|
||||
darkThemeMq.addListener(e => {
|
||||
if (e.matches) {
|
||||
document.body.setAttribute('arco-theme', 'dark');
|
||||
} else {
|
||||
document.body.removeAttribute('arco-theme');
|
||||
}
|
||||
}); */
|
||||
document.body.removeAttribute('arco-theme');
|
||||
//rcloneInfo.endpoint.auth.user = randomString(32)
|
||||
//rcloneInfo.endpoint.auth.pass = randomString(128)
|
||||
|
||||
await invoke('start_rclone', {
|
||||
parameter: ' rcd --rc-addr=:' + rcloneInfo.endpoint.localhost.port.toString()
|
||||
+ ' --rc-user=' + rcloneInfo.endpoint.auth.user
|
||||
+ ' --rc-pass=' + rcloneInfo.endpoint.auth.pass
|
||||
+ ' --rc-allow-origin=* --rc-no-auth'//--rc-no-auth
|
||||
})
|
||||
rcloneInfo.endpoint.url = 'http://localhost:' + rcloneInfo.endpoint.localhost.port.toString()
|
||||
|
||||
startUpdateCont()
|
||||
await reupRcloneVersion()
|
||||
await reupStorage()
|
||||
await reupMount()
|
||||
|
||||
//开始任务队列
|
||||
await startTaskScheduler()
|
||||
}
|
||||
|
||||
async function reupRcloneVersion() {
|
||||
@@ -64,10 +63,7 @@ function main() {
|
||||
}
|
||||
|
||||
async function exit() {
|
||||
await rclone_api_post(
|
||||
'/core/quit',
|
||||
)
|
||||
|
||||
await stopRclone()
|
||||
await invoke('write_config_file', {
|
||||
configData: nmConfig
|
||||
});
|
||||
|
||||
@@ -4,13 +4,26 @@ import { RcloneStats } from "../../type/rclone/stats";
|
||||
import { rclone_api_post } from "../../utils/rclone/request";
|
||||
|
||||
async function reupStats() {
|
||||
const stats:RcloneStats = await rclone_api_post(
|
||||
const stats: RcloneStats = await rclone_api_post(
|
||||
'/core/stats',
|
||||
)
|
||||
rcloneInfo.stats = stats
|
||||
|
||||
let realSpeed: number = 0
|
||||
|
||||
if (stats.transferring && stats.transferring.length > 0) {
|
||||
stats.transferring.forEach(item => {
|
||||
realSpeed += item.speed
|
||||
})
|
||||
}
|
||||
|
||||
rcloneInfo.stats = {
|
||||
...stats,
|
||||
realSpeed: realSpeed
|
||||
}
|
||||
|
||||
//历史状态
|
||||
rcloneStatsHistory.push(stats)
|
||||
|
||||
|
||||
if (rcloneStatsHistory.length > 32) {
|
||||
rcloneStatsHistory.splice(0, rcloneStatsHistory.length - 32);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
import { DefaultParams } from "../../type/rclone/storage/defaults";
|
||||
import { rclone_api_post } from "../../utils/rclone/request";
|
||||
import { isEmptyObject } from "../../utils/rclone/utils";
|
||||
import { isEmptyObject } from "../../utils/utils";
|
||||
import { reupStorage } from "./storage";
|
||||
|
||||
|
||||
|
||||
@@ -114,6 +114,7 @@ function formatPathRclone(path: string, isDir?: boolean): string {
|
||||
}
|
||||
}
|
||||
|
||||
path = path.replace(/\/+/g, '/');
|
||||
return path;
|
||||
}
|
||||
|
||||
@@ -128,13 +129,14 @@ async function copyFile(storageName: string, path: string, destStoragename: stri
|
||||
}, true)
|
||||
}
|
||||
|
||||
async function moveFile(storageName: string, path: string, destStoragename: string, destPath: string) {
|
||||
async function moveFile(storageName: string, path: string, destStoragename: string, destPath: string, newNmae?: string) {
|
||||
|
||||
const backData = await rclone_api_post(
|
||||
'/operations/movefile', {
|
||||
srcFs: storageName + ':',
|
||||
srcRemote: formatPathRclone(path),
|
||||
dstFs: destStoragename + ':',
|
||||
dstRemote: formatPathRclone(destPath, true) + getFileName(path)
|
||||
dstRemote: formatPathRclone(destPath, true) + (newNmae ? newNmae : getFileName(path))
|
||||
}, true)
|
||||
}
|
||||
|
||||
@@ -152,11 +154,11 @@ async function copyDir(storageName: string, path: string, destStoragename: strin
|
||||
}, true)
|
||||
}
|
||||
|
||||
async function moveDir(storageName: string, path: string, destStoragename: string, destPath: string) {
|
||||
async function moveDir(storageName: string, path: string, destStoragename: string, destPath: string, newNmae?: string) {
|
||||
const backData = await rclone_api_post(
|
||||
'/sync/move', {
|
||||
srcFs: storageName + ':' + formatPathRclone(path, true),
|
||||
dstFs: destStoragename + ':' + formatPathRclone(destPath, true) + getFileName(path)
|
||||
dstFs: destStoragename + ':' + formatPathRclone(destPath, true) + (newNmae ? newNmae : getFileName(path))
|
||||
}, true)
|
||||
}
|
||||
|
||||
|
||||
29
src/controller/task/runner.ts
Normal file
29
src/controller/task/runner.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { TaskListItem } from "../../type/config";
|
||||
|
||||
async function runTask(task:TaskListItem ) :Promise<TaskListItem>{
|
||||
|
||||
const executeTask = (task: TaskListItem) => {
|
||||
console.log(`Executing ${task.taskType} task: ${task.name}`);
|
||||
// 实际的任务逻辑,可能包括复制、移动、删除文件等操作
|
||||
console.log(`${task.name} task completed.`);
|
||||
|
||||
//一次性任务,执行完毕后禁用
|
||||
if(task.run.mode==='disposable'){
|
||||
task.enable=false;
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
if (task.enable) {
|
||||
executeTask(task);
|
||||
task.runInfo = { ...task.runInfo, error: false, mag: 'Task executed successfully.' };
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Error executing task ${task.name}:`, error);
|
||||
task.runInfo = { ...task.runInfo, error: true, mag: error instanceof Error ? error.message : String(error) };
|
||||
}
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
export{runTask}
|
||||
76
src/controller/task/scheduler.ts
Normal file
76
src/controller/task/scheduler.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
import { TaskListItem } from "../../type/config";
|
||||
import { runTask } from "./runner";
|
||||
import { delTask } from "./task";
|
||||
|
||||
class TaskScheduler {
|
||||
tasks: TaskListItem[];
|
||||
|
||||
constructor() {
|
||||
this.tasks = [];
|
||||
}
|
||||
|
||||
public async addTask(task: TaskListItem) {
|
||||
if (task.enable) {
|
||||
this.tasks.push(task);
|
||||
this.scheduleTask(task);
|
||||
}
|
||||
}
|
||||
|
||||
private async scheduleTask(task: TaskListItem) {
|
||||
switch (task.run.mode) {
|
||||
case 'start':
|
||||
await this.executeTask(task);
|
||||
this.cancelTask(task.name);
|
||||
break;
|
||||
case 'disposable':
|
||||
await this.executeTask(task);
|
||||
this.cancelTask(task.name);
|
||||
delTask(task.name);
|
||||
break;
|
||||
case 'time':
|
||||
const executeTaskInterval = () => {
|
||||
const now = new Date();
|
||||
const scheduledTime = new Date(now);
|
||||
scheduledTime.setDate(scheduledTime.getDate() + task.run.time.intervalDays);
|
||||
scheduledTime.setHours(task.run.time.h, task.run.time.m, task.run.time.s);
|
||||
|
||||
// 如果设置的时间比现在的时间早,则表示下一次执行是明天
|
||||
if (scheduledTime < now) {
|
||||
scheduledTime.setDate(scheduledTime.getDate() + 1);
|
||||
}
|
||||
|
||||
const timeout = scheduledTime.getTime() - now.getTime();
|
||||
if (timeout >= 0) {
|
||||
task.run.runId = window.setTimeout(async () => {
|
||||
await this.executeTask(task);
|
||||
// 完成执行后,重新计划下一次执行
|
||||
executeTaskInterval();
|
||||
}, timeout);
|
||||
}
|
||||
};
|
||||
executeTaskInterval();
|
||||
break;
|
||||
case 'interval':
|
||||
task.run.runId = window.setInterval(async () => await this.executeTask(task), task.run.interval);
|
||||
break;
|
||||
default:
|
||||
console.error('Invalid task mode:', task.run.mode);
|
||||
}
|
||||
}
|
||||
|
||||
public async executeTask(task: TaskListItem) {
|
||||
const updatedTask = await runTask(task)
|
||||
this.tasks = this.tasks.map(t => t.name === updatedTask.name ? updatedTask : t);
|
||||
}
|
||||
|
||||
cancelTask(taskName: string) {
|
||||
const task = this.tasks.find(t => t.name === taskName);
|
||||
if (task && task.run.runId !== undefined) {
|
||||
window.clearInterval(task.run.runId);
|
||||
window.clearTimeout(task.run.runId);
|
||||
console.log(`${taskName} task cancelled.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export { TaskScheduler }
|
||||
33
src/controller/task/task.ts
Normal file
33
src/controller/task/task.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { nmConfig } from "../../services/config";
|
||||
import { TaskListItem } from "../../type/config";
|
||||
import { TaskScheduler } from "./scheduler";
|
||||
|
||||
const taskScheduler = new TaskScheduler()
|
||||
|
||||
function saveTask(taskInfo: TaskListItem) {
|
||||
const existingTaskIndex = nmConfig.task.findIndex(
|
||||
(task) => task.name === taskInfo.name
|
||||
);
|
||||
|
||||
if (existingTaskIndex !== -1) {
|
||||
// 存在同名任务,更新已有任务
|
||||
nmConfig.task[existingTaskIndex] = taskInfo;
|
||||
} else {
|
||||
// 不存在同名任务,直接添加新任务
|
||||
nmConfig.task.push(taskInfo);
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
function delTask(taskName: string) {
|
||||
nmConfig.task = nmConfig.task.filter((task) => task.name !== taskName);
|
||||
return true;
|
||||
}
|
||||
|
||||
async function startTaskScheduler() {
|
||||
for (let task of nmConfig.task) {
|
||||
await taskScheduler.addTask(task)
|
||||
}
|
||||
}
|
||||
|
||||
export { saveTask, delTask, taskScheduler, startTaskScheduler }
|
||||
@@ -5,8 +5,9 @@ import { invoke } from '@tauri-apps/api';
|
||||
|
||||
import { appWindow } from "@tauri-apps/api/window";
|
||||
import { app } from "@tauri-apps/api";
|
||||
import { nmConfig } from "../services/config";
|
||||
|
||||
import { nmConfig, osInfo } from "../services/config";
|
||||
import { Aria2 } from "../utils/aria2/aria2";
|
||||
import { checkUpdate } from "./update/update";
|
||||
|
||||
export async function Test() {
|
||||
|
||||
@@ -36,4 +37,25 @@ export async function Test() {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* const aria2Test = new Aria2('https://down.hotpe.top/d/Package/HotPE-V2.7.240201.exe',
|
||||
'F:/',
|
||||
'test1.7z',
|
||||
8, (back) => {
|
||||
console.log(back);
|
||||
})
|
||||
|
||||
|
||||
|
||||
aria2Test.start() */
|
||||
//console.log(await runCmd('curl', [url,'-o', path]));
|
||||
|
||||
console.log(osInfo);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
23
src/controller/update/update.ts
Normal file
23
src/controller/update/update.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { fs } from "@tauri-apps/api";
|
||||
import { downloadFile, takeRightStr } from "../../utils/utils";
|
||||
import { ResItem, ResList } from "../../type/controller/update";
|
||||
import { nmConfig, osInfo } from "../../services/config";
|
||||
import { getVersion } from "@tauri-apps/api/app";
|
||||
import { Modal } from "@arco-design/web-react";
|
||||
|
||||
|
||||
async function checkUpdate(updateCall: (resList: ResItem,localUpdateId: number) => void) {
|
||||
const localUpdateId = await getUpdateId()
|
||||
|
||||
const resList: ResItem = (await (await fetch(nmConfig.api.url + '/GetUpdate/?arch=' + osInfo.arch + '&osType=' + osInfo.osType)).json()).data
|
||||
|
||||
if (resList.id && Number(resList.id) < localUpdateId) {
|
||||
updateCall(resList,localUpdateId)
|
||||
}
|
||||
}
|
||||
|
||||
async function getUpdateId() {
|
||||
return Number(takeRightStr(await getVersion(), '-'))
|
||||
}
|
||||
|
||||
export { checkUpdate }
|
||||
@@ -7,19 +7,26 @@ function listenWindow() {
|
||||
windowsHide()
|
||||
})
|
||||
|
||||
// 阻止F5或Ctrl+R(Windows/Linux)和Command+R(Mac)刷新页面
|
||||
document.addEventListener('keydown', function (event) {
|
||||
// 阻止F5或Ctrl+R(Windows/Linux)和Command+R(Mac)刷新页面
|
||||
if (event.key === 'F5' || (event.ctrlKey && event.key === 'r') || (event.metaKey && event.key === 'r')) {
|
||||
event.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
//禁止右键
|
||||
document.oncontextmenu = () => {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
function windowsHide(){
|
||||
function windowsHide() {
|
||||
appWindow.hide()
|
||||
}
|
||||
|
||||
function windowsMini(){
|
||||
function windowsMini() {
|
||||
appWindow.minimize()
|
||||
}
|
||||
|
||||
@@ -27,4 +34,5 @@ listen('exit_app', async () => {
|
||||
await exit()
|
||||
});
|
||||
|
||||
export { listenWindow,windowsHide,windowsMini }
|
||||
export { listenWindow, windowsHide, windowsMini }
|
||||
|
||||
|
||||
@@ -6,6 +6,24 @@ p {
|
||||
color: var(--color-text-1);
|
||||
}
|
||||
|
||||
|
||||
/* 字体 */
|
||||
@font-face {
|
||||
font-family: "Inter";
|
||||
src: url("./assets/font/HarmonyOS_Sans_Regular.ttf") format("truetype");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Inter-Bold";
|
||||
src: url("./assets/font/HarmonyOS_Sans_Bold.ttf") format("truetype");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'emoji';
|
||||
src: url('./assets/font/seguiemj.woff2') format('woff2');
|
||||
}
|
||||
|
||||
|
||||
/* 美化滚动条 */
|
||||
::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
|
||||
@@ -1,23 +1,128 @@
|
||||
import React, { useEffect, useReducer, useState } from 'react'
|
||||
|
||||
import { Button } from "@arco-design/web-react"
|
||||
import { Button, Card, Descriptions, Grid, Modal, Space, Typography } from "@arco-design/web-react"
|
||||
import { Test } from "../../controller/test"
|
||||
import { rcloneInfo } from '../../services/rclone'
|
||||
import { hooks } from '../../services/hook';
|
||||
import { checkUpdate } from '../../controller/update/update';
|
||||
import { getVersion } from '@tauri-apps/api/app';
|
||||
import { shell } from '@tauri-apps/api';
|
||||
import { formatETA, formatSize } from '../../utils/utils';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { nmConfig } from '../../services/config';
|
||||
const Row = Grid.Row;
|
||||
const Col = Grid.Col;
|
||||
|
||||
let checkedUpdate: boolean = false;
|
||||
|
||||
function Home_page() {
|
||||
const { t } = useTranslation()
|
||||
const [ignored, forceUpdate] = useReducer(x => x + 1, 0);//刷新组件
|
||||
|
||||
const [modal, contextHolder] = Modal.useModal();
|
||||
|
||||
useEffect(() => {
|
||||
hooks.upStats = forceUpdate;
|
||||
|
||||
if (!checkedUpdate) {
|
||||
checkUpdate(async (info) => {
|
||||
modal.confirm!({
|
||||
title: '发现新版本',
|
||||
content: <>
|
||||
{`当前版本为${await getVersion()},最新版本为${info.name}`}
|
||||
<br />
|
||||
是否前往官网获取最新版?
|
||||
</>,
|
||||
onOk: () => {
|
||||
shell.open(info.website!)
|
||||
},
|
||||
})
|
||||
})
|
||||
checkedUpdate = true;
|
||||
}
|
||||
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div>
|
||||
{contextHolder}
|
||||
<Space direction='vertical' style={{ width: '100%' }}>
|
||||
<h2 style={{ fontSize: '1.5rem', fontWeight: 'bold' }}>欢迎使用,统一管理和挂载云存储设施。</h2>
|
||||
{/*<Row >
|
||||
<Col flex={'auto'}style={{ paddingLeft: '0rem', paddingRight: '0rem' }} >
|
||||
<Card style={{padding:'1.5rem',textAlign:'center'}} bordered={false}>
|
||||
<span style={{fontSize:'4.5rem',fontFamily:'emoji'}}>🧐</span>
|
||||
<p style={{fontSize:'1rem',fontWeight:'bold'}}>初次使用,请点击下方按钮进行配置</p>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row> */}
|
||||
<Card>
|
||||
状态概览
|
||||
<br />
|
||||
运行时间:{formatETA(rcloneInfo.stats.elapsedTime)}
|
||||
</Card>
|
||||
<Card>
|
||||
存储和挂载概览
|
||||
<br />
|
||||
存储数:{rcloneInfo.storageList.length}
|
||||
<br />
|
||||
挂载数:{nmConfig.mount.lists.length}
|
||||
<br />
|
||||
已挂载:{rcloneInfo.mountList.length}
|
||||
</Card>
|
||||
<Card>
|
||||
传输概览
|
||||
<Descriptions colon=' :' data={[
|
||||
{
|
||||
label:t('speed'),
|
||||
value: `${formatSize(rcloneInfo.stats.realSpeed!)}/s`
|
||||
},
|
||||
|
||||
{
|
||||
label: t('size'),
|
||||
value: `${formatSize(rcloneInfo.stats.bytes)}/${formatSize(rcloneInfo.stats.totalBytes)}`
|
||||
},
|
||||
|
||||
...(rcloneInfo.stats.transferTime > 0 ? [
|
||||
{
|
||||
label:t('used_time'),
|
||||
value: formatETA(rcloneInfo.stats.transferTime)
|
||||
}
|
||||
] : []),
|
||||
...(Number(rcloneInfo.stats.eta) > 0 ? [
|
||||
{
|
||||
label: t('eta'),
|
||||
value: formatETA(rcloneInfo.stats.eta!)
|
||||
}
|
||||
] : []),
|
||||
...(rcloneInfo.stats.transferring && Number(rcloneInfo.stats.transferring.length) > 0 ? [
|
||||
{
|
||||
label: t('transferring'),
|
||||
value: rcloneInfo.stats.transferring.length
|
||||
}
|
||||
] : []),
|
||||
...(Number(rcloneInfo.stats.totalTransfers) > 0 ? [
|
||||
{
|
||||
label: t('transferred'),
|
||||
value: rcloneInfo.stats.totalTransfers
|
||||
}
|
||||
] : []),
|
||||
|
||||
]} />
|
||||
</Card>
|
||||
|
||||
</Space>
|
||||
</div>
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
/* 软件名称:NetMount
|
||||
软件功能:挂载云存储到本地
|
||||
|
||||
主菜单(位于左边):首页(待实现),存储(添加存储,编辑存储,浏览和管理存储内文件),挂载存储(挂载为本地路径或盘符),传输(当前在传输的文件信息、速度、剩余时间等),任务(定时或间隔,可执行存储的文件同步、文件复制、文件删除、挂载等)
|
||||
|
||||
软件整体布局为左:主菜单,右:对应页面
|
||||
|
||||
现在就还有软件首页没有写了,请你为我的软件设计一个首页 */
|
||||
|
||||
export { Home_page }
|
||||
@@ -3,7 +3,7 @@ import React, { useEffect, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { ParametersType } from '../../type/rclone/storage/defaults';
|
||||
import { getProperties, getURLSearchParam } from '../../utils/rclone/utils';
|
||||
import { getProperties, getURLSearchParam } from '../../utils/utils';
|
||||
import { defaultMountConfig, defaultVfsConfig } from '../../controller/storage/mount/parameters/defaults';
|
||||
import { InputItem_module } from '../other/inputItem';
|
||||
import { rcloneInfo } from '../../services/rclone';
|
||||
|
||||
@@ -5,7 +5,7 @@ import { searchStorage, storageListAll } from "../../controller/storage/listAll"
|
||||
import { CSSProperties, useEffect, useState } from "react";
|
||||
import { checkParams, createStorage } from "../../controller/storage/create";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
import { getProperties, getURLSearchParam } from "../../utils/rclone/utils";
|
||||
import { getProperties, getURLSearchParam } from "../../utils/utils";
|
||||
import { getStorageParams } from "../../controller/storage/storage";
|
||||
import { InputItem_module } from "../other/inputItem";
|
||||
const FormItem = Form.Item;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import React, { CSSProperties, useEffect, useReducer, useState } from 'react'
|
||||
import { BackTop, Badge, Button, Divider, Dropdown, Grid, Input, Link, List, Menu, Message, Modal, Notification, Popconfirm, Select, Space, Spin, Table, TableColumnProps, Tabs, Typography, Upload } from '@arco-design/web-react';
|
||||
import { IconCopy, IconDelete, IconFolderAdd, IconLeft, IconMore, IconPaste, IconRefresh, IconScissor, IconUpCircle, IconUpload } from '@arco-design/web-react/icon';
|
||||
import { BackTop, Badge, Button, Divider, Dropdown, Grid, Input, Link, List, Menu, Message, Modal, Notification, Popconfirm, Select, Space, Spin, Table, TableColumnProps, Tabs, Tooltip, Typography, Upload } from '@arco-design/web-react';
|
||||
import { IconCopy, IconDelete, IconEdit, IconFolderAdd, IconLeft, IconMore, IconPaste, IconRefresh, IconScissor, IconUpCircle, IconUpload } from '@arco-design/web-react/icon';
|
||||
import { rcloneInfo } from '../../services/rclone';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { copyDir, copyFile, delDir, delFile, formatPathRclone, getFileList, mkDir, moveDir, moveFile } from '../../controller/storage/storage';
|
||||
import { FileInfo } from '../../type/rclone/rcloneInfo';
|
||||
import { formatSize, getURLSearchParam } from '../../utils/rclone/utils';
|
||||
import { formatSize, getURLSearchParam } from '../../utils/utils';
|
||||
import { rcloneApiHeaders } from '../../utils/rclone/request';
|
||||
import { RequestOptions } from '@arco-design/web-react/es/Upload';
|
||||
import { NoData_module } from '../other/noData';
|
||||
@@ -82,19 +82,26 @@ function ExplorerItem() {
|
||||
{
|
||||
title: t('name'),
|
||||
dataIndex: 'fileName',
|
||||
ellipsis: true,
|
||||
},
|
||||
{
|
||||
title: t('modified_time'),
|
||||
dataIndex: 'fileModTime',
|
||||
ellipsis: true,
|
||||
width: '10.5rem',
|
||||
},
|
||||
{
|
||||
title: t('size'),
|
||||
dataIndex: 'fileSize',
|
||||
ellipsis: true,
|
||||
width: '7rem',
|
||||
},
|
||||
{
|
||||
title: t('actions'),
|
||||
dataIndex: 'actions',
|
||||
align: 'right'
|
||||
align: 'right',
|
||||
width: '10rem',
|
||||
|
||||
}
|
||||
]
|
||||
|
||||
@@ -176,6 +183,7 @@ function ExplorerItem() {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function UploadFile() {
|
||||
|
||||
const customRequest = (option: RequestOptions) => {
|
||||
@@ -216,7 +224,25 @@ function ExplorerItem() {
|
||||
}
|
||||
}
|
||||
|
||||
function fileRename(filePath: string, isDir: boolean) {
|
||||
console.log(getParentPath(filePath));
|
||||
|
||||
let nameTemp = filePath.split('/').pop()!;
|
||||
modal.info!({
|
||||
title: t('rename'),
|
||||
icon: null,
|
||||
content: <Input placeholder={t('please_input')} defaultValue={nameTemp} onChange={(value) => nameTemp = value} />,
|
||||
onOk: async () => {
|
||||
if (nameTemp) {
|
||||
isDir ? await moveDir(storageName!, filePath, storageName!, getParentPath(filePath), nameTemp) :
|
||||
await moveFile(storageName!, filePath, storageName!, getParentPath(filePath), nameTemp);
|
||||
fileInfo();
|
||||
} else {
|
||||
Message.error(t('name_cannot_empty'))
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={{ height: '100%', width: '100%' }}>
|
||||
@@ -224,14 +250,12 @@ function ExplorerItem() {
|
||||
{contextHolder}
|
||||
<Row >
|
||||
<Col flex='2rem'>
|
||||
<Button /* type='secondary' */ icon={<IconLeft />} onClick={() => { updatePath(getParentPath(path!)) }} disabled={!storageName} type='text' />
|
||||
<Button /* type='secondary' */ icon={<IconLeft />} onClick={() => { updatePath(getParentPath(path!)) }} disabled={!storageName} type='text' title={t('parent_directory')} />
|
||||
</Col>
|
||||
<Col flex='2rem'>
|
||||
<Button /* type='secondary' */ icon={<IconRefresh />} onClick={fileInfo} disabled={!storageName} type='text' />
|
||||
<Button /* type='secondary' */ icon={<IconRefresh />} onClick={fileInfo} disabled={!storageName} type='text' title={t('refresh')} />
|
||||
</Col>
|
||||
|
||||
<Col style={{ paddingLeft: '1rem', paddingRight: '0.2rem' }} flex='10rem'>
|
||||
|
||||
<Select /* bordered={false} */ value={storageName} placeholder={t('please_select')} onChange={(value) => {
|
||||
if (value !== storageName) {
|
||||
setStorageName(value)
|
||||
@@ -239,7 +263,6 @@ function ExplorerItem() {
|
||||
setPath('/')
|
||||
}
|
||||
}
|
||||
|
||||
}>
|
||||
{
|
||||
rcloneInfo.storageList.map((item) => {
|
||||
@@ -255,13 +278,13 @@ function ExplorerItem() {
|
||||
</Col>
|
||||
|
||||
<Col flex='2rem'>
|
||||
<Button icon={<IconFolderAdd />} onClick={MakeDir} disabled={!storageName && !path} type='text' />
|
||||
<Button icon={<IconFolderAdd />} onClick={MakeDir} disabled={!storageName && !path} type='text' title={t('create_directory')} />
|
||||
</Col>
|
||||
<Col flex='2rem'>
|
||||
<Button icon={<IconUpload />} onClick={UploadFile} disabled={!storageName && !path} type='text' />
|
||||
<Button icon={<IconUpload />} onClick={UploadFile} disabled={!storageName && !path} type='text' title={t('upload_file')} />
|
||||
</Col>
|
||||
<Col flex='2rem' >
|
||||
<Badge count={clipList.length} maxCount={9}>
|
||||
<Badge count={clipList.length} maxCount={9} title={t('clip_board')}>
|
||||
<Dropdown disabled={clipList.length == 0} droplist={
|
||||
<Menu>
|
||||
<Menu.Item onClick={() => {
|
||||
@@ -285,59 +308,38 @@ function ExplorerItem() {
|
||||
title: t('success'),
|
||||
content: t('transm_task_created'),
|
||||
})
|
||||
|
||||
}} key='p' disabled={!storageName && !path}>粘贴({clipList.length})</Menu.Item>
|
||||
<Menu.Item onClick={() => setClipList([])} key='q'>清空剪切板</Menu.Item>
|
||||
}} key='p' disabled={!storageName && !path}>{t('paste')}({clipList.length})</Menu.Item>
|
||||
<Menu.Item onClick={() => setClipList([])} key='q'>{t('empty_the_clipboard')}</Menu.Item>
|
||||
</Menu>} position='bl'>
|
||||
<Button icon={<IconPaste />} type='text' />
|
||||
</Dropdown>
|
||||
</Badge>
|
||||
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
|
||||
<div style={{ height: 'calc(100% - 2rem)', paddingTop: '1rem' }}>
|
||||
|
||||
|
||||
<div style={{ height: 'calc(100% - 2rem)', marginTop:'1rem',overflow: 'auto' }}>
|
||||
{storageName ?
|
||||
<>{
|
||||
fileList ?
|
||||
<Table columns={columns}
|
||||
loading={loading}
|
||||
pagination={false}
|
||||
tableLayoutFixed
|
||||
rowKey='Path'
|
||||
/* rowSelection={
|
||||
{
|
||||
type: 'checkbox',
|
||||
selectedRowKeys: selectedRowKeys,
|
||||
onChange: (selectedKeys, selectedRows) => {
|
||||
|
||||
setSelectedRowKeys(selectedKeys);
|
||||
console.log('onChange:', selectedKeys, selectedRows);
|
||||
},
|
||||
onSelect: (selected, record, selectedRows) => {
|
||||
console.log('onSelect:', selected, record, selectedRows);
|
||||
},
|
||||
|
||||
}
|
||||
}
|
||||
*/
|
||||
size='small'
|
||||
noDataElement={<NoData_module />}
|
||||
data={
|
||||
fileList.map((item) => {
|
||||
|
||||
return {
|
||||
...item, fileName: <Link style={{ width: '100%' }} onClick={() => { item.IsDir && updatePath(item.Path) }}><Typography.Ellipsis showTooltip>{item.Name}</Typography.Ellipsis></Link>,
|
||||
fileSize: (item.Size != -1 ? formatSize(item.Size) : t('dir')),
|
||||
fileModTime: (new Date(item.ModTime)).toLocaleString(),
|
||||
actions: <Space size={'mini'}>
|
||||
<Button onClick={() => { addCilp({ isMove: false, storageName: storageName!, path: item.Path, isDir: item.IsDir }) }} type='text' icon={<IconCopy />} />
|
||||
<Button onClick={() => { addCilp({ isMove: true, storageName: storageName!, path: item.Path, isDir: item.IsDir }) }} type='text' icon={<IconScissor />} />
|
||||
<Button onClick={() => { addCilp({ isMove: false, storageName: storageName!, path: item.Path, isDir: item.IsDir }) }} type='text' icon={<IconCopy />} title={t('copy')} />
|
||||
<Button onClick={() => { addCilp({ isMove: true, storageName: storageName!, path: item.Path, isDir: item.IsDir }) }} type='text' icon={<IconScissor />} title={t('cut')} />
|
||||
<Dropdown unmountOnExit={false} droplist={
|
||||
<Menu>
|
||||
<Menu.Item key='rename' style={{ color: 'var(primary-4)' }} onClick={() => fileRename(item.Path, item.IsDir)}><IconEdit /> {t('rename')}</Menu.Item>
|
||||
<Menu.Item key='del' /* style={{ color: 'var(danger-4)' }} */>
|
||||
<Popconfirm
|
||||
focusLock
|
||||
@@ -347,14 +349,11 @@ function ExplorerItem() {
|
||||
delFile(storageName!, item.Path, fileInfo)
|
||||
}}
|
||||
>
|
||||
<IconDelete />
|
||||
{t('delete')}
|
||||
<IconDelete /> {t('delete')}
|
||||
</Popconfirm></Menu.Item>
|
||||
{/* <Menu.Item key='rename' style={{ color: 'var(primary-4)' }}></Menu.Item> */}
|
||||
</Menu>} position='bl'>
|
||||
<Button icon={<IconMore />} type='text' />
|
||||
<Button icon={<IconMore />} type='text' title={t('more')} />
|
||||
</Dropdown>
|
||||
|
||||
</Space>
|
||||
}
|
||||
})} />
|
||||
|
||||
298
src/page/task/add.tsx
Normal file
298
src/page/task/add.tsx
Normal file
@@ -0,0 +1,298 @@
|
||||
import { Button, Divider, Form, Grid, Input, InputNumber, Notification, Select, Space, Tooltip } from '@arco-design/web-react';
|
||||
import React, { useReducer, useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { nmConfig, roConfig } from '../../services/config';
|
||||
import { TaskListItem } from '../../type/config';
|
||||
import { rcloneInfo } from '../../services/rclone';
|
||||
import { IconQuestionCircle } from '@arco-design/web-react/icon';
|
||||
import { formatPathRclone } from '../../controller/storage/storage';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { saveTask } from '../../controller/task/task';
|
||||
const Row = Grid.Row;
|
||||
const Col = Grid.Col;
|
||||
|
||||
const formatPath = (path: string) => {
|
||||
path = path.replace(/\\/g, '/');
|
||||
path = path.replace(/\/+/g, '/');
|
||||
|
||||
if (path.substring(0, 1) != '/') {
|
||||
path = '/' + path;
|
||||
}
|
||||
|
||||
return path
|
||||
}
|
||||
|
||||
// 定义状态和 action 类型
|
||||
type TaskInfoState = TaskListItem;
|
||||
|
||||
type Action =
|
||||
| { type: 'setName'; payload: string }
|
||||
| { type: 'setRunTypeMode'; payload: string }
|
||||
| { type: 'setTaskType'; payload: string }
|
||||
| { type: 'setSourceStorageName'; payload: string }
|
||||
| { type: 'setSourcePath'; payload: string }
|
||||
| { type: 'setTargetStorageName'; payload: string }
|
||||
| { type: 'setTargetPath'; payload: string }
|
||||
| { type: 'setIntervalDays'; payload: number }
|
||||
| { type: 'setRunTime'; payload: { h: number, m: number, s: number } };
|
||||
|
||||
// 定义 reducer 函数
|
||||
const reducer = (state: TaskInfoState, action: Action): TaskInfoState => {
|
||||
switch (action.type) {
|
||||
case 'setName':
|
||||
return { ...state, name: action.payload };
|
||||
case 'setRunTypeMode':
|
||||
return { ...state, run: { ...state.run, mode: action.payload } };
|
||||
case 'setTaskType':
|
||||
return { ...state, taskType: action.payload };
|
||||
case 'setSourceStorageName':
|
||||
return { ...state, source: { ...state.source, storageName: action.payload } };
|
||||
case 'setSourcePath':
|
||||
return { ...state, source: { ...state.source, path: formatPath(action.payload) } };
|
||||
case 'setTargetStorageName':
|
||||
return { ...state, target: { ...state.target, storageName: action.payload } };
|
||||
case 'setTargetPath':
|
||||
return { ...state, target: { ...state.target, path: formatPath(action.payload) } };
|
||||
case 'setIntervalDays':
|
||||
return { ...state, run: { ...state.run, time: { ...state.run.time, intervalDays: action.payload } } };
|
||||
case 'setRunTime':
|
||||
return { ...state, run: { ...state.run, time: { ...state.run.time, ...action.payload } } };
|
||||
default:
|
||||
throw new Error('Invalid action');
|
||||
}
|
||||
};
|
||||
|
||||
function AddTask_page() {
|
||||
const { t } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
const [taskInfo, dispatch] = useReducer(reducer, {
|
||||
name: 'task_' + (nmConfig.task ? nmConfig.task.length + 1 : 1),
|
||||
taskType: roConfig.options.task.taskType.select[roConfig.options.task.taskType.defIndex],
|
||||
source: {
|
||||
storageName:
|
||||
rcloneInfo.storageList && rcloneInfo.storageList.length > 0
|
||||
? rcloneInfo.storageList[0].name
|
||||
: '',
|
||||
path: '/',
|
||||
},
|
||||
target: {
|
||||
storageName:
|
||||
rcloneInfo.storageList && rcloneInfo.storageList.length > 0
|
||||
? (rcloneInfo.storageList.length > 1
|
||||
? rcloneInfo.storageList[1].name
|
||||
: rcloneInfo.storageList[0].name)
|
||||
: '',
|
||||
path: '/',
|
||||
},
|
||||
run: {
|
||||
mode: roConfig.options.task.runMode.select[roConfig.options.task.runMode.defIndex], time: {
|
||||
intervalDays: 1,
|
||||
h: 10,
|
||||
m: 30,
|
||||
s: 0,
|
||||
}
|
||||
},
|
||||
enable: true,
|
||||
});
|
||||
|
||||
const [timeMultiplier, setTimeMultiplier] = useState({
|
||||
...roConfig.options.task.dateMultiplier.select[roConfig.options.task.dateMultiplier.defIndex],
|
||||
multiplicand: 1
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
if (taskInfo.run.mode === 'time') {
|
||||
setTimeMultiplier({ ...roConfig.options.task.dateMultiplier.select[roConfig.options.task.dateMultiplier.defIndex], multiplicand: 1 })
|
||||
} else if (taskInfo.run.mode === 'interval') {
|
||||
setTimeMultiplier({ ...roConfig.options.task.intervalMultiplier.select[roConfig.options.task.intervalMultiplier.defIndex], multiplicand: 1 })
|
||||
}
|
||||
}, [taskInfo.run.mode])
|
||||
|
||||
|
||||
return (
|
||||
<div style={{ width: '100%', height: '100%' }}>
|
||||
<Form style={{ paddingRight: '10%' }}>
|
||||
<Form.Item label={t('task_name')}>
|
||||
<Input
|
||||
value={taskInfo.name}
|
||||
onChange={(value) => dispatch({ type: 'setName', payload: value })}
|
||||
placeholder={t('please_input')}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item label={t('task_run_mode')}>
|
||||
<Select
|
||||
onChange={(value) => dispatch({ type: 'setRunTypeMode', payload: value })}
|
||||
value={taskInfo.run.mode}
|
||||
>
|
||||
{roConfig.options.task.runMode.select.map((item) => (
|
||||
<Select.Option value={item}>{t(`task_run_mode_${item}_opt`)}</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
{taskInfo.run.mode != 'start' && taskInfo.run.mode !== 'disposable' &&
|
||||
<>
|
||||
<Form.Item label={t('interval')}>
|
||||
<Row>
|
||||
<Col flex={'6rem'}>
|
||||
<Select value={timeMultiplier.value} onChange={(value) => {
|
||||
setTimeMultiplier({ ...(taskInfo.run.mode === 'time' ? roConfig.options.task.dateMultiplier.select.find(item => item.value === value)! : roConfig.options.task.intervalMultiplier.select.find(item => item.value === value)!), multiplicand: timeMultiplier.multiplicand });
|
||||
}}>
|
||||
{(taskInfo.run.mode === 'time' ? roConfig.options.task.dateMultiplier.select : roConfig.options.task.intervalMultiplier.select).map((item) => (
|
||||
<Select.Option value={item.value}>{t(item.name)}(*{item.value})</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Col>
|
||||
<Col flex={'auto'}>
|
||||
<InputNumber mode='button' min={1} max={10000} value={timeMultiplier.multiplicand} precision={0}
|
||||
onChange={
|
||||
(value) => {
|
||||
setTimeMultiplier({ ...timeMultiplier, multiplicand: value });
|
||||
}
|
||||
} />
|
||||
</Col>
|
||||
</Row>
|
||||
</Form.Item>
|
||||
|
||||
{
|
||||
taskInfo.run.mode === 'time' && <>
|
||||
<Form.Item label={t('time')}>
|
||||
<Row gutter={10}>
|
||||
<Col flex={'1'}>
|
||||
<InputNumber
|
||||
min={0} max={23} precision={0}
|
||||
value={taskInfo.run.time.h}
|
||||
suffix={t('hour')}
|
||||
onChange={(value) => dispatch({ type: 'setRunTime', payload: { ...taskInfo.run.time, h: value } })}
|
||||
/>
|
||||
</Col>
|
||||
<Col flex={'1'}>
|
||||
<InputNumber
|
||||
min={0} max={59} precision={0}
|
||||
value={taskInfo.run.time.m}
|
||||
suffix={t('minute')}
|
||||
onChange={(value) => dispatch({ type: 'setRunTime', payload: { ...taskInfo.run.time, m: value } })}
|
||||
/>
|
||||
</Col>
|
||||
<Col flex={'1'}>
|
||||
<InputNumber
|
||||
min={0} max={59} precision={0}
|
||||
value={taskInfo.run.time.s}
|
||||
suffix={t('second')}
|
||||
onChange={(value) => dispatch({ type: 'setRunTime', payload: { ...taskInfo.run.time, s: value } })}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</Form.Item>
|
||||
</>
|
||||
}
|
||||
</>
|
||||
}
|
||||
|
||||
<Form.Item label={t('task_type')}>
|
||||
<Select
|
||||
onChange={(value) => dispatch({ type: 'setTaskType', payload: value })}
|
||||
value={taskInfo.taskType}
|
||||
>
|
||||
{roConfig.options.task.taskType.select.map((item) => (
|
||||
<Select.Option value={item}>{t(`${item}`)}</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label={t('source_path')}>
|
||||
<Row>
|
||||
<Col flex={'7rem'}>
|
||||
<Select
|
||||
value={taskInfo.source.storageName}
|
||||
placeholder={t('please_select')}
|
||||
onChange={(value) => dispatch({ type: 'setSourceStorageName', payload: value })}
|
||||
>
|
||||
{rcloneInfo.storageList.map((item) => (
|
||||
<Select.Option key={item.name} value={item.name}>
|
||||
{item.name}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Col>
|
||||
<Col flex={'auto'}>
|
||||
<Input
|
||||
value={taskInfo.source.path}
|
||||
onChange={(value) => dispatch({ type: 'setSourcePath', payload: value })}
|
||||
disabled={!taskInfo.source.storageName}
|
||||
/>
|
||||
</Col>
|
||||
<Col flex={'2rem'}>
|
||||
<Tooltip content={t('explain_for_task_path_format')}>
|
||||
<Button icon={<IconQuestionCircle />}></Button>
|
||||
</Tooltip>
|
||||
</Col>
|
||||
</Row>
|
||||
</Form.Item>
|
||||
|
||||
{taskInfo.taskType !== 'delete' && (
|
||||
<Form.Item label={t('target_path')}>
|
||||
<Row>
|
||||
<Col flex={'7rem'}>
|
||||
<Select
|
||||
value={taskInfo.target.storageName}
|
||||
placeholder={t('please_select')}
|
||||
onChange={(value) => dispatch({ type: 'setTargetStorageName', payload: value })}
|
||||
>
|
||||
{rcloneInfo.storageList.map((item) => (
|
||||
<Select.Option key={item.name} value={item.name}>
|
||||
{item.name}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Col>
|
||||
<Col flex={'auto'}>
|
||||
<Input
|
||||
value={taskInfo.target.path}
|
||||
onChange={(value) => dispatch({ type: 'setTargetPath', payload: value })}
|
||||
disabled={!taskInfo.target.storageName}
|
||||
/>
|
||||
</Col>
|
||||
<Col flex={'2rem'}>
|
||||
<Tooltip content={t('explain_for_task_path_format')}>
|
||||
<Button icon={<IconQuestionCircle />}></Button>
|
||||
</Tooltip>
|
||||
</Col>
|
||||
</Row>
|
||||
</Form.Item>
|
||||
)}
|
||||
</Form>
|
||||
<div style={{ marginTop: '20px', textAlign: 'right' }}>
|
||||
<Space>
|
||||
<Button onClick={() => {
|
||||
navigate('/task/')
|
||||
}}>{t('step_back')}</Button>
|
||||
<Button type='primary' onClick={() => {
|
||||
if (taskInfo.run.mode === 'time') {
|
||||
taskInfo.run.time.intervalDays = timeMultiplier.multiplicand * timeMultiplier.value;
|
||||
} else if (taskInfo.run.mode === 'interval') {
|
||||
taskInfo.run.interval = timeMultiplier.multiplicand * timeMultiplier.value;
|
||||
}
|
||||
if (nmConfig.task && nmConfig.task.forEach(item => item.name == taskInfo.name)! || !taskInfo.name) {
|
||||
Notification.error({
|
||||
title: t('error'),
|
||||
content: t('the_task_name_is_illegal'),
|
||||
})
|
||||
} else {
|
||||
if (saveTask(taskInfo)) {
|
||||
Notification.success({
|
||||
title: t('success'),
|
||||
content: t('task_added_successfully'),
|
||||
})
|
||||
navigate('/task/')
|
||||
}
|
||||
}
|
||||
}}>{t('add')}</Button>
|
||||
</Space>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
export { AddTask_page };
|
||||
@@ -1,12 +1,63 @@
|
||||
import React from 'react'
|
||||
import React, { useReducer } from 'react'
|
||||
import { DevTips_module } from '../other/devTips'
|
||||
import { Button, Space, Table, TableColumnProps } from '@arco-design/web-react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { nmConfig } from '../../services/config'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { delTask } from '../../controller/task/task'
|
||||
import { NoData_module } from '../other/noData'
|
||||
|
||||
function Task_page() {
|
||||
return (
|
||||
<div>
|
||||
<DevTips_module/>
|
||||
</div>
|
||||
)
|
||||
const { t } = useTranslation()
|
||||
const navigate = useNavigate();
|
||||
const [ignored, forceUpdate] = useReducer(x => x + 1, 0);//刷新组件
|
||||
|
||||
const columns: TableColumnProps[] = [
|
||||
{
|
||||
title: t('task_name'),
|
||||
dataIndex: 'name',
|
||||
},
|
||||
{
|
||||
title: t('state'),
|
||||
dataIndex: 'state',
|
||||
}, {
|
||||
title: t('cycle'),
|
||||
dataIndex: 'cycle',
|
||||
}
|
||||
, {
|
||||
title: t('run_info'),
|
||||
dataIndex: 'runInfo',
|
||||
},
|
||||
{
|
||||
title: t('actions'),
|
||||
dataIndex: 'actions',
|
||||
}
|
||||
];
|
||||
console.log(nmConfig.task);
|
||||
|
||||
return (
|
||||
<div style={{ width: '100%', height: '100%' }}>
|
||||
<div style={{ width: '100%', height: '2rem' }}>
|
||||
<Space>
|
||||
<Button type='primary' onClick={() => { navigate('/task/add') }}>{t('add')}</Button>
|
||||
<Button onClick={() => { forceUpdate() }}>{t('refresh')}</Button>
|
||||
</Space>
|
||||
</div>
|
||||
<div style={{ height: "calc(100% - 3rem)", marginTop: "1rem" }}>
|
||||
<Table columns={columns} noDataElement={ <NoData_module />} data={nmConfig.task.map((taskItem) => {
|
||||
return {
|
||||
...taskItem,
|
||||
state: taskItem.enable ? t('enabled') : t('disabled'),
|
||||
cycle: t('task_run_mode_' + taskItem.run.mode),
|
||||
runInfo:taskItem.runInfo?.mag,
|
||||
actions: <>
|
||||
<Button onClick={()=>{delTask(taskItem.name);forceUpdate()}}>{t('delete')}</Button>
|
||||
</>
|
||||
}
|
||||
})} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export { Task_page }
|
||||
@@ -3,7 +3,7 @@ import { rcloneInfo, rcloneStatsHistory } from '../../services/rclone'
|
||||
import { hooks } from '../../services/hook'
|
||||
import { RcloneTransferItem } from '../../type/rclone/stats'
|
||||
import { Card, Descriptions, List, Progress, Space, Statistic, Grid, Typography } from '@arco-design/web-react'
|
||||
import { formatETA, formatSize } from '../../utils/rclone/utils'
|
||||
import { formatETA, formatSize } from '../../utils/utils'
|
||||
import { Area } from '@ant-design/charts'
|
||||
import { NoData_module } from '../other/noData'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
@@ -37,7 +37,7 @@ function Transmit_page() {
|
||||
<Descriptions colon=' :' data={[
|
||||
{
|
||||
label:t('speed'),
|
||||
value: `${formatSize(rcloneInfo.stats.speed)}/s`
|
||||
value: `${formatSize(rcloneInfo.stats.realSpeed!)}/s`
|
||||
},
|
||||
|
||||
{
|
||||
@@ -47,7 +47,7 @@ function Transmit_page() {
|
||||
|
||||
...(rcloneInfo.stats.transferTime > 0 ? [
|
||||
{
|
||||
label:t('time'),
|
||||
label:t('used_time'),
|
||||
value: formatETA(rcloneInfo.stats.transferTime)
|
||||
}
|
||||
] : []),
|
||||
@@ -77,7 +77,6 @@ function Transmit_page() {
|
||||
>
|
||||
|
||||
<List noDataElement={ <NoData_module />}>
|
||||
|
||||
{
|
||||
transmitList.map((item, index) => {
|
||||
return <List.Item key={index}>
|
||||
|
||||
@@ -65,7 +65,8 @@
|
||||
"transferring": "传输中",
|
||||
"overview":"总览",
|
||||
"transferred": "已传输",
|
||||
"time": "用时",
|
||||
"used_time": "用时",
|
||||
"time":"时间",
|
||||
"speed": "速度",
|
||||
"eta": "剩余时间",
|
||||
"speed_avg":"平均速度",
|
||||
@@ -73,5 +74,45 @@
|
||||
"source":"来源",
|
||||
"read_config":"读取配置",
|
||||
"init":"初始化",
|
||||
"transm_task_created":"已创建任务,请到[传输]页查看信息"
|
||||
"transm_task_created":"已创建任务,请到[传输]页查看信息",
|
||||
"clip_board": "剪切板",
|
||||
"paste":"粘贴",
|
||||
"empty_the_clipboard":"清空剪贴板",
|
||||
"parent_directory":"上级目录",
|
||||
"cut":"剪切",
|
||||
"more":"更多",
|
||||
"copy":"复制",
|
||||
"rename": "重命名",
|
||||
"name_cannot_empty":"名称不能为空",
|
||||
"task_name":"任务名称",
|
||||
"state":"状态",
|
||||
"enabled":"启用",
|
||||
"disabled":"禁用",
|
||||
"cycle":"周期",
|
||||
"run_info":"运行信息",
|
||||
"task_type":"任务类型",
|
||||
"task_run_mode_start":"启动时",
|
||||
"task_run_mode_start_opt":"启动时(软件启动时执行)",
|
||||
"task_run_mode_time":"定时",
|
||||
"task_run_mode_time_opt":"定时(在特定时间执行)",
|
||||
"task_run_mode_interval":"间隔",
|
||||
"task_run_mode_interval_opt":"间隔(每隔一段时间执行)",
|
||||
"task_run_mode_disposable":"一次性",
|
||||
"task_run_mode_disposable_opt":"一次性(添加后立即执行,并自动删除任务)",
|
||||
"task_run_mode": "执行模式",
|
||||
"move":"移动",
|
||||
"sync":"同步",
|
||||
"source_path":"源路径",
|
||||
"target_path":"目标路径",
|
||||
"prompt_for_leaving_the_add_or_edit_page":"离开添加或编辑页面,未保存的设置将丢失。",
|
||||
"explain_for_task_path_format":"路径格式:目录以斜杠结尾,文件以不以斜杠结尾",
|
||||
"interval":"间隔",
|
||||
"day":"天",
|
||||
"week":"周",
|
||||
"month":"月",
|
||||
"hour":"小时",
|
||||
"minute":"分钟",
|
||||
"second":"秒",
|
||||
"the_task_name_is_illegal":"任务名称不合法",
|
||||
"task_added_successfully":"任务添加成功"
|
||||
}
|
||||
@@ -1,11 +1,12 @@
|
||||
import { NMConfig } from "../type/config"
|
||||
import { NMConfig, OSInfo } from "../type/config"
|
||||
|
||||
let nmConfig: NMConfig = {
|
||||
mount: {
|
||||
lists: [],
|
||||
},
|
||||
task: {
|
||||
|
||||
task: [],
|
||||
api: {
|
||||
url: 'https://api.hotpe.top/test/NetMount',
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,4 +14,41 @@ const setNmConfig = (config: NMConfig) => {
|
||||
nmConfig = config
|
||||
}
|
||||
|
||||
export { nmConfig ,setNmConfig}
|
||||
|
||||
let osInfo: OSInfo = {
|
||||
arch: 'unknown',
|
||||
osType: 'unknown',
|
||||
platform: 'unknown',
|
||||
tempDir: '',
|
||||
osVersion: ''
|
||||
}
|
||||
|
||||
const setOsInfo = (osinfo: OSInfo) => {
|
||||
osInfo = osinfo
|
||||
}
|
||||
|
||||
const roConfig = {
|
||||
options: {
|
||||
task:{
|
||||
runMode: {
|
||||
defIndex: 0,
|
||||
select: [ 'start', 'time', 'interval','disposable']
|
||||
},
|
||||
taskType:{
|
||||
defIndex: 0,
|
||||
select: [ 'sync', 'copy','move', 'delete']
|
||||
},
|
||||
dateMultiplier:{
|
||||
defIndex: 0,
|
||||
select: [{ name: 'day', value: 1 }, { name: 'week', value: 7 }, { name: 'month', value: 30 }]
|
||||
},
|
||||
intervalMultiplier:{
|
||||
defIndex: 0,
|
||||
select: [{ name: 'hour', value: 60*60 }, { name: 'minute', value: 60 }, { name: 'second', value: 1 }]
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export { nmConfig, setNmConfig, osInfo, setOsInfo, roConfig }
|
||||
@@ -4,7 +4,9 @@ import { RcloneInfo } from "../type/rclone/rcloneInfo"
|
||||
import { RcloneStats } from "../type/rclone/stats"
|
||||
|
||||
let rcloneInfo: RcloneInfo = {
|
||||
|
||||
process:{
|
||||
|
||||
},
|
||||
endpoint: {
|
||||
url: '',
|
||||
isLocal: true,
|
||||
|
||||
58
src/type/config.d.ts
vendored
58
src/type/config.d.ts
vendored
@@ -1,10 +1,14 @@
|
||||
import { Arch, OsType, Platform } from "@tauri-apps/api/os"
|
||||
import { ParametersType } from "./rclone/storage/defaults"
|
||||
|
||||
interface NMConfig {
|
||||
mount: {
|
||||
lists: MountListItem[]
|
||||
},
|
||||
task: TaskListItem
|
||||
task: TaskListItem[],
|
||||
api: {
|
||||
url: string
|
||||
}
|
||||
}
|
||||
|
||||
interface MountListItem {
|
||||
@@ -15,23 +19,41 @@ interface MountListItem {
|
||||
}
|
||||
|
||||
interface TaskListItem {
|
||||
[key: string]: {
|
||||
taskType: 'copy' | 'move'| 'delete'| 'sync',
|
||||
source: string,
|
||||
target?: string,
|
||||
parameters?: ParametersType,
|
||||
autoRun: {
|
||||
enable: boolean,
|
||||
type: 'time' | 'interval'|'start',//start:软件启动时执行,time:定时执行,interval:周期执行
|
||||
time?: {
|
||||
intervalDay: number,//间隔天数
|
||||
h: number,//小时
|
||||
m: number,//分钟
|
||||
s: number,//秒
|
||||
},
|
||||
interval?: number,
|
||||
name: string,
|
||||
taskType: 'copy' | 'move' | 'delete' | 'sync' | string,
|
||||
source: {
|
||||
storageName: string,
|
||||
path: string,
|
||||
},
|
||||
target: {
|
||||
storageName: string,
|
||||
path: string,
|
||||
},
|
||||
parameters?: ParametersType,
|
||||
enable: boolean
|
||||
run: {
|
||||
runId?: number,//任务id,setTimeout或setInterval的返回值
|
||||
mode: 'time' | 'interval' | 'start' |'disposable'| string,//start:软件启动时执行,time:定时执行,interval:间隔执行 , disposable:一次性执行(执行后删除任务)
|
||||
time: {
|
||||
intervalDays: number,//间隔天数
|
||||
h: number,//小时
|
||||
m: number,//分钟
|
||||
s: number,//秒
|
||||
},
|
||||
exeId?:number,//任务id,setTimeout或setInterval的返回值
|
||||
interval?: number,//周期执行,单位ms
|
||||
},
|
||||
runInfo?: {
|
||||
error:boolean
|
||||
mag: string,
|
||||
}
|
||||
}
|
||||
export { NMConfig, MountListItem,TaskListItem}
|
||||
|
||||
interface OSInfo {
|
||||
arch: Arch | 'unknown',
|
||||
osType: OsType | 'unknown',
|
||||
platform: Platform | 'unknown',
|
||||
tempDir: string,
|
||||
osVersion: string
|
||||
}
|
||||
|
||||
export { NMConfig, MountListItem, TaskListItem, OSInfo }
|
||||
20
src/type/controller/update.d.ts
vendored
Normal file
20
src/type/controller/update.d.ts
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
export interface ResList {
|
||||
[key: string]: ResItem;
|
||||
}
|
||||
|
||||
export interface ResItem {
|
||||
id: string;
|
||||
name: string;
|
||||
pushTime: string;
|
||||
body?: string;
|
||||
assets: ResAsset[];
|
||||
website?: string;
|
||||
download_url?: string;
|
||||
}
|
||||
|
||||
export interface ResAsset {
|
||||
name: string;
|
||||
size: number;
|
||||
download_url: string;
|
||||
}
|
||||
|
||||
5
src/type/rclone/rcloneInfo.d.ts
vendored
5
src/type/rclone/rcloneInfo.d.ts
vendored
@@ -1,6 +1,11 @@
|
||||
import { Child, Command } from "@tauri-apps/api/shell";
|
||||
import { RcloneStats } from "./stats";
|
||||
|
||||
interface RcloneInfo {
|
||||
process:{
|
||||
command?:Command,
|
||||
child?:Child
|
||||
},
|
||||
endpoint: {
|
||||
url: string,
|
||||
isLocal: boolean,// 是否为本地地址
|
||||
|
||||
1
src/type/rclone/stats.d.ts
vendored
1
src/type/rclone/stats.d.ts
vendored
@@ -51,6 +51,7 @@ interface RcloneStats {
|
||||
serverSideMoveBytes: number; // 通过服务器端移动传输的字节数
|
||||
serverSideMoves: number; // 服务器端移动操作的数量
|
||||
speed: number; // 当前速度(字节/秒)
|
||||
realSpeed?: number; // 实时速度(字节/秒)
|
||||
totalBytes: number; // 总共处理的字节数
|
||||
totalChecks: number; // 总共完成的校验数
|
||||
totalTransfers: number; // 总共完成的传输操作数
|
||||
|
||||
13
src/type/utils/aria2.d.ts
vendored
Normal file
13
src/type/utils/aria2.d.ts
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
// 定义Aria2的属性
|
||||
interface Aria2Attrib {
|
||||
state: 'doing' | 'done' | 'request' | 'error',//状态,错误:error,发送请求:request,下载中:doing,完成:done
|
||||
speed: string,//速度
|
||||
percentage: number,//进度百分比
|
||||
eta: string,//剩余时间
|
||||
size: string,//总大小
|
||||
newSize: string,//已下载大小
|
||||
message: string,//当前的Aria2返回
|
||||
|
||||
}
|
||||
|
||||
export {Aria2Attrib}
|
||||
108
src/utils/aria2/aria2.ts
Normal file
108
src/utils/aria2/aria2.ts
Normal file
@@ -0,0 +1,108 @@
|
||||
import { Child, Command } from '@tauri-apps/api/shell';
|
||||
import { takeMidStr, takeRightStr } from '../utils';
|
||||
import { Aria2Attrib } from '../../type/utils/aria2';
|
||||
|
||||
class Aria2 {
|
||||
private filePath: string = '';
|
||||
private command: Command;
|
||||
private process: Child | null = null;
|
||||
|
||||
constructor(url: string, saveDir: string, saveName: string, thread: number = 8, callback: (attrib: Aria2Attrib) => void) {
|
||||
this.filePath = saveDir + saveName;
|
||||
const args = [
|
||||
'-d', saveDir,
|
||||
'-o', saveName,
|
||||
'-s', thread.toString(),
|
||||
'-x', thread.toString(),
|
||||
'--file-allocation=none',
|
||||
'-c',
|
||||
'--check-certificate=false',
|
||||
'--force-save=false',
|
||||
url
|
||||
]
|
||||
|
||||
this.command = new Command('ria2c', args);
|
||||
|
||||
this.command.stdout.on('data', (data) => {
|
||||
const output = data.toString();
|
||||
if (output.includes('NOTICE') || output.includes('#')) {
|
||||
callback(this.parseOutput(output));
|
||||
}
|
||||
});
|
||||
|
||||
this.command.on('close', (data) => {
|
||||
this.process = null;
|
||||
const attrib: Aria2Attrib = this.parseOutput('');
|
||||
if (data.code === 0) {
|
||||
attrib.state = 'done';
|
||||
} else {
|
||||
attrib.state = 'error';
|
||||
}
|
||||
callback(attrib);
|
||||
});
|
||||
}
|
||||
|
||||
// 解析aria2的命令行输出
|
||||
private parseOutput(output: string): Aria2Attrib {
|
||||
let tempAria2Attrib: Aria2Attrib = {
|
||||
state: 'request',
|
||||
speed: '',
|
||||
percentage: 0,
|
||||
eta: '',
|
||||
size: '',
|
||||
newSize: '',
|
||||
message: output
|
||||
}
|
||||
|
||||
if (output.includes('DL:')) {//正在下载,[#46fea8 210MiB/583MiB(36%) CN:4 DL:10MiB ETA:35s]
|
||||
tempAria2Attrib.state = 'doing';
|
||||
|
||||
//速度speed,str.substring(str.indexOf("DL:") + 3, str.indexOf("iB ETA")) + 'B/S'
|
||||
|
||||
|
||||
//进度百分比,Number(str.substring(str.indexOf("B(") + 2, str.indexOf("%)"))))
|
||||
tempAria2Attrib.percentage = Number(takeMidStr(output, 'B(', '%)'))
|
||||
|
||||
|
||||
if (output.includes('ETA')) {
|
||||
tempAria2Attrib.speed = takeMidStr(output, 'DL:', 'iB ETA') + 'B/s'
|
||||
//剩余时间 eta
|
||||
tempAria2Attrib.eta = takeMidStr(output, 'ETA:', ']')
|
||||
} else {
|
||||
tempAria2Attrib.speed = takeMidStr(output, 'DL:', 'iB]') + 'B/s'
|
||||
}
|
||||
|
||||
//总大小,size
|
||||
tempAria2Attrib.size = takeMidStr(output, '/', 'iB(') + 'B'
|
||||
|
||||
//已下载大小,newSize
|
||||
tempAria2Attrib.newSize = takeRightStr(takeMidStr(output, '[#', 'iB/'), ' ') + 'B'
|
||||
} else {
|
||||
tempAria2Attrib.state = 'request'
|
||||
}
|
||||
return tempAria2Attrib
|
||||
}
|
||||
|
||||
// 启动aria2下载
|
||||
async start(): Promise<void> {
|
||||
this.process = await this.command.spawn()
|
||||
}
|
||||
|
||||
// 停止aria2下载
|
||||
async stop(): Promise<boolean> {
|
||||
if (this.process) {
|
||||
try {
|
||||
await this.process.kill();
|
||||
this.process = null;
|
||||
return true;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export { Aria2 };
|
||||
37
src/utils/rclone/process.ts
Normal file
37
src/utils/rclone/process.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import { invoke } from "@tauri-apps/api";
|
||||
import { Command } from "@tauri-apps/api/shell";
|
||||
import { rcloneInfo } from "../../services/rclone";
|
||||
import { rclone_api_post } from "./request";
|
||||
|
||||
|
||||
async function startRclone() {
|
||||
if (rcloneInfo.process.child) {
|
||||
await stopRclone()
|
||||
}
|
||||
|
||||
//rcloneInfo.endpoint.auth.user = randomString(32)
|
||||
//rcloneInfo.endpoint.auth.pass = randomString(128)
|
||||
|
||||
rcloneInfo.endpoint.url = 'http://localhost:' + rcloneInfo.endpoint.localhost.port.toString()
|
||||
|
||||
const args = [
|
||||
'rcd',
|
||||
`--rc-addr=:${rcloneInfo.endpoint.localhost.port.toString()}`,
|
||||
`--rc-user=${rcloneInfo.endpoint.auth.user}`,
|
||||
`--rc-pass=${rcloneInfo.endpoint.auth.pass}`,
|
||||
'--rc-allow-origin=*',
|
||||
'--rc-no-auth'
|
||||
];
|
||||
|
||||
rcloneInfo.process.command = new Command('rclone', args)
|
||||
rcloneInfo.process.child = await rcloneInfo.process.command.spawn()
|
||||
}
|
||||
|
||||
async function stopRclone() {
|
||||
await rclone_api_post('/core/quit')
|
||||
if (rcloneInfo.process.child) {
|
||||
await rcloneInfo.process.child.kill()
|
||||
}
|
||||
}
|
||||
|
||||
export { startRclone, stopRclone }
|
||||
@@ -27,20 +27,33 @@ function rclone_api_post(path: string, data?: object, ignoreError?: boolean) {
|
||||
body: JSON.stringify(data)
|
||||
}).then((response) => {
|
||||
if (!response.ok && !ignoreError) {
|
||||
Message.error(`Request failed with status ${response.status}: ${response.statusText}`);
|
||||
throw new Error(`Request failed with status ${response.status}: ${response.statusText}`);
|
||||
printError(response);
|
||||
}
|
||||
return response.json();
|
||||
}).then((jsonResponse) => {
|
||||
return jsonResponse;
|
||||
}).catch((error) => {
|
||||
if (ignoreError) { return }
|
||||
Message.error(error.message);
|
||||
console.error("Error fetching from Rclone API:", error.message);
|
||||
throw error;
|
||||
printError(error);
|
||||
});
|
||||
}
|
||||
|
||||
async function printError(error: Response) {
|
||||
console.log(error);
|
||||
|
||||
let str = ''
|
||||
|
||||
if (error.status) {
|
||||
str += `HTTP ${error.status} - ${error.statusText}\n`
|
||||
}
|
||||
if (error.body) {
|
||||
str += "\n" + (await error.json()).error;
|
||||
}
|
||||
if (str) {
|
||||
Message.error('Error:' + str);
|
||||
}
|
||||
}
|
||||
|
||||
/* export function rclone_api_get(path:string){
|
||||
return fetch(rcloneApiEndpoint + path,{
|
||||
method: 'GET',
|
||||
|
||||
27
src/utils/tauri/cmd.ts
Normal file
27
src/utils/tauri/cmd.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { Command } from "@tauri-apps/api/shell"
|
||||
|
||||
function runCmd(cmd: string, args: string[]): Promise<string> {
|
||||
return new Promise( (resolve, reject) => {
|
||||
const command = new Command(cmd, args)
|
||||
|
||||
let output = ""
|
||||
|
||||
command.stdout.on('data', (data: string) => {
|
||||
output += data.toString()
|
||||
|
||||
})
|
||||
|
||||
command.on("error", (err: Error) => {
|
||||
reject(err)
|
||||
})
|
||||
|
||||
command.on('close', () => {
|
||||
|
||||
resolve(output)
|
||||
})
|
||||
|
||||
command.spawn()
|
||||
})
|
||||
}
|
||||
|
||||
export { runCmd }
|
||||
15
src/utils/tauri/osInfo.ts
Normal file
15
src/utils/tauri/osInfo.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { os } from "@tauri-apps/api";
|
||||
import { OSInfo } from "../../type/config";
|
||||
import { setOsInfo } from "../../services/config";
|
||||
|
||||
async function getOsInfo() {
|
||||
setOsInfo({
|
||||
arch: await os.arch(),
|
||||
osType: await os.type(),
|
||||
platform: await os.platform(),
|
||||
tempDir: await os.tempdir(),
|
||||
osVersion: await os.version(),
|
||||
})
|
||||
}
|
||||
|
||||
export { getOsInfo }
|
||||
@@ -1,3 +1,6 @@
|
||||
import { fs, invoke } from "@tauri-apps/api";
|
||||
import { runCmd } from "./tauri/cmd";
|
||||
|
||||
export function isEmptyObject(back: any): boolean {
|
||||
return Object.keys(back).length === 0 && back.constructor === Object;
|
||||
}
|
||||
@@ -38,28 +41,28 @@ export function formatSize(v: number) {
|
||||
|
||||
//格式化剩余时间
|
||||
export function formatETA(etaInSeconds: number): string {
|
||||
if (isNaN(etaInSeconds) || etaInSeconds <= 0) {
|
||||
return '未知';
|
||||
}
|
||||
if (isNaN(etaInSeconds) || etaInSeconds <= 0) {
|
||||
return '未知';
|
||||
}
|
||||
|
||||
const hours = Math.floor(etaInSeconds / 3600);
|
||||
const minutes = Math.floor((etaInSeconds % 3600) / 60);
|
||||
const seconds = Math.floor(etaInSeconds % 60);
|
||||
const hours = Math.floor(etaInSeconds / 3600);
|
||||
const minutes = Math.floor((etaInSeconds % 3600) / 60);
|
||||
const seconds = Math.floor(etaInSeconds % 60);
|
||||
|
||||
let formattedETA = '';
|
||||
|
||||
if (hours > 0) {
|
||||
formattedETA += `${hours.toString().padStart(2, '0')}h `;
|
||||
}
|
||||
if (minutes > 0) {
|
||||
formattedETA += `${minutes.toString().padStart(2, '0')}m `;
|
||||
}
|
||||
let formattedETA = '';
|
||||
|
||||
formattedETA += `${seconds.toString().padStart(2, '0')}s`;
|
||||
if (hours > 0) {
|
||||
formattedETA += `${hours.toString().padStart(2, '0')}h `;
|
||||
}
|
||||
if (minutes > 0) {
|
||||
formattedETA += `${minutes.toString().padStart(2, '0')}m `;
|
||||
}
|
||||
|
||||
return formattedETA;
|
||||
formattedETA += `${seconds.toString().padStart(2, '0')}s`;
|
||||
|
||||
return formattedETA;
|
||||
}
|
||||
|
||||
|
||||
export function randomString(length: number): string {
|
||||
const alphanumericChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||
//const specialChars = '!@#$%^&*()_+~`|}{[]:;?><,./-=';
|
||||
@@ -71,4 +74,24 @@ export function randomString(length: number): string {
|
||||
).join('');
|
||||
|
||||
return randomString;
|
||||
}
|
||||
}
|
||||
|
||||
export function takeMidStr(input: string, startMarker: string, endMarker: string): string {
|
||||
const startIndex = input.indexOf(startMarker) + startMarker.length;
|
||||
const endIndex = input.indexOf(endMarker, startIndex);
|
||||
return input.substring(startIndex, endIndex);
|
||||
}
|
||||
|
||||
//取字符串右边
|
||||
export function takeRightStr(str: string, taggedStr: string) {
|
||||
return str.substring(str.indexOf(taggedStr) + taggedStr.length, str.length)
|
||||
}
|
||||
|
||||
//下载文件
|
||||
export async function downloadFile(url: string, path: string) {
|
||||
await invoke('download_file', {
|
||||
url: url,
|
||||
outPath: path
|
||||
})
|
||||
return await fs.exists(path)
|
||||
}
|
||||
Reference in New Issue
Block a user