feat: 优化排版,完善获取git提交记录以及预览功能

This commit is contained in:
Ben
2025-04-25 18:15:48 +08:00
parent 33cc46f03e
commit d43be4c058
19 changed files with 957 additions and 221 deletions

View File

@@ -6,6 +6,7 @@
"dependencies": { "dependencies": {
"@tauri-apps/api": "^2", "@tauri-apps/api": "^2",
"@tauri-apps/plugin-dialog": "^2", "@tauri-apps/plugin-dialog": "^2",
"@tauri-apps/plugin-http": "~2",
"@tauri-apps/plugin-opener": "^2", "@tauri-apps/plugin-opener": "^2",
"antd": "^5.24.8", "antd": "^5.24.8",
"react": "^18.3.1", "react": "^18.3.1",
@@ -277,6 +278,8 @@
"@tauri-apps/plugin-dialog": ["@tauri-apps/plugin-dialog@2.2.1", "", { "dependencies": { "@tauri-apps/api": "^2.0.0" } }, "sha512-wZmCouo4PgTosh/UoejPw9DPs6RllS5Pp3fuOV2JobCu36mR5AXU2MzU9NZiVaFi/5Zfc8RN0IhcZHnksJ1o8A=="], "@tauri-apps/plugin-dialog": ["@tauri-apps/plugin-dialog@2.2.1", "", { "dependencies": { "@tauri-apps/api": "^2.0.0" } }, "sha512-wZmCouo4PgTosh/UoejPw9DPs6RllS5Pp3fuOV2JobCu36mR5AXU2MzU9NZiVaFi/5Zfc8RN0IhcZHnksJ1o8A=="],
"@tauri-apps/plugin-http": ["@tauri-apps/plugin-http@2.4.3", "", { "dependencies": { "@tauri-apps/api": "^2.0.0" } }, "sha512-Us8X+FikzpaZRNr4kH4HLwyXascHbM42p6LxAqRTQnHPrrqp1usaH4vxWAZalPvTbHJ3gBEMJPHusFJgtjGJjA=="],
"@tauri-apps/plugin-opener": ["@tauri-apps/plugin-opener@2.2.6", "", { "dependencies": { "@tauri-apps/api": "^2.0.0" } }, "sha512-bSdkuP71ZQRepPOn8BOEdBKYJQvl6+jb160QtJX/i2H9BF6ZySY/kYljh76N2Ne5fJMQRge7rlKoStYQY5Jq1w=="], "@tauri-apps/plugin-opener": ["@tauri-apps/plugin-opener@2.2.6", "", { "dependencies": { "@tauri-apps/api": "^2.0.0" } }, "sha512-bSdkuP71ZQRepPOn8BOEdBKYJQvl6+jb160QtJX/i2H9BF6ZySY/kYljh76N2Ne5fJMQRge7rlKoStYQY5Jq1w=="],
"@types/babel__core": ["@types/babel__core@7.20.5", "", { "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", "@types/babel__generator": "*", "@types/babel__template": "*", "@types/babel__traverse": "*" } }, "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA=="], "@types/babel__core": ["@types/babel__core@7.20.5", "", { "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", "@types/babel__generator": "*", "@types/babel__template": "*", "@types/babel__traverse": "*" } }, "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA=="],

View File

