feat: 优化排版,完善获取git提交记录以及预览功能
This commit is contained in:
3
bun.lock
3
bun.lock
@@ -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=="],
|
||||||
|
|||||||
@@ -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
372
src-tauri/Cargo.lock
generated
@@ -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"
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|
||||||
|
|||||||
@@ -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" }]
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -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(),
|
||||||
|
|||||||
@@ -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!())
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
78
src/components/GitRepo/AddRepoModal.tsx
Normal file
78
src/components/GitRepo/AddRepoModal.tsx
Normal 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;
|
||||||
66
src/components/GitRepo/GenerateReportModal.tsx
Normal file
66
src/components/GitRepo/GenerateReportModal.tsx
Normal 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;
|
||||||
@@ -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>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
|
||||||
45
src/components/ReportPreview/CommitList.tsx
Normal file
45
src/components/ReportPreview/CommitList.tsx
Normal 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;
|
||||||
88
src/components/ReportPreview/index.tsx
Normal file
88
src/components/ReportPreview/index.tsx
Normal 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;
|
||||||
@@ -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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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
6
src/types/types.ts
Normal 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
47
src/utils/deepseekApi.ts
Normal 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
59
src/utils/timeUtils.ts
Normal 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];
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user