Compare commits

..

21 Commits

Author SHA1 Message Date
目棃
18c57bdba8 🚀 v0.4.1 2024-01-19 10:23:36 +08:00
目棃
e32c544c6f 🚨 修复 qodana 报错 2024-01-18 23:50:48 +08:00
目棃
4a8f6c1640 ️ 优化部分逻辑 2024-01-18 21:08:17 +08:00
目棃
2ad534cf3a 🔥 清理无用 css 2024-01-18 17:14:48 +08:00
目棃
1c3176cb6a ✏️ 修复路径映射错误 2024-01-18 17:03:53 +08:00
目棃
a1e0b79cca 支持用户修改数据目录
close #78
2024-01-18 16:56:43 +08:00
目棃
7a8a9802d6 🌱 考虑 flv 视频流? 2024-01-18 15:54:54 +08:00
目棃
8390ce2309 💄 取消 hint 统一为 cancel 2024-01-18 15:44:31 +08:00
目棃
16b981c5bc ✏️ 完善返回类型 2024-01-18 15:28:54 +08:00
目棃
9fa2b22f49 ️ 支持全屏 2024-01-18 15:24:25 +08:00
目棃
6f872b5937 🐛 修复视频地址获取失败 2024-01-17 23:57:00 +08:00
目棃
fd7564e149 ️ 优化底部 hint 2024-01-17 21:10:09 +08:00
目棃
566e049734 👔 完善检测逻辑 2024-01-17 20:13:18 +08:00
目棃
efd0a49ea3 ♻️ 完善 fp 获取,添加强制更新入口 2024-01-17 20:09:38 +08:00
目棃
5e9773832a 🐛 修复 macOS 启动崩溃
fix #82
2024-01-17 19:40:56 +08:00
目棃
802b9de645 🐛 修复首页启动卡数据库加载
fix #79
2024-01-17 12:15:25 +08:00
目棃
ffd0651ea4 ♻️ GCG图鉴样式重构 2024-01-16 22:14:14 +08:00
目棃
b6eb523522 ️ 来源也算进搜索内容里面 2024-01-16 16:28:05 +08:00
目棃
7df23fd6bb ✏️ 类型修正,修复 qodana 报错 2024-01-16 12:07:56 +08:00
目棃
7cc27cce96 ♻️ 首页日历组件添加wiki跳转,移除wiki子窗口 2024-01-16 12:05:27 +08:00
目棃
b8d823298e ⬆️ 升级依赖 2024-01-16 11:35:20 +08:00
46 changed files with 1234 additions and 1424 deletions

View File

@@ -44,7 +44,7 @@ jobs:
- name: setup pnpm
uses: pnpm/action-setup@v2
with:
version: 8.11.0
version: 8.14.1
- name: Install frontend dependencies
run: pnpm install

View File

@@ -18,7 +18,7 @@ jobs:
- name: setup pnpm
uses: pnpm/action-setup@v2
with:
version: 8.12.1
version: 8.14.1
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: "Qodana Scan"

View File