@@ -4,11 +4,9 @@
"version": "0.1.0", "version": "0.1.0",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "tauri dev",
"build": "tsc && vite build", "vite:dev": "vite",
"preview": "vite preview",
"tauri": "tauri", "tauri": "tauri",
"td": "tauri dev",
"lint": "bunx @biomejs/biome lint ./src", "lint": "bunx @biomejs/biome lint ./src",
"format": "bunx @biomejs/biome format --write ./src", "format": "bunx @biomejs/biome format --write ./src",
"check": "bunx @biomejs/biome check --apply ./src" "check": "bunx @biomejs/biome check --apply ./src"
@@ -16,6 +14,7 @@
"dependencies": { "dependencies": {
"@tauri-apps/api": "^2", "@tauri-apps/api": "^2",
"@tauri-apps/plugin-dialog": "^2", "@tauri-apps/plugin-dialog": "^2",
"@tauri-apps/plugin-http": "~2",
"@tauri-apps/plugin-opener": "^2", "@tauri-apps/plugin-opener": "^2",
"antd": "^5.24.8", "antd": "^5.24.8",
"react": "^18.3.1", "react": "^18.3.1",

372
src-tauri/Cargo.lock generated
View File

@@ -549,10 +549,39 @@ version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747"
dependencies = [ dependencies = [
"percent-encoding",
"time", "time",
"version_check", "version_check",
] ]
[[package]]
name = "cookie_store"
version = "0.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2eac901828f88a5241ee0600950ab981148a18f2f756900ffba1b125ca6a3ef9"
dependencies = [
"cookie",
"document-features",
"idna",
"log",
"publicsuffix",
"serde",
"serde_derive",
"serde_json",
"time",
"url",
]
[[package]]
name = "core-foundation"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
dependencies = [
"core-foundation-sys",
"libc",
]
[[package]] [[package]]
name = "core-foundation" name = "core-foundation"
version = "0.10.0" version = "0.10.0"
@@ -576,7 +605,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1" checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1"
dependencies = [ dependencies = [
"bitflags 2.9.0", "bitflags 2.9.0",
"core-foundation", "core-foundation 0.10.0",
"core-graphics-types", "core-graphics-types",
"foreign-types", "foreign-types",
"libc", "libc",
@@ -589,7 +618,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb"
dependencies = [ dependencies = [
"bitflags 2.9.0", "bitflags 2.9.0",
"core-foundation", "core-foundation 0.10.0",
"libc", "libc",
] ]
@@ -646,6 +675,7 @@ dependencies = [
"tauri", "tauri",
"tauri-build", "tauri-build",
"tauri-plugin-dialog", "tauri-plugin-dialog",
"tauri-plugin-http",
"tauri-plugin-opener", "tauri-plugin-opener",
] ]
@@ -721,6 +751,12 @@ dependencies = [
"syn 2.0.100", "syn 2.0.100",
] ]
[[package]]
name = "data-url"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c297a1c74b71ae29df00c3e22dd9534821d60eb9af5a0192823fa2acea70c2a"
[[package]] [[package]]
name = "deranged" name = "deranged"
version = "0.4.0" version = "0.4.0"
@@ -837,6 +873,15 @@ dependencies = [
"syn 2.0.100", "syn 2.0.100",
] ]
[[package]]
name = "document-features"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95249b50c6c185bee49034bcb378a49dc2b5dff0be90ff6616d31d64febab05d"
dependencies = [
"litrs",
]
[[package]] [[package]]
name = "dpi" name = "dpi"
version = "0.1.1" version = "0.1.1"
@@ -893,6 +938,15 @@ version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ef6b89e5b37196644d8796de5268852ff179b44e96276cf4290264843743bb7" checksum = "4ef6b89e5b37196644d8796de5268852ff179b44e96276cf4290264843743bb7"
[[package]]
name = "encoding_rs"
version = "0.8.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3"
dependencies = [
"cfg-if",
]
[[package]] [[package]]
name = "endi" name = "endi"
version = "1.1.0" version = "1.1.0"
@@ -1275,8 +1329,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"js-sys",
"libc", "libc",
"wasi 0.11.0+wasi-snapshot-preview1", "wasi 0.11.0+wasi-snapshot-preview1",
"wasm-bindgen",
] ]
[[package]] [[package]]
@@ -1286,9 +1342,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"js-sys",
"libc", "libc",
"r-efi", "r-efi",
"wasi 0.14.2+wasi-0.2.4", "wasi 0.14.2+wasi-0.2.4",
"wasm-bindgen",
] ]
[[package]] [[package]]
@@ -1460,6 +1518,25 @@ dependencies = [
"syn 2.0.100", "syn 2.0.100",
] ]
[[package]]
name = "h2"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75249d144030531f8dee69fe9cea04d3edf809a017ae445e2abdff6629e86633"
dependencies = [
"atomic-waker",
"bytes",
"fnv",
"futures-core",
"futures-sink",
"http",
"indexmap 2.9.0",
"slab",
"tokio",
"tokio-util",
"tracing",
]
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.12.3" version = "0.12.3"
@@ -1559,6 +1636,7 @@ dependencies = [
"bytes", "bytes",
"futures-channel", "futures-channel",
"futures-util", "futures-util",
"h2",
"http", "http",
"http-body", "http-body",
"httparse", "httparse",
@@ -1569,6 +1647,24 @@ dependencies = [
"want", "want",
] ]
[[package]]
name = "hyper-rustls"
version = "0.27.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2"
dependencies = [
"futures-util",
"http",
"hyper",
"hyper-util",
"rustls",
"rustls-pki-types",
"tokio",
"tokio-rustls",
"tower-service",
"webpki-roots",
]
[[package]] [[package]]
name = "hyper-util" name = "hyper-util"
version = "0.1.11" version = "0.1.11"
@@ -2061,6 +2157,12 @@ version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856"
[[package]]
name = "litrs"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5"
[[package]] [[package]]
name = "lock_api" name = "lock_api"
version = "0.4.12" version = "0.4.12"
@@ -2890,6 +2992,22 @@ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]]
name = "psl-types"
version = "2.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33cb294fe86a74cbcf50d4445b37da762029549ebeea341421c7c70370f86cac"
[[package]]
name = "publicsuffix"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f42ea446cab60335f76979ec15e12619a2165b5ae2c12166bef27d283a9fadf"
dependencies = [
"idna",
"psl-types",
]
[[package]] [[package]]
name = "quick-xml" name = "quick-xml"
version = "0.32.0" version = "0.32.0"
@@ -2899,6 +3017,60 @@ dependencies = [
"memchr", "memchr",
] ]
[[package]]
name = "quinn"
version = "0.11.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3bd15a6f2967aef83887dcb9fec0014580467e33720d073560cf015a5683012"
dependencies = [
"bytes",
"cfg_aliases",
"pin-project-lite",
"quinn-proto",
"quinn-udp",
"rustc-hash",
"rustls",
"socket2",
"thiserror 2.0.12",
"tokio",
"tracing",
"web-time",
]
[[package]]
name = "quinn-proto"
version = "0.11.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bcbafbbdbb0f638fe3f35f3c56739f77a8a1d070cb25603226c83339b391472b"
dependencies = [
"bytes",
"getrandom 0.3.2",
"rand 0.9.1",
"ring",
"rustc-hash",
"rustls",
"rustls-pki-types",
"slab",
"thiserror 2.0.12",
"tinyvec",
"tracing",
"web-time",
]
[[package]]
name = "quinn-udp"
version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "541d0f57c6ec747a90738a52741d3221f7960e8ac2f0ff4b1a63680e033b4ab5"
dependencies = [
"cfg_aliases",
"libc",
"once_cell",
"socket2",
"tracing",
"windows-sys 0.59.0",
]
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.40" version = "1.0.40"
@@ -3087,12 +3259,17 @@ checksum = "d19c46a6fdd48bc4dab94b6103fccc55d34c67cc0ad04653aad4ea2a07cd7bbb"
dependencies = [ dependencies = [
"base64 0.22.1", "base64 0.22.1",
"bytes", "bytes",
"cookie",
"cookie_store",
"encoding_rs",
"futures-core", "futures-core",
"futures-util", "futures-util",
"h2",
"http", "http",
"http-body", "http-body",
"http-body-util", "http-body-util",
"hyper", "hyper",
"hyper-rustls",
"hyper-util", "hyper-util",
"ipnet", "ipnet",
"js-sys", "js-sys",
@@ -3101,11 +3278,17 @@ dependencies = [
"once_cell", "once_cell",
"percent-encoding", "percent-encoding",
"pin-project-lite", "pin-project-lite",
"quinn",
"rustls",
"rustls-pemfile",
"rustls-pki-types",
"serde", "serde",
"serde_json", "serde_json",
"serde_urlencoded", "serde_urlencoded",
"sync_wrapper", "sync_wrapper",
"system-configuration",
"tokio", "tokio",
"tokio-rustls",
"tokio-util", "tokio-util",
"tower", "tower",
"tower-service", "tower-service",
@@ -3114,6 +3297,7 @@ dependencies = [
"wasm-bindgen-futures", "wasm-bindgen-futures",
"wasm-streams", "wasm-streams",
"web-sys", "web-sys",
"webpki-roots",
"windows-registry", "windows-registry",
] ]
@@ -3142,12 +3326,32 @@ dependencies = [
"windows-sys 0.59.0", "windows-sys 0.59.0",
] ]
[[package]]
name = "ring"
version = "0.17.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7"
dependencies = [
"cc",
"cfg-if",
"getrandom 0.2.16",
"libc",
"untrusted",
"windows-sys 0.52.0",
]
[[package]] [[package]]
name = "rustc-demangle" name = "rustc-demangle"
version = "0.1.24" version = "0.1.24"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
[[package]]
name = "rustc-hash"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d"
[[package]] [[package]]
name = "rustc_version" name = "rustc_version"
version = "0.4.1" version = "0.4.1"
@@ -3183,6 +3387,49 @@ dependencies = [
"windows-sys 0.59.0", "windows-sys 0.59.0",
] ]
[[package]]
name = "rustls"
version = "0.23.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df51b5869f3a441595eac5e8ff14d486ff285f7b8c0df8770e49c3b56351f0f0"
dependencies = [
"once_cell",
"ring",
"rustls-pki-types",
"rustls-webpki",
"subtle",
"zeroize",
]
[[package]]
name = "rustls-pemfile"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50"
dependencies = [
"rustls-pki-types",
]
[[package]]
name = "rustls-pki-types"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c"
dependencies = [
"web-time",
]
[[package]]
name = "rustls-webpki"
version = "0.103.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fef8b8769aaccf73098557a87cd1816b4f9c7c16811c9c77142aa695c16f2c03"
dependencies = [
"ring",
"rustls-pki-types",
"untrusted",
]
[[package]] [[package]]
name = "rustversion" name = "rustversion"
version = "1.0.20" version = "1.0.20"
@@ -3574,6 +3821,12 @@ version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "subtle"
version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]] [[package]]
name = "swift-rs" name = "swift-rs"
version = "1.0.7" version = "1.0.7"
@@ -3627,6 +3880,27 @@ dependencies = [
"syn 2.0.100", "syn 2.0.100",
] ]
[[package]]
name = "system-configuration"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b"
dependencies = [
"bitflags 2.9.0",
"core-foundation 0.9.4",
"system-configuration-sys",
]
[[package]]
name = "system-configuration-sys"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4"
dependencies = [
"core-foundation-sys",
"libc",
]
[[package]] [[package]]
name = "system-deps" name = "system-deps"
version = "6.2.2" version = "6.2.2"
@@ -3647,7 +3921,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e59c1f38e657351a2e822eadf40d6a2ad4627b9c25557bc1180ec1b3295ef82" checksum = "1e59c1f38e657351a2e822eadf40d6a2ad4627b9c25557bc1180ec1b3295ef82"
dependencies = [ dependencies = [
"bitflags 2.9.0", "bitflags 2.9.0",
"core-foundation", "core-foundation 0.10.0",
"core-graphics", "core-graphics",
"crossbeam-channel", "crossbeam-channel",
"dispatch", "dispatch",
@@ -3868,6 +4142,30 @@ dependencies = [
"uuid", "uuid",
] ]
[[package]]
name = "tauri-plugin-http"
version = "2.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40dcd6c922a1885e1f0bcebc6768fec6e005bd4b9001c5d90a2f5d4cab297729"
dependencies = [
"bytes",
"cookie_store",
"data-url",
"http",
"regex",
"reqwest",
"schemars",
"serde",
"serde_json",
"tauri",
"tauri-plugin",
"tauri-plugin-fs",
"thiserror 2.0.12",
"tokio",
"url",
"urlpattern",
]
[[package]] [[package]]
name = "tauri-plugin-opener" name = "tauri-plugin-opener"
version = "2.2.6" version = "2.2.6"
@@ -4098,6 +4396,21 @@ dependencies = [
"zerovec", "zerovec",
] ]
[[package]]
name = "tinyvec"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71"
dependencies = [
"tinyvec_macros",
]
[[package]]
name = "tinyvec_macros"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]] [[package]]
name = "tokio" name = "tokio"
version = "1.44.2" version = "1.44.2"
@@ -4111,10 +4424,32 @@ dependencies = [
"pin-project-lite", "pin-project-lite",
"signal-hook-registry", "signal-hook-registry",
"socket2", "socket2",
"tokio-macros",
"tracing", "tracing",
"windows-sys 0.52.0", "windows-sys 0.52.0",
] ]
[[package]]
name = "tokio-macros"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.100",
]
[[package]]
name = "tokio-rustls"
version = "0.26.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b"
dependencies = [
"rustls",
"tokio",
]
[[package]] [[package]]
name = "tokio-util" name = "tokio-util"
version = "0.7.14" version = "0.7.14"
@@ -4346,6 +4681,12 @@ version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
[[package]]
name = "untrusted"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
[[package]] [[package]]
name = "url" name = "url"
version = "2.5.4" version = "2.5.4"
@@ -4570,6 +4911,16 @@ dependencies = [
"wasm-bindgen", "wasm-bindgen",
] ]
[[package]]
name = "web-time"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]] [[package]]
name = "webkit2gtk" name = "webkit2gtk"
version = "2.0.1" version = "2.0.1"
@@ -4614,6 +4965,15 @@ dependencies = [
"system-deps", "system-deps",
] ]
[[package]]
name = "webpki-roots"
version = "0.26.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2210b291f7ea53617fbafcc4939f10914214ec15aace5ba62293a668f322c5c9"
dependencies = [
"rustls-pki-types",
]
[[package]] [[package]]
name = "webview2-com" name = "webview2-com"
version = "0.37.0" version = "0.37.0"
@@ -5423,6 +5783,12 @@ dependencies = [
"synstructure", "synstructure",
] ]
[[package]]
name = "zeroize"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
[[package]] [[package]]
name = "zerovec" name = "zerovec"
version = "0.10.4" version = "0.10.4"

