feat: 添加二维码生成功能并简化架构

- 新增二维码生成服务层,支持自定义内容和配置
  - 移除 Platform 抽象层,简化为三层架构
  - 更新开发文档和架构说明
  - 添加前端二维码生成页面和状态管理
This commit is contained in:
2026-02-10 19:06:36 +08:00
parent 927eaa1e03
commit b2754bdad5
24 changed files with 1642 additions and 278 deletions

648
src-tauri/Cargo.lock generated
View File

@@ -17,6 +17,24 @@ dependencies = [
"memchr",
]
[[package]]
name = "aligned"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee4508988c62edf04abd8d92897fca0c2995d907ce1dfeaf369dac3716a40685"
dependencies = [
"as-slice",
]
[[package]]
name = "aligned-vec"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc890384c8602f339876ded803c97ad529f3842aba97f6392b3dba0dd171769b"
dependencies = [
"equator",
]
[[package]]
name = "alloc-no-stdlib"
version = "2.0.4"
@@ -47,6 +65,38 @@ version = "1.0.101"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f0e0fee31ef5ed1ba1316088939cea399010ed7731dba877ed44aeb407a75ea"
[[package]]
name = "arbitrary"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1"
[[package]]
name = "arg_enum_proc_macro"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.114",
]
[[package]]
name = "arrayvec"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
[[package]]
name = "as-slice"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "516b6b4f0e40d50dcda9365d53964ec74560ad4284da2e7fc97122cd83174516"
dependencies = [
"stable_deref_trait",
]
[[package]]
name = "async-broadcast"
version = "0.7.2"
@@ -213,6 +263,49 @@ version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]]
name = "av-scenechange"
version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f321d77c20e19b92c39e7471cf986812cbb46659d2af674adc4331ef3f18394"
dependencies = [
"aligned",
"anyhow",
"arg_enum_proc_macro",
"arrayvec",
"log",
"num-rational",
"num-traits",
"pastey",
"rayon",
"thiserror 2.0.18",
"v_frame",
"y4m",
]
[[package]]
name = "av1-grain"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8cfddb07216410377231960af4fcab838eaa12e013417781b78bd95ee22077f8"
dependencies = [
"anyhow",
"arrayvec",
"log",
"nom",
"num-rational",
"v_frame",
]
[[package]]
name = "avif-serialize"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47c8fbc0f831f4519fe8b810b6a7a91410ec83031b8233f730a0480029f6a23f"
dependencies = [
"arrayvec",
]
[[package]]
name = "base64"
version = "0.21.7"
@@ -225,6 +318,12 @@ version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "bit_field"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e4b40c7323adcfc0a41c4b88143ed58346ff65a288fc144329c5c45e05d70c6"
[[package]]
name = "bitflags"
version = "1.3.2"
@@ -240,6 +339,15 @@ dependencies = [
"serde_core",
]
[[package]]
name = "bitstream-io"
version = "4.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60d4bd9d1db2c6bdf285e223a7fa369d5ce98ec767dec949c6ca62863ce61757"
dependencies = [
"core2",
]
[[package]]
name = "block-buffer"
version = "0.10.4"
@@ -292,6 +400,12 @@ dependencies = [
"alloc-stdlib",
]
[[package]]
name = "built"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4ad8f11f288f48ca24471bbd51ac257aaeaaa07adae295591266b792902ae64"
[[package]]
name = "bumpalo"
version = "3.19.1"
@@ -310,6 +424,12 @@ version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "byteorder-lite"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
[[package]]
name = "bytes"
version = "1.11.1"
@@ -393,6 +513,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47b26a0954ae34af09b50f0de26458fa95369a0d478d8236d3f93082b219bd29"
dependencies = [
"find-msvc-tools",
"jobserver",
"libc",
"shlex",
]
@@ -441,6 +563,12 @@ dependencies = [
"windows-link 0.2.1",
]
[[package]]
name = "color_quant"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
[[package]]
name = "combine"
version = "4.6.7"
@@ -516,6 +644,15 @@ dependencies = [
"libc",
]
[[package]]
name = "core2"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505"
dependencies = [
"memchr",
]
[[package]]
name = "cpufeatures"
version = "0.2.17"
@@ -568,6 +705,12 @@ version = "0.8.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
[[package]]
name = "crunchy"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
[[package]]
name = "crypto-common"
version = "0.1.7"
@@ -843,6 +986,26 @@ dependencies = [
"syn 2.0.114",
]
[[package]]
name = "equator"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4711b213838dfee0117e3be6ac926007d7f433d7bbe33595975d4190cb07e6fc"
dependencies = [
"equator-macro",
]
[[package]]
name = "equator-macro"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.114",
]
[[package]]
name = "equivalent"
version = "1.0.2"
@@ -891,12 +1054,47 @@ dependencies = [
"pin-project-lite",
]
[[package]]
name = "exr"
version = "1.74.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4300e043a56aa2cb633c01af81ca8f699a321879a7854d3896a0ba89056363be"
dependencies = [
"bit_field",
"half",
"lebe",
"miniz_oxide",
"rayon-core",
"smallvec",
"zune-inflate",
]
[[package]]
name = "fastrand"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
[[package]]
name = "fax"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f05de7d48f37cd6730705cbca900770cab77a89f413d23e100ad7fad7795a0ab"
dependencies = [
"fax_derive",
]
[[package]]
name = "fax_derive"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0aca10fb742cb43f9e7bb8467c91aa9bcb8e3ffbc6a6f7389bb93ffc920577d"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.114",
]
[[package]]
name = "fdeflate"
version = "0.3.7"
@@ -1267,6 +1465,16 @@ dependencies = [
"wasip3",
]
[[package]]
name = "gif"
version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f5df2ba84018d80c213569363bdcd0c64e6933c67fe4c1d60ecf822971a3c35e"
dependencies = [
"color_quant",
"weezl",
]
[[package]]
name = "gio"
version = "0.18.4"
@@ -1433,6 +1641,17 @@ dependencies = [
"syn 2.0.114",
]
[[package]]
name = "half"
version = "2.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b"
dependencies = [
"cfg-if",
"crunchy",
"zerocopy",
]
[[package]]
name = "hashbrown"
version = "0.12.3"
@@ -1604,7 +1823,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e795dff5605e0f04bff85ca41b51a96b83e80b281e96231bcaaf1ac35103371"
dependencies = [
"byteorder",
"png",
"png 0.17.16",
]
[[package]]
@@ -1721,6 +1940,46 @@ dependencies = [
"icu_properties",
]
[[package]]
name = "image"
version = "0.25.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6506c6c10786659413faa717ceebcb8f70731c0a60cbae39795fdf114519c1a"
dependencies = [
"bytemuck",
"byteorder-lite",
"color_quant",
"exr",
"gif",
"image-webp",
"moxcms",
"num-traits",
"png 0.18.0",
"qoi",
"ravif",
"rayon",
"rgb",
"tiff",
"zune-core 0.5.1",
"zune-jpeg 0.5.12",
]
[[package]]
name = "image-webp"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "525e9ff3e1a4be2fbea1fdf0e98686a6d98b4d8f937e1bf7402245af1909e8c3"
dependencies = [
"byteorder-lite",
"quick-error",
]
[[package]]
name = "imgref"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7c5cedc30da3a610cac6b4ba17597bdf7152cf974e8aab3afb3d54455e371c8"
[[package]]
name = "indexmap"
version = "1.9.3"
@@ -1753,6 +2012,17 @@ dependencies = [
"cfb",
]
[[package]]
name = "interpolate_name"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.114",
]
[[package]]
name = "ipnet"
version = "2.11.0"
@@ -1788,6 +2058,15 @@ dependencies = [
"once_cell",
]
[[package]]
name = "itertools"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "1.0.17"
@@ -1839,6 +2118,16 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
[[package]]
name = "jobserver"
version = "0.1.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33"
dependencies = [
"getrandom 0.3.4",
"libc",
]
[[package]]
name = "js-sys"
version = "0.3.85"
@@ -1906,6 +2195,12 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
[[package]]
name = "lebe"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a79a3332a6609480d7d0c9eab957bca6b455b91bb84e66d19f5ff66294b85b8"
[[package]]
name = "libappindicator"
version = "0.9.0"
@@ -1936,6 +2231,16 @@ version = "0.2.181"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "459427e2af2b9c839b132acb702a1c654d95e10f8c326bfc2ad11310e458b1c5"
[[package]]
name = "libfuzzer-sys"
version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5037190e1f70cbeef565bd267599242926f724d3b8a9f510fd7e0b540cfa4404"
dependencies = [
"arbitrary",
"cc",
]
[[package]]
name = "libloading"
version = "0.7.4"
@@ -1983,6 +2288,15 @@ version = "0.4.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
[[package]]
name = "loop9"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062"
dependencies = [
"imgref",
]
[[package]]
name = "mac"
version = "0.1.1"
@@ -2020,6 +2334,16 @@ version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5"
[[package]]
name = "maybe-rayon"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519"
dependencies = [
"cfg-if",
"rayon",
]
[[package]]
name = "memchr"
version = "2.8.0"
@@ -2062,6 +2386,16 @@ dependencies = [
"windows-sys 0.61.2",
]
[[package]]
name = "moxcms"
version = "0.7.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac9557c559cd6fc9867e122e20d2cbefc9ca29d80d027a8e39310920ed2f0a97"
dependencies = [
"num-traits",
"pxfm",
]
[[package]]
name = "muda"
version = "0.17.1"
@@ -2077,7 +2411,7 @@ dependencies = [
"objc2-core-foundation",
"objc2-foundation",
"once_cell",
"png",
"png 0.17.16",
"serde",
"thiserror 2.0.18",
"windows-sys 0.60.2",
@@ -2125,6 +2459,21 @@ version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb"
[[package]]
name = "nom"
version = "8.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405"
dependencies = [
"memchr",
]
[[package]]
name = "noop_proc_macro"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8"
[[package]]
name = "ntapi"
version = "0.4.2"
@@ -2134,12 +2483,53 @@ dependencies = [
"winapi",
]
[[package]]
name = "num-bigint"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
dependencies = [
"num-integer",
"num-traits",
]
[[package]]
name = "num-conv"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050"
[[package]]
name = "num-derive"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.114",
]
[[package]]
name = "num-integer"
version = "0.1.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
dependencies = [
"num-traits",
]
[[package]]
name = "num-rational"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824"
dependencies = [
"num-bigint",
"num-integer",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.19"
@@ -2471,6 +2861,18 @@ dependencies = [
"windows-link 0.2.1",
]
[[package]]
name = "paste"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
[[package]]
name = "pastey"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35fb2e5f958ec131621fdd531e9fc186ed768cbe395337403ae56c17a74c68ec"
[[package]]
name = "pathdiff"
version = "0.2.3"
@@ -2672,6 +3074,19 @@ dependencies = [
"miniz_oxide",
]
[[package]]
name = "png"
version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97baced388464909d42d89643fe4361939af9b7ce7a31ee32a168f832a70f2a0"
dependencies = [
"bitflags 2.10.0",
"crc32fast",
"fdeflate",
"flate2",
"miniz_oxide",
]
[[package]]
name = "polling"
version = "3.11.0"
@@ -2794,6 +3209,58 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "profiling"
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773"
dependencies = [
"profiling-procmacros",
]
[[package]]
name = "profiling-procmacros"
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52717f9a02b6965224f95ca2a81e2e0c5c43baacd28ca057577988930b6c3d5b"
dependencies = [
"quote",
"syn 2.0.114",
]
[[package]]
name = "pxfm"
version = "0.1.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7186d3822593aa4393561d186d1393b3923e9d6163d3fbfd6e825e3e6cf3e6a8"
dependencies = [
"num-traits",
]
[[package]]
name = "qoi"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001"
dependencies = [
"bytemuck",
]
[[package]]
name = "qrcode"
version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d68782463e408eb1e668cf6152704bd856c78c5b6417adaee3203d8f4c1fc9ec"
dependencies = [
"image",
]
[[package]]
name = "quick-error"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
[[package]]
name = "quick-xml"
version = "0.38.4"
@@ -2843,6 +3310,16 @@ dependencies = [
"rand_core 0.6.4",
]
[[package]]
name = "rand"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
dependencies = [
"rand_chacha 0.9.0",
"rand_core 0.9.5",
]
[[package]]
name = "rand_chacha"
version = "0.2.2"
@@ -2863,6 +3340,16 @@ dependencies = [
"rand_core 0.6.4",
]
[[package]]
name = "rand_chacha"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
dependencies = [
"ppv-lite86",
"rand_core 0.9.5",
]
[[package]]
name = "rand_core"
version = "0.5.1"
@@ -2881,6 +3368,15 @@ dependencies = [
"getrandom 0.2.17",
]
[[package]]
name = "rand_core"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c"
dependencies = [
"getrandom 0.3.4",
]
[[package]]
name = "rand_hc"
version = "0.2.0"
@@ -2899,6 +3395,56 @@ dependencies = [
"rand_core 0.5.1",
]
[[package]]
name = "rav1e"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43b6dd56e85d9483277cde964fd1bdb0428de4fec5ebba7540995639a21cb32b"
dependencies = [
"aligned-vec",
"arbitrary",
"arg_enum_proc_macro",
"arrayvec",
"av-scenechange",
"av1-grain",
"bitstream-io",
"built",
"cfg-if",
"interpolate_name",
"itertools",
"libc",
"libfuzzer-sys",
"log",
"maybe-rayon",
"new_debug_unreachable",
"noop_proc_macro",
"num-derive",
"num-traits",
"paste",
"profiling",
"rand 0.9.2",
"rand_chacha 0.9.0",
"simd_helpers",
"thiserror 2.0.18",
"v_frame",
"wasm-bindgen",
]
[[package]]
name = "ravif"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef69c1990ceef18a116855938e74793a5f7496ee907562bd0857b6ac734ab285"
dependencies = [
"avif-serialize",
"imgref",
"loop9",
"quick-error",
"rav1e",
"rayon",
"rgb",
]
[[package]]
name = "raw-window-handle"
version = "0.6.2"
@@ -3028,6 +3574,12 @@ dependencies = [
"web-sys",
]
[[package]]
name = "rgb"
version = "0.8.52"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c6a884d2998352bb4daf0183589aec883f16a6da1f4dde84d8e2e9a5409a1ce"
[[package]]
name = "rustc_version"
version = "0.4.1"
@@ -3342,6 +3894,15 @@ version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2"
[[package]]
name = "simd_helpers"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6"
dependencies = [
"quote",
]
[[package]]
name = "siphasher"
version = "0.3.11"
@@ -3654,6 +4215,9 @@ dependencies = [
name = "tauri-app"
version = "0.1.0"
dependencies = [
"base64 0.22.1",
"image",
"qrcode",
"serde",
"serde_derive",
"serde_json",
@@ -3699,7 +4263,7 @@ dependencies = [
"ico",
"json-patch",
"plist",
"png",
"png 0.17.16",
"proc-macro2",
"quote",
"semver",
@@ -3948,6 +4512,20 @@ dependencies = [
"syn 2.0.114",
]
[[package]]
name = "tiff"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af9605de7fee8d9551863fd692cce7637f548dbd9db9180fcc07ccc6d26c336f"
dependencies = [
"fax",
"flate2",
"half",
"quick-error",
"weezl",
"zune-jpeg 0.4.21",
]
[[package]]
name = "time"
version = "0.3.47"
@@ -4204,7 +4782,7 @@ dependencies = [
"objc2-core-graphics",
"objc2-foundation",
"once_cell",
"png",
"png 0.17.16",
"serde",
"thiserror 2.0.18",
"windows-sys 0.60.2",
@@ -4347,6 +4925,17 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "v_frame"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "666b7727c8875d6ab5db9533418d7c764233ac9c0cff1d469aec8fa127597be2"
dependencies = [
"aligned-vec",
"num-traits",
"wasm-bindgen",
]
[[package]]
name = "version-compare"
version = "0.2.1"
@@ -4624,6 +5213,12 @@ dependencies = [
"windows-core 0.61.2",
]
[[package]]
name = "weezl"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88"
[[package]]
name = "winapi"
version = "0.3.9"
@@ -5401,6 +5996,12 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56"
[[package]]
name = "y4m"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a5a4b21e1a62b67a2970e6831bc091d7b87e119e7f9791aef9702e3bef04448"
[[package]]
name = "yoke"
version = "0.8.1"
@@ -5565,6 +6166,45 @@ version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4de98dfa5d5b7fef4ee834d0073d560c9ca7b6c46a71d058c48db7960f8cfaf7"
[[package]]
name = "zune-core"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a"
[[package]]
name = "zune-core"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb8a0807f7c01457d0379ba880ba6322660448ddebc890ce29bb64da71fb40f9"
[[package]]
name = "zune-inflate"
version = "0.2.54"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02"
dependencies = [
"simd-adler32",
]
[[package]]
name = "zune-jpeg"
version = "0.4.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29ce2c8a9384ad323cf564b67da86e21d3cfdff87908bc1223ed5c99bc792713"
dependencies = [
"zune-core 0.4.12",
]
[[package]]
name = "zune-jpeg"
version = "0.5.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "410e9ecef634c709e3831c2cfdb8d9c32164fae1c67496d5b68fff728eec37fe"
dependencies = [
"zune-core 0.5.1",
]
[[package]]
name = "zvariant"
version = "5.9.2"

View File

@@ -24,6 +24,9 @@ tauri-plugin-global-shortcut = "2"
serde = { version = "1", features = ["derive"] }
serde_json = { version = "1", features = ["preserve_order"] }
sysinfo = "0.30"
qrcode = "0.14"
image = "0.25"
base64 = "0.22"
[target.'cfg(windows)'.dependencies]
windows = { version = "0.58", features = [

View File

@@ -4,5 +4,6 @@
pub mod json_format_commands;
pub mod picker_color_commands;
pub mod qrcode_commands;
pub mod system_info_commands;
pub mod window_commands;

View File

@@ -0,0 +1,71 @@
//! 二维码生成命令
//!
//! 定义二维码生成相关的 Tauri 命令
use crate::models::qrcode::{QrConfig, QrResult};
use crate::services::qrcode_service::QrCodeService;
/// 生成二维码预览
///
/// Tauri 命令,用于从前端调用生成二维码预览
///
/// # 参数
///
/// * `config` - 二维码配置
///
/// # 返回
///
/// 返回二维码生成结果,包含 Base64 编码的图片数据
///
/// # 前端调用示例
///
/// ```typescript
/// import { invoke } from '@tauri-apps/api/core';
///
/// const result = await invoke('generate_qr_preview', {
/// config: {
/// content: 'https://example.com',
/// size: 512,
/// margin: 4,
/// errorCorrection: 'M'
/// }
/// });
/// console.log(result.data); // "data:image/png;base64,..."
/// ```
#[tauri::command]
pub async fn generate_qr_preview(config: QrConfig) -> Result<QrResult, String> {
QrCodeService::generate_preview(&config).map_err(|e| e.to_string())
}
/// 生成二维码并保存到文件
///
/// Tauri 命令,用于将生成的二维码保存为文件
///
/// # 参数
///
/// * `config` - 二维码配置
/// * `output_path` - 输出文件路径
///
/// # 返回
///
/// 成功时返回 Ok(()),失败时返回错误字符串
///
/// # 前端调用示例
///
/// ```typescript
/// import { invoke } from '@tauri-apps/api/core';
///
/// await invoke('generate_qr_file', {
/// config: {
/// content: 'https://example.com',
/// size: 1024,
/// margin: 4,
/// errorCorrection: 'H'
/// },
/// outputPath: '/path/to/output.png'
/// });
/// ```
#[tauri::command]
pub async fn generate_qr_file(config: QrConfig, output_path: String) -> Result<(), String> {
QrCodeService::generate_to_file(&config, &output_path).map_err(|e| e.to_string())
}

View File

@@ -43,6 +43,21 @@ pub enum AppError {
///
/// 表示获取系统信息时失败
SystemInfoFailed(String),
/// 无效数据
///
/// 表示提供的数据无效或不符合要求
InvalidData(String),
/// IO 错误
///
/// 表示文件或网络 IO 操作失败
IoError(String),
/// 二维码生成失败
///
/// 表示二维码生成过程失败
QrCodeGenerationFailed(String),
}
impl fmt::Display for AppError {
@@ -55,6 +70,9 @@ impl fmt::Display for AppError {
AppError::InvalidColorData(msg) => write!(f, "颜色数据无效: {}", msg),
AppError::ColorConversionFailed(msg) => write!(f, "颜色转换失败: {}", msg),
AppError::SystemInfoFailed(msg) => write!(f, "系统信息获取失败: {}", msg),
AppError::InvalidData(msg) => write!(f, "数据无效: {}", msg),
AppError::IoError(msg) => write!(f, "IO 错误: {}", msg),
AppError::QrCodeGenerationFailed(msg) => write!(f, "二维码生成失败: {}", msg),
}
}
}

View File

@@ -48,6 +48,9 @@ pub fn run() {
commands::json_format_commands::compact_json,
// 操作系统信息命令
commands::system_info_commands::get_system_info,
// 二维码生成命令
commands::qrcode_commands::generate_qr_preview,
commands::qrcode_commands::generate_qr_file,
])
.run(tauri::generate_context!())
.expect("运行 Tauri 应用时出错");

View File

@@ -4,4 +4,5 @@
pub mod color;
pub mod json_format;
pub mod qrcode;
pub mod system_info;

View File

@@ -0,0 +1,63 @@
//! 二维码生成相关数据模型
use serde::{Deserialize, Serialize};
/// 二维码配置
///
/// 定义生成二维码所需的参数
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QrConfig {
/// 二维码内容
pub content: String,
/// 输出尺寸(像素)
pub size: u32,
/// 边距(模块数)
pub margin: u32,
/// 容错级别: "L", "M", "Q", "H"
pub error_correction: String,
}
/// 二维码样式(阶段 2 使用)
///
/// 定义二维码的视觉样式
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QrStyle {
/// 点形状: "square", "circle", "rounded"
pub dot_shape: String,
/// 码眼形状: "square", "circle", "rounded"
pub eye_shape: String,
/// 前景色Hex 颜色代码)
pub foreground_color: String,
/// 背景色Hex 颜色代码)
pub background_color: String,
/// 是否使用渐变
pub is_gradient: bool,
/// 渐变颜色列表(如果 is_gradient 为 true
pub gradient_colors: Option<Vec<String>>,
}
/// Logo 配置(阶段 2 使用)
///
/// 定义 Logo 的位置和样式
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LogoConfig {
/// Logo 文件路径
pub path: String,
/// 缩放比例 (0.1 - 0.3)
pub scale: f32,
/// 是否添加边框
pub has_border: bool,
/// 边框宽度(像素)
pub border_width: u32,
}
/// 二维码生成结果
///
/// 包含生成的二维码图片数据
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QrResult {
/// Base64 编码的图片数据
pub data: String,
/// 图片格式(如 "png"
pub format: String,
}

View File

@@ -3,5 +3,6 @@
//! 提供应用的核心业务逻辑实现
pub mod json_format_service;
pub mod qrcode_service;
pub mod system_info_service;
pub mod window_service;

View File

@@ -0,0 +1,136 @@
//! 二维码生成服务
//!
//! 提供二维码生成的核心业务逻辑
use crate::error::{AppError, AppResult};
use crate::models::qrcode::{QrConfig, QrResult};
use crate::utils::qrcode_renderer::{image_to_base64, render_basic_qr};
/// 二维码生成服务
pub struct QrCodeService;
impl QrCodeService {
/// 生成二维码预览
///
/// 根据配置生成二维码并返回 Base64 编码的图片数据
///
/// # 参数
///
/// * `config` - 二维码配置
///
/// # 返回
///
/// 返回二维码生成结果,包含 Base64 编码的图片数据
///
/// # 错误
///
/// - 配置无效时返回 `AppError::InvalidData`
/// - 图片编码失败时返回 `AppError::IoError`
pub fn generate_preview(config: &QrConfig) -> AppResult<QrResult> {
// 验证尺寸
if config.size < 128 || config.size > 4096 {
return Err(AppError::InvalidData(
"尺寸必须在 128 到 4096 之间".to_string(),
));
}
// 验证边距
if config.margin > 50 {
return Err(AppError::InvalidData("边距不能超过 50".to_string()));
}
// 渲染二维码
let img = render_basic_qr(config)?;
// 转换为 Base64
let base64_data = image_to_base64(&img)?;
Ok(QrResult {
data: base64_data,
format: "png".to_string(),
})
}
/// 生成二维码并保存到文件
///
/// 根据配置生成二维码并保存为 PNG 文件
///
/// # 参数
///
/// * `config` - 二维码配置
/// * `output_path` - 输出文件路径
///
/// # 返回
///
/// 成功时返回 Ok(()),失败时返回错误
///
/// # 错误
///
/// - 配置无效时返回 `AppError::InvalidData`
/// - 文件写入失败时返回 `AppError::IoError`
pub fn generate_to_file(config: &QrConfig, output_path: &str) -> AppResult<()> {
// 验证尺寸
if config.size < 128 || config.size > 4096 {
return Err(AppError::InvalidData(
"尺寸必须在 128 到 4096 之间".to_string(),
));
}
// 渲染二维码
let img = render_basic_qr(config)?;
// 保存到文件
img.save(output_path)
.map_err(|e| AppError::IoError(format!("保存文件失败: {}", e)))?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_generate_preview() {
let config = QrConfig {
content: "https://example.com".to_string(),
size: 512,
margin: 4,
error_correction: "M".to_string(),
};
let result = QrCodeService::generate_preview(&config);
assert!(result.is_ok());
let qr_result = result.unwrap();
assert!(qr_result.data.starts_with("data:image/png;base64,"));
assert_eq!(qr_result.format, "png");
}
#[test]
fn test_generate_preview_invalid_size() {
let config = QrConfig {
content: "https://example.com".to_string(),
size: 50, // 太小
margin: 4,
error_correction: "M".to_string(),
};
let result = QrCodeService::generate_preview(&config);
assert!(result.is_err());
}
#[test]
fn test_generate_preview_invalid_margin() {
let config = QrConfig {
content: "https://example.com".to_string(),
size: 512,
margin: 100, // 太大
error_correction: "M".to_string(),
};
let result = QrCodeService::generate_preview(&config);
assert!(result.is_err());
}
}

View File

@@ -4,5 +4,6 @@
pub mod color_conversion;
pub mod json_formatter;
pub mod qrcode_renderer;
pub mod screen;
pub mod shortcut;

View File

@@ -0,0 +1,153 @@
//! 二维码渲染工具函数
//!
//! 提供二维码矩阵到图像的渲染功能
use crate::error::{AppError, AppResult};
use crate::models::qrcode::QrConfig;
use base64::Engine;
use image::Luma;
use image::Rgba;
use image::RgbaImage;
use qrcode::QrCode;
use std::io::Cursor;
/// 渲染基础黑白二维码
///
/// 根据配置生成黑白二维码图片
///
/// # 参数
///
/// * `config` - 二维码配置
///
/// # 返回
///
/// 返回生成的图片数据
///
/// # 错误
///
/// - 二维码内容为空时返回 `InvalidData`
/// - 二维码生成失败时返回相应错误
pub fn render_basic_qr(config: &QrConfig) -> AppResult<RgbaImage> {
// 验证内容
if config.content.trim().is_empty() {
return Err(AppError::InvalidData(
"二维码内容不能为空".to_string(),
));
}
// 解析容错级别
let ec_level = match config.error_correction.as_str() {
"L" => qrcode::EcLevel::L,
"M" => qrcode::EcLevel::M,
"Q" => qrcode::EcLevel::Q,
"H" => qrcode::EcLevel::H,
_ => qrcode::EcLevel::M, // 默认使用 M 级别
};
// 生成二维码
let qr_code = QrCode::with_error_correction_level(config.content.as_bytes(), ec_level)
.map_err(|e| AppError::InvalidData(format!("二维码生成失败: {}", e)))?;
// 计算包含边距的尺寸
let qr_size = qr_code.width() as u32;
let total_size = qr_size + 2 * config.margin;
// 创建图像缓冲区Luma 格式,用于生成二维码)
let qr_image = qr_code.render::<Luma<u8>>().quiet_zone(false).min_dimensions(total_size, total_size).max_dimensions(total_size, total_size).build();
// 获取二维码图像尺寸
let (width, height) = qr_image.dimensions();
// 计算缩放比例以匹配目标尺寸
let scale = (config.size as f32 / width as f32).max(1.0) as u32;
let scaled_width = width * scale;
let scaled_height = height * scale;
// 创建 RGBA 图像并填充白色背景
let mut img = RgbaImage::new(scaled_width, scaled_height);
for pixel in img.pixels_mut() {
*pixel = Rgba([255, 255, 255, 255]);
}
// 渲染二维码
for y in 0..height {
for x in 0..width {
let pixel = qr_image.get_pixel(x, y);
// 如果是黑色像素
if pixel[0] == 0 {
// 计算缩放后的区域
let start_x = x * scale;
let start_y = y * scale;
let end_x = start_x + scale;
let end_y = start_y + scale;
// 绘制黑色矩形
for py in start_y..end_y.min(scaled_height) {
for px in start_x..end_x.min(scaled_width) {
img.put_pixel(px, py, Rgba([0, 0, 0, 255]));
}
}
}
}
}
Ok(img)
}
/// 将图片转换为 Base64 字符串
///
/// # 参数
///
/// * `img` - 图片数据
///
/// # 返回
///
/// 返回 Base64 编码的 PNG 图片数据(带 data URL 前缀)
pub fn image_to_base64(img: &RgbaImage) -> AppResult<String> {
let mut bytes = Vec::new();
// 写入 PNG 格式
let mut cursor = Cursor::new(&mut bytes);
img.write_to(&mut cursor, image::ImageFormat::Png)
.map_err(|e| AppError::IoError(format!("图片编码失败: {}", e)))?;
// Base64 编码
let base64_str = base64::engine::general_purpose::STANDARD.encode(&bytes);
Ok(format!("data:image/png;base64,{}", base64_str))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_render_basic_qr() {
let config = QrConfig {
content: "https://example.com".to_string(),
size: 512,
margin: 4,
error_correction: "M".to_string(),
};
let result = render_basic_qr(&config);
assert!(result.is_ok());
let img = result.unwrap();
assert_eq!(img.dimensions().0, 512);
assert_eq!(img.dimensions().1, 512);
}
#[test]
fn test_render_empty_content() {
let config = QrConfig {
content: "".to_string(),
size: 512,
margin: 4,
error_correction: "M".to_string(),
};
let result = render_basic_qr(&config);
assert!(result.is_err());
}
}