@@ -9,6 +9,27 @@ Update: 2024-01-15
>
> 更新于 `2024-01-15 17:29:15`
## [0.4.1](https://github.com/BTMuli/TeyvatGuide/releases/v0.4.1) (2024-01-19)
### Feat
- 组件:首页素材日历添加 wiki 页面跳转
- 应用:完善 fp 获取,添加强制更新入口
- 图鉴:名片图鉴搜索支持搜索来源
- 应用:支持修改数据目录 [`#78`](https://github.com/BTMuli/TeyvatGuide/issues/78)
### Fix
- 应用:修复首页启动卡数据加载 [`#79`](https://github.com/BTMuli/TeyvatGuide/issues/79)
- 应用:修复 macOS 启动崩溃 [`#82`](https://github.com/BTMuli/TeyvatGuide/issues/82)
- 图鉴:完善切换时的底部 hint
### Change
- 图鉴:卡牌图鉴样式重构
- 组件:统一底部弹窗样式
- 应用:调整部分点击跳转逻辑
## [0.4.0](https://github.com/BTMuli/TeyvatGuide/releases/v0.4.0) (2024-01-15)
### Feat

View File

@@ -1,9 +1,9 @@
{
"name": "TeyvatGuide",
"version": "0.4.0",
"version": "0.4.1",
"description": "Game Tool for Genshin Impact player",
"private": true,
"packageManager": "pnpm@8.14.0",
"packageManager": "pnpm@8.14.1",
"scripts": {
"build": "tauri build",
"debug": "tauri build --debug",
@@ -67,7 +67,7 @@
"dependencies": {
"@mdi/font": "7.4.47",
"@tauri-apps/api": "^1.5.3",
"artplayer": "^5.1.0",
"artplayer": "^5.1.1",
"clipboard": "^2.0.11",
"color-convert": "^2.0.1",
"echarts": "^5.4.3",
@@ -78,38 +78,38 @@
"qrcode.vue": "^3.4.1",
"tauri-plugin-sql-api": "github:tauri-apps/tauri-plugin-sql#v1",
"uuid": "^9.0.1",
"vue": "^3.4.7",
"vue": "^3.4.14",
"vue-echarts": "^6.6.8",
"vue-json-viewer": "^3.0.4",
"vue-router": "^4.2.5",
"vuetify": "^3.4.9",
"vuetify": "^3.4.10",
"wcag-color": "^1.1.1"
},
"devDependencies": {
"@tauri-apps/cli": "^1.5.9",
"@types/color-convert": "^2.0.3",
"@types/js-md5": "^0.7.2",
"@types/node": "^20.10.7",
"@types/node": "^20.11.3",
"@types/uuid": "^9.0.7",
"@typescript-eslint/eslint-plugin": "^6.18.1",
"@typescript-eslint/parser": "^6.18.1",
"@vitejs/plugin-vue": "^5.0.2",
"@typescript-eslint/eslint-plugin": "^6.19.0",
"@typescript-eslint/parser": "^6.19.0",
"@vitejs/plugin-vue": "^5.0.3",
"concurrently": "^8.2.2",
"eslint": "^8.56.0",
"eslint-config-prettier": "^9.1.0",
"eslint-config-standard-with-typescript": "^43.0.0",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-jsonc": "^2.11.2",
"eslint-plugin-jsonc": "^2.12.0",
"eslint-plugin-n": "^16.6.2",
"eslint-plugin-prettier": "^5.1.2",
"eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-promise": "^6.1.1",
"eslint-plugin-vue": "^9.19.2",
"eslint-plugin-yml": "^1.11.0",
"eslint-plugin-vue": "^9.20.1",
"eslint-plugin-yml": "^1.12.0",
"husky": "^8.0.3",
"jsonc-eslint-parser": "^2.4.0",
"lint-staged": "^15.2.0",
"oxlint": "^0.1.2",
"prettier": "3.1.1",
"oxlint": "^0.2.0",
"prettier": "3.2.2",
"stylelint": "^16.1.0",
"stylelint-config-idiomatic-order": "^10.0.0",
"stylelint-config-standard-vue": "^1.0.0",
@@ -119,7 +119,7 @@
"stylelint-prettier": "^5.0.0",
"typescript": "^5.3.3",
"vite": "^5.0.11",
"vite-plugin-vue-devtools": "^7.0.6",
"vite-plugin-vue-devtools": "^7.0.10",
"vite-plugin-vuetify": "^2.0.1",
"yaml-eslint-parser": "^1.2.2"
}

492
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

207
src-tauri/Cargo.lock generated
View File

@@ -4,7 +4,7 @@ version = 3
[[package]]
name = "TeyvatGuide"
version = "0.4.0"
version = "0.4.1"
dependencies = [
"serde",
"serde_json",
@@ -39,7 +39,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01"
dependencies = [
"cfg-if",
"getrandom 0.2.11",
"getrandom 0.2.12",
"once_cell",
"version_check",
"zerocopy",
@@ -187,9 +187,9 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
[[package]]
name = "base64"
version = "0.21.5"
version = "0.21.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9"
checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
[[package]]
name = "base64ct"
@@ -494,9 +494,9 @@ dependencies = [
[[package]]
name = "cpufeatures"
version = "0.2.11"
version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0"
checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
dependencies = [
"libc",
]
@@ -527,54 +527,46 @@ dependencies = [
[[package]]
name = "crossbeam-channel"
version = "0.5.10"
version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82a9b73a36529d9c47029b9fb3a6f0ea3cc916a261195352ba19e770fc1748b2"
checksum = "176dc175b78f56c0f321911d9c8eb2b77a78a4860b9c19db83835fea1a46649b"
dependencies = [
"cfg-if",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-deque"
version = "0.8.4"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fca89a0e215bab21874660c67903c5f143333cab1da83d041c7ded6053774751"
checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
dependencies = [
"cfg-if",
"crossbeam-epoch",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
version = "0.9.17"
version = "0.9.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e3681d554572a651dda4186cd47240627c3d0114d45a95f6ad27f2f22e7548d"
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
dependencies = [
"autocfg",
"cfg-if",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-queue"
version = "0.3.10"
version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adc6598521bb5a83d491e8c1fe51db7296019d2ca3cb93cc6c2a20369a4d78a2"
checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35"
dependencies = [
"cfg-if",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.18"
version = "0.8.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3a430a770ebd84726f584a90ee7f020d28db52c6d02138900f22341f866d39c"
dependencies = [
"cfg-if",
]
checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
[[package]]
name = "crypto-common"
@@ -610,7 +602,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331"
dependencies = [
"quote",
"syn 2.0.46",
"syn 2.0.48",
]
[[package]]
@@ -620,7 +612,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30d2b3721e861707777e3195b0158f950ae6dc4a27e4d02ff9f67e3eb3de199e"
dependencies = [
"quote",
"syn 2.0.46",
"syn 2.0.48",
]
[[package]]
@@ -644,7 +636,7 @@ dependencies = [
"proc-macro2",
"quote",
"strsim",
"syn 2.0.46",
"syn 2.0.48",
]
[[package]]
@@ -655,7 +647,7 @@ checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5"
dependencies = [
"darling_core",
"quote",
"syn 2.0.46",
"syn 2.0.48",
]
[[package]]
@@ -790,11 +782,12 @@ dependencies = [
[[package]]
name = "embed-resource"
version = "2.4.0"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f54cc3e827ee1c3812239a9a41dede7b4d7d5d5464faa32d71bd7cba28ce2cb2"
checksum = "3bde55e389bea6a966bd467ad1ad7da0ae14546a5bc794d16d1e55e7fca44881"
dependencies = [
"cc",
"memchr",
"rustc_version",
"toml 0.8.8",
"vswhom",
@@ -1015,7 +1008,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.46",
"syn 2.0.48",
]
[[package]]
@@ -1188,9 +1181,9 @@ dependencies = [
[[package]]
name = "getrandom"
version = "0.2.11"
version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f"
checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5"
dependencies = [
"cfg-if",
"libc",
@@ -1365,9 +1358,9 @@ dependencies = [
[[package]]
name = "h2"
version = "0.3.22"
version = "0.3.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d6250322ef6e60f93f9a2162799302cd6f68f79f6e5d85c8c16f14d1d958178"
checksum = "b553656127a00601c8ae5590fcfdc118e4083a7924b6cf4ffc1ea4b99dc429d7"
dependencies = [
"bytes",
"fnv",
@@ -1606,9 +1599,9 @@ dependencies = [
[[package]]
name = "ignore"
version = "0.4.21"
version = "0.4.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "747ad1b4ae841a78e8aba0d63adbfbeaea26b517b63705d47856b73015d27060"
checksum = "b46810df39e66e925525d6e38ce1e7f6e1d208f72dc39757880fcb66e2c58af1"
dependencies = [
"crossbeam-deque",
"globset",
@@ -1622,14 +1615,13 @@ dependencies = [
[[package]]
name = "image"
version = "0.24.7"
version = "0.24.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f3dfdbdd72063086ff443e297b61695500514b1e41095b6fb9a5ab48a70a711"
checksum = "034bbe799d1909622a74d1193aa50147769440040ff36cb2baa947609b0a4e23"
dependencies = [
"bytemuck",
"byteorder",
"color_quant",
"num-rational",
"num-traits",
"png",
"tiff",
@@ -1760,15 +1752,15 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
[[package]]
name = "jpeg-decoder"
version = "0.3.0"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc0000e42512c92e31c2252315bda326620a4e034105e900c98ec492fa077b3e"
checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0"
[[package]]
name = "js-sys"
version = "0.3.66"
version = "0.3.67"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca"
checksum = "9a1d36f1235bc969acba30b7f5990b864423a6068a10f7c90ae8f0112e3a59d1"
dependencies = [
"wasm-bindgen",
]
@@ -1809,9 +1801,9 @@ dependencies = [
[[package]]
name = "libc"
version = "0.2.151"
version = "0.2.152"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4"
checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7"
[[package]]
name = "libm"
@@ -2137,17 +2129,6 @@ dependencies = [
"num-traits",
]
[[package]]
name = "num-rational"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.17"
@@ -2288,7 +2269,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.46",
"syn 2.0.48",
]
[[package]]
@@ -2505,7 +2486,7 @@ dependencies = [
"phf_shared 0.11.2",
"proc-macro2",
"quote",
"syn 2.0.46",
"syn 2.0.48",
]
[[package]]
@@ -2580,7 +2561,7 @@ version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5699cc8a63d1aa2b1ee8e12b9ad70ac790d65788cd36101fa37f87ea46c4cef"
dependencies = [
"base64 0.21.5",
"base64 0.21.7",
"indexmap 2.1.0",
"line-wrap",
"quick-xml",
@@ -2590,9 +2571,9 @@ dependencies = [
[[package]]
name = "png"
version = "0.17.10"
version = "0.17.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd75bf2d8dd3702b9707cdbc56a5b9ef42cec752eb8b3bafc01234558442aa64"
checksum = "1f6c3c3e617595665b8ea2ff95a86066be38fb121ff920a9c0eb282abcd1da5a"
dependencies = [
"bitflags 1.3.2",
"crc32fast",
@@ -2661,9 +2642,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068"
[[package]]
name = "proc-macro2"
version = "1.0.74"
version = "1.0.76"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2de98502f212cfcea8d0bb305bd0f49d7ebdd75b64ba0a68f937d888f4e0d6db"
checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c"
dependencies = [
"unicode-ident",
]
@@ -2746,7 +2727,7 @@ version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom 0.2.11",
"getrandom 0.2.12",
]
[[package]]
@@ -2788,7 +2769,7 @@ version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4"
dependencies = [
"getrandom 0.2.11",
"getrandom 0.2.12",
"libredox",
"thiserror",
]
@@ -2843,7 +2824,7 @@ version = "0.11.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37b1ae8d9ac08420c66222fb9096fc5de435c3c48542bc5336c51892cffafb41"
dependencies = [
"base64 0.21.5",
"base64 0.21.7",
"bytes",
"encoding_rs",
"futures-core",
@@ -2938,9 +2919,9 @@ dependencies = [
[[package]]
name = "rustix"
version = "0.38.28"
version = "0.38.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316"
checksum = "322394588aaf33c24007e8bb3238ee3e4c5c09c084ab32bc73890b99ff326bca"
dependencies = [
"bitflags 2.4.1",
"errno",
@@ -3051,29 +3032,29 @@ dependencies = [
[[package]]
name = "serde"
version = "1.0.194"
version = "1.0.195"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b114498256798c94a0689e1a15fec6005dee8ac1f41de56404b67afc2a4b773"
checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.194"
version = "1.0.195"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3385e45322e8f9931410f01b3031ec534c3947d0e94c18049af4d9f9907d4e0"
checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.46",
"syn 2.0.48",
]
[[package]]
name = "serde_json"
version = "1.0.110"
version = "1.0.111"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fbd975230bada99c8bb618e0c365c2eefa219158d5c6c29610fd09ff1833257"
checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4"
dependencies = [
"itoa 1.0.10",
"ryu",
@@ -3088,7 +3069,7 @@ checksum = "0b2e6b945e9d3df726b65d6ee24060aff8e3533d431f677a9695db04eff9dfdb"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.46",
"syn 2.0.48",
]
[[package]]
@@ -3118,7 +3099,7 @@ version = "3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64cd236ccc1b7a29e7e2739f27c0b2dd199804abc4290e32f59f3b68d6405c23"
dependencies = [
"base64 0.21.5",
"base64 0.21.7",
"chrono",
"hex",
"indexmap 1.9.3",
@@ -3138,7 +3119,7 @@ dependencies = [
"darling",
"proc-macro2",
"quote",
"syn 2.0.46",
"syn 2.0.48",
]
[[package]]
@@ -3237,9 +3218,9 @@ dependencies = [
[[package]]
name = "smallvec"
version = "1.11.2"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970"
checksum = "2593d31f82ead8df961d8bd23a64c2ccf2eb5dd34b0a34bfb4dd54011c72009e"
[[package]]
name = "socket2"
@@ -3416,7 +3397,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e37195395df71fd068f6e2082247891bc11e3289624bbc776a0cdfa1ca7f1ea4"
dependencies = [
"atoi",
"base64 0.21.5",
"base64 0.21.7",
"bitflags 2.4.1",
"byteorder",
"bytes",
@@ -3459,7 +3440,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6ac0ac3b7ccd10cc96c7ab29791a7dd236bd94021f31eec7ba3d46a74aa1c24"
dependencies = [
"atoi",
"base64 0.21.5",
"base64 0.21.7",
"bitflags 2.4.1",
"byteorder",
"crc",
@@ -3599,9 +3580,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.46"
version = "2.0.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89456b690ff72fddcecf231caedbe615c59480c93358a93dfae7fc29e3ebbf0e"
checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f"
dependencies = [
"proc-macro2",
"quote",
@@ -3818,7 +3799,7 @@ version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1554c5857f65dbc377cefb6b97c8ac77b1cb2a90d30d3448114d5d6b48a77fc"
dependencies = [
"base64 0.21.5",
"base64 0.21.7",
"brotli",
"ico",
"json-patch",
@@ -3870,7 +3851,7 @@ dependencies = [
[[package]]
name = "tauri-plugin-sql"
version = "0.0.0"
source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v1#539b566fade8381ba0704984acd4f67aa8a72958"
source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v1#83f9899f7853dec8a8bd19e32d86550d14646d50"
dependencies = [
"futures-core",
"log",
@@ -4012,7 +3993,7 @@ checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.46",
"syn 2.0.48",
]
[[package]]
@@ -4027,9 +4008,9 @@ dependencies = [
[[package]]
name = "tiff"
version = "0.9.0"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d172b0f4d3fba17ba89811858b9d3d97f928aece846475bbda076ca46736211"
checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e"
dependencies = [
"flate2",
"jpeg-decoder",
@@ -4231,7 +4212,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.46",
"syn 2.0.48",
]
[[package]]
@@ -4357,7 +4338,7 @@ version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560"
dependencies = [
"getrandom 0.2.11",
"getrandom 0.2.12",
]
[[package]]
@@ -4443,9 +4424,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
version = "0.2.89"
version = "0.2.90"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e"
checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
@@ -4453,24 +4434,24 @@ dependencies = [
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.89"
version = "0.2.90"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826"
checksum = "fcdc935b63408d58a32f8cc9738a0bffd8f05cc7c002086c6ef20b7312ad9dcd"
dependencies = [
"bumpalo",
"log",
"once_cell",
"proc-macro2",
"quote",
"syn 2.0.46",
"syn 2.0.48",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-futures"
version = "0.4.39"
version = "0.4.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac36a15a220124ac510204aec1c3e5db8a22ab06fd6706d881dc6149f8ed9a12"
checksum = "bde2032aeb86bdfaecc8b261eef3cba735cc426c1f3a3416d1e0791be95fc461"
dependencies = [
"cfg-if",
"js-sys",
@@ -4480,9 +4461,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.89"
version = "0.2.90"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2"
checksum = "3e4c238561b2d428924c49815533a8b9121c664599558a5d9ec51f8a1740a999"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@@ -4490,22 +4471,22 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.89"
version = "0.2.90"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283"
checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.46",
"syn 2.0.48",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.89"
version = "0.2.90"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f"
checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b"
[[package]]
name = "wasm-streams"
@@ -4522,9 +4503,9 @@ dependencies = [
[[package]]
name = "web-sys"
version = "0.3.66"
version = "0.3.67"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f"
checksum = "58cd2333b6e0be7a39605f0e255892fd7418a682d8da8fe042fe25128794d2ed"
dependencies = [
"js-sys",
"wasm-bindgen",
@@ -5013,9 +4994,9 @@ checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
[[package]]
name = "winnow"
version = "0.5.31"
version = "0.5.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97a4882e6b134d6c28953a387571f1acdd3496830d5e36c5e3a1075580ea641c"
checksum = "b7cf47b659b318dccbd69cc4797a39ae128f533dce7902a1096044d1967b9c16"
dependencies = [
"memchr",
]
@@ -5123,9 +5104,9 @@ dependencies = [
[[package]]
name = "xattr"
version = "1.2.0"
version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "914566e6413e7fa959cc394fb30e563ba80f3541fbd40816d4c05a0fc3f2a0f1"
checksum = "8da84f1a25939b27f6820d92aed108f83ff920fdf11a7b19366c27c4cda81d4f"
dependencies = [
"libc",
"linux-raw-sys",
@@ -5149,7 +5130,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.46",
"syn 2.0.48",
]
[[package]]

View File

@@ -1,6 +1,6 @@
[package]
name = "TeyvatGuide"
version = "0.4.0"
version = "0.4.1"
description = "Game Tool for Genshin Impact player"
authors = ["BTMuli <bt-muli@outlook.com>"]
license = "MIT"

View File

@@ -8,7 +8,7 @@
},
"package": {
"productName": "TeyvatGuide",
"version": "0.4.0"
"version": "0.4.1"
},
"tauri": {
"allowlist": {
@@ -147,7 +147,7 @@
"resizable": false,
"title": "米游社",
"label": "mhy_client",
"url": "about:blank",
"url": "",
"userAgent": "Mozilla/5.0 (Linux; Android 12) Mobile miHoYoBBS/2.67.1",
"visible": false,
"width": 400,

View File

@@ -81,23 +81,30 @@ async function listenOnInit(): Promise<void> {
}
async function checkAppLoad(): Promise<void> {
const checkDB = await TGSqlite.check();
let checkDB = false;
try {
checkDB = await TGSqlite.check();
} catch (error) {
console.error(error);
}
if (!checkDB) {
appStore.loading = false;
await TGSqlite.reset();
showSnackbar({
text: "检测到数据库不完整!已重置数据库!",
color: "error",
timeout: 3000,
});
await createDataDir();
router.go(0);
await resetDB();
} else {
appStore.loading = true;
console.info("数据库已加载!");
}
}
async function resetDB(): Promise<void> {
await TGSqlite.reset();
showSnackbar({
text: "检测到数据库不完整!已重置数据库!",
color: "error",
timeout: 3000,
});
await createDataDir();
appStore.loading = true;
}
// 检测 deviceFp
async function checkDeviceFp(): Promise<void> {
const appData = await TGSqlite.getAppData();
@@ -105,7 +112,6 @@ async function checkDeviceFp(): Promise<void> {
const deviceLocal = appStore.deviceInfo;
if (deviceInfo === undefined) {
if (deviceLocal.device_fp === "0000000000000") {
// 获取 deviceFp
appStore.deviceInfo = await TGRequest.Device.getFp(appStore.deviceInfo);
console.info("deviceInfo 已重新获取!");
}
@@ -161,11 +167,13 @@ async function checkUserLoad(): Promise<void> {
const accountLocal = userStore.account.value;
const accountDB = await TGSqlite.getCurAccount();
if (accountDB === false) {
showSnackbar({
text: "获取 GameAccount 失败!请尝试更新数据库!",
color: "error",
timeout: 3000,
});
if (appStore.isLogin) {
showSnackbar({
text: "获取 GameAccount 失败!请尝试更新数据库!",
color: "error",
timeout: 3000,
});
}
return;
}
if (accountDB !== accountLocal) {
@@ -174,6 +182,16 @@ async function checkUserLoad(): Promise<void> {
} else {
console.info("curAccount 数据已加载!");
}
const userDir = appData.find((item) => item.key === "userDir")?.value;
if (userDir === undefined) {
await TGSqlite.saveAppData("userDir", appStore.userDir);
} else if (userDir !== appStore.userDir) {
appStore.userDir = userDir;
console.info("userDir 数据已更新!");
} else {
console.info("userDir 数据已加载!");
}
}
// 创建数据文件夹

View File

@@ -1,7 +1,7 @@
/**
* @file assets/themes/dark.css
* @description 主题样式文件-深色主题
* @since Beta v0.3.9
* @since Beta v0.4.1
*/
/* dark mode */
@@ -46,21 +46,6 @@ html.dark {
--common-shadow-t-4: rgb(0 0 0 / 40%);
--common-shadow-t-8: rgb(0 0 0 / 80%);
/* box bg */
--common-bg-1: #3d424b; /* 一级背景色 */
--common-bgt-1: #faf7e8; /* 一级背景色对应的文本色 */
/* text */
--common-text-title: var(--tgc-yellow-2); /* title米色 */
--common-text-content: var(--tgc-white-1); /* text */
--common-text-quote: var(--tgc-dark-7); /* quote */
--back-top-shadow: #000000;
--post-default-text: #faf7e8;
--content-bg-2: #393b40;
--content-bg-3: #2a2a2a;
--content-text-2: #faf7e8;
--content-text-3: #e1e1e1;
}

View File

@@ -1,7 +1,7 @@
/**
* @file assets/themes/default.css
* @description 主题样式文件-默认(浅色)主题
* @since Beta v0.3.9
* @since Beta v0.4.1
*/
/* default(light) theme */
@@ -48,15 +48,4 @@ html.default {
/* common text color */
--common-text-title: var(--tgc-dark-8); /* title */
--common-text-content: var(--tgc-blue-1); /* text todo */
--common-text-quote: var(--tgc-white-4); /* quote todo */
--back-top-shadow: #546d8b;
--post-default-text: #1e1e1e;
--content-bg-2: #faf7e8;
--content-bg-3: #5b738f;
--content-text-2: #393b40;
--content-text-3: #546d8b;
}

View File

@@ -110,7 +110,7 @@
title="角色图鉴"
value="wiki-character"
:link="true"
href="/wiki/character"
href="/wiki/character/0"
>
<template #prepend>
<img src="/source/UI/wikiAvatar.webp" alt="characterIcon" class="side-icon-menu" />
@@ -121,7 +121,7 @@
title="武器图鉴"
value="wiki-weapon"
:link="true"
href="/wiki/weapon"
href="/wiki/weapon/0"
>
<template #prepend>
<img src="/source/UI/wikiWeapon.webp" alt="weaponIcon" class="side-icon-menu" />

View File

@@ -54,9 +54,6 @@
</div>
</div>
</div>
<v-snackbar v-model="showBar" :color="barColor" timeout="1000">
{{ barText }}
</v-snackbar>
</div>
</template>
<script lang="ts" setup>
@@ -66,16 +63,13 @@ import Mys from "../../plugins/Mys";
import { useHomeStore } from "../../store/modules/home";
import { createPost, createTGWindow } from "../../utils/TGWindow";
import { stamp2LastTime } from "../../utils/toolFunc";
import showSnackbar from "../func/snackbar";
// store
const homeStore = useHomeStore();
// loading
const loading = ref<boolean>(true);
// snackbar
const showBar = ref<boolean>(false);
const barText = ref<string>("");
const barColor = ref<string>("error");
const hasNew = ref<boolean>(false);
const showNew = ref<boolean>(false);
@@ -88,10 +82,7 @@ const poolTimePass = ref<Record<number, number>>({});
const timer = ref<Record<number, any>>({});
// expose
defineExpose({
name: "限时祈愿",
loading,
});
defineExpose({ name: "限时祈愿", loading });
function poolLastInterval(postId: number): TGApp.Plugins.Mys.Gacha.RenderCard | undefined {
const pool = poolCards.value.find((pool) => pool.postId === postId);
@@ -191,9 +182,10 @@ function checkCover(data: TGApp.Plugins.Mys.Gacha.Data[]): boolean {
async function toOuter(url: string, title: string): Promise<void> {
if (!url) {
barText.value = "链接为空!";
barColor.value = "error";
showBar.value = true;
showSnackbar({
text: "链接为空!",
color: "error",
});
return;
}
createTGWindow(url, "Sub_window", `Pool_${title}`, 1200, 800, true, true);

View File

@@ -26,20 +26,13 @@
<v-btn variant="outlined" @click="toDetail(itemVal)">详情</v-btn>
</div>
</div>
<div class="close-div">
<div class="close-btn" @click="onCancel">
<v-icon>mdi-close</v-icon>
</div>
</div>
</div>
</TOverlay>
</template>
<script setup lang="ts">
import { computed } from "vue";
import { useRouter } from "vue-router";
import Mys from "../../plugins/Mys";
import { createTGWindow } from "../../utils/TGWindow";
import showSnackbar from "../func/snackbar";
import TibCalendarItem from "../itembox/tib-calendar-item.vue";
import TibCalendarMaterial from "../itembox/tib-calendar-material.vue";
import TOverlay from "../main/t-overlay.vue";
@@ -52,12 +45,15 @@ interface ToCalendarProps {
interface ToCalendarEmits {
(e: "update:modelValue", value: boolean): void;
(e: "cancel"): void;
}
const emits = defineEmits<ToCalendarEmits>();
const props = defineProps<ToCalendarProps>();
const router = useRouter();
const visible = computed({
get: () => props.modelValue,
set: (value) => {
@@ -72,17 +68,12 @@ const onCancel = (): void => {
emits("cancel");
};
function toDetail(item: TGApp.App.Calendar.Item): void {
if (item.contentId === 0) {
const itemType = item.itemType === "character" ? "角色" : "武器";
showSnackbar({
text: `[${itemType}] ${item.name} 暂无详情`,
color: "warn",
});
return;
async function toDetail(item: TGApp.App.Calendar.Item): Promise<void> {
if (item.itemType === "character") {
await router.push(`/wiki/character/${item.id}`);
} else {
await router.push(`/wiki/weapon/${item.id}`);
}
const url = Mys.Api.Obc.replace("{contentId}", item.contentId.toString());
createTGWindow(url, "Sub_window", `Content_${item.contentId} ${item.name}`, 1200, 800, true);
}
</script>
<style scoped>
@@ -161,25 +152,4 @@ function toDetail(item: TGApp.App.Calendar.Item): void {
font-family: var(--font-title);
font-size: 20px;
}
.close-div {
display: flex;
width: 100%;
height: 60px;
align-items: center;
justify-content: center;
}
.close-btn {
display: flex;
width: 30px;
height: 30px;
align-items: center;
justify-content: center;
border: 1px solid var(--common-shadow-2);
border-radius: 50%;
background: var(--app-page-bg);
color: var(--tgc-yellow-1);
cursor: pointer;
}
</style>

View File

@@ -2,10 +2,11 @@
<div class="tp-video-box">
<!-- todo https://socialsisteryi.github.io/bilibili-API-collect/docs/video/videostream_url.html#%E8%A7%86%E9%A2%91%E4%BC%B4%E9%9F%B3%E9%9F%B3%E8%B4%A8%E4%BB%A3%E7%A0%81 -->
<iframe
ref="videoRef"
class="tp-video-container"
data-html2canvas-ignore
:src="props.data.insert.video"
:allowfullscreen="false"
:allowfullscreen="true"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
sandbox="allow-forms allow-same-origin allow-popups allow-presentation allow-scripts"
>
@@ -19,8 +20,12 @@
</div>
</template>
<script lang="ts" setup>
import md5 from "js-md5";
import { onMounted, ref } from "vue";
// todo https://artplayer.org/?libs=https://cdnjs.cloudflare.com/ajax/libs/dashjs/4.5.2/dash.all.min.js&example=dash
// todo flv
// https://artplayer.org/document/library/flv.html
// https://api.bilibili.com/x/player/playurl?avid=666064953&cid=1400018762&qn=64&otype=json
import { window as TauriWindow } from "@tauri-apps/api";
import { onBeforeMount, onMounted, ref } from "vue";
import Bili from "../../plugins/Bili";
import { saveImgLocal } from "../../utils/TGShare";
@@ -38,20 +43,15 @@ interface TpVideoProps {
const props = defineProps<TpVideoProps>();
const videoAspectRatio = ref<number>(16 / 9);
const videoData = ref<TGApp.Plugins.Bili.Video.ViewData>();
const videoRef = ref<HTMLIFrameElement | null>(null);
console.log("tpVideo", props.data.insert.video);
onMounted(async () => {
onBeforeMount(async () => {
const url = new URL(props.data.insert.video);
const aid = url.searchParams.get("aid") ?? undefined;
const bvid = url.searchParams.get("bvid") ?? undefined;
videoData.value = await Bili.video.view(aid, bvid);
if (!videoData.value) {
return;
}
if (!videoData.value?.pic.startsWith("blob")) {
videoData.value.pic = await saveImgLocal(videoData.value?.pic);
}
const meta = videoData.value.dimension;
if (meta.width > meta.height) {
videoAspectRatio.value = meta.width / meta.height;
@@ -60,9 +60,21 @@ onMounted(async () => {
}
});
onMounted(async () => {
if (videoData.value && videoData.value.pic && !videoData.value.pic.startsWith("blob:")) {
videoData.value.pic = await saveImgLocal(videoData.value.pic);
}
videoRef.value?.addEventListener("fullscreenchange", async () => {
if (document.fullscreenElement) {
await TauriWindow.getCurrent().setFullscreen(true);
} else {
await TauriWindow.getCurrent().setFullscreen(false);
}
});
});
function getVideoTime(): string {
const duration = videoData.value?.duration ?? 0;
console.log("duration", duration);
const seconds = duration % 60;
const minutes = Math.floor(duration / 60) % 60;
const hours = Math.floor(duration / 3600);
@@ -74,11 +86,6 @@ function getVideoTime(): string {
result += `${seconds.toString().padStart(2, "0")}`;
return result;
}
// 计算 md5
function getVideoHash(): string {
return md5.md5(props.data.insert.video);
}
</script>
<style lang="css" scoped>
.tp-video-box {

View File

@@ -157,9 +157,7 @@ watch(
},
);
onMounted(async () => {
await loadData();
});
onMounted(async () => await loadData());
async function toWiki(): Promise<void> {
if (props.item.contentId === 0) {

View File

@@ -93,6 +93,10 @@ async function loadData(): Promise<void> {
const res = await getWikiData("Weapon", props.item.id.toString());
if (res === undefined) return;
data.value = res.default;
showSnackbar({
text: `成功获取武器 ${props.item.name} 的 Wiki 数据`,
color: "success",
});
selectItems.value = data.value?.affix.Descriptions.map((item) => item.Level) ?? [];
} catch (error) {
showSnackbar({

View File

@@ -0,0 +1,100 @@
<template>
<v-card class="twg-box" @click="toWiki" :title.attr="props.data.name">
<img class="twg-border" src="/WIKI/GCG/bg/special.webp" alt="border" />
<img class="twg-cover" :src="props.data.icon" alt="cover" />
<div class="twg-name">
<span>{{ props.data.name }}</span>
</div>
</v-card>
</template>
<script lang="ts" setup>
import Mys from "../../plugins/Mys";
import { createTGWindow } from "../../utils/TGWindow";
import showSnackbar from "../func/snackbar";
interface TwgCardProps {
data: TGApp.App.GCG.WikiBriefInfo;
}
const props = defineProps<TwgCardProps>();
function toWiki(): void {
if (!props.data.contentId) return;
if (props.data.contentId === 0) {
showSnackbar({
text: `卡牌 ${props.data.name} 暂无外部链接`,
color: "error",
});
return;
}
const url = Mys.Api.Obc.replace("{contentId}", props.data.contentId.toString());
createTGWindow(
url,
"Sub_window",
`Content_${props.data.contentId} ${props.data.name}`,
1200,
800,
true,
);
}
</script>
<style lang="css" scoped>
.twg-box {
position: relative;
overflow: hidden;
width: 100%;
border-radius: 10px;
aspect-ratio: 7 / 12;
transition: all 0.3s;
}
.twg-cover {
position: absolute;
z-index: -1;
top: 0;
left: 0;
width: 100%;
height: 100%;
border-radius: 10px;
object-fit: cover;
transition: all 0.3s;
}
.twg-box:hover .twg-cover {
transform: scale(1.2);
transition: all 0.5s;
}
.twg-border {
position: absolute;
top: 0;
left: 0;
overflow: hidden;
width: 100%;
height: 100%;
border-radius: 10px;
}
.twg-name {
position: absolute;
bottom: 0;
left: 0;
display: flex;
width: 100%;
align-items: center;
justify-content: center;
border-radius: 0 0 10px 10px;
background: rgb(0 0 0 / 50%);
color: white;
}
.twg-name span {
overflow: hidden;
margin: 0 10px;
font-size: 14px;
text-align: center;
text-overflow: ellipsis;
white-space: nowrap;
word-break: break-all;
}
</style>

View File

@@ -41,7 +41,7 @@
</div>
</template>
<script lang="ts" setup>
import { dialog, fs, path } from "@tauri-apps/api";
import { dialog, path } from "@tauri-apps/api";
import { storeToRefs } from "pinia";
import { onMounted, ref, watch } from "vue";
@@ -52,11 +52,13 @@ import GroOverview from "../../components/gachaRecord/gro-overview.vue";
import ToLoading from "../../components/overlay/to-loading.vue";
import { AppCharacterData, AppWeaponData } from "../../data";
import TGSqlite from "../../plugins/Sqlite";
import { useAppStore } from "../../store/modules/app";
import { useUserStore } from "../../store/modules/user";
import { backupUigfData, exportUigfData, readUigfData, verifyUigfData } from "../../utils/UIGF";
import TGRequest from "../../web/request/TGRequest";
// store
const appStore = useAppStore();
const userStore = storeToRefs(useUserStore());
const account = userStore.account.value;
const authkey = ref<string>("");
@@ -102,7 +104,7 @@ async function confirmRefresh(): Promise<void> {
});
if (!confirmRes) {
showSnackbar({
color: "grey",
color: "cancel",
text: "已取消刷新祈愿数据",
});
return;
@@ -244,7 +246,7 @@ async function handleImportBtn(savePath?: string): Promise<void> {
});
if (!res) {
showSnackbar({
color: "grey",
color: "cancel",
text: "已取消祈愿数据导入",
});
return;
@@ -269,7 +271,7 @@ async function handleImportBtn(savePath?: string): Promise<void> {
}
} else {
showSnackbar({
color: "grey",
color: "cancel",
text: "已取消文件选择",
});
}
@@ -291,7 +293,7 @@ async function handleExportBtn(): Promise<void> {
});
if (!res) {
showSnackbar({
color: "grey",
color: "cancel",
text: "已取消祈愿数据导出",
});
return;
@@ -308,7 +310,7 @@ async function handleExportBtn(): Promise<void> {
});
if (!file) {
showSnackbar({
color: "grey",
color: "cancel",
text: "已取消文件保存",
});
return;
@@ -324,8 +326,7 @@ async function handleExportBtn(): Promise<void> {
// 恢复UID祈愿数据相当于导入祈愿数据不过目录固定
async function restoreGacha(): Promise<void> {
const backupPath = `${await path.appLocalDataDir()}userData`;
await handleImportBtn(backupPath);
await handleImportBtn(appStore.userDir);
}
// 备份当前 UID 的祈愿数据
@@ -343,17 +344,14 @@ async function backupGacha(): Promise<void> {
});
if (!res) {
showSnackbar({
color: "grey",
color: "cancel",
text: "已取消祈愿数据备份",
});
return;
}
loadingTitle.value = "正在备份祈愿数据";
loading.value = true;
if (!(await fs.exists("userData", { dir: fs.BaseDirectory.AppLocalData }))) {
await fs.createDir("userData", { dir: fs.BaseDirectory.AppLocalData, recursive: true });
}
await backupUigfData(uidCur.value, gachaListCur.value);
await backupUigfData(appStore.userDir, uidCur.value, gachaListCur.value);
loading.value = false;
showSnackbar({
text: `已成功备份 ${uidCur.value} 的祈愿数据`,
@@ -375,7 +373,7 @@ async function deleteGacha(): Promise<void> {
});
if (!firstConfirm) {
showSnackbar({
color: "grey",
color: "cancel",
text: "已取消祈愿数据删除",
});
return;
@@ -390,7 +388,7 @@ async function deleteGacha(): Promise<void> {
}
if (!secondConfirm) {
showSnackbar({
color: "grey",
color: "cancel",
text: "已取消祈愿数据删除",
});
return;

View File

@@ -17,6 +17,7 @@
</template>
<script lang="ts" setup>
import { onBeforeMount, ref } from "vue";
import { useRoute } from "vue-router";
import showConfirm from "../../components/func/confirm";
import showSnackbar from "../../components/func/snackbar";
@@ -26,11 +27,25 @@ import { AppCharacterData } from "../../data";
import Mys from "../../plugins/Mys";
import { createTGWindow } from "../../utils/TGWindow";
const id = useRoute().params.id.toString() ?? "0";
const cardsInfo = AppCharacterData;
const curItem = ref<TGApp.App.Character.WikiBriefInfo>();
onBeforeMount(() => {
curItem.value = cardsInfo[0];
if (id === "0") {
curItem.value = cardsInfo[0];
} else {
const item = cardsInfo.find((item) => item.id.toString() === id);
if (item) {
curItem.value = item;
} else {
showSnackbar({
text: `角色 ${id} 不存在`,
color: "warn",
});
curItem.value = cardsInfo[0];
}
}
});
async function switchC(item: TGApp.App.Character.WikiBriefInfo): Promise<void> {

View File

@@ -1,168 +1,96 @@
<template>
<ToLoading v-model="loading" title="正在加载卡牌列表" />
<v-tabs v-model="tab" align-tabs="start" class="cards-tab">
<div v-show="!doSearch">
<v-tab value="character"> 角色牌 </v-tab>
<v-tab value="action"> 行动牌 </v-tab>
<v-tab value="monster"> 魔物牌 </v-tab>
<div v-if="!doSearch">
<v-tab value="character">角色牌</v-tab>
<v-tab value="action">行动牌</v-tab>
<v-tab value="monster">魔物牌</v-tab>
</div>
<v-spacer />
<v-text-field
v-model="search"
append-icon="mdi-magnify"
class="card-search"
prepend-inner-icon="mdi-magnify"
label="搜索"
:single-line="true"
hide-details
@click:append="searchCard"
@keyup.enter="searchCard"
/>
<v-spacer />
</v-tabs>
<div v-show="!doSearch">
<div v-if="!doSearch">
<v-window v-model="tab">
<v-window-item value="character">
<v-window-item v-for="(item, index) in tabValues" :key="index" :value="item">
<div class="cards-grid">
<v-card
v-for="item in CardsInfoC"
:key="item.contentId"
class="card-cls"
@click="toOuter(item.name, item.contentId)"
>
<div class="card-border">
<img src="/WIKI/GCG/bg/normal.webp" alt="border" />
</div>
<div class="card-cover">
<img :src="item.icon" alt="cover" />
</div>
<div class="card-content">
<span>{{ item.name }}</span>
</div>
</v-card>
</div>
</v-window-item>
<v-window-item value="action">
<div class="cards-grid">
<v-card
v-for="item in CardsInfoA"
:key="item.contentId"
class="card-cls"
@click="toOuter(item.name, item.contentId)"
>
<div class="card-border">
<img src="/WIKI/GCG/bg/normal.webp" alt="border" />
</div>
<div class="card-cover">
<img :src="item.icon" alt="cover" />
</div>
<div class="card-content">
<span>{{ item.name }}</span>
</div>
</v-card>
</div>
</v-window-item>
<v-window-item value="monster">
<div class="cards-grid">
<v-card
v-for="item in CardsInfoM"
:key="item.contentId"
class="card-cls"
@click="toOuter(item.name, item.contentId)"
>
<div class="card-border">
<img src="/WIKI/GCG/bg/normal.webp" alt="border" />
</div>
<div class="card-cover">
<img :src="item.icon" alt="cover" />
</div>
<div class="card-content">
<span>{{ item.name }}</span>
</div>
</v-card>
<TwgCard v-for="(card, index) in cardsInfo[item]" :key="index" :data="card" />
</div>
</v-window-item>
</v-window>
</div>
<div v-show="doSearch">
<div v-else>
<div class="cards-grid">
<v-card
v-for="item in CardsInfoS"
:key="item.contentId"
class="card-cls"
@click="toOuter(item.name, item.contentId)"
>
<div class="card-border">
<img src="/WIKI/GCG/bg/special.webp" alt="border" />
</div>
<div class="card-cover">
<img :src="item.icon" alt="cover" />
</div>
<div class="card-content">
<span>{{ item.name }}</span>
</div>
</v-card>
<TwgCard v-for="(item, index) in CardsInfoS" :key="index" :data="item" />
</div>
</div>
</template>
<script lang="ts" setup>
import { computed, onMounted, ref } from "vue";
import { onBeforeMount, onMounted, ref } from "vue";
import showSnackbar from "../../components/func/snackbar";
import ToLoading from "../../components/overlay/to-loading.vue";
import TwgCard from "../../components/wiki/twg-card.vue";
import { AppGCGData } from "../../data";
import Mys from "../../plugins/Mys";
import { createTGWindow } from "../../utils/TGWindow";
// loading
const loading = ref<boolean>(true);
const allCards = computed(() => AppGCGData);
// search
const doSearch = ref<boolean>(false);
const search = ref<string>("");
// data
const tab = ref<string>("character");
const CardsInfoC = ref<TGApp.App.GCG.WikiBriefInfo[]>([]);
const CardsInfoA = ref<TGApp.App.GCG.WikiBriefInfo[]>([]);
const CardsInfoM = ref<TGApp.App.GCG.WikiBriefInfo[]>([]);
const cardsInfo = ref<{ [key: string]: TGApp.App.GCG.WikiBriefInfo[] }>({
character: [],
action: [],
monster: [],
});
const tabValues = ref<string[]>(["character", "action", "monster"]);
const CardsInfoS = ref<TGApp.App.GCG.WikiBriefInfo[]>([]);
onMounted(() => {
for (const item of allCards.value) {
if (item.type === "角色牌") CardsInfoC.value.push(item);
if (item.type === "行动牌") CardsInfoA.value.push(item);
if (item.type === "魔物牌") CardsInfoM.value.push(item);
onBeforeMount(() => {
for (const item of AppGCGData) {
if (item.type === "角色牌") cardsInfo.value.character.push(item);
if (item.type === "行动牌") cardsInfo.value.action.push(item);
if (item.type === "魔物牌") cardsInfo.value.monster.push(item);
}
loading.value = false;
});
function toOuter(cardName: string, cardId: number): void {
console.log(cardName, cardId);
// 若不存在 contentId
if (cardId === 0) {
showSnackbar({
text: "该卡牌暂无外部链接",
color: "error",
});
return;
}
const url = Mys.Api.Obc.replace("{contentId}", cardId.toString());
createTGWindow(url, "Sub_window", `Content_${cardId} ${cardName}`, 1200, 800, true);
}
onMounted(() => {
loading.value = false;
});
async function searchCard(): Promise<void> {
loading.value = true;
if (search.value === "") {
setTimeout(() => {
doSearch.value = false;
if (CardsInfoS.value.length === 0) {
showSnackbar({
text: "未搜索任何卡牌",
color: "error",
});
loading.value = false;
}, 1000);
return;
}
doSearch.value = false;
loading.value = false;
CardsInfoS.value = [];
showSnackbar({
text: "已重置搜索",
color: "success",
});
return;
}
doSearch.value = true;
const res: TGApp.App.GCG.WikiBriefInfo[] = [];
await Promise.allSettled(
allCards.value.map((item) => (item.name.includes(search.value) ? res.push(item) : null)),
AppGCGData.map((item) => (item.name.includes(search.value) ? res.push(item) : null)),
);
res.sort((a, b) => a.name.localeCompare(b.name));
console.log(res);
loading.value = false;
if (res.length === 0) {
showSnackbar({
@@ -171,89 +99,30 @@ async function searchCard(): Promise<void> {
});
doSearch.value = false;
} else {
CardsInfoS.value = res;
CardsInfoS.value = res.sort((a, b) => a.name.localeCompare(b.name));
showSnackbar({
text: `找到 ${res.length} 张相关卡牌`,
color: "success",
});
}
}
</script>
<style lang="css" scoped>
.cards-tab {
margin-bottom: 20px;
color: var(--content-text-3);
font-family: Genshin, serif;
color: var(--common-text-title);
font-family: var(--font-title);
}
.card-search {
margin-left: 10px;
color: var(--box-text-1);
}
.cards-grid {
display: grid;
overflow: hidden;
padding: 10px;
border-radius: 0 0 10px 10px;
grid-gap: 10px;
grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
}
.cards-grid :hover {
cursor: pointer;
}
.card-cls {
position: relative;
overflow: hidden;
width: 140px;
height: 240px;
border-radius: 10px;
transition: all 0.3s;
}
.card-cover {
position: absolute;
z-index: -1;
top: 0;
left: 0;
width: 100%;
height: 100%;
transition: all 0.3s;
}
.card-cls:hover .card-cover {
transform: scale(1.1);
transition: all 0.3s;
}
.card-border {
position: absolute;
top: 0;
left: 0;
overflow: hidden;
border-radius: 10px;
}
.card-border img {
width: 100%;
height: 100%;
border-radius: 10px;
}
.card-cover img {
width: 100%;
height: 100%;
border-radius: 10px;
object-fit: cover;
}
.card-content {
position: absolute;
bottom: 0;
left: 0;
display: flex;
overflow: hidden;
width: 100%;
height: 40px;
align-items: center;
justify-content: center;
border-radius: 0 0 10px 10px;
background: rgb(0 0 0 / 50%);
color: white;
font-family: Genshin, serif;
font-size: small;
}
</style>

View File

@@ -112,7 +112,11 @@ function searchNamecard() {
}
} else {
const searchResult = AppNameCardsData.filter((item) => {
return item.name.includes(search.value) || item.desc.includes(search.value);
return (
item.name.includes(search.value) ||
item.desc.includes(search.value) ||
item.source.includes(search.value)
);
});
sortData(searchResult);
}

View File

@@ -18,6 +18,7 @@
<script lang="ts" setup>
import { onBeforeMount, ref } from "vue";
import { useRoute } from "vue-router";
import showConfirm from "../../components/func/confirm";
import showSnackbar from "../../components/func/snackbar";
@@ -27,11 +28,25 @@ import { AppWeaponData } from "../../data";
import Mys from "../../plugins/Mys";
import { createTGWindow } from "../../utils/TGWindow";
const id = useRoute().params.id.toString() ?? "0";
const cardsInfo = AppWeaponData;
const curItem = ref<TGApp.App.Weapon.WikiBriefInfo>();
onBeforeMount(() => {
curItem.value = cardsInfo[0];
if (id === "0") {
curItem.value = cardsInfo[0];
} else {
const item = cardsInfo.find((item) => item.id.toString() === id);
if (item) {
curItem.value = item;
} else {
showSnackbar({
text: `武器 ${id} 不存在`,
color: "warn",
});
curItem.value = cardsInfo[0];
}
}
});
async function switchW(item: TGApp.App.Weapon.WikiBriefInfo): Promise<void> {

View File

@@ -282,7 +282,7 @@ async function importJson(): Promise<void> {
});
if (!selectedFile) {
showSnackbar({
color: "grey",
color: "cancel",
text: "已取消文件选择",
});
return;

View File

@@ -16,8 +16,8 @@
<v-window-item v-for="(value, index) in tabValues" :key="index" :value="value">
<div class="anno-grid">
<v-card v-for="item in annoCards[value]" :key="item.id" class="anno-card">
<div class="anno-cover" @click="createAnno(item)" :title="item.title">
<img :src="item.banner" alt="cover" />
<div class="anno-cover" :title="item.title">
<img :src="item.banner" alt="cover" @click="createAnno(item)" />
<div class="anno-info">
<div class="anno-id">ID:{{ item.id }}</div>
<div class="anno-time">

View File

@@ -105,7 +105,12 @@
/>
</template>
</v-list-item>
<v-list-item prepend-icon="mdi-refresh" title="刷新设备信息" @click="confirmUpdateDevice" />
<v-list-item prepend-icon="mdi-refresh">
<v-list-item-title @click="confirmUpdateDevice()">刷新设备信息</v-list-item-title>
<template #append>
<v-icon @click="confirmUpdateDevice(true)">mdi-bug</v-icon>
</template>
</v-list-item>
<v-list-item prepend-icon="mdi-database-remove" title="清除缓存" @click="confirmDelCache" />
<v-list-item
v-show="showReset"
@@ -116,23 +121,31 @@
<v-list-item prepend-icon="mdi-cog-sync" title="恢复默认设置" @click="confirmResetApp" />
<v-list-subheader :inset="true" class="config-header" title="路径" />
<v-divider :inset="true" class="border-opacity-75" />
<v-list-item
prepend-icon="mdi-folder-key"
title="本地数据库路径"
:subtitle="appStore.dataPath.dbDataPath"
/>
<v-list-item prepend-icon="mdi-folder-key">
<v-list-item-title style="cursor: pointer" @click="confirmCUD"
>用户数据目录</v-list-item-title
>
<v-list-item-subtitle>{{ appStore.userDir }}</v-list-item-subtitle>
<template #append>
<v-icon @click="copyPath('user')">mdi-content-copy</v-icon>
</template>
</v-list-item>
<v-list-item
prepend-icon="mdi-folder-account"
title="本地用户数据路径"
:subtitle="appStore.dataPath.userDataDir"
/>
title="应用数据路径"
:subtitle="appStore.dbPath"
>
<template #append>
<v-icon @click="copyPath('db')">mdi-content-copy</v-icon>
</template>
</v-list-item>
</v-list>
<TAppBadge />
</div>
</template>
<script lang="ts" setup>
import { app, fs, invoke, os, process as TauriProcess } from "@tauri-apps/api";
import { app, dialog, fs, invoke, os, process as TauriProcess } from "@tauri-apps/api";
import { storeToRefs } from "pinia";
import { computed, onMounted, ref } from "vue";
@@ -146,13 +159,11 @@ import { useAchievementsStore } from "../../store/modules/achievements";
import { useAppStore } from "../../store/modules/app";
import { useHomeStore } from "../../store/modules/home";
import { useUserStore } from "../../store/modules/user";
import { backUpUserData, restoreUserData } from "../../utils/dataBS";
import { getBuildTime } from "../../utils/TGBuild";
import { bytesToSize, getCacheDir, getDeviceInfo } from "../../utils/toolFunc";
import { backupUiafData, restoreUiafData } from "../../utils/UIAF";
import { bytesToSize, getCacheDir, getDeviceInfo, getRandomString } from "../../utils/toolFunc";
import { getDeviceFp } from "../../web/request/getDeviceFp";
import TGRequest from "../../web/request/TGRequest";
import { backupAbyssData, backupCookieData } from "../../web/utils/backupData";
import { restoreAbyssData, restoreCookieData } from "../../web/utils/restoreData";
// Store
const appStore = useAppStore();
@@ -239,7 +250,7 @@ async function confirmRefreshUser(): Promise<void> {
});
if (!res) {
showSnackbar({
color: "grey",
color: "cancel",
text: "已取消刷新",
});
return;
@@ -347,30 +358,16 @@ async function confirmBackup(): Promise<void> {
});
if (!res) {
showSnackbar({
color: "grey",
color: "cancel",
text: "已取消备份",
});
return;
}
loadingTitle.value = "正在备份数据...";
loading.value = true;
if (!(await fs.exists("userData", { dir: fs.BaseDirectory.AppLocalData }))) {
await fs.createDir("userData", { dir: fs.BaseDirectory.AppLocalData, recursive: true });
}
console.info("数据文件夹创建完成!");
loadingSub.value = "正在获取成就数据";
const achievements = await TGSqlite.getUIAF();
loadingSub.value = "正在备份成就数据";
await backupUiafData(achievements);
loadingSub.value = "正在获取 Cookie";
const cookie = await TGSqlite.getCookie();
loadingSub.value = "正在备份 Cookie";
await backupCookieData(cookie);
loadingSub.value = "正在获取深渊数据";
const abyss = await TGSqlite.getAbyss();
loadingSub.value = "正在备份深渊数据";
await backupAbyssData(abyss);
loadingSub.value = "";
const saveDir = appStore.userDir;
loadingSub.value = "祈愿数据需单独备份";
await backUpUserData(saveDir);
loading.value = false;
showSnackbar({ text: "数据已备份!" });
}
@@ -383,44 +380,23 @@ async function confirmRestore(): Promise<void> {
});
if (!resConfirm) {
showSnackbar({
color: "grey",
color: "cancel",
text: "已取消恢复",
});
return;
}
// todo 自定义路径
loadingTitle.value = "正在恢复数据...";
loading.value = true;
if (!(await fs.exists("userData", { dir: fs.BaseDirectory.AppLocalData }))) {
showSnackbar({
color: "error",
text: "数据文件夹不存在!",
});
return;
}
const fail: string[] = [];
let res: boolean;
loadingSub.value = "正在恢复成就数据";
res = await restoreUiafData();
if (!res) {
fail.push("成就数据");
}
loadingSub.value = "正在恢复祈愿数据";
res = await restoreCookieData();
userStore.cookie.value = await TGSqlite.getCookie();
if (!res) {
fail.push("Cookie");
}
loadingSub.value = "正在恢复深渊数据";
res = await restoreAbyssData();
if (!res) {
fail.push("深渊数据");
}
fail.length > 0
? showSnackbar({ text: `${fail.join("、")} 恢复失败!`, color: "error" })
: showSnackbar({ text: "数据已恢复!" });
loadingSub.value = "祈愿数据需单独恢复";
const saveDir = appStore.userDir;
await restoreUserData(saveDir);
loading.value = false;
}
// todo 设置自定义数据保存路径并进行数据迁移
// todo macOS 需要测试
// 更新数据
async function confirmUpdate(title?: string): Promise<void> {
const res = await showConfirm({
@@ -429,7 +405,7 @@ async function confirmUpdate(title?: string): Promise<void> {
});
if (!res) {
showSnackbar({
color: "grey",
color: "cancel",
text: "已取消更新数据库",
});
return;
@@ -448,7 +424,33 @@ async function confirmUpdate(title?: string): Promise<void> {
}
// 更新设备信息
async function confirmUpdateDevice(): Promise<void> {
async function confirmUpdateDevice(force?: boolean): Promise<void> {
if (force !== undefined && force) {
const resF = await showConfirm({
title: "确认强制更新设备信息吗?",
text: `DeviceFp:${appStore.deviceInfo.device_fp}`,
});
if (!resF) {
showSnackbar({
text: "已取消强制更新设备信息",
color: "cancel",
});
return;
}
appStore.deviceInfo = await TGRequest.Device.getFp();
if (appStore.deviceInfo.device_fp === "0000000000000") {
appStore.deviceInfo.device_fp = getRandomString(13, "hex");
showSnackbar({
text: `设备信息获取失败!已使用随机值 ${appStore.deviceInfo.device_fp} 代替`,
color: "warn",
});
} else {
showSnackbar({
text: "设备信息已更新! DeviceFp: " + appStore.deviceInfo.device_fp,
});
}
return;
}
const localFp = getDeviceInfo("device_fp");
if (localFp !== "0000000000000") {
const res = await showConfirm({
@@ -463,7 +465,16 @@ async function confirmUpdateDevice(): Promise<void> {
return;
}
}
console.log(appStore.deviceInfo);
appStore.deviceInfo = await TGRequest.Device.getFp(appStore.deviceInfo);
console.log(appStore.deviceInfo);
if (appStore.deviceInfo.device_fp === "0000000000000") {
appStore.deviceInfo.device_fp = getRandomString(13, "hex");
showSnackbar({
text: "设备信息获取失败!已使用随机值代替",
});
return;
}
showSnackbar({
text: "设备信息已更新! DeviceFp: " + appStore.deviceInfo.device_fp,
});
@@ -498,7 +509,7 @@ async function confirmDelCache(): Promise<void> {
});
if (!res) {
showSnackbar({
color: "grey",
color: "cancel",
text: "已取消清除缓存",
});
return;
@@ -523,7 +534,7 @@ async function confirmResetApp(): Promise<void> {
});
if (!res) {
showSnackbar({
color: "grey",
color: "cancel",
text: "已取消恢复默认设置",
});
return;
@@ -546,7 +557,7 @@ async function tryShowReset(): Promise<void> {
});
if (!res) {
showSnackbar({
color: "grey",
color: "cancel",
text: "已取消",
});
return;
@@ -574,7 +585,7 @@ async function confirmResetDB(title?: string): Promise<void> {
});
if (!res) {
showSnackbar({
color: "grey",
color: "cancel",
text: "已取消重置数据库",
});
return;
@@ -613,6 +624,67 @@ function submitHome(): void {
homeStore.setShowValue(show);
showSnackbar({ text: "已修改!" });
}
function copyPath(type: "db" | "user"): void {
const path = type === "db" ? appStore.dbPath : appStore.userDir;
navigator.clipboard.writeText(path);
const content = type === "db" ? "数据库" : "用户数据";
showSnackbar({
text: `${content}路径已复制!`,
});
}
async function confirmCUD(): Promise<void> {
const oriDir = appStore.userDir;
const check = await showConfirm({
title: "确认修改用户数据路径吗?",
text: "祈愿数据需修改后重新手动备份!",
});
if (!check) {
showSnackbar({
color: "cancel",
text: "已取消修改",
});
return;
}
const dir = await dialog.open({
directory: true,
defaultPath: oriDir,
multiple: false,
});
if (dir === null) {
showSnackbar({
color: "error",
text: "路径不能为空!",
});
return;
}
if (typeof dir !== "string") {
showSnackbar({
color: "error",
text: "路径错误!",
});
return;
}
if (dir === oriDir) {
showSnackbar({
color: "warn",
text: "路径未修改!",
});
return;
}
appStore.userDir = dir;
await TGSqlite.saveAppData("userDir", dir);
await backUpUserData(dir);
await fs.removeDir(oriDir, { recursive: true });
showSnackbar({
text: "已重新备份数据!即将刷新页面!",
timeout: 3000,
});
setTimeout(() => {
window.location.reload();
}, 4000);
}
</script>
<style lang="css" scoped>

View File

@@ -36,7 +36,6 @@ function readLoading(): void {
let loadingMap = itemRefs.value.map((item) => {
return item.loading ? item.name : null;
});
if (!appStore.loading) loadingMap.push("数据库");
loadingSubtitle.value = "正在加载 " + loadingMap.filter((item) => item)?.join("、");
if (loadingMap.every((item) => !item)) {
loading.value = false;
@@ -45,7 +44,6 @@ function readLoading(): void {
onMounted(async () => {
loadingTitle.value = "正在加载首页";
loading.value = true;
const isProdEnv = import.meta.env.MODE === "production";
// 获取当前环境
if (isProdEnv && appStore.devMode) {

View File

@@ -29,8 +29,8 @@
<v-window-item v-for="(value, index) in tabValues" :key="index" :value="value">
<div class="news-grid">
<v-card v-for="item in postData[value]" :key="item.postId" class="news-card">
<div class="news-cover" @click="createPost(item)">
<img :src="item.cover" alt="cover" />
<div class="news-cover">
<img :src="item.cover" alt="cover" @click="createPost(item)" />
<div v-if="value === 'activity'" class="news-card-act">
<div
class="nca-status"

View File

@@ -14,31 +14,8 @@
</div>
</div>
</div>
<!-- <TpVideo :data="mock" />-->
</template>
<script lang="ts" setup>
import { onMounted } from "vue";
import Bili from "../../plugins/Bili";
// import TpVideo from "../../components/post/TpVideo.vue";
const mock = {
insert: {
video: "https://player.bilibili.com/player.html?aid=540893019&autoplay=false&bvid=BV1ri4y1s7sY",
},
};
onMounted(async () => {
const url = new URL(mock.insert.video);
const aid = url.searchParams.get("aid") ?? undefined;
const bvid = url.searchParams.get("bvid") ?? undefined;
const baseData = await Bili.video.view(aid, bvid);
console.log("baseData", baseData);
const cid = baseData.cid;
const urlData = await Bili.video.url(cid, undefined, bvid);
console.log("urlData", urlData);
});
</script>
<script lang="ts" setup></script>
<style lang="css" scoped>
.test-box {
display: flex;

View File

@@ -1,7 +1,7 @@
/**
* @file plugins/Bili/request/getVideoUrl.ts
* @description Bili 插件视频请求文件
* @since Beta v0.4.0
* @since Beta v0.4.1
*/
import { http } from "@tauri-apps/api";
@@ -10,44 +10,33 @@ import getWrid from "../utils/getWrid";
/**
* @description 获取视频播放地址
* @since Beta v0.4.0
* @since Beta v0.4.1
* @see https://socialsisteryi.github.io/bilibili-API-collect/docs/video/videostream_url.html#dash%E6%A0%BC%E5%BC%8F
* @param {string} [aid] 视频AV号
* @param {string} [bvid] 视频BV号
* @param {string} bvid 视频BV号
* @param {number} cid 视频分P号
* @returns {Promise<string>} 视频播放地址
* @returns {Promise<TGApp.Plugins.Bili.Video.UrlData>} 视频播放地址
*/
async function getVideoUrl(cid: number, aid?: string, bvid?: string): Promise<unknown> {
const url = "https://api.bilibili.com/x/player/wbi/playurl";
async function getVideoUrl(cid: number, bvid: string): Promise<TGApp.Plugins.Bili.Video.UrlData> {
const url = "https://api.bilibili.com/x/player/playurl";
let params: Record<string, string> = {
bvid,
cid: cid.toString(),
platform: "html5",
high_quality: "1",
fnval: "16",
platform: "pc",
};
if (aid) {
params = {
aid,
...params,
};
} else if (bvid) {
params = {
bvid,
...params,
};
} else {
throw new Error("参数错误");
}
const wrid = await getWrid(params);
const wridRes = await getWrid(params);
params = {
...params,
wts: wrid[0].toString(),
wrid: wrid[1],
wts: wridRes[0],
wrid: wridRes[1],
};
console.log("params", params);
return await http
.fetch<TGApp.Plugins.Bili.Video.UrlResponse>(url, {
method: "GET",
query: params,
headers: {
referer: "https://www.bilibili.com/",
},
})
.then((res) => {
return res.data.data;

View File

@@ -1,12 +1,12 @@
/**
* @file plugins/Bili/types/Video.d.ts
* @description Bili 插件视频类型定义文件
* @since Beta v0.4.0
* @since Beta v0.4.1
*/
/**
* @description Bili 插件视频类型
* @since Beta v0.4.0
* @since Beta v0.4.1
* @namespace Video
* @memberof TGApp.Plugins.Bili
*/
@@ -169,5 +169,174 @@ declare namespace TGApp.Plugins.Bili.Video {
data: UrlData;
}
type UrlData = unknown;
/**
* @description Bili 视频播放地址返回数据
* @since Beta v0.4.1
* @interface UrlData
* @property {string} from 视频来源
* @property {string} result 视频播放地址
* @property {string} message 视频播放地址
* @property {number} quality 视频清晰度
* @property {string} format 视频格式
* @property {number} timelength 视频时长 (ms)
* @property {string} accept_format 视频支持格式
* @property {string[]} accept_description 视频支持格式描述
* @property {number[]} accept_quality 视频支持清晰度
* @property {number} video_codecid 视频编码ID
* @property {string} seek_param 视频跳转参数
* @property {string} seek_type 视频跳转类型
* @property {UrlDash} dash 视频播放地址
* @property {UrlDurl[]} durl 视频播放地址
* @property {UrlFormats} support_formats 视频支持格式
* @property {unknown} high_format 视频高清格式
* @property {number} last_play_time 视频上次播放时间
* @property {number} last_play_cid 视频上次播放分P号
* @return UrlData
*/
interface UrlData {
from: string;
result: string;
message: string;
quality: number;
format: string;
timelength: number;
accept_format: string;
accept_description: string[];
accept_quality: number[];
video_codecid: number;
seek_param: string;
seek_type: string;
dash: UrlDash; // dash 返回
durl: UrlDurl[]; // mp4 返回
support_formats: UrlFormats;
high_format: unknown;
last_play_time: number;
last_play_cid: number;
}
/**
* @description Bili 视频播放地址
* @since Beta v0.4.1
* @interface UrlDash
* @property {number} duration 视频播放地址时长
* @property {number} minBufferTime 视频播放地址缓冲时间
* @property {number} min_buffer_time 视频播放地址缓冲时间
* @property {UrlDashData[]} video 视频播放地址
* @property {UrlDashData[]} audio 视频播放地址
* @property {number} dolby.type 杜比音频类型
* @property {number} dolby.audio 杜比音频
* @property {unknown} flac flac音频
* @return UrlDash
*/
interface UrlDash {
duration: number;
minBufferTime: number;
min_buffer_time: number;
video: UrlDashData[];
audio: UrlDashData[];
dolby: {
type: number;
audio: number;
};
flac: unknown;
}
/**
* @description Bili 视频dash返回视频数据
* @since Beta v0.4.1
* @interface UrlDashData
* @property {number} id 视频播放地址ID
* @property {string} baseUrl 视频播放地址
* @property {string} base_url 视频播放地址
* @property {string[]} backupUrl 视频备用播放地址
* @property {string[]} backup_url 视频备用播放地址
* @property {number} bandwidth 视频播放地址带宽
* @property {string} mimeType 视频播放地址类型
* @property {string} mime_type 视频播放地址类型
* @property {string} codecs 视频播放地址编码
* @property {number} width 视频播放地址宽度
* @property {number} height 视频播放地址高度
* @property {string} frameRate 视频播放地址帧率
* @property {string} frame_rate 视频播放地址帧率
* @property {string} sar 视频播放地址比例
* @property {number} startWithSap 是否从SAP开始
* @property {number} start_with_sap 是否从SAP开始
* @property {string} segmentBase.Initialization 初始化
* @property {string} segmentBase.indexRange 索引范围
* @property {string} segment_base.initialization 初始化
* @property {string} segment_base.index_range 索引范围
* @property {number} codecid 视频播放地址编码ID
* @return UrlDashData
*/
interface UrlDashData {
id: number;
baseUrl: string;
base_url: string;
backupUrl: string[];
backup_url: string[];
bandwidth: number;
mimeType: string;
mime_type: string;
codecs: string;
width: number;
height: number;
frameRate: string;
frame_rate: string;
sar: string;
startWithSap: number;
start_with_sap: number;
segmentBase: {
Initialization: string;
indexRange: string;
};
segment_base: {
initialization: string;
index_range: string;
};
codecid: number;
}
/**
* @description Bili 视频播放地址
* @since Beta v0.4.1
* @interface UrlDurl
* @property {number} order 视频播放地址序号
* @property {number} length 视频播放地址长度
* @property {number} size 视频播放地址大小
* @property {string} ahead 视频播放地址
* @property {string} vhead 视频播放地址
* @property {string} url 视频播放地址
* @property {unknown} backup_url 视频备用播放地址
* @return UrlDurl
*/
interface UrlDurl {
order: number;
length: number;
size: number;
ahead: string;
vhead: string;
url: string;
backup_url: unknown;
}
/**
* @description Bili 视频支持格式
* @since Beta v0.4.1
* @interface UrlFormats
* @property {number} quality 视频清晰度
* @property {string} format 视频格式
* @property {string} new_description 视频格式描述
* @property {string} display_desc 视频格式描述
* @property {string} superscript 视频格式描述
* @property {unknown} codecs 视频编码
* @return UrlFormats
*/
interface UrlFormats {
quality: number;
format: string;
new_description: string;
display_desc: string;
superscript: string;
codecs: unknown;
}
}

View File

@@ -1,7 +1,7 @@
/**
* @file plugins/Bili/utils/getWrid.ts
* @description Bili 插件获取 wrid 工具函数
* @since Beta v0.4.0
* @since Beta v0.4.1
*/
import md5 from "js-md5";
@@ -20,7 +20,7 @@ function getKeyVal(key: string): string {
/**
* @description 获取 mixin_key
* @since Beta v0.4.0
* @since Beta v0.4.1
* @return {Promise<string>} mixin_key
*/
async function getMixinKey(): Promise<string> {
@@ -35,46 +35,36 @@ async function getMixinKey(): Promise<string> {
];
const res = [];
for (const i of MIXIN_KEY_ENC_TAB) {
if (key.length < i) {
continue;
}
res.push(key[i]);
}
// 截取 res 的前 32 位
return res.join("").slice(0, 32);
}
/**
* @description 获取 wrid
* @since Beta v0.4.0
* @since Beta v0.4.1
* @param {Record<string,string|number>} params 请求参数
* @param {number} nts 时间戳(秒)
* @returns {Promise<string>} wrid
* @returns {Promise<[string|string]>} wrid
*/
async function getWrid(
params: Record<string, string | number>,
nts?: number,
): Promise<[number, string]> {
async function getWrid(params: Record<string, string | number>): Promise<[string, string]> {
const mixin_key = await getMixinKey();
let wts: number;
if (!nts) {
wts = Math.floor(Date.now() / 1000);
} else {
wts = nts;
}
const obj: Record<string, unknown> = {
const wts = Math.floor(Date.now() / 1000);
const obj: Record<string, string | number> = {
...params,
wts,
};
const keys = Object.keys(obj).sort();
let md5Str = "";
for (const key of keys) {
md5Str += `${key}=${obj[key]}&`;
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
if (i === keys.length - 1) {
md5Str += `${key}=${obj[key]}`;
} else {
md5Str += `${key}=${obj[key]}&`;
}
}
md5Str = md5Str.slice(0, -1);
md5Str += mixin_key;
const wrid = md5.md5(md5Str);
return [wts, wrid];
const wrid = md5.md5(`${md5Str}${mixin_key}`);
return [wts.toString(), wrid];
}
export default getWrid;

View File

@@ -1,7 +1,7 @@
/**
* @file router/modules/wiki.ts
* @description wiki 路由模块
* @since Beta v0.3.9
* @since Beta v0.4.1
*/
const wikiRoutes = [
@@ -11,7 +11,7 @@ const wikiRoutes = [
component: async () => await import("../../pages/WIKI/Abyss.vue"),
},
{
path: "/wiki/character",
path: "/wiki/character/:id",
name: "角色图鉴",
component: async () => await import("../../pages/WIKI/Character.vue"),
},
@@ -31,25 +31,15 @@ const wikiRoutes = [
component: async () => await import("../../pages/WIKI/Material.vue"),
},
{
path: "/wiki/weapon",
path: "/wiki/weapon/:id",
name: "武器图鉴",
component: async () => await import("../../pages/WIKI/Weapon.vue"),
},
{
path: "/wiki/detail/character/:id",
name: "角色详情",
component: async () => await import("../../views/tw-character.vue"),
},
{
path: "/wiki/detail/GCG/:id",
name: "卡牌详情",
component: async () => await import("../../views/tw-gcg.vue"),
},
{
path: "/wiki/detail/weapon/:id",
name: "武器详情",
component: async () => await import("../../views/tw-weapon.vue"),
},
];
export default wikiRoutes;

View File

@@ -1,7 +1,7 @@
/**
* @file store/modules/app.ts
* @description App store module
* @since Beta v0.3.9
* @since Beta v0.4.1
*/
import { path } from "@tauri-apps/api";
@@ -33,15 +33,10 @@ export const useAppStore = defineStore(
const theme = ref("default");
// 是否登录
const isLogin = ref(false);
const dataPath = reactive({
userDataDir,
dbDataPath,
});
// 用户数据路径
const userPath = ref({
UIAF: `${dataPath.userDataDir}/UIAF.json`,
});
// 用户数据目录
const userDir = ref(userDataDir);
// 数据库路径
const dbPath = ref(dbDataPath);
// 设备信息
const deviceInfo = ref<TGApp.App.Device.DeviceInfo>(getInitDeviceInfo());
@@ -69,10 +64,10 @@ export const useAppStore = defineStore(
buildTime,
sidebar,
devMode,
dataPath,
userPath,
deviceInfo,
isLogin,
userDir,
dbPath,
init,
changeTheme,
};
@@ -82,7 +77,7 @@ export const useAppStore = defineStore(
{
key: "appPath",
storage: window.localStorage,
paths: ["dataPath", "userPath"],
paths: ["userDir", "dbPath"],
},
{
key: "app",

View File

@@ -1,22 +1,23 @@
/**
* @file types/App/Device.d.ts
* @description App 设备信息类型定义文件
* @since Beta v0.3.6
* @since Beta v0.4.1
*/
/**
* @description App 设备信息类型 namespace
* @since Beta v0.3.6
* @since Beta v0.4.1
* @namespace TGApp.App.Device
* @memberof TGApp.App
*/
declare namespace TGApp.App.Device {
/**
* @description 设备信息
* @since Beta v0.3.6
* @since Beta v0.4.1
* @interface DeviceInfo
* @property {string} device_id - 设备 ID
* @property {string} model - 设备型号
* @property {string} product - 产品
* @property {string} device_name - 设备名称
* @property {string} seed_id - 种子 ID
* @property {string} seed_time - 种子时间
* @property {string} device_fp - 设备指纹
@@ -24,7 +25,8 @@ declare namespace TGApp.App.Device {
*/
interface DeviceInfo {
device_id: string;
model: string;
product: string;
device_name: string;
seed_id: string;
seed_time: string;
device_fp: string;

View File

@@ -1,19 +1,20 @@
/**
* @file types/Sqlite/AppData.d.ts
* @description Sqlite AppData 类型定义文件
* @since Beta v0.3.8
* @since Beta v0.4.1
*/
declare namespace TGApp.Sqlite.AppData {
/**
* @description AppData 数据库 - key 枚举
* @since Beta v0.3.8
* @since Beta v0.4.1
* @enum {string}
* @property {string} APP_VERSION - App 版本
* @property {string} DATA_UPDATED - 数据库更新时间
* @property {string} COOKIE - Cookie
* @property {string} USER_INFO - 用户信息
* @property {string} DEVICE_INFO - 设备信息
* @property {string} USER_DIR - 用户数据目录
* @return {string}
*/
enum DBKey {
@@ -22,6 +23,7 @@ declare namespace TGApp.Sqlite.AppData {
COOKIE = "cookie",
USER_INFO = "userInfo",
DEVICE_INFO = "deviceInfo",
USER_DIR = "userDir",
}
/**

View File

@@ -1,12 +1,10 @@
/**
* @file utils/UIAF.ts
* @description UIAF工具类
* @since Beta v0.3.4
* @since Beta v0.4.1
*/
import { app, fs, path } from "@tauri-apps/api";
import TGSqlite from "../plugins/Sqlite";
import { app, fs } from "@tauri-apps/api";
/**
* @description 根据 completed 跟 progress 获取 status
@@ -64,32 +62,3 @@ export async function readUiafData(userPath: string): Promise<TGApp.Plugins.UIAF
const fileData = await fs.readTextFile(userPath);
return <TGApp.Plugins.UIAF.Data>JSON.parse(fileData);
}
/**
* @description 根据成就数据导出 UIAF 数据
* @since Alpha v0.2.3
* @param {TGApp.Plugins.UIAF.Achievement[]} achievementData - 成就数据
* @returns {Promise<void>}
*/
export async function backupUiafData(
achievementData: TGApp.Plugins.UIAF.Achievement[],
): Promise<void> {
const savePath = `${await path.appLocalDataDir()}userData\\UIAF.json`;
await fs.writeTextFile(savePath, JSON.stringify(achievementData, null, 2));
}
/**
* @description 根据 UIAF 数据恢复成就数据
* @since Alpha v0.2.3
* @returns {Promise<boolean>} 恢复的成就数量
*/
export async function restoreUiafData(): Promise<boolean> {
const uiafPath = `${await path.appLocalDataDir()}userData\\UIAF.json`;
// 检测是否存在 UIAF 数据
if (!(await fs.exists(uiafPath))) {
return false;
}
const uiafData: TGApp.Plugins.UIAF.Achievement[] = JSON.parse(await fs.readTextFile(uiafPath));
await TGSqlite.mergeUIAF(uiafData);
return true;
}

View File

@@ -1,7 +1,7 @@
/**
* @file utils/UIGF.ts
* @description UIGF工具类
* @since Beta v0.3.8
* @since Beta v0.4.1
*/
import { app, fs, path } from "@tauri-apps/api";
@@ -111,15 +111,17 @@ export async function exportUigfData(
/**
* @description 备份 UIGF 数据
* @since Alpha v0.2.3
* @since Beta v0.4.1
* @param {string} dirPath - 备份路径
* @param {string} uid - UID
* @param {TGApp.Sqlite.GachaRecords.SingleTable[]} gachaList - 祈愿列表
* @returns {Promise<void>}
*/
export async function backupUigfData(
dirPath: string,
uid: string,
gachaList: TGApp.Sqlite.GachaRecords.SingleTable[],
): Promise<void> {
const savePath = `${await path.appLocalDataDir()}userData\\UIGF_${uid}.json`;
await exportUigfData(uid, gachaList, savePath);
if (!(await fs.exists(dirPath))) await fs.createDir(dirPath, { recursive: true });
await exportUigfData(uid, gachaList, `${dirPath}${path.sep}UIGF_${uid}.json`);
}

122
src/utils/dataBS.ts Normal file
View File

@@ -0,0 +1,122 @@
/**
* @file utils/dataBS.ts
* @description 用户数据的备份、恢复、迁移
* @since Beta v0.4.1
*/
import { fs, path } from "@tauri-apps/api";
import showSnackbar from "../components/func/snackbar";
import TGSqlite from "../plugins/Sqlite";
/**
* @description 备份用户数据
* @since Beta v0.4.1
* @param {string} dir 备份目录路径
* @returns {Promise<void>}
*/
export async function backUpUserData(dir: string): Promise<void> {
if (!(await fs.exists(dir))) {
console.log("备份目录不存在,创建备份目录");
await fs.createDir(dir, { recursive: true });
}
// 备份成就数据
const dataAchi = await TGSqlite.getUIAF();
await fs.writeTextFile(`${dir}${path.sep}UIAF.json`, JSON.stringify(dataAchi));
// 备份 ck
const dataCK = await TGSqlite.getCookie();
await fs.writeTextFile(`${dir}${path.sep}cookie.json`, JSON.stringify(dataCK));
// 备份深渊数据
const dataAbyss = await TGSqlite.getAbyss();
await fs.writeTextFile(`${dir}${path.sep}abyss.json`, JSON.stringify(dataAbyss));
// todo 添加祈愿数据备份支持
}
/**
* @description 恢复用户数据
* @since Beta v0.4.1
* @param {string} dir 备份目录路径
* @returns {Promise<void>}
*/
export async function restoreUserData(dir: string): Promise<void> {
let errNum = 0;
if (!(await fs.exists(dir))) {
showSnackbar({
text: "备份目录不存在",
color: "error",
});
return;
}
// 恢复成就数据
const dataAchiPath = `${dir}${path.sep}UIAF.json`;
if (await fs.exists(dataAchiPath)) {
try {
const dataAchi: TGApp.Plugins.UIAF.Achievement[] = JSON.parse(
await fs.readTextFile(dataAchiPath),
);
await TGSqlite.mergeUIAF(dataAchi);
} catch (e) {
showSnackbar({
text: "成就数据恢复失败",
color: "error",
});
errNum++;
}
} else {
showSnackbar({
text: "成就数据恢复失败,备份文件不存在",
color: "warn",
});
}
// 恢复 ck
const dataCKPath = `${dir}${path.sep}cookie.json`;
if (await fs.exists(dataCKPath)) {
try {
const dataCK = await fs.readTextFile(dataCKPath);
await TGSqlite.saveAppData("cookie", JSON.stringify(JSON.parse(dataCK)));
} catch (e) {
showSnackbar({
text: "Cookie 数据恢复失败",
color: "error",
});
errNum++;
}
} else {
showSnackbar({
text: "Cookie 数据恢复失败,备份文件不存在",
color: "warn",
});
}
// 恢复深渊数据
const dataAbyssPath = `${dir}${path.sep}abyss.json`;
if (await fs.exists(dataAbyssPath)) {
try {
const dataAbyss: TGApp.Sqlite.Abyss.SingleTable[] = JSON.parse(
await fs.readTextFile(dataAbyssPath),
);
await TGSqlite.restoreAbyss(dataAbyss);
} catch (e) {
showSnackbar({
text: "深渊数据恢复失败",
color: "error",
});
errNum++;
}
} else {
showSnackbar({
text: "深渊数据恢复失败,备份文件不存在",
color: "warn",
});
}
if (errNum === 0) {
showSnackbar({
text: "数据恢复成功",
color: "success",
});
} else {
showSnackbar({
text: `数据恢复失败,失败数量:${errNum}`,
color: "error",
});
}
}

View File

@@ -1,7 +1,7 @@
/**
* @file utils/toolFunc.ts
* @description 一些工具函数
* @since Beta v0.3.9
* @since Beta v0.4.1
*/
import { os, path } from "@tauri-apps/api";
@@ -60,13 +60,14 @@ export function getNowStr(): string {
/**
* @description 获取设备信息(初始化时)
* @since Beta v0.3.6
* @since Beta v0.4.1
* @returns {TGApp.App.Device.DeviceInfo} 设备信息
*/
export function getInitDeviceInfo(): TGApp.App.Device.DeviceInfo {
return {
device_id: v4(),
model: getRandomString(6),
product: getRandomString(6, "upperNumber"),
device_name: getRandomString(12, "upperNumber"),
seed_id: v4(),
seed_time: Date.now().toString(),
device_fp: "0000000000000",
@@ -125,7 +126,7 @@ export async function getCacheDir(): Promise<string[] | false> {
/**
* @description 获取随机字符串
* @since Beta v0.3.6
* @since Beta v0.4.1
* @param {number} length 字符串长度
* @param {string} type
* @returns {string} 随机字符串
@@ -147,6 +148,9 @@ export function getRandomString(length: number, type: string = "all"): string {
case "upper":
str = char.toUpperCase();
break;
case "upperNumber":
str = char.toUpperCase() + num;
break;
case "letter":
str = char + char.toUpperCase();
break;

View File

@@ -68,7 +68,7 @@ onMounted(async () => {
}
.jv-container {
background: var(--content-bg-2) !important;
background: var(--box-bg-2) !important;
}
.jv-key,

View File

@@ -1,239 +0,0 @@
<template>
<TSwitchTheme />
<ToLoading v-model="loading" :empty="loadingEmpty" :title="loadingTitle" :subtitle="loadingSub" />
<div class="twc-box" v-if="data !== undefined">
<div class="twc-brief">
<TItembox :model-value="box" />
<div class="twc-brief-info">
<div class="twc-bi-top">
<span>{{ data.name }} {{ data.title }}</span>
<span>{{ data.description }}</span>
</div>
<div class="twc-bi-grid1">
<div class="twc-big-item">
<span>所属</span>
<span>{{ data.brief.camp }}</span>
</div>
<div class="twc-big-item">
<span>命之座</span>
<span>{{ data.brief.constellation }}</span>
</div>
<div class="twc-big-item">
<span>生日</span>
<span>{{ data.brief.birth }}</span>
</div>
</div>
<div class="twc-bi-grid2">
<div class="twc-big-item">
<span>汉语CV</span>
<span>{{ data.brief.cv.cn }}</span>
</div>
<div class="twc-big-item">
<span>日语CV</span>
<span>{{ data.brief.cv.jp }}</span>
</div>
<div class="twc-big-item">
<span>英语CV</span>
<span>{{ data.brief.cv.en }}</span>
</div>
<div class="twc-big-item">
<span>韩语CV</span>
<span>{{ data.brief.cv.kr }}</span>
</div>
</div>
</div>
</div>
<TwcMaterials :data="data.materials" />
<TwcSkills :data="data.skills" />
<TwcConstellations :data="data.constellation" />
<v-expansion-panels class="twc-text-item">
<v-expansion-panel>
<template #title><span class="twc-text-title">资料</span></template>
<template #text>
<v-expansion-panels variant="popout">
<v-expansion-panel
expand-icon="mdi-menu-down"
v-for="(item, index) in data?.talks"
:key="index"
>
<template #title
><span class="twc-text-item-title">{{ item.Title }}</span></template
>
<template #text
><span class="twc-text-item-content">{{ item.Context }}</span></template
>
</v-expansion-panel>
</v-expansion-panels>
</template>
</v-expansion-panel>
<v-expansion-panel>
<template #title><span class="twc-text-title">故事</span></template>
<template #text>
<v-expansion-panels variant="popout">
<v-expansion-panel
expand-icon="mdi-menu-down"
v-for="(item, index) in data.stories"
:key="index"
>
<template #title
><span class="twc-text-item-title">{{ item.Title }}</span></template
>
<template #text
><span class="twc-text-item-content">{{ item.Context }}</span></template
>
</v-expansion-panel>
</v-expansion-panels>
</template>
</v-expansion-panel>
</v-expansion-panels>
</div>
</template>
<script lang="ts" setup>
import { appWindow } from "@tauri-apps/api/window";
import { computed, onMounted, ref } from "vue";
import { useRoute } from "vue-router";
import TSwitchTheme from "../components/app/t-switchTheme.vue";
import showSnackbar from "../components/func/snackbar";
import TItembox, { TItemBoxData } from "../components/main/t-itembox.vue";
import ToLoading from "../components/overlay/to-loading.vue";
import TwcConstellations from "../components/wiki/twc-constellations.vue";
import TwcMaterials from "../components/wiki/twc-materials.vue";
import TwcSkills from "../components/wiki/twc-skills.vue";
import { getWikiData } from "../data";
// 路由数据
const id = <string>useRoute().params.id;
// 加载
const loading = ref<boolean>(true);
const loadingEmpty = ref<boolean>(false);
const loadingTitle = ref<string>("正在加载");
const loadingSub = ref<string>();
// 数据
const data = ref<TGApp.App.Character.WikiItem>();
const box = computed(() => {
return <TItemBoxData>{
bg: `/icon/bg/${data.value?.star}-Star.webp`,
icon: `/WIKI/character/${data.value?.id}.webp`,
size: "128px",
height: "128px",
display: "inner",
lt: `/icon/element/${data.value?.element}元素.webp`,
ltSize: "40px",
innerHeight: 30,
innerIcon: `/icon/weapon/${data.value?.weapon}.webp`,
innerText: data.value?.name,
clickable: false,
};
});
onMounted(async () => {
await appWindow.show();
try {
const res = await getWikiData("Character", id);
if (res === undefined) return;
data.value = res.default;
await appWindow.setTitle(`Wiki_Character - ${data.value.name}`);
loading.value = false;
} catch (error) {
loadingEmpty.value = true;
if (error instanceof Error) {
loadingTitle.value = error.name;
loadingSub.value = error.message;
} else {
loadingTitle.value = "未知错误";
loadingSub.value = <string>error;
}
showSnackbar({
text: "Wiki 数据获取失败,即将关闭窗口",
color: "error",
});
setTimeout(() => {
appWindow.close();
}, 3000);
}
});
</script>
<style lang="css" scoped>
.twc-box {
display: flex;
width: 800px;
flex-direction: column;
margin: 0 auto;
row-gap: 10px;
}
.twc-brief {
display: flex;
align-items: flex-start;
column-gap: 10px;
}
.twc-brief-info {
display: flex;
flex-direction: column;
justify-content: space-between;
}
.twc-bi-top {
display: flex;
flex-direction: column;
}
.twc-bi-top :nth-child(1) {
color: var(--common-text-title);
font-family: var(--font-title);
font-size: 20px;
}
.twc-bi-top :nth-child(2) {
display: flex;
align-items: flex-end;
font-size: 14px;
opacity: 0.8;
}
.twc-bi-grid1 {
display: grid;
column-gap: 10px;
grid-template-columns: repeat(3, 1fr);
}
.twc-bi-grid2 {
display: grid;
column-gap: 10px;
grid-template-columns: repeat(2, 1fr);
}
.twc-big-item {
display: flex;
column-gap: 5px;
}
.twc-big-item :nth-child(1) {
font-weight: bold;
}
.twc-text-title {
color: var(--common-text-title);
font-family: var(--font-title);
font-size: 18px;
}
.twc-text-item {
display: flex;
flex-direction: column;
row-gap: 5px;
}
.twc-text-item-title {
font-weight: bold;
}
.twc-text-item-content {
font-size: 14px;
white-space: pre-wrap;
word-break: break-all;
}
</style>

View File

@@ -1,171 +0,0 @@
<template>
<TSwitchTheme />
<ToLoading v-model="loading" :empty="loadingEmpty" :title="loadingTitle" :subtitle="loadingSub" />
<div class="tww-box" v-if="data !== undefined">
<div class="tww-brief">
<TItembox :model-value="box" />
<div class="tww-brief-info">
<div class="tww-brief-title">{{ data.name }}</div>
<v-rating
class="tww-brief-rating"
v-model="select"
:length="selectItems.length"
:size="24"
dense
/>
<div class="tww-brief-desc">{{ data.description }}</div>
</div>
</div>
<TwcMaterials :data="data.materials" />
<v-expansion-panels class="tww-affix">
<v-expansion-panel expand-icon="mdi-menu-down">
<template #title>
<span class="tww-text-title">{{ data.affix.Name }}-精炼 {{ select }}</span>
</template>
<template #text>
<span
class="tww-text-content"
v-html="parseHtmlText(data.affix.Descriptions[select - 1].Description)"
/>
</template>
</v-expansion-panel>
</v-expansion-panels>
<v-expansion-panels class="tww-story">
<v-expansion-panel
expand-icon="mdi-menu-down"
v-for="(story, index) in data.story"
:key="index"
>
<template #title>
<span class="tww-text-title">
{{ data.story.length > 1 ? `故事 ${index + 1}` : "故事" }}
</span>
</template>
<template #text>
<span class="tww-text-content">{{ parseHtmlText(story) }}</span>
</template>
</v-expansion-panel>
</v-expansion-panels>
</div>
</template>
<script lang="ts" setup>
import { appWindow } from "@tauri-apps/api/window";
import { computed, onMounted, ref, toRaw } from "vue";
import { useRoute } from "vue-router";
import TSwitchTheme from "../components/app/t-switchTheme.vue";
import showSnackbar from "../components/func/snackbar";
import TItembox, { TItemBoxData } from "../components/main/t-itembox.vue";
import ToLoading from "../components/overlay/to-loading.vue";
import TwcMaterials from "../components/wiki/twc-materials.vue";
import { getWikiData } from "../data";
import { parseHtmlText } from "../utils/toolFunc";
// 路由数据
const id = <string>useRoute().params.id;
// 加载
const loading = ref<boolean>(true);
const loadingEmpty = ref<boolean>(false);
const loadingTitle = ref<string>("正在加载");
const loadingSub = ref<string>();
// 数据
const data = ref<TGApp.App.Weapon.WikiItem>();
const box = computed(() => {
return <TItemBoxData>{
bg: `/icon/bg/${data.value?.star}-Star.webp`,
icon: `/WIKI/weapon/${data.value?.id}.webp`,
size: "128px",
height: "128px",
display: "inner",
lt: `/icon/weapon/${data.value?.weapon}.webp`,
ltSize: "40px",
innerHeight: 0,
clickable: false,
};
});
const select = ref<number>(1);
const selectItems = ref<number[]>([]);
onMounted(async () => {
await appWindow.show();
try {
const res = await getWikiData("Weapon", id);
if (res !== undefined) {
data.value = res.default;
console.log(toRaw(data.value));
}
selectItems.value = data.value?.affix.Descriptions.map((item) => item.Level) ?? [];
loading.value = false;
} catch (error) {
loadingEmpty.value = true;
if (error instanceof Error) {
loadingTitle.value = error.name;
loadingSub.value = error.message;
} else {
loadingTitle.value = "未知错误";
loadingSub.value = <string>error;
}
showSnackbar({
text: "Wiki 数据获取失败,即将关闭窗口",
color: "error",
});
setTimeout(() => {
appWindow.close();
}, 3000);
}
});
</script>
<style lang="css" scoped>
.tww-box {
display: flex;
width: 800px;
flex-direction: column;
margin: 0 auto;
row-gap: 10px;
}
.tww-brief {
display: flex;
align-items: flex-start;
column-gap: 10px;
}
.tww-brief-info {
display: flex;
flex-direction: column;
justify-content: space-between;
}
.tww-brief-title {
color: var(--common-text-title);
font-family: var(--font-title);
font-size: 20px;
}
.tww-brief-rating {
color: var(--common-text-title);
}
.tww-brief-desc {
display: flex;
align-items: flex-end;
opacity: 0.8;
}
.tww-story {
display: flex;
flex-direction: column;
row-gap: 5px;
}
.tww-text-title {
font-weight: bold;
}
.tww-text-content {
font-size: 14px;
white-space: pre-wrap;
word-break: break-all;
}
</style>

View File

@@ -1,7 +1,7 @@
/**
* @file src/web/request/getDeviceFp.ts
* @description 获取设备指纹
* @since Beta v0.3.6
* @since Beta v0.4.1
*/
import { http } from "@tauri-apps/api";
@@ -11,7 +11,7 @@ import TGConstant from "../constant/TGConstant";
/**
* @description 获取设备指纹
* @since Beta v0.3.6
* @since Beta v0.4.1
* @param {TGApp.App.Device.DeviceInfo} Info - 设备信息
* @returns {Promise<string>} 设备指纹
*/
@@ -20,38 +20,59 @@ export async function getDeviceFp(
): Promise<TGApp.App.Device.DeviceInfo> {
const info = Info ?? getInitDeviceInfo();
const deviceFPHeader = {
cpuType: "arm64-v8a",
proxyStatus: 0,
isRoot: 0,
romCapacity: "512",
productName: info.model,
romRemain: "256",
manufacturer: "Xiaomi",
appMemory: "512",
deviceName: info.device_name,
productName: info.product,
romRemain: "512",
hostname: "dg02-pool03-kvm87",
screenSize: "1080x1920",
osVersion: "13",
screenSize: "1440x2905",
isTablet: 0,
aaid: "",
vendor: "中国移动",
accelerometer: "true",
buildTags: "release-keys",
model: info.model,
model: info.device_name,
brand: "Xiaomi",
oaid: "",
hardware: "qcom",
deviceType: "OP5913L1",
devId: "unknown",
serialNumber: "unknown",
buildTime: "1588876800000", // 2020-05-08
buildUser: "root",
ramCapacity: "2048",
magnetometer: "true",
display: `OP5913L1-user ${info.model} 10 QKQ1.190825.002 V12.0.1.0.QFJCNXM release-keys`,
ramRemain: "1024",
deviceInfo: "unknown",
gyroscope: "true",
sdCardCapacity: 512215,
buildTime: "1693626947000",
buildUser: "android-build",
simState: "5",
ramRemain: "239814",
appUpdateTimeDiff: 1702604034882,
deviceInfo: `XiaoMi ${info.device_name} OP5913L1:13 SKQ1.221119.001 T.118e6c7-5aa23-73911:user release-keys`,
vaid: "",
buildType: "user",
sdkVersion: "29",
board: "sdm660",
sdkVersion: "34",
ui_mode: "UI_MODE_TYPE_NORMAL",
isMockLocation: 0,
cpuType: "arm64-v8a",
isAirMode: 0,
ringMode: 2,
chargeStatus: 1,
manufacturer: "XiaoMi",
emulatorStatus: 0,
appMemory: "512",
osVersion: "14",
vendor: "unknown",
accelerometer: "1.4883357x9.80665x-0.1963501", // 这边与 hutao 数据不一致
sdRemain: 239600,
buildTags: "release-keys",
packageName: "com.mihoyo.hyperion",
networkType: "WiFi",
oaid: "",
debugStatus: 1,
ramCapacity: "469679",
magnetometer: "20.081251x-27.457501x2.1937501",
display: `${info.product}_13.1.0.181(CN01)`,
appInstallTimeDiff: 1688455751496,
packageVersion: "2.20.1",
gyroscope: "0.030226856x-0.014647375x-0.0013732915", // 这边与 hutao 数据不一致
batteryStatus: 100,
hasKeyboard: 0,
board: "taro",
};
const url = "https://public-data-api.mihoyo.com/device-fp/api/getFp";
const data = {

View File

@@ -1,29 +0,0 @@
/**
* @file web/utils/backupData.ts
* @description 数据备份
* @since Alpha v0.1.5
*/
import { fs, path } from "@tauri-apps/api";
/**
* @description 备份 Cookie 数据
* @since Alpha v0.2.0
* @param {TGApp.User.Account.Cookie} cookie cookie
* @returns {Promise<void>}
*/
export async function backupCookieData(cookie: TGApp.User.Account.Cookie): Promise<void> {
const savePath = `${await path.appLocalDataDir()}\\userData\\cookie.json`;
await fs.writeTextFile(savePath, JSON.stringify(cookie, null, 2));
}
/**
* @description 备份深渊数据
* @since Alpha v0.2.0
* @param {TGApp.Sqlite.Abyss.SingleTable[]} abyssData 深渊数据
* @returns {Promise<void>}
*/
export async function backupAbyssData(abyssData: TGApp.Sqlite.Abyss.SingleTable[]): Promise<void> {
const savePath = `${await path.appLocalDataDir()}\\userData\\abyss.json`;
await fs.writeTextFile(savePath, JSON.stringify(abyssData, null, 2));
}

View File

@@ -1,37 +0,0 @@
/**
* @file web/utils/restoreData.ts
* @description 数据恢复
* @since Beta v0.3.9
*/
import { fs, path } from "@tauri-apps/api";
import TGSqlite from "../../plugins/Sqlite";
/**
* @description 恢复 Cookie 数据
* @since Alpha v0.2.0
* @returns {Promise<boolean>}
*/
export async function restoreCookieData(): Promise<boolean> {
const cookiePath = `${await path.appLocalDataDir()}\\userData\\cookie.json`;
if (!(await fs.exists(cookiePath))) return false;
const cookieData = await fs.readTextFile(cookiePath);
await TGSqlite.saveAppData("cookie", JSON.stringify(JSON.parse(cookieData)));
return true;
}
/**
* @description 恢复深渊数据
* @since Beta v0.3.9
* @returns {Promise<boolean>}
*/
export async function restoreAbyssData(): Promise<boolean> {
const abyssPath = `${await path.appLocalDataDir()}\\userData\\abyss.json`;
if (!(await fs.exists(abyssPath))) return false;
const abyssData = await fs.readTextFile(abyssPath);
const parseData = JSON.parse(abyssData);
if (!parseData || !Array.isArray(parseData)) return false;
await TGSqlite.restoreAbyss(<TGApp.Sqlite.Abyss.SingleTable[]>parseData);
return true;
}