View File

@@ -24,4 +24,5 @@ serde = { version = "1", features = ["derive"] }
serde_json = "1" serde_json = "1"
git2 = "0.18" git2 = "0.18"
tauri-plugin-dialog = "2" tauri-plugin-dialog = "2"
tauri-plugin-http = "2"

View File

@@ -8,6 +8,11 @@
"permissions": [ "permissions": [
"core:default", "core:default",
"opener:default", "opener:default",
"dialog:default" "dialog:default",
{
"identifier": "http:default",
"allow": [{ "url": "https://api.deepseek.com" }],
"deny": [{ "url": "https://private.tauri.app" }]
}
] ]
} }

View File

@@ -19,7 +19,12 @@ pub fn validate_repo_path(path: &str) -> Result<bool, String> {
} }
#[tauri::command] #[tauri::command]
pub fn get_commits(repo_path: &str, repo_name: &str) -> Result<Vec<CommitInfo>, String> { pub fn get_commits(
repo_path: &str,
repo_name: &str,
author_filter: Option<String>,
time_range: Option<(i64, i64)>,
) -> Result<Vec<CommitInfo>, String> {
let repo = match Repository::open(repo_path) { let repo = match Repository::open(repo_path) {
Ok(repo) => repo, Ok(repo) => repo,
Err(e) => return Err(format!("无法打开仓库: {}", e)), Err(e) => return Err(format!("无法打开仓库: {}", e)),
@@ -37,6 +42,20 @@ pub fn get_commits(repo_path: &str, repo_name: &str) -> Result<Vec<CommitInfo>,
let commits: Result<Vec<CommitInfo>, _> = revwalk let commits: Result<Vec<CommitInfo>, _> = revwalk
.filter_map(|id| id.ok()) .filter_map(|id| id.ok())
.filter_map(|id| repo.find_commit(id).ok()) .filter_map(|id| repo.find_commit(id).ok())
.filter(|commit| {
if let Some(author) = &author_filter {
if commit.author().email().unwrap_or("") != author {
return false;
}
}
if let Some((start, end)) = time_range {
let commit_time = commit.time().seconds();
if commit_time < start || commit_time > end {
return false;
}
}
true
})
.map(|commit: Commit| { .map(|commit: Commit| {
Ok(CommitInfo { Ok(CommitInfo {
id: commit.id().to_string(), id: commit.id().to_string(),

View File

@@ -7,6 +7,7 @@ use git::{get_commits, validate_repo_path};
fn main() { fn main() {
tauri::Builder::default() tauri::Builder::default()
.plugin(tauri_plugin_http::init())
.plugin(tauri_plugin_dialog::init()) .plugin(tauri_plugin_dialog::init())
.invoke_handler(tauri::generate_handler![validate_repo_path, get_commits]) .invoke_handler(tauri::generate_handler![validate_repo_path, get_commits])
.run(tauri::generate_context!()) .run(tauri::generate_context!())

View File

@@ -4,7 +4,7 @@
"version": "0.1.0", "version": "0.1.0",
"identifier": "com.cs-auto-report.app", "identifier": "com.cs-auto-report.app",
"build": { "build": {
"beforeDevCommand": "bun run dev", "beforeDevCommand": "bun run vite:dev",
"devUrl": "http://localhost:1420", "devUrl": "http://localhost:1420",
"beforeBuildCommand": "bun run build", "beforeBuildCommand": "bun run build",
"frontendDist": "../dist" "frontendDist": "../dist"

View File

@@ -0,0 +1,78 @@
import type React from "react";
import { useState } from "react";
import { Input, Button, Space, Modal, message } from "antd";
import { open } from "@tauri-apps/plugin-dialog";
interface AddRepoModalProps {
isOpen: boolean;
onCancel: () => void;
onSubmit: (repoPath: string, name: string) => void;
}
const AddRepoModal: React.FC<AddRepoModalProps> = ({
isOpen,
onCancel,
onSubmit,
}) => {
const [name, setName] = useState("");
const [repoPath, setRepoPath] = useState("");
const handleSelectDirectory = async () => {
try {
const selected = await open({
directory: true,
multiple: false,
title: "选择Git仓库目录",
});
console.log("选择的目录:", selected);
if (selected) {
setRepoPath(selected);
}
} catch (error) {
console.error("选择目录失败:", error);
}
};
const handleSubmit = () => {
if (!repoPath) {
message.warning("请选择仓库路径");
return;
}
if (!name) {
message.warning("请输入仓库名称");
return;
}
onSubmit(repoPath, name);
setName("");
setRepoPath("");
};
return (
<Modal
title="添加Git仓库"
open={isOpen}
onCancel={onCancel}
footer={
<Button type="primary" onClick={handleSubmit}>
</Button>
}
>
<div className="repo-inputs">
<Space.Compact style={{ width: "100%", marginBottom: 16 }}>
<Input placeholder="选择Git仓库本地路径" value={repoPath} readOnly />
<Button onClick={handleSelectDirectory}></Button>
</Space.Compact>
<Input
placeholder="请输入仓库名称"
value={name}
onChange={(e) => setName(e.target.value)}
style={{ marginBottom: 16 }}
/>
</div>
</Modal>
);
};
export default AddRepoModal;

View File

@@ -0,0 +1,66 @@
import type React from "react";
import { useState } from "react";
import { Modal, Switch, Space, Select } from "antd";
import { ReportType } from "../../types/types";
interface GenerateReportModalProps {
open: boolean;
onCancel: () => void;
onSubmit: (reportType: ReportType, useAI: boolean) => void;
}
const GenerateReportModal: React.FC<GenerateReportModalProps> = ({
open,
onCancel,
onSubmit,
}) => {
const [reportType, setReportType] = useState<ReportType>(
ReportType.WEEKLY_REPORT,
);
const [useAI, setUseAI] = useState(false);
const handleSubmit = () => {
onSubmit(reportType, useAI);
};
return (
<Modal
title="生成报告"
open={open}
onCancel={onCancel}
onOk={handleSubmit}
okText="生成预览"
cancelText="取消"
>
<Space direction="vertical">
<div className="flex gap-x-2 items-center">
<div></div>
<Select
value={reportType}
style={{ width: 120 }}
onChange={(value) => setReportType(value)}
options={[
{ value: ReportType.WEEKLY_REPORT, label: "本周周报" },
{ value: ReportType.LAST_WEEK_REPORT, label: "上周周报" },
{ value: ReportType.DAILY_REPORT, label: "本日日报" },
{ value: ReportType.YESTERDAY_REPORT, label: "昨日日报" },
]}
/>
</div>
<div className="flex gap-x-2">
<div style={{ marginBottom: 8 }}>AI自动整理</div>
<Switch
checked={useAI}
onChange={setUseAI}
checkedChildren="开启"
unCheckedChildren="关闭"
/>
</div>
</Space>
</Modal>
);
};
export default GenerateReportModal;

View File

@@ -1,17 +1,9 @@
import type React from "react"; import type React from "react";
import { useState } from "react"; import { useState } from "react";
import { import { Card, Button, List, Checkbox, Tooltip } from "antd";
Card, import AddRepoModal from "./AddRepoModal";
Input, import GenerateReportModal from "./GenerateReportModal";
Button, import type { ReportType } from "../../types/types";
List,
Space,
Modal,
Checkbox,
message,
Tooltip,
} from "antd";
import { open } from "@tauri-apps/plugin-dialog";
interface GitRepoFormData { interface GitRepoFormData {
repoPath: string; repoPath: string;
@@ -22,53 +14,33 @@ interface GitRepoProps {
onAddRepo: (repo: GitRepoFormData) => void; onAddRepo: (repo: GitRepoFormData) => void;
repos: GitRepoFormData[]; repos: GitRepoFormData[];
className?: string; className?: string;
onGenerateReport: (reportType: ReportType, useAI: boolean) => void;
onReposSelect?: (selectedRepos: string[]) => void;
} }
const GitRepo: React.FC<GitRepoProps> = ({ onAddRepo, repos: initialRepos, className }) => { const GitRepo: React.FC<GitRepoProps> = ({
const [name, setName] = useState(""); onAddRepo,
const [repoPath, setRepoPath] = useState(""); repos: initialRepos,
className,
onGenerateReport,
onReposSelect,
}) => {
const [repos, setRepos] = useState<GitRepoFormData[]>(() => { const [repos, setRepos] = useState<GitRepoFormData[]>(() => {
const savedRepos = localStorage.getItem('gitRepos'); const savedRepos = localStorage.getItem("gitRepos");
return savedRepos ? JSON.parse(savedRepos) : initialRepos; return savedRepos ? JSON.parse(savedRepos) : initialRepos;
}); });
const handleSubmit = () => { const [isModalOpen, setIsModalOpen] = useState(false);
if (!repoPath) {
message.warning("请选择仓库路径"); const handleModalSubmit = (repoPath: string, name: string) => {
return;
}
if (!name) {
message.warning("请输入仓库名称");
return;
}
const newRepo = { name, repoPath }; const newRepo = { name, repoPath };
const updatedRepos = [...repos, newRepo]; const updatedRepos = [...repos, newRepo];
localStorage.setItem('gitRepos', JSON.stringify(updatedRepos)); localStorage.setItem("gitRepos", JSON.stringify(updatedRepos));
setRepos(updatedRepos); setRepos(updatedRepos);
onAddRepo(newRepo); onAddRepo(newRepo);
setName(""); setIsModalOpen(false);
setRepoPath("");
}; };
const handleSelectDirectory = async () => {
try {
const selected = await open({
directory: true,
multiple: false,
title: "选择Git仓库目录",
});
console.log("选择的目录:", selected);
if (selected) {
setRepoPath(selected);
}
} catch (error) {
console.error("选择目录失败:", error);
}
};
const [isModalOpen, setIsModalOpen] = useState(false);
const showModal = () => { const showModal = () => {
setIsModalOpen(true); setIsModalOpen(true);
}; };
@@ -78,41 +50,33 @@ const GitRepo: React.FC<GitRepoProps> = ({ onAddRepo, repos: initialRepos, class
}; };
const [selectedRepos, setSelectedRepos] = useState<string[]>([]); const [selectedRepos, setSelectedRepos] = useState<string[]>([]);
const [isGenerateModalOpen, setIsGenerateModalOpen] = useState(false);
const handleCheckboxChange = (checkedValues: string[]) => { const handleCheckboxChange = (checkedValues: string[]) => {
setSelectedRepos(checkedValues); setSelectedRepos(checkedValues);
onReposSelect?.(checkedValues);
console.log("选中的仓库:", checkedValues); console.log("选中的仓库:", checkedValues);
}; };
const handleGenerateReport = (reportType: ReportType, useAI: boolean) => {
console.log("生成报告:", { reportType, useAI, selectedRepos });
setIsGenerateModalOpen(false);
onGenerateReport(reportType, useAI);
};
return ( return (
<div className={className}> <div className={className}>
<Card title="Git仓库" className="size-full"> <Card title="Git仓库" className="size-full">
<Modal <AddRepoModal
title="添加Git仓库" isOpen={isModalOpen}
open={isModalOpen}
onCancel={handleCancel} onCancel={handleCancel}
footer={ onSubmit={handleModalSubmit}
<Button type="primary" onClick={handleSubmit}>
</Button>
}
>
<div className="repo-inputs">
<Space.Compact style={{ width: "100%", marginBottom: 16 }}>
<Input
placeholder="选择Git仓库本地路径"
value={repoPath}
readOnly
/> />
<Button onClick={handleSelectDirectory}></Button> <GenerateReportModal
</Space.Compact> open={isGenerateModalOpen}
<Input onCancel={() => setIsGenerateModalOpen(false)}
placeholder="请输入仓库名称" onSubmit={handleGenerateReport}
value={name}
onChange={(e) => setName(e.target.value)}
style={{ marginBottom: 16 }}
/> />
</div>
</Modal>
<Checkbox.Group <Checkbox.Group
className="size-full" className="size-full"
@@ -141,7 +105,12 @@ const GitRepo: React.FC<GitRepoProps> = ({ onAddRepo, repos: initialRepos, class
footer={ footer={
selectedRepos.length > 0 && ( selectedRepos.length > 0 && (
<div className="flex justify-end items-center"> <div className="flex justify-end items-center">
<Button type="primary"></Button> <Button
type="primary"
onClick={() => setIsGenerateModalOpen(true)}
>
</Button>
</div> </div>
) )
} }

View File

@@ -1,115 +0,0 @@
import type React from 'react';
import { useState } from 'react';
import { Card, Form, Select, Button, Input, message } from 'antd';
interface CommitRecord {
id: string;
message: string;
author: string;
date: string;
repository: string;
}
interface ReportGeneratorProps {
commits: CommitRecord[];
onGenerateReport: (reportData: ReportData) => void;
}
interface ReportData {
type: 'daily' | 'weekly';
content: string;
dateRange: [string, string];
}
const { Option } = Select;
const { TextArea } = Input;
const ReportGenerator: React.FC<ReportGeneratorProps> = ({ commits, onGenerateReport }) => {
const [form] = Form.useForm();
const [reportType, setReportType] = useState<'daily' | 'weekly'>('daily');
const generateReportContent = (type: 'daily' | 'weekly') => {
if (!commits.length) {
message.warning('没有可用的提交记录');
return '';
}
const groupedCommits = commits.reduce((acc, commit) => {
const repo = commit.repository;
if (!acc[repo]) {
acc[repo] = [];
}
acc[repo].push(commit);
return acc;
}, {} as Record<string, CommitRecord[]>);
let content = `${type === 'daily' ? '日报' : '周报'}\n\n`;
for (const [repo, repoCommits] of Object.entries(groupedCommits)) {
content += `## ${repo}\n`;
for (const commit of repoCommits) {
content += `- ${commit.message}\n`;
};
content += '\n';
};
return content;
};
const handleSubmit = () => {
const content = generateReportContent(reportType);
const now = new Date();
const reportData: ReportData = {
type: reportType,
content,
dateRange: [now.toISOString(), now.toISOString()]
};
onGenerateReport(reportData);
};
const handleTypeChange = (value: 'daily' | 'weekly') => {
setReportType(value);
const content = generateReportContent(value);
// @ts-ignore
form.setFieldsValue({ content });
};
return (
<Card title="报告生成器" className="size-full overflow-y-auto">
<Form
form={form}
layout="vertical"
onFinish={handleSubmit}
>
<Form.Item
name="type"
label="报告类型"
initialValue="daily"
>
<Select onChange={handleTypeChange}>
<Option value="daily"></Option>
<Option value="weekly"></Option>
</Select>
</Form.Item>
<Form.Item
name="content"
label="报告内容"
>
<TextArea
rows={10}
placeholder="报告内容将根据提交记录自动生成"
/>
</Form.Item>
<Form.Item>
<Button type="primary" htmlType="submit">
</Button>
</Form.Item>
</Form>
</Card>
);
};
export default ReportGenerator;

View File

@@ -0,0 +1,45 @@
import type React from 'react';
import { List } from 'antd';
import dayjs from 'dayjs';
interface CommitInfo {
id: string;
message: string;
author: string;
date: string;
}
interface CommitListProps {
commits: CommitInfo[];
}
const CommitList: React.FC<CommitListProps> = ({ commits }) => {
return (
<div className="size-full overflow-y-auto">
<List
dataSource={commits}
renderItem={(commit) => (
<List.Item>
<List.Item.Meta
title={commit.message}
description={
<>
<div>: {commit.author}</div>
<div>
:{" "}
{dayjs(
new Date(Number.parseInt(commit.date) * 1000),
).format("YYYY-MM-DD HH:mm")}
</div>
<div>ID: {commit.id}</div>
</>
}
/>
</List.Item>
)}
/>
</div>
);
};
export default CommitList;

View File

@@ -0,0 +1,88 @@
import type React from 'react';
import { useState } from 'react';
import { Card, Tabs, Typography, message, Button } from "antd";
import { processWithDeepSeek } from '../../utils/deepseekApi';
import CommitList from './CommitList';
interface CommitInfo {
id: string;
message: string;
author: string;
date: string;
}
interface ReportPreviewProps {
selectedRepos: string[];
commits?: CommitInfo[];
reportContent?: string;
}
const { TabPane } = Tabs;
const { Title, Paragraph } = Typography;
const ReportPreview: React.FC<ReportPreviewProps> = ({
selectedRepos,
commits = [],
reportContent = '',
}) => {
const [isProcessing, setIsProcessing] = useState(false);
const handleAICleanup = async () => {
if (!reportContent) return;
setIsProcessing(true);
try {
const apiKey = localStorage.getItem("userKey") || "";
const aiResponse = await processWithDeepSeek(reportContent, apiKey);
message.success("AI整理完成");
return aiResponse;
} catch (error) {
message.error("AI整理失败");
console.error("DeepSeek处理失败:", error);
return reportContent;
} finally {
setIsProcessing(false);
}
};
if (selectedRepos.length === 0) {
return (
<Card className="size-full flex items-center justify-center">
<Typography.Text type="secondary">Git仓库以预览报告内容</Typography.Text>
</Card>
);
}
return (
<Card className="size-full">
<Tabs defaultActiveKey="commits">
<TabPane
tab="提交记录"
key="commits"
className="h-full"
>
<div className="h-[calc(100vh-100px)] overflow-auto">
<CommitList commits={commits} />
</div>
</TabPane>
<TabPane tab="报告预览" key="preview" className="h-full">
<div className="h-[calc(100vh-100px)] overflow-auto">
<div className="flex justify-between items-center mb-4">
<Title level={4}></Title>
<Button
type="primary"
loading={isProcessing}
onClick={handleAICleanup}
>
AI整理
</Button>
</div>
<Paragraph>{reportContent || "暂无报告内容"}</Paragraph>
</div>
</TabPane>
</Tabs>
</Card>
);
};
export default ReportPreview;

View File

@@ -1,14 +1,57 @@
import type React from "react"; import type React from "react";
import { Card, Input } from "antd"; import { Card, Input, Space } from "antd";
import { useEffect, useState } from "react";
interface SettingProps { interface SettingProps {
className?: string; className?: string;
} }
const Setting: React.FC<SettingProps> = ({ className }) => { const Setting: React.FC<SettingProps> = ({ className }) => {
const [email, setEmail] = useState<string>('');
const [key, setKey] = useState<string>('');
useEffect(() => {
const savedEmail = localStorage.getItem('userEmail');
const savedKey = localStorage.getItem('userKey');
if (savedEmail) {
setEmail(savedEmail);
}
if (savedKey) {
setKey(savedKey);
}
}, []);
const handleEmailChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const value = e.target.value;
setEmail(value);
localStorage.setItem('userEmail', value);
};
const handleKeyChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const value = e.target.value;
setKey(value);
localStorage.setItem('userKey', value);
};
return ( return (
<Card title="设置" className={`${className} size-full`}> <Card
<Input addonBefore="Key" defaultValue="" /> title="设置"
className={`${className} size-full flex flex-col gap-y-2`}
>
<Space direction="vertical">
<Input
addonBefore="邮箱"
value={email}
onChange={handleEmailChange}
placeholder="请输入您的Git邮箱"
/>
<Input.Password
addonBefore="Key"
value={key}
onChange={handleKeyChange}
placeholder="请输入您的Key"
/>
</Space>
</Card> </Card>
); );
}; };

View File

@@ -1,32 +1,30 @@
import { invoke } from '@tauri-apps/api/core';
import type React from 'react'; import type React from 'react';
import { useState } from 'react'; import { useState } from 'react';
import { message } from 'antd'; import { message } from 'antd';
import GitRepo from '../../components/GitRepo'; import GitRepo from '../../components/GitRepo';
import ReportGenerator from '../../components/ReportGenerator';
import Setting from '../../components/Setting'; import Setting from '../../components/Setting';
import ReportPreview from '../../components/ReportPreview';
import type { ReportType } from "../../types/types";
import { getTimeRange } from '../../utils/timeUtils';
interface GitRepoData { interface GitRepoData {
repoPath: string; repoPath: string;
name: string; name: string;
} }
interface CommitRecord { interface CommitInfo {
id: string; id: string;
message: string; message: string;
author: string; author: string;
date: string; date: string;
repository: string;
}
interface ReportData {
type: 'daily' | 'weekly';
content: string;
dateRange: [string, string];
} }
const Home: React.FC = () => { const Home: React.FC = () => {
const [repos, setRepos] = useState<GitRepoData[]>([]); const [repos, setRepos] = useState<GitRepoData[]>([]);
const [commits, setCommits] = useState<CommitRecord[]>([]); const [selectedRepos, setSelectedRepos] = useState<string[]>([]);
const [commits, setCommits] = useState<CommitInfo[]>([]);
const [reportContent, setReportContent] = useState<string>('');
const handleAddRepo = async (repo: GitRepoData) => { const handleAddRepo = async (repo: GitRepoData) => {
try { try {
@@ -38,27 +36,85 @@ const Home: React.FC = () => {
} }
}; };
const handleGenerateReport = (reportData: ReportData) => { const handleGenerateReport = async (reportType: ReportType, useAI: boolean) => {
if (selectedRepos.length === 0) {
message.warning('请先选择仓库');
return;
}
try { try {
// TODO: 调用Tauri后端API保存报告 const commits = await Promise.all(
message.success('报告生成成功'); selectedRepos.map(async (repoPath) => {
const repoName = repos.find(r => r.repoPath === repoPath)?.name || '';
const userEmail = localStorage.getItem('userEmail');
const result = await invoke<CommitInfo[]>("get_commits", {
repoPath,
repoName,
authorFilter: userEmail || null,
timeRange: reportType? getTimeRange(reportType): null,
});
return result;
})
);
console.log(commits);
const flatCommits = commits.flat();
setCommits(flatCommits);
let reportText = `已生成 ${selectedRepos.length} 个仓库的报告内容\n\n` +
flatCommits.map(commit =>
`- ${commit.message} (${commit.author}${new Date(parseInt(commit.date) * 1000).toLocaleDateString()})`
).join('\n');
if (useAI) {
try {
const aiResponse = await invoke<string>("process_with_deepseek", {
content: reportText
});
reportText = aiResponse;
} catch (error) { } catch (error) {
message.error('报告生成失败'); console.error("DeepSeek处理失败:", error);
}
}
setReportContent(reportText);
message.success("报告生成成功");
} catch (error) {
message.error("报告生成失败");
setCommits([]);
setReportContent('');
} }
}; };
const handleReposSelect = (selected: string[]) => {
setSelectedRepos(selected);
if (selected.length === 0) {
setCommits([]);
setReportContent('');
}
};
return ( return (
<div className="flex size-full overflow-hidden"> <div className="flex size-full overflow-hidden">
<div className="h-full w-80 flex flex-col"> <div className="h-full w-80 flex flex-col">
<div> <div>
<Setting /> <Setting />
</div> </div>
<GitRepo className="flex-1" onAddRepo={handleAddRepo} repos={repos} /> <GitRepo
</div> className="flex-1"
<div className="flex-1 h-full overflow-auto"> onAddRepo={handleAddRepo}
<ReportGenerator repos={repos}
commits={commits}
onGenerateReport={handleGenerateReport} onGenerateReport={handleGenerateReport}
onReposSelect={handleReposSelect}
/>
</div>
<div className="flex-1 h-full overflow-hidden">
<ReportPreview
selectedRepos={selectedRepos}
commits={commits}
reportContent={reportContent}
/> />
</div> </div>
</div> </div>

6
src/types/types.ts Normal file
View File

@@ -0,0 +1,6 @@
export enum ReportType {
WEEKLY_REPORT = 'weekly_report',
LAST_WEEK_REPORT = 'last_week_report',
DAILY_REPORT = 'daily_report',
YESTERDAY_REPORT = 'yesterday_report'
}

47
src/utils/deepseekApi.ts Normal file
View File

@@ -0,0 +1,47 @@
import { message } from 'antd';
import { fetch } from "@tauri-apps/plugin-http";
export interface DeepSeekResponse {
result: string;
}
export const processWithDeepSeek = async (content: string, apiKey: string): Promise<string> => {
console.log(apiKey);
try {
const response = await fetch<DeepSeekResponse>(
"https://api.deepseek.com/v1/chat/completions",
{
method: "POST",
body: {
model: "deepseek-chat",
messages: [
{
role: "system",
content: "You are a helpful assistant.",
},
{
role: "user",
content: content,
},
],
temperature: 0.7,
max_tokens: 2000,
stream: false,
},
response: "json",
headers: {
Authorization: `Bearer ${apiKey}`,
"Content-Type": "application/json",
Accept: "application/json",
},
},
);
console.log(response)
return response.data.result;
} catch (error) {
console.error('DeepSeek API请求失败:', error);
message.error('DeepSeek API请求失败');
throw error;
}
};

59
src/utils/timeUtils.ts Normal file
View File

@@ -0,0 +1,59 @@
import { ReportType } from "../types/types";
export const getTimeRange = (reportType: ReportType): [number, number] => {
const today = new Date();
let startTimestamp: number;
let endTimestamp: number;
switch (reportType) {
case ReportType.WEEKLY_REPORT:
{
// 本周周报
const startOfWeek = new Date(
today.setDate(
today.getDate() - today.getDay() + (today.getDay() === 0 ? -6 : 1),
),
);
startOfWeek.setHours(0, 0, 0, 0);
startTimestamp = Math.floor(startOfWeek.getTime() / 1000);
endTimestamp = Math.floor(Date.now() / 1000);
}
break;
case ReportType.LAST_WEEK_REPORT: // 上周周报
{
const lastWeek = new Date(today.setDate(today.getDate() - 7));
const startOfLastWeek = new Date(
lastWeek.setDate(
lastWeek.getDate() -
lastWeek.getDay() +
(lastWeek.getDay() === 0 ? -6 : 1),
),
);
startOfLastWeek.setHours(0, 0, 0, 0);
const endOfLastWeek = new Date(startOfLastWeek);
endOfLastWeek.setDate(startOfLastWeek.getDate() + 6);
startTimestamp = Math.floor(startOfLastWeek.getTime() / 1000);
endTimestamp = Math.floor(endOfLastWeek.getTime() / 1000);
}
break;
case ReportType.DAILY_REPORT: // 日报
{
const startOfDay = new Date(today.setHours(0, 0, 0, 0));
startTimestamp = Math.floor(startOfDay.getTime() / 1000);
endTimestamp = Math.floor(Date.now() / 1000);
}
break;
case ReportType.YESTERDAY_REPORT: // 昨日日报
{
const yesterday = new Date(today.setDate(today.getDate() - 1));
const startOfYesterday = new Date(yesterday.setHours(0, 0, 0, 0));
const endOfYesterday = new Date(yesterday.setHours(23, 59, 59, 999));
startTimestamp = Math.floor(startOfYesterday.getTime() / 1000);
endTimestamp = Math.floor(endOfYesterday.getTime() / 1000);
}
break;
}
return [startTimestamp, endTimestamp];
};