perf(component): ⚡ 优化群备注和群昵称(beta)

This commit is contained in:
Dawn
2025-04-04 01:27:24 +08:00
parent 2c4841bb11
commit fab231a916
23 changed files with 5163 additions and 3063 deletions
+1
View File
@@ -185,6 +185,7 @@ Thanks to the following sponsors for their support!
|------|--------|------|------|
| 2025-02-17 | 禾硕 | ¥168 | 支付宝赞赏 |
| 2025-02-8 | Boom.... | ¥100 | 微信赞赏码 |
| 2025-04-01 | 墨 | ¥88.88 | 微信转账 |
| 2025-02-8 | 邓伟 | ¥88 | 微信赞赏码 |
| 2025-02-7 | dennis | ¥80 | gitee码云赞赏 |
| 2025-02-6 | 小二 | ¥62 | 微信转账 |
+1
View File
@@ -186,6 +186,7 @@ sudo xattr -rd com.apple.quarantine 你的安装包路径/下载的安装包名
|------|--------|------|------|
| 2025-02-17 | 禾硕 | ¥168 | 支付宝赞赏 |
| 2025-02-8 | Boom.... | ¥100 | 微信赞赏码 |
| 2025-04-01 | 墨 | ¥88.88 | 微信转账 |
| 2025-02-8 | 邓伟 | ¥88 | 微信赞赏码 |
| 2025-02-7 | dennis | ¥80 | gitee码云赞赏 |
| 2025-02-6 | 小二 | ¥62 | 微信转账 |
+3 -3
View File
@@ -58,7 +58,7 @@
"dependencies": {
"@actions/github": "^6.0.0",
"@fingerprintjs/fingerprintjs": "^4.6.0",
"@tauri-apps/api": "2.4.0",
"@tauri-apps/api": "2.4.1",
"@tauri-apps/plugin-autostart": "2.2.0",
"@tauri-apps/plugin-clipboard-manager": "2.2.2",
"@tauri-apps/plugin-dialog": "^2.2.0",
@@ -67,7 +67,7 @@
"@tauri-apps/plugin-notification": "^2.2.2",
"@tauri-apps/plugin-os": "2.2.0",
"@tauri-apps/plugin-process": "2.2.0",
"@tauri-apps/plugin-shell": "^2.2.0",
"@tauri-apps/plugin-shell": "^2.2.1",
"@tauri-apps/plugin-sql": "^2.2.0",
"@tauri-apps/plugin-updater": "2.6.1",
"colorthief": "^2.6.0",
@@ -95,7 +95,7 @@
"@release-it/bumper": "^6.0.1",
"@release-it/conventional-changelog": "8.0.2",
"@rollup/plugin-terser": "^0.4.4",
"@tauri-apps/cli": "2.4.0",
"@tauri-apps/cli": "2.4.1",
"@types/crypto-js": "^4.2.2",
"@types/lodash-es": "^4.17.12",
"@types/node": "^22.10.7",
+66 -66
View File
@@ -15,8 +15,8 @@ importers:
specifier: ^4.6.0
version: 4.6.0
'@tauri-apps/api':
specifier: 2.4.0
version: 2.4.0
specifier: 2.4.1
version: 2.4.1
'@tauri-apps/plugin-autostart':
specifier: 2.2.0
version: 2.2.0
@@ -42,7 +42,7 @@ importers:
specifier: 2.2.0
version: 2.2.0
'@tauri-apps/plugin-shell':
specifier: ^2.2.0
specifier: ^2.2.1
version: 2.2.1
'@tauri-apps/plugin-sql':
specifier: ^2.2.0
@@ -121,8 +121,8 @@ importers:
specifier: ^0.4.4
version: 0.4.4(rollup@4.35.0)
'@tauri-apps/cli':
specifier: 2.4.0
version: 2.4.0
specifier: 2.4.1
version: 2.4.1
'@types/crypto-js':
specifier: ^4.2.2
version: 4.2.2
@@ -1451,82 +1451,82 @@ packages:
resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==}
engines: {node: '>=18'}
'@tauri-apps/api@2.4.0':
resolution: {integrity: sha512-F1zXTsmwcCp+ocg6fbzD/YL0OHeSG1eynCag1UNlX2tD5+dlXy7eRbTu9cAcscPjcR7Nix7by2wiv/+VfWUieg==}
'@tauri-apps/api@2.4.1':
resolution: {integrity: sha512-5sYwZCSJb6PBGbBL4kt7CnE5HHbBqwH+ovmOW6ZVju3nX4E3JX6tt2kRklFEH7xMOIwR0btRkZktuLhKvyEQYg==}
'@tauri-apps/cli-darwin-arm64@2.4.0':
resolution: {integrity: sha512-MVzYrahJBgDyzUJ2gNEU8H+0oCVEucN115+CVorFnidHcJ6DtDRMCaKLkpjOZNfJyag1WQ25fu7imvZSe0mz/g==}
'@tauri-apps/cli-darwin-arm64@2.4.1':
resolution: {integrity: sha512-QME7s8XQwy3LWClTVlIlwXVSLKkeJ/z88pr917Mtn9spYOjnBfsgHAgGdmpWD3NfJxjg7CtLbhH49DxoFL+hLg==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [darwin]
'@tauri-apps/cli-darwin-x64@2.4.0':
resolution: {integrity: sha512-/4IdbWv6IWSuBn0WVe5JkiSIP1gZhXCZRcumSsYq3ZmOlq4BqXeXT36Oig5mlDnS/2/UpNS94kd8gOA1DNdIeQ==}
'@tauri-apps/cli-darwin-x64@2.4.1':
resolution: {integrity: sha512-/r89IcW6Ya1sEsFUEH7wLNruDTj7WmDWKGpPy7gATFtQr5JEY4heernqE82isjTUimnHZD8SCr0jA3NceI4ybw==}
engines: {node: '>= 10'}
cpu: [x64]
os: [darwin]
'@tauri-apps/cli-linux-arm-gnueabihf@2.4.0':
resolution: {integrity: sha512-rOjlk3Vd6R847LXds4pOAFKUL5NVdSWlaiQvr4H9WDUwIWWoxnj7SQfpryTtElDb2wV7a0BNaOCXCpyFEAXjkw==}
'@tauri-apps/cli-linux-arm-gnueabihf@2.4.1':
resolution: {integrity: sha512-9tDijkRB+CchAGjXxYdY9l/XzFpLp1yihUtGXJz9eh+3qIoRI043n3e+6xmU8ZURr7XPnu+R4sCmXs6HD+NCEQ==}
engines: {node: '>= 10'}
cpu: [arm]
os: [linux]
'@tauri-apps/cli-linux-arm64-gnu@2.4.0':
resolution: {integrity: sha512-X/uCwao6R/weWT2y4f3JKJMeUsujo9H4nBMAv9RZhRsz93n9Amw9ETavAOP11pyhl57YkasvKTCRQN6FwsaoXg==}
'@tauri-apps/cli-linux-arm64-gnu@2.4.1':
resolution: {integrity: sha512-pnFGDEXBAzS4iDYAVxTRhAzNu3K2XPGflYyBc0czfHDBXopqRgMyj5Q9Wj7HAwv6cM8BqzXINxnb2ZJFGmbSgA==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@tauri-apps/cli-linux-arm64-musl@2.4.0':
resolution: {integrity: sha512-GhvQtrTjadW3eLSmfrSfybSqgJMZzUXC+0WqDzFovLug3a1a1go0m9QK9YGvYLkyEpTY5zRxLtwv+tbZXN4tZw==}
'@tauri-apps/cli-linux-arm64-musl@2.4.1':
resolution: {integrity: sha512-Hp0zXgeZNKmT+eoJSCxSBUm2QndNuRxR55tmIeNm3vbyUMJN/49uW7nurZ5fBPsacN4Pzwlx1dIMK+Gnr9A69w==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
libc: [musl]
'@tauri-apps/cli-linux-riscv64-gnu@2.4.0':
resolution: {integrity: sha512-NgeNihQ9uHS/ibMWLge5VA/BgsS/g8VPSVtCp8DSPyub3bBuCy79A8V+bzNKlMOiDVrqK4vQ//FS9kSxoJOtXw==}
'@tauri-apps/cli-linux-riscv64-gnu@2.4.1':
resolution: {integrity: sha512-3T3bo2E4fdYRvzcXheWUeQOVB+LunEEi92iPRgOyuSVexVE4cmHYl+MPJF+EUV28Et0hIVTsHibmDO0/04lAFg==}
engines: {node: '>= 10'}
cpu: [riscv64]
os: [linux]
libc: [glibc]
'@tauri-apps/cli-linux-x64-gnu@2.4.0':
resolution: {integrity: sha512-ebRmV2HLIVms1KlNNueQCp3OrXBv6cimU3mYEh5NbZ8dH88f2sF46dFCyPq8Qr/Zti4qPEaAArVG7RYFjfECPw==}
'@tauri-apps/cli-linux-x64-gnu@2.4.1':
resolution: {integrity: sha512-kLN0FdNONO+2i+OpU9+mm6oTGufRC00e197TtwjpC0N6K2K8130w7Q3FeODIM2CMyg0ov3tH+QWqKW7GNhHFzg==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
libc: [glibc]
'@tauri-apps/cli-linux-x64-musl@2.4.0':
resolution: {integrity: sha512-FOp2cBFyq5LnUr3he95Z99PQm3nCSJv2GZNeH7UqmUpeHwdcYuhBERU7C+8VDJJPR98Q5fkcoV00Pc4nw0v5KQ==}
'@tauri-apps/cli-linux-x64-musl@2.4.1':
resolution: {integrity: sha512-a8exvA5Ub9eg66a6hsMQKJIkf63QAf9OdiuFKOsEnKZkNN2x0NLgfvEcqdw88VY0UMs9dBoZ1AGbWMeYnLrLwQ==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
libc: [musl]
'@tauri-apps/cli-win32-arm64-msvc@2.4.0':
resolution: {integrity: sha512-SVf1wDagYsaFM+mpUYKmjNveKodcUSGPEM27WmMW4Enh6aXGzTJi4IYOE3GEFOJF1BpRNscslwE1Rd064kfpcg==}
'@tauri-apps/cli-win32-arm64-msvc@2.4.1':
resolution: {integrity: sha512-4JFrslsMCJQG1c573T9uqQSAbF3j/tMKkMWzsIssv8jvPiP++OG61A2/F+y9te9/Q/O95cKhDK63kaiO5xQaeg==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [win32]
'@tauri-apps/cli-win32-ia32-msvc@2.4.0':
resolution: {integrity: sha512-j+fOFVeSSejk9hrUePY7bJuaYr+80xr+ftjXzxCj0CS0d2oSbq+lT8/zS514WemJk9e9yxUus+2ke/Ng17wkkQ==}
'@tauri-apps/cli-win32-ia32-msvc@2.4.1':
resolution: {integrity: sha512-9eXfFORehYSCRwxg2KodfmX/mhr50CI7wyBYGbPLePCjr5z0jK/9IyW6r0tC+ZVjwpX48dkk7hKiUgI25jHjzA==}
engines: {node: '>= 10'}
cpu: [ia32]
os: [win32]
'@tauri-apps/cli-win32-x64-msvc@2.4.0':
resolution: {integrity: sha512-nv84b3a8eI5Y7ksTLBKjjvtr9NOlAGGGo7OJbjKT+xZLdiPOZ0nJ2cT+4IdfnNAZ33pKJugAfuj1fBvQKeTy0w==}
'@tauri-apps/cli-win32-x64-msvc@2.4.1':
resolution: {integrity: sha512-60a4Ov7Jrwqz2hzDltlS7301dhSAmM9dxo+IRBD3xz7yobKrgaHXYpWvnRomYItHcDd51VaKc9292H8/eE/gsw==}
engines: {node: '>= 10'}
cpu: [x64]
os: [win32]
'@tauri-apps/cli@2.4.0':
resolution: {integrity: sha512-Esg7s20tuSULd2YF3lmtMa1vF7yr5eh/TlBHXjEyrC+XSD9aBxHVoXb6oz7oKybDY9Jf9OiBa0bf2PbybcmOLA==}
'@tauri-apps/cli@2.4.1':
resolution: {integrity: sha512-9Ta81jx9+57FhtU/mPIckDcOBtPTUdKM75t4+aA0X84b8Sclb0jy1xA8NplmcRzp2fsfIHNngU2NiRxsW5+yOQ==}
engines: {node: '>= 10'}
hasBin: true
@@ -6343,98 +6343,98 @@ snapshots:
'@sindresorhus/merge-streams@4.0.0': {}
'@tauri-apps/api@2.4.0': {}
'@tauri-apps/api@2.4.1': {}
'@tauri-apps/cli-darwin-arm64@2.4.0':
'@tauri-apps/cli-darwin-arm64@2.4.1':
optional: true
'@tauri-apps/cli-darwin-x64@2.4.0':
'@tauri-apps/cli-darwin-x64@2.4.1':
optional: true
'@tauri-apps/cli-linux-arm-gnueabihf@2.4.0':
'@tauri-apps/cli-linux-arm-gnueabihf@2.4.1':
optional: true
'@tauri-apps/cli-linux-arm64-gnu@2.4.0':
'@tauri-apps/cli-linux-arm64-gnu@2.4.1':
optional: true
'@tauri-apps/cli-linux-arm64-musl@2.4.0':
'@tauri-apps/cli-linux-arm64-musl@2.4.1':
optional: true
'@tauri-apps/cli-linux-riscv64-gnu@2.4.0':
'@tauri-apps/cli-linux-riscv64-gnu@2.4.1':
optional: true
'@tauri-apps/cli-linux-x64-gnu@2.4.0':
'@tauri-apps/cli-linux-x64-gnu@2.4.1':
optional: true
'@tauri-apps/cli-linux-x64-musl@2.4.0':
'@tauri-apps/cli-linux-x64-musl@2.4.1':
optional: true
'@tauri-apps/cli-win32-arm64-msvc@2.4.0':
'@tauri-apps/cli-win32-arm64-msvc@2.4.1':
optional: true
'@tauri-apps/cli-win32-ia32-msvc@2.4.0':
'@tauri-apps/cli-win32-ia32-msvc@2.4.1':
optional: true
'@tauri-apps/cli-win32-x64-msvc@2.4.0':
'@tauri-apps/cli-win32-x64-msvc@2.4.1':
optional: true
'@tauri-apps/cli@2.4.0':
'@tauri-apps/cli@2.4.1':
optionalDependencies:
'@tauri-apps/cli-darwin-arm64': 2.4.0
'@tauri-apps/cli-darwin-x64': 2.4.0
'@tauri-apps/cli-linux-arm-gnueabihf': 2.4.0
'@tauri-apps/cli-linux-arm64-gnu': 2.4.0
'@tauri-apps/cli-linux-arm64-musl': 2.4.0
'@tauri-apps/cli-linux-riscv64-gnu': 2.4.0
'@tauri-apps/cli-linux-x64-gnu': 2.4.0
'@tauri-apps/cli-linux-x64-musl': 2.4.0
'@tauri-apps/cli-win32-arm64-msvc': 2.4.0
'@tauri-apps/cli-win32-ia32-msvc': 2.4.0
'@tauri-apps/cli-win32-x64-msvc': 2.4.0
'@tauri-apps/cli-darwin-arm64': 2.4.1
'@tauri-apps/cli-darwin-x64': 2.4.1
'@tauri-apps/cli-linux-arm-gnueabihf': 2.4.1
'@tauri-apps/cli-linux-arm64-gnu': 2.4.1
'@tauri-apps/cli-linux-arm64-musl': 2.4.1
'@tauri-apps/cli-linux-riscv64-gnu': 2.4.1
'@tauri-apps/cli-linux-x64-gnu': 2.4.1
'@tauri-apps/cli-linux-x64-musl': 2.4.1
'@tauri-apps/cli-win32-arm64-msvc': 2.4.1
'@tauri-apps/cli-win32-ia32-msvc': 2.4.1
'@tauri-apps/cli-win32-x64-msvc': 2.4.1
'@tauri-apps/plugin-autostart@2.2.0':
dependencies:
'@tauri-apps/api': 2.4.0
'@tauri-apps/api': 2.4.1
'@tauri-apps/plugin-clipboard-manager@2.2.2':
dependencies:
'@tauri-apps/api': 2.4.0
'@tauri-apps/api': 2.4.1
'@tauri-apps/plugin-dialog@2.2.0':
dependencies:
'@tauri-apps/api': 2.4.0
'@tauri-apps/api': 2.4.1
'@tauri-apps/plugin-fs@2.2.0':
dependencies:
'@tauri-apps/api': 2.4.0
'@tauri-apps/api': 2.4.1
'@tauri-apps/plugin-http@2.4.2':
dependencies:
'@tauri-apps/api': 2.4.0
'@tauri-apps/api': 2.4.1
'@tauri-apps/plugin-notification@2.2.2':
dependencies:
'@tauri-apps/api': 2.4.0
'@tauri-apps/api': 2.4.1
'@tauri-apps/plugin-os@2.2.0':
dependencies:
'@tauri-apps/api': 2.4.0
'@tauri-apps/api': 2.4.1
'@tauri-apps/plugin-process@2.2.0':
dependencies:
'@tauri-apps/api': 2.4.0
'@tauri-apps/api': 2.4.1
'@tauri-apps/plugin-shell@2.2.1':
dependencies:
'@tauri-apps/api': 2.4.0
'@tauri-apps/api': 2.4.1
'@tauri-apps/plugin-sql@2.2.0':
dependencies:
'@tauri-apps/api': 2.4.0
'@tauri-apps/api': 2.4.1
'@tauri-apps/plugin-updater@2.6.1':
dependencies:
'@tauri-apps/api': 2.4.0
'@tauri-apps/api': 2.4.1
'@tokenizer/token@0.3.0': {}
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 160 KiB

After

Width:  |  Height:  |  Size: 161 KiB

+15 -3
View File
@@ -15,6 +15,10 @@
"automerge": true
},
"rangeStrategy": "bump",
"lockFileMaintenance": {
"enabled": true,
"schedule": ["on sunday at 9:00am"]
},
"packageRules": [
{
"matchUpdateTypes": ["minor", "patch"],
@@ -25,13 +29,15 @@
"groupName": "Tauri dependencies",
"groupSlug": "tauri-deps",
"matchPackagePatterns": ["^@tauri-apps/"],
"matchManagers": ["npm"]
"matchManagers": ["npm"],
"rangeStrategy": "replace"
},
{
"groupName": "UnoCSS dependencies",
"groupSlug": "unocss-deps",
"matchPackagePatterns": ["^@unocss/"],
"matchManagers": ["npm"]
"matchManagers": ["npm"],
"rangeStrategy": "replace"
},
{
"description": "Rust crate updates",
@@ -39,7 +45,13 @@
"groupName": "Rust dependencies",
"groupSlug": "rust-deps",
"enabled": true,
"registryUrls": ["https://crates.io"]
"registryUrls": ["https://crates.io"],
"rangeStrategy": "replace"
},
{
"matchUpdateTypes": ["patch", "digest", "pin"],
"matchManagers": ["npm", "cargo"],
"rangeStrategy": "replace"
}
],
"enabledManagers": ["npm", "cargo"],
+14 -14
View File
@@ -5598,9 +5598,9 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
[[package]]
name = "tauri"
version = "2.4.0"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "511dd38065a5d3b36c33cdba4362b99a40a5103bebcd4aebb930717e7c8ba292"
checksum = "4d08db1ff9e011e04014e737ec022610d756c0eae0b3b3a9037bccaf3003173a"
dependencies = [
"anyhow",
"bytes",
@@ -5649,9 +5649,9 @@ dependencies = [
[[package]]
name = "tauri-build"
version = "2.1.0"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ffa8732a66f90903f5a585215f3cf1e87988d0359bc88c18a502efe7572c1de"
checksum = "0fd20e4661c2cce65343319e6e8da256958f5af958cafc47c0d0af66a55dcd17"
dependencies = [
"anyhow",
"cargo_toml",
@@ -5671,9 +5671,9 @@ dependencies = [
[[package]]
name = "tauri-codegen"
version = "2.1.0"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c266a247f14d63f40c6282c2653a8bac5cc3d482ca562a003a88513653ea817a"
checksum = "458258b19032450ccf975840116ecf013e539eadbb74420bd890e8c56ab2b1a4"
dependencies = [
"base64 0.22.1",
"brotli",
@@ -5698,9 +5698,9 @@ dependencies = [
[[package]]
name = "tauri-macros"
version = "2.1.0"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f47a1cf94b3bd6c4dc37dce1a43fc96120ff29a91757f0ab3cf713c7ad846e7c"
checksum = "d402813d3b9c773a0fa58697c457c771f10e735498fdcb7b343264d18e5a601f"
dependencies = [
"heck 0.5.0",
"proc-macro2",
@@ -5989,9 +5989,9 @@ dependencies = [
[[package]]
name = "tauri-runtime"
version = "2.5.0"
version = "2.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e9c7bce5153f1ca7bc45eba37349b31ba50e975e28edc8b5766c5ec02b0b63a"
checksum = "00ada7ac2f9276f09b8c3afffd3215fd5d9bff23c22df8a7c70e7ef67cacd532"
dependencies = [
"cookie",
"dpi",
@@ -6009,9 +6009,9 @@ dependencies = [
[[package]]
name = "tauri-runtime-wry"
version = "2.5.0"
version = "2.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "087188020fd6facb8578fe9b38e81fa0fe5fb85744c73da51a299f94a530a1e3"
checksum = "cf2e5842c57e154af43a20a49c7efee0ce2578c20b4c2bdf266852b422d2e421"
dependencies = [
"gtk",
"http 1.3.1",
@@ -6036,9 +6036,9 @@ dependencies = [
[[package]]
name = "tauri-utils"
version = "2.3.0"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82dcced4014e59af9790cc22f5d271df3be09ecd6728ec68861642553c8d01b7"
checksum = "1f037e66c7638cc0a2213f61566932b9a06882b8346486579c90e4b019bac447"
dependencies = [
"anyhow",
"brotli",
+3 -3
View File
@@ -24,16 +24,16 @@ name = "hula_app_lib"
crate-type = ["staticlib", "cdylib", "rlib"]
[build-dependencies]
tauri-build = { version = "2.1.0", features = [] }
tauri-build = { version = "2.1.1", features = [] }
[dependencies]
tauri = { version = "2.4.0", features = [
tauri = { version = "2.4.1", features = [
"macos-private-api",
"tray-icon",
"image-png",
] }
tauri-plugin-os = "2.2.1"
tauri-plugin-shell = "2.0.1"
tauri-plugin-shell = "2.2.1"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
tauri-plugin-http = { version = "2.4.2", features = ["unsafe-headers"] }
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1 -3
View File
@@ -116,9 +116,7 @@ onMounted(async () => {
if (process.env.NODE_ENV !== 'development') {
/** 禁用浏览器默认的快捷键 */
window.addEventListener('keydown', (e) => {
// 排除ctrl+c ctrl+v ctrl+a ctrl+enter
if (e.ctrlKey && (e.key === 'c' || e.key === 'v' || e.key === 'a' || e.key === 'Enter')) return
if (e.ctrlKey || e.metaKey || e.altKey) {
if (e.ctrlKey && (e.key === 'f' || e.key === 'r' || e.key === 'g' || e.key === 'j')) {
e.preventDefault()
}
})
+1 -1
View File
@@ -37,7 +37,7 @@
:img="localImageUrl"
:outputSize="0.1"
:autoCrop="true"
:outputType="'jpeg'"
outputType="webp"
:fixedBox="true"
:fixed="true"
:centerBox="true"
+97 -9
View File
@@ -112,16 +112,47 @@
<!-- 群信息列表 -->
<n-flex vertical class="select-none w-full px-30px box-border">
<n-flex align="center" justify="space-between" class="py-12px border-b text-(14px [--chat-text-color])">
<n-flex
align="center"
justify="space-between"
class="py-6px h-26px pr-4px border-b text-(14px [--chat-text-color])">
<span>群备注</span>
<span>{{ item.remark || '设置群聊备注' }}</span>
<div v-if="isEditingRemark" class="flex items-center">
<n-input
ref="remarkInputRef"
v-model:value="remarkValue"
size="tiny"
class="border-(1px solid #90909080)"
placeholder="请输入群聊备注"
clearable
@blur="handleRemarkUpdate"
@keydown.enter="handleRemarkUpdate" />
</div>
<span v-else class="cursor-pointer" @click="startEditRemark">
{{ item.remark || '设置群聊备注' }}
</span>
</n-flex>
<n-flex align="center" justify="space-between" class="py-12px border-b text-(14px [--chat-text-color])">
<n-flex
align="center"
justify="space-between"
:class="{ 'pr-4px': item.myName }"
class="py-6px border-b h-26px text-(14px [--chat-text-color])">
<span>我的本群昵称</span>
<span class="flex items-center cursor-pointer">
<div v-if="isEditingNickname" class="flex items-center">
<n-input
ref="nicknameInputRef"
v-model:value="nicknameValue"
size="tiny"
class="border-(1px solid #90909080)"
placeholder="请输入本群昵称"
clearable
@blur="handleNicknameUpdate"
@keydown.enter="handleNicknameUpdate" />
</div>
<span v-else class="flex items-center cursor-pointer" @click="startEditNickname">
<p class="text-#909090">{{ item.myName || '未设置' }}</p>
<n-icon size="16" class="ml-1">
<n-icon v-if="!item.myName" size="16" class="ml-1">
<svg><use href="#right"></use></svg>
</n-icon>
</span>
@@ -175,14 +206,27 @@ const { content } = defineProps<{
const item = ref<any>(null)
const options = ref<Array<{ name: string; src: string }>>([])
// 编辑群备注相关状态
const isEditingRemark = ref(false)
const remarkValue = ref('')
const remarkInputRef = useTemplateRef('remarkInputRef')
// 编辑本群昵称相关状态
const isEditingNickname = ref(false)
const nicknameValue = ref('')
const nicknameInputRef = useTemplateRef('nicknameInputRef')
watchEffect(() => {
if (content.type === RoomTypeEnum.SINGLE) {
item.value = useUserInfo(content.uid).value
} else {
apis.groupDetail({ id: content.uid }).then((response) => {
console.log(response)
item.value = response
// 初始化备注和昵称值
remarkValue.value = response.remark || ''
nicknameValue.value = response.myName || ''
// 获取群成员列表
if (item.value && item.value.roomId) {
fetchGroupMembers(item.value.roomId)
@@ -191,6 +235,52 @@ watchEffect(() => {
}
})
// 开始编辑群备注
const startEditRemark = () => {
remarkValue.value = item.value.remark || ''
isEditingRemark.value = true
nextTick(() => {
remarkInputRef.value?.focus()
})
}
// 处理群备注更新
const handleRemarkUpdate = async () => {
if (remarkValue.value !== item.value.remark) {
await apis.updateMyRoomInfo({
id: item.value.roomId,
remark: remarkValue.value,
myName: item.value.myName || ''
})
item.value.remark = remarkValue.value
window.$message.success('群备注更新成功')
}
isEditingRemark.value = false
}
// 开始编辑本群昵称
const startEditNickname = () => {
nicknameValue.value = item.value.myName || ''
isEditingNickname.value = true
nextTick(() => {
nicknameInputRef.value?.focus()
})
}
// 处理本群昵称更新
const handleNicknameUpdate = async () => {
if (nicknameValue.value !== item.value.myName) {
await apis.updateMyRoomInfo({
id: item.value.roomId,
myName: nicknameValue.value,
remark: item.value.remark || ''
})
item.value.myName = nicknameValue.value
window.$message.success('本群昵称更新成功')
}
isEditingNickname.value = false
}
// 复制
const handleCopy = (account: string) => {
if (account) {
@@ -199,7 +289,7 @@ const handleCopy = (account: string) => {
}
}
// 获取群成员列表
// 获取群组详情和成员信息
const fetchGroupMembers = async (roomId: string) => {
try {
const params = {
@@ -209,8 +299,6 @@ const fetchGroupMembers = async (roomId: string) => {
}
const response = await apis.getGroupList(params)
if (response && response.list) {
console.log(response)
// 使用每个成员的uid获取详细信息
const memberDetails = response.list.map((member: UserItem) => {
const userInfo = useUserInfo(member.uid).value
+23 -26
View File
@@ -10,8 +10,10 @@
<n-avatar class="rounded-8px select-none" :size="28" :src="currentUserAvatar" />
<label class="flex-y-center gap-6px">
<p class="text-(16px [--text-color])">{{ myGroupRemark || activeItem.name }}</p>
<p v-if="activeItem.type === RoomTypeEnum.GROUP" class="text-(11px #808080)">
[{{ activeItem?.memberNum }}]
<p
v-if="activeItem.type === RoomTypeEnum.GROUP && groupStore.countInfo?.memberNum"
class="text-(11px #808080)">
[{{ groupStore.countInfo?.memberNum }}]
</p>
</label>
<svg
@@ -555,32 +557,27 @@ const saveGroupInfo = async () => {
// 只有当数据发生变化时才发送请求
if (nicknameChanged || remarkChanged) {
try {
// 使用updateMyRoomInfo接口更新我在群里的昵称和群备注
await apis.updateMyRoomInfo({
id: activeItem.roomId,
myName: groupDetail.value.myNickname,
remark: groupDetail.value.groupRemark
})
// 使用updateMyRoomInfo接口更新我在群里的昵称和群备注
await apis.updateMyRoomInfo({
id: activeItem.roomId,
myName: groupDetail.value.myNickname,
remark: groupDetail.value.groupRemark
})
// 更新原始值为当前值
originalGroupDetail.value = {
myNickname: groupDetail.value.myNickname,
groupRemark: groupDetail.value.groupRemark
}
// 更新群聊缓存信息
groupStore.countInfo = {
...groupStore.countInfo,
myName: groupDetail.value.myNickname,
remark: groupDetail.value.groupRemark
}
window.$message.success('群聊信息已更新')
} catch (error) {
window.$message.error('群聊信息更新失败')
console.error('更新群聊信息失败:', error)
// 更新原始值为当前值
originalGroupDetail.value = {
myNickname: groupDetail.value.myNickname,
groupRemark: groupDetail.value.groupRemark
}
// 更新群聊缓存信息
groupStore.countInfo = {
...groupStore.countInfo,
myName: groupDetail.value.myNickname,
remark: groupDetail.value.groupRemark
}
window.$message.success('群聊信息已更新')
}
}
+3 -3
View File
@@ -6,7 +6,7 @@ export interface AvatarUploadOptions {
onSuccess?: (downloadUrl: string) => void
// 上传场景,默认为头像
scene?: UploadSceneEnum
// 文件大小限制(KB),默认为500KB
// 文件大小限制(KB),默认为100KB
sizeLimit?: number
}
/**
@@ -49,8 +49,8 @@ export const useAvatarUpload = (options: AvatarUploadOptions = {}) => {
// 处理裁剪
const handleCrop = async (cropBlob: Blob) => {
try {
const fileName = `avatar_${Date.now()}.png`
const file = new File([cropBlob], fileName, { type: 'image/png' })
const fileName = `avatar_${Date.now()}.webp`
const file = new File([cropBlob], fileName, { type: 'image/webp' })
// 检查裁剪后的文件大小
if (file.size > sizeLimit * 1024) {
+9 -5
View File
@@ -196,10 +196,12 @@ useMitt.on(WsResponseMessageType.MSG_RECALL, (data: RevokedMsgType) => {
chatStore.updateRecallStatus(data)
})
useMitt.on(WsResponseMessageType.MY_ROOM_INFO_CHANGE, (data: { myName: string; roomId: string; uid: string }) => {
groupStore.updateUserStatus({
uid: data.uid,
myName: data.myName
})
// 更新用户在群聊中的昵称
cachedStore.updateUserGroupNickname(data)
// 如果当前正在查看的是该群聊,则更新群组信息
if (globalStore.currentSession?.roomId === data.roomId) {
groupStore.getCountStatistic()
}
})
useMitt.on(WsResponseMessageType.RECEIVE_MESSAGE, async (data: MessageType) => {
chatStore.pushMsg(data)
@@ -287,7 +289,9 @@ onBeforeMount(async () => {
onMounted(async () => {
// 初始化配置
await configStore.initConfig()
if (!localStorage.getItem('config')) {
await configStore.initConfig()
}
await getCurrentWebviewWindow().show()
let permissionGranted = await isPermissionGranted()
+51 -1
View File
@@ -16,6 +16,9 @@ export const useCachedStore = defineStore(StoresEnum.CACHED, () => {
// 徽章信息缓存列表,key为徽章ID
const badgeCachedList = reactive<Record<string, Partial<CacheBadgeItem>>>({})
// 用户在不同群聊中的昵称映射表,格式为 {uid: {roomId: myName}}
const userGroupNicknameMap = reactive<Record<string, Record<string, string>>>({})
// 获取当前聊天室ID
const currentRoomId = computed(() => globalStore.currentSession?.roomId)
@@ -199,6 +202,50 @@ export const useCachedStore = defineStore(StoresEnum.CACHED, () => {
userAvatarUpdated.value = !userAvatarUpdated.value
}
/**
* 更新用户在特定群聊中的昵称
* @param data 包含用户ID、群聊ID和新昵称的数据
*/
const updateUserGroupNickname = (data: { uid: string; roomId: string; myName: string }) => {
const { uid, roomId, myName } = data
// 初始化用户的群聊昵称映射(如果不存在)
if (!userGroupNicknameMap[uid]) {
userGroupNicknameMap[uid] = {}
}
// 更新用户在特定群聊中的昵称
userGroupNicknameMap[uid][roomId] = myName
// 如果用户在缓存中存在,标记需要刷新
if (userCachedList[uid]) {
userCachedList[uid] = {
...userCachedList[uid],
needRefresh: true,
lastModifyTime: 0 // 重置时间确保下次获取时能刷新
}
}
// 触发UI更新
userAvatarUpdated.value = !userAvatarUpdated.value
}
/**
* 获取用户在特定群聊中的昵称
* @param uid 用户ID
* @param roomId 群聊ID
* @returns 用户在群聊中的昵称,如果没有则返回用户的名称
*/
const getUserGroupNickname = (uid: string, roomId: string): string => {
// 如果存在群聊昵称映射,则返回昵称
if (userGroupNicknameMap[uid]?.[roomId]) {
return userGroupNicknameMap[uid][roomId]
}
// 否则返回用户的名称
return userCachedList[uid]?.name || ''
}
return {
userCachedList,
badgeCachedList,
@@ -210,6 +257,9 @@ export const useCachedStore = defineStore(StoresEnum.CACHED, () => {
filterUsersByUidList,
updateUserState,
userAvatarUpdated,
updateUserCache
updateUserCache,
updateUserGroupNickname,
getUserGroupNickname,
userGroupNicknameMap
}
})
+14 -169
View File
@@ -151,142 +151,9 @@ export const useChatStore = defineStore(
}
})
/** ========================== localStorage 存储逻辑 ==================================== */
// 本地存储消息的键名前缀
const MESSAGE_STORAGE_KEY_PREFIX = 'hula_messages_'
// 本地存储消息选项的键名前缀
const MESSAGE_OPTIONS_STORAGE_KEY_PREFIX = 'hula_message_options_'
// 本地存储回复映射的键名前缀
const REPLY_MAPPING_STORAGE_KEY_PREFIX = 'hula_reply_mapping_'
// localStorage 存储大小限制 (约 5MB)
const STORAGE_SIZE_LIMIT = 5 * 1024 * 1024
// 将消息保存到 localStorage
const saveMessagesToStorage = (roomId: string) => {
try {
if (!roomId) return
// 获取当前房间的消息
const messages = messageMap.get(roomId)
if (!messages || messages.size === 0) return
// 将 Map 转换为数组以便序列化
const messagesArray = Array.from(messages.entries())
const serializedMessages = JSON.stringify(messagesArray)
// 检查序列化后的数据大小
if (serializedMessages.length > STORAGE_SIZE_LIMIT) {
console.warn(`消息数据过大,无法完全保存到 localStorage: ${roomId}`)
// 只保存最新的消息
const recentMessages = Array.from(messages.entries()).slice(-KEEP_MESSAGE_COUNT)
localStorage.setItem(`${MESSAGE_STORAGE_KEY_PREFIX}${roomId}`, JSON.stringify(recentMessages))
} else {
localStorage.setItem(`${MESSAGE_STORAGE_KEY_PREFIX}${roomId}`, serializedMessages)
}
// 保存消息加载状态
const options = messageOptions.get(roomId)
if (options) {
localStorage.setItem(`${MESSAGE_OPTIONS_STORAGE_KEY_PREFIX}${roomId}`, JSON.stringify(options))
}
// 保存回复映射
const replyMap = replyMapping.get(roomId)
if (replyMap) {
const replyMapArray = Array.from(replyMap.entries())
localStorage.setItem(`${REPLY_MAPPING_STORAGE_KEY_PREFIX}${roomId}`, JSON.stringify(replyMapArray))
}
} catch (error) {
console.error('保存消息到 localStorage 失败:', error)
}
}
// 从 localStorage 加载消息
const loadMessagesFromStorage = (roomId: string): boolean => {
try {
if (!roomId) return false
// 加载消息
const serializedMessages = localStorage.getItem(`${MESSAGE_STORAGE_KEY_PREFIX}${roomId}`)
if (serializedMessages) {
const messagesArray = JSON.parse(serializedMessages)
// 只加载最新的20条消息
const recentMessages = messagesArray.slice(-pageSize)
const messagesMap = new Map(recentMessages)
messageMap.set(roomId, messagesMap as Map<string, MessageType>)
// 加载消息选项
const serializedOptions = localStorage.getItem(`${MESSAGE_OPTIONS_STORAGE_KEY_PREFIX}${roomId}`)
if (serializedOptions) {
const options = JSON.parse(serializedOptions)
messageOptions.set(roomId, options)
}
// 加载回复映射
const serializedReplyMap = localStorage.getItem(`${REPLY_MAPPING_STORAGE_KEY_PREFIX}${roomId}`)
if (serializedReplyMap) {
const replyMapArray = JSON.parse(serializedReplyMap)
const replyMap = new Map(replyMapArray)
replyMapping.set(roomId, replyMap as Map<string, string[]>)
}
return true
}
return false
} catch (error) {
console.error('从 localStorage 加载消息失败:', error)
return false
}
}
// 清理过期或不需要的消息缓存
const cleanupMessageStorage = () => {
try {
// 获取所有 localStorage 键
const keys = Object.keys(localStorage)
// 找出所有消息相关的键
const messageKeys = keys.filter(
(key) =>
key.startsWith(MESSAGE_STORAGE_KEY_PREFIX) ||
key.startsWith(MESSAGE_OPTIONS_STORAGE_KEY_PREFIX) ||
key.startsWith(REPLY_MAPPING_STORAGE_KEY_PREFIX)
)
// 获取当前活跃的房间ID列表
const activeRoomIds = sessionList.map((session) => session.roomId)
// 删除不在活跃会话列表中的消息缓存
messageKeys.forEach((key) => {
// 提取房间ID
let roomId = ''
if (key.startsWith(MESSAGE_STORAGE_KEY_PREFIX)) {
roomId = key.substring(MESSAGE_STORAGE_KEY_PREFIX.length)
} else if (key.startsWith(MESSAGE_OPTIONS_STORAGE_KEY_PREFIX)) {
roomId = key.substring(MESSAGE_OPTIONS_STORAGE_KEY_PREFIX.length)
} else if (key.startsWith(REPLY_MAPPING_STORAGE_KEY_PREFIX)) {
roomId = key.substring(REPLY_MAPPING_STORAGE_KEY_PREFIX.length)
}
// 如果房间ID不在活跃列表中,删除相关缓存
if (roomId && !activeRoomIds.includes(roomId)) {
localStorage.removeItem(key)
}
})
} catch (error) {
console.error('清理消息缓存失败:', error)
}
}
/** ========================== end ==================================== */
// 监听当前房间ID的变化
watch(currentRoomId, (val, oldVal) => {
if (oldVal !== undefined && val !== oldVal) {
// 保存旧房间的消息到 localStorage
if (oldVal) {
saveMessagesToStorage(oldVal)
}
// 1. 立即清空当前消息列表
if (currentMessageMap.value) {
currentMessageMap.value.clear()
@@ -304,34 +171,24 @@ export const useChatStore = defineStore(
currentReplyMap.value.clear()
}
// 4. 尝试从 localStorage 加载新房间的消息
const loadedFromStorage = val ? loadMessagesFromStorage(val) : false
// 5. 如果没有从 localStorage 加载到消息,则从服务器加载
if (!loadedFromStorage) {
// 使用 nextTick 确保状态已更新
nextTick(async () => {
try {
// 从服务器加载消息
await getMsgList()
} catch (error) {
console.error('无法加载消息:', error)
currentMessageOptions.value = {
isLast: false,
isLoading: false,
cursor: ''
}
// 4. 尝试从服务器加载新房间的消息
nextTick(async () => {
try {
// 从服务器加载消息
await getMsgList()
} catch (error) {
console.error('无法加载消息:', error)
currentMessageOptions.value = {
isLast: false,
isLoading: false,
cursor: ''
}
})
} else {
// 从 localStorage 加载成功,更新加载状态
currentMessageOptions.value.isLoading = false
}
}
})
// 群组的时候去请求
if (currentRoomType.value === RoomTypeEnum.GROUP) {
groupStore.getGroupUserList(true)
groupStore.getCountStatistic()
cachedStore.getGroupAtUserBaseInfo()
}
@@ -412,9 +269,6 @@ export const useChatStore = defineStore(
currentMessageOptions.value.isLast = data.isLast
currentMessageOptions.value.isLoading = false
}
// 消息发送成功才保存消息到localStorage
saveMessagesToStorage(requestRoomId)
}
// 获取会话列表
@@ -446,9 +300,6 @@ export const useChatStore = defineStore(
sortAndUniqueSessionList()
// 清理不再活跃的会话消息缓存
cleanupMessageStorage()
// sessionList[0].unreadCount = 0
if (!isFirstInit || isFresh) {
isFirstInit = true
@@ -530,9 +381,6 @@ export const useChatStore = defineStore(
messagesToDelete.forEach((id) => current.delete(id))
}
// 保存消息到localStorage
saveMessagesToStorage(msg.message.roomId)
// 获取用户信息缓存
const uid = msg.fromUser.uid
const cacheUser = cachedStore.userCachedList[uid]
@@ -852,10 +700,7 @@ export const useChatStore = defineStore(
recalledMessages,
clearAllExpirationTimers,
updateTotalUnreadCount,
clearUnreadCount,
saveMessagesToStorage,
loadMessagesFromStorage,
cleanupMessageStorage
clearUnreadCount
}
},
{
+1 -1
View File
@@ -156,7 +156,7 @@ export const useGroupStore = defineStore(StoresEnum.GROUP, () => {
* 更新用户在线状态
* @param item 需要更新状态的用户
*/
const updateUserStatus = async (item: UserItem | OnStatusChangeType['member'] | { uid: string; myName: string }) => {
const updateUserStatus = async (item: UserItem | OnStatusChangeType['member']) => {
const findIndex = userList.value.findIndex((i) => i.uid === item.uid)
if (findIndex !== -1) {
userList.value[findIndex] = { ...userList.value[findIndex], ...item }
+6 -1
View File
@@ -26,6 +26,7 @@ declare module 'vue' {
LoadingSpinner: typeof import('./../components/common/LoadingSpinner.vue')['default']
MsgInput: typeof import('./../components/rightBox/MsgInput.vue')['default']
NaiveProvider: typeof import('./../components/common/NaiveProvider.vue')['default']
NAlert: typeof import('naive-ui')['NAlert']
NAutoComplete: typeof import('naive-ui')['NAutoComplete']
NAvatar: typeof import('naive-ui')['NAvatar']
NAvatarGroup: typeof import('naive-ui')['NAvatarGroup']
@@ -35,6 +36,7 @@ declare module 'vue' {
NCheckbox: typeof import('naive-ui')['NCheckbox']
NCollapse: typeof import('naive-ui')['NCollapse']
NCollapseItem: typeof import('naive-ui')['NCollapseItem']
NCollapseTransition: typeof import('naive-ui')['NCollapseTransition']
NConfigProvider: typeof import('naive-ui')['NConfigProvider']
NDialogProvider: typeof import('naive-ui')['NDialogProvider']
NDropdown: typeof import('naive-ui')['NDropdown']
@@ -47,20 +49,23 @@ declare module 'vue' {
NIconWrapper: typeof import('naive-ui')['NIconWrapper']
NImage: typeof import('naive-ui')['NImage']
NImageGroup: typeof import('naive-ui')['NImageGroup']
NIn: typeof import('naive-ui')['NIn']
NInput: typeof import('naive-ui')['NInput']
NLoadingBarProvider: typeof import('naive-ui')['NLoadingBarProvider']
NMessageProvider: typeof import('naive-ui')['NMessageProvider']
NModal: typeof import('naive-ui')['NModal']
NModalProvider: typeof import('naive-ui')['NModalProvider']
NNotificationProvider: typeof import('naive-ui')['NNotificationProvider']
NPopconfirm: typeof import('naive-ui')['NPopconfirm']
NPopover: typeof import('naive-ui')['NPopover']
NPopselect: typeof import('naive-ui')['NPopselect']
NProgress: typeof import('naive-ui')['NProgress']
NQrCode: typeof import('naive-ui')['NQrCode']
NRadio: typeof import('naive-ui')['NRadio']
NResult: typeof import('naive-ui')['NResult']
NScrollbar: typeof import('naive-ui')['NScrollbar']
NSelect: typeof import('naive-ui')['NSelect']
NSkeleton: typeof import('naive-ui')['NSkeleton']
NSpace: typeof import('naive-ui')['NSpace']
NSpin: typeof import('naive-ui')['NSpin']
NSwitch: typeof import('naive-ui')['NSwitch']
NTab: typeof import('naive-ui')['NTab']
+1 -1
View File
@@ -201,7 +201,7 @@ watch(
if (newVal) {
// 判断是否是群聊
if (newVal.type === RoomTypeEnum.GROUP) {
// 确保获取到最新的群组详情
// 在这里请求是因为这里一开始选中就会触发,而在chat.ts中则需要切换会话才会触发
await groupStore.getCountStatistic()
// 将群组详情信息传递给handleMsgClick方法
handleMsgClick({
-1
View File
@@ -152,7 +152,6 @@ const updateConnectionState = (newState: ConnectionState) => {
}
self.onmessage = (e: MessageEvent<string>) => {
console.log(e.data)
const { type, value } = JSON.parse(e.data)
switch (type) {
case 'initWS': {