mirror of
https://github.com/BTMuli/TeyvatGuide.git
synced 2026-03-23 05:09:45 +08:00
Compare commits
43 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2ab31d8f5c | ||
|
|
1af990512d | ||
|
|
d96d451156 | ||
|
|
f029306ebb | ||
|
|
d3c5baa0c2 | ||
|
|
ba0802752c | ||
|
|
ff94e12ff5 | ||
|
|
0fbf1f7c2a | ||
|
|
68809a93c6 | ||
|
|
0edcadef63 | ||
|
|
9f9c30914f | ||
|
|
04cf372798 | ||
|
|
6617a26c90 | ||
|
|
d244423800 | ||
|
|
3366efaadd | ||
|
|
d74e7a7a31 | ||
|
|
2d0b409813 | ||
|
|
942068faea | ||
|
|
0f0f7684d2 | ||
|
|
531cb32f72 | ||
|
|
a368223805 | ||
|
|
6eab6c81f1 | ||
|
|
68594a2a76 | ||
|
|
5d5f22d76e | ||
|
|
65e948c34c | ||
|
|
68dead3d84 | ||
|
|
babc6a9a75 | ||
|
|
6db4ff5ac9 | ||
|
|
ce1b6f365e | ||
|
|
b6ed9668ac | ||
|
|
2a2a190f5f | ||
|
|
5d03a32362 | ||
|
|
33d9ba5c4d | ||
|
|
9020214d23 | ||
|
|
78c3f79bfd | ||
|
|
ee0fc6dbae | ||
|
|
8c51b79558 | ||
|
|
8c1899637f | ||
|
|
56df920a7d | ||
|
|
64c6f4ab8f | ||
|
|
d3902d6e31 | ||
|
|
01e355b0d6 | ||
|
|
c40b3c6ff0 |
36
CHANGELOG.md
36
CHANGELOG.md
@@ -2,12 +2,44 @@
|
||||
Author: 目棃
|
||||
Description: CHANGELOG
|
||||
Date: 2025-09-09
|
||||
Update: 2025-10-22
|
||||
Update: 2025-11-19
|
||||
---
|
||||
|
||||
> 本文档 [`Frontmatter`](https://github.com/BTMuli/MuCli#Frontmatter) 由 [MuCli](https://github.com/BTMuli/Mucli) 自动生成于 `2025-09-09 14:30:56`
|
||||
>
|
||||
> 更新于 `2025-10-22 13:59:03`
|
||||
> 更新于 `2025-11-19 14:08:20`
|
||||
|
||||
## [0.8.6](https://github.com/BTMuli/TeyvatGuide/releases/v0.8.6) (2025-11-19)
|
||||
|
||||
> 关于胡桃数据库导入功能的说明请参考 [导入胡桃数据库](https://app.btmuli.ink/docs/TeyvatGuide/import-hutao-db.html)
|
||||
|
||||
- 👽️ 移除剧诗概览,支持导入胡桃剧诗数据
|
||||
- 👽️ 移除深渊上传,支持导入胡桃深渊数据
|
||||
- 🔥 移除胡桃深渊统计页面
|
||||
- 🚸 调整导入祈愿记录浮窗ui,显示导入进度
|
||||
- 🐛 修复图片渲染异常
|
||||
- 🥅 处理清除缓存异常,清除缓存后重启
|
||||
- 🚸 帖子详情添加AIGC相关注释
|
||||
- 🚸 添加跳转视频链接
|
||||
- 📝 更新相关文档
|
||||
|
||||
## [0.8.5](https://github.com/BTMuli/TeyvatGuide/releases/v0.8.5) (2025-11-10)
|
||||
|
||||
- 🍱 更新下半数据
|
||||
|
||||
## [0.8.4](https://github.com/BTMuli/TeyvatGuide/releases/v0.8.4) (2025-10-27)
|
||||
|
||||
- 👽️ 公告添加千星奇域分类
|
||||
- 🚸 兑换码浮窗显示游戏名称
|
||||
- ✨ 嵌入官方公告页面(已登录)
|
||||
- ✨ 嵌入官方祈愿详情(已登录)
|
||||
- ✨ 完善投稿活动类型声明,渲染投稿活动&交互
|
||||
- 🐛 修复部分帖子解析异常
|
||||
- ✨ 重构帖子解析逻辑,增加新类型解析
|
||||
- 💄 调整名片样式
|
||||
- ✨ 添加getRegionRoleInfo事件处理
|
||||
- 🐛 公告解析剔除多余换行
|
||||
- ✨ 千星奇域祈愿页面草创
|
||||
|
||||
## [0.8.3](https://github.com/BTMuli/TeyvatGuide/releases/v0.8.3) (2025-10-22)
|
||||
|
||||
|
||||
12
README.md
12
README.md
@@ -2,12 +2,12 @@
|
||||
Author: 目棃
|
||||
Description: 说明文档
|
||||
Date: 2023-03-05
|
||||
Update: 2025-10-08
|
||||
Update: 2025-11-19
|
||||
---
|
||||
|
||||
> 本文档 [`Frontmatter`](https://github.com/BTMuli/MuCli#Frontmatter) 由 [MuCli](https://github.com/BTMuli/Mucli) 自动生成于 `2023-03-05 14:41:55`
|
||||
>
|
||||
> 更新于 `2025-10-08 10:08:49`
|
||||
> 更新于 `2025-11-19 13:21:38`
|
||||
|
||||
[](https://deepwiki.com/BTMuli/TeyvatGuide)  
|
||||
|
||||
@@ -65,13 +65,13 @@ Game Tool for Genshin Impact player, supports Windows and macOS.
|
||||
- [x] 真境剧诗
|
||||
- [x] 幽境危战
|
||||
- [x] 祈愿数据获取(近一年)
|
||||
- [x] 千星奇域祈愿数据获取(近一年)
|
||||
- [x] 用户收藏帖子获取
|
||||
- [x] 用户关注帖子获取
|
||||
- [x] 一键完成米游币每日任务
|
||||
- [x] 一键完成游戏签到
|
||||
|
||||
- Wiki 功能:
|
||||
- [x] 深渊数据库(Hutao API)
|
||||
- [x] 角色图鉴
|
||||
- [x] 武器图鉴
|
||||
- [x] 名片图鉴
|
||||
@@ -90,7 +90,7 @@ Game Tool for Genshin Impact player, supports Windows and macOS.
|
||||
|
||||
## UI 参考 / UI Reference
|
||||
|
||||
- [Snap.Hutao](https://github.com/DGP-Studio/Snap.Hutao)
|
||||
- ~~[Snap.Hutao](https://github.com/DGP-Studio/Snap.Hutao)~~
|
||||
- [Starward](https://github.com/Scighost/Starward)
|
||||
- [米游社](https://www.miyoushe.com/ys/)
|
||||
- [原神](https://yuanshen.com/)
|
||||
@@ -102,6 +102,8 @@ Game Tool for Genshin Impact player, supports Windows and macOS.
|
||||
- UIAF:[UIAF v1.1](docs/standards/UIAF.md)
|
||||
- UIGF:[UIGF v3.0](docs/standards/UIGF3.md),[UIGF v4.0](docs/standards/UIGF.md)
|
||||
- [macOS 平台门禁属性导致应用无法打开应用的修复指引](docs/macos-gatekeeper/README.md)
|
||||
- [隐私政策](https://app.btmuli.ink/docs/TeyvatGuide/privacy.html)
|
||||
- [如何导入胡桃数据库](https://app.btmuli.ink/docs/TeyvatGuide/import-hutao-db.html)
|
||||
|
||||
## 特定项目 / Special Project
|
||||
|
||||
@@ -136,7 +138,7 @@ Game Tool for Genshin Impact player, supports Windows and macOS.
|
||||
本项目在开发过程中参考了诸多相关开源项目,特此鸣谢。
|
||||
|
||||
- [UIGF Organization](https://github.com/UIGF-org)
|
||||
- [Snap.Hutao](https://github.com/DGP-Studio/Snap.Hutao)
|
||||
- ~~[Snap.Hutao](https://github.com/DGP-Studio/Snap.Hutao)~~
|
||||
- [StarWard](https://github.com/Scighost/Starward)
|
||||
- [xunkong](https://github.com/xunkong/xunkong)
|
||||
- [gs-helper](https://github.com/vikiboss/gs-helper)
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
Author: 目棃
|
||||
Description: 项目资源说明
|
||||
Date: 2023-03-10
|
||||
Update: 2025-02-28
|
||||
Update: 2025-11-19
|
||||
---
|
||||
|
||||
> 本文档 [`Frontmatter`](https://github.com/BTMuli/MuCli#Frontmatter) 由 [MuCli](https://github.com/BTMuli/Mucli) 自动生成于 `2023-03-10 22:05:44`
|
||||
>
|
||||
> 更新于 `2025-02-28 09:40:33`
|
||||
> 更新于 `2025-11-19 12:31:22`
|
||||
|
||||
## 说明
|
||||
|
||||
@@ -40,8 +40,8 @@ Update: 2025-02-28
|
||||
相关仓库:
|
||||
|
||||
- [TGAssistant](https://github.com/BTMuli/TGAssistant):项目下游仓库,用于处理项目数据。
|
||||
- [Snap.Metadata](https://github.com/DGP-Studio/Snap.Metadata):胡桃元数据仓库,项目大部分数据来源于此。
|
||||
- [Snap.Static](https://github.com/DGP-Studio/Snap.Static):胡桃静态资源仓库,项目部分图像资源来源于此。
|
||||
- ~~[Snap.Metadata](https://github.com/DGP-Studio/Snap.Metadata)~~:胡桃元数据仓库,项目大部分数据来源于此。
|
||||
- ~~[Snap.Static](https://github.com/DGP-Studio/Snap.Static)~~:胡桃静态资源仓库,项目部分图像资源来源于此。
|
||||
- [amos-data](https://github.com/yuehaiteam/amos-data):成就数据仓库,成就数据的详细信息来源于此。
|
||||
|
||||
## 字体
|
||||
|
||||
66
package.json
66
package.json
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"name": "teyvatguide",
|
||||
"version": "0.8.3",
|
||||
"version": "0.8.6",
|
||||
"description": "Game Tool for GenshinImpact player",
|
||||
"private": true,
|
||||
"packageManager": "pnpm@10.19.0",
|
||||
"packageManager": "pnpm@10.22.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "tauri build",
|
||||
@@ -72,63 +72,63 @@
|
||||
"dependencies": {
|
||||
"@mdi/font": "7.4.47",
|
||||
"@tauri-apps/api": "^2.9.0",
|
||||
"@tauri-apps/plugin-deep-link": "^2.4.3",
|
||||
"@tauri-apps/plugin-dialog": "^2.4.0",
|
||||
"@tauri-apps/plugin-fs": "^2.4.2",
|
||||
"@tauri-apps/plugin-http": "^2.5.2",
|
||||
"@tauri-apps/plugin-log": "^2.7.0",
|
||||
"@tauri-apps/plugin-opener": "^2.5.0",
|
||||
"@tauri-apps/plugin-os": "^2.3.1",
|
||||
"@tauri-apps/plugin-process": "^2.3.0",
|
||||
"@tauri-apps/plugin-shell": "^2.3.1",
|
||||
"@tauri-apps/plugin-sql": "^2.3.0",
|
||||
"@tauri-apps/plugin-deep-link": "^2.4.5",
|
||||
"@tauri-apps/plugin-dialog": "^2.4.2",
|
||||
"@tauri-apps/plugin-fs": "^2.4.4",
|
||||
"@tauri-apps/plugin-http": "^2.5.4",
|
||||
"@tauri-apps/plugin-log": "^2.7.1",
|
||||
"@tauri-apps/plugin-opener": "^2.5.2",
|
||||
"@tauri-apps/plugin-os": "^2.3.2",
|
||||
"@tauri-apps/plugin-process": "^2.3.1",
|
||||
"@tauri-apps/plugin-shell": "^2.3.3",
|
||||
"@tauri-apps/plugin-sql": "^2.3.1",
|
||||
"ajv": "^8.17.1",
|
||||
"artplayer": "^5.3.0",
|
||||
"color-convert": "^3.1.2",
|
||||
"color-convert": "^3.1.3",
|
||||
"echarts": "^6.0.0",
|
||||
"html2canvas": "^1.4.1",
|
||||
"js-md5": "^0.8.3",
|
||||
"jsencrypt": "^3.5.4",
|
||||
"pinia": "^3.0.3",
|
||||
"pinia-plugin-persistedstate": "^4.5.0",
|
||||
"pinia": "^3.0.4",
|
||||
"pinia-plugin-persistedstate": "^4.7.1",
|
||||
"qrcode.vue": "^3.6.0",
|
||||
"sass-embedded": "^1.93.2",
|
||||
"sass-embedded": "^1.93.3",
|
||||
"swiper": "^12.0.3",
|
||||
"uuid": "^13.0.0",
|
||||
"vue": "^3.5.22",
|
||||
"vue": "^3.5.24",
|
||||
"vue-echarts": "^8.0.1",
|
||||
"vue-json-pretty": "^2.5.0",
|
||||
"vue-json-pretty": "^2.6.0",
|
||||
"vue-router": "^4.6.3",
|
||||
"vuetify": "^3.10.6",
|
||||
"vuetify": "^3.10.11",
|
||||
"wcag-color": "^1.1.1",
|
||||
"xml-js": "^1.6.11"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@btmuli/stylelint-plugin-color": "^0.1.0",
|
||||
"@eslint/eslintrc": "^3.3.1",
|
||||
"@eslint/js": "^9.38.0",
|
||||
"@tauri-apps/cli": "2.9.0",
|
||||
"@eslint/js": "^9.39.1",
|
||||
"@tauri-apps/cli": "2.9.4",
|
||||
"@types/color-convert": "^2.0.4",
|
||||
"@types/fs-extra": "^11.0.4",
|
||||
"@types/js-md5": "^0.8.0",
|
||||
"@types/node": "^24.9.1",
|
||||
"@typescript-eslint/parser": "^8.46.2",
|
||||
"@typescript/native-preview": "7.0.0-dev.20251021.1",
|
||||
"@vitejs/plugin-vue": "^6.0.1",
|
||||
"@types/node": "^24.10.1",
|
||||
"@typescript-eslint/parser": "^8.47.0",
|
||||
"@typescript/native-preview": "7.0.0-dev.20251118.1",
|
||||
"@vitejs/plugin-vue": "^6.0.2",
|
||||
"app-root-path": "^3.1.0",
|
||||
"concurrently": "^9.2.1",
|
||||
"eslint": "^9.38.0",
|
||||
"eslint": "^9.39.1",
|
||||
"eslint-plugin-import": "^2.32.0",
|
||||
"eslint-plugin-jsonc": "^2.21.0",
|
||||
"eslint-plugin-prettier": "^5.5.4",
|
||||
"eslint-plugin-vue": "^10.5.1",
|
||||
"eslint-plugin-yml": "^1.19.0",
|
||||
"fs-extra": "^11.3.2",
|
||||
"globals": "^16.4.0",
|
||||
"globals": "^16.5.0",
|
||||
"husky": "^9.1.7",
|
||||
"jsonc-eslint-parser": "^2.4.1",
|
||||
"lint-staged": "^16.2.5",
|
||||
"oxlint": "^1.23.0",
|
||||
"lint-staged": "^16.2.6",
|
||||
"oxlint": "^1.29.0",
|
||||
"prettier": "3.6.2",
|
||||
"stylelint": "^16.25.0",
|
||||
"stylelint-config-idiomatic-order": "^10.0.0",
|
||||
@@ -141,12 +141,12 @@
|
||||
"stylelint-scss": "^6.12.1",
|
||||
"tsx": "^4.20.6",
|
||||
"typescript": "^5.9.3",
|
||||
"typescript-eslint": "^8.46.2",
|
||||
"vite": "npm:rolldown-vite@^7.1.19",
|
||||
"vite-plugin-vue-devtools": "^8.0.3",
|
||||
"typescript-eslint": "^8.47.0",
|
||||
"vite": "npm:rolldown-vite@^7.2.6",
|
||||
"vite-plugin-vue-devtools": "^8.0.5",
|
||||
"vite-plugin-vuetify": "^2.1.2",
|
||||
"vue-eslint-parser": "^10.2.0",
|
||||
"vue-tsc": "^3.1.1",
|
||||
"vue-tsc": "^3.1.4",
|
||||
"yaml-eslint-parser": "^1.3.0"
|
||||
}
|
||||
}
|
||||
|
||||
2165
pnpm-lock.yaml
generated
2165
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
BIN
public/icon/nation/千星奇域.webp
Normal file
BIN
public/icon/nation/千星奇域.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
27
src-tauri/Cargo.lock
generated
27
src-tauri/Cargo.lock
generated
@@ -4,7 +4,7 @@ version = 4
|
||||
|
||||
[[package]]
|
||||
name = "TeyvatGuide"
|
||||
version = "0.8.3"
|
||||
version = "0.8.6"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"log",
|
||||
@@ -4869,9 +4869,9 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
|
||||
|
||||
[[package]]
|
||||
name = "tauri"
|
||||
version = "2.9.0"
|
||||
version = "2.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f07c6590706b2fc0ab287b041cf5ce9c435b3850bdae5571e19d9d27584e89d"
|
||||
checksum = "9e492485dd390b35f7497401f67694f46161a2a00ffd800938d5dd3c898fb9d8"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
@@ -4912,7 +4912,6 @@ dependencies = [
|
||||
"tokio",
|
||||
"tray-icon",
|
||||
"url",
|
||||
"urlpattern",
|
||||
"webkit2gtk",
|
||||
"webview2-com",
|
||||
"window-vibrancy",
|
||||
@@ -4921,9 +4920,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-build"
|
||||
version = "2.5.0"
|
||||
version = "2.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f71be1f494b683ac439e6d61c16ab5c472c6f9c6ee78995b29556d9067c021a1"
|
||||
checksum = "87d6f8cafe6a75514ce5333f115b7b1866e8e68d9672bf4ca89fc0f35697ea9d"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cargo_toml",
|
||||
@@ -4943,9 +4942,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-codegen"
|
||||
version = "2.5.0"
|
||||
version = "2.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c1fe64c74cc40f90848281a90058a6db931eb400b60205840e09801ee30f190"
|
||||
checksum = "b7ef707148f0755110ca54377560ab891d722de4d53297595380a748026f139f"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"brotli",
|
||||
@@ -4970,9 +4969,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-macros"
|
||||
version = "2.5.0"
|
||||
version = "2.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "260c5d2eb036b76206b9fca20b7be3614cfd21046c5396f7959e0e64a4b07f2f"
|
||||
checksum = "71664fd715ee6e382c05345ad258d6d1d50f90cf1b58c0aa726638b33c2a075d"
|
||||
dependencies = [
|
||||
"heck 0.5.0",
|
||||
"proc-macro2",
|
||||
@@ -5202,9 +5201,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-runtime"
|
||||
version = "2.9.0"
|
||||
version = "2.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3367f0b47df90e9195cd9f04a56b0055a2cba45aa11923c6c253d748778176fc"
|
||||
checksum = "9368f09358496f2229313fccb37682ad116b7f46fa76981efe116994a0628926"
|
||||
dependencies = [
|
||||
"cookie",
|
||||
"dpi",
|
||||
@@ -5227,9 +5226,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-runtime-wry"
|
||||
version = "2.9.0"
|
||||
version = "2.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "80d91d29ca680c545364cf75ba2f2e3c7ea2ab6376bfa3be26b56fa2463a5b5e"
|
||||
checksum = "929f5df216f5c02a9e894554401bcdab6eec3e39ec6a4a7731c7067fc8688a93"
|
||||
dependencies = [
|
||||
"gtk",
|
||||
"http",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "TeyvatGuide"
|
||||
version = "0.8.3"
|
||||
version = "0.8.6"
|
||||
description = "Game Tool for Genshin Impact player"
|
||||
authors = ["BTMuli <bt-muli@outlook.com>"]
|
||||
license = "MIT"
|
||||
@@ -17,14 +17,14 @@ name = "teyvat_guide_lib"
|
||||
crate-type = ["staticlib", "cdylib", "rlib"]
|
||||
|
||||
[build-dependencies]
|
||||
tauri-build = { version = "2.5.0", features = [] }
|
||||
tauri-build = { version = "2.5.2", features = [] }
|
||||
|
||||
[dependencies]
|
||||
chrono = "0.4.42"
|
||||
log = "0.4.28"
|
||||
serde = { version = "1.0.228", features = ["derive"] }
|
||||
serde_json = "1.0.145"
|
||||
tauri = { version = "2.9.0", features = [] }
|
||||
tauri = { version = "2.9.3", features = [] }
|
||||
tauri-utils = "2.8.0"
|
||||
url = "2.5.7"
|
||||
walkdir = "2.5.0"
|
||||
|
||||
@@ -8,7 +8,7 @@ mod utils;
|
||||
use tauri::{AppHandle, Manager, WebviewWindowBuilder};
|
||||
use tauri_utils::config::WebviewUrl;
|
||||
|
||||
static BBS_VERSION: &'static str = "2.93.0";
|
||||
static BBS_VERSION: &'static str = "2.95.1";
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn create_mhy_client(handle: AppHandle, func: String, url: String) {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"$schema": "https://schema.tauri.app/config/2",
|
||||
"productName": "TeyvatGuide",
|
||||
"identifier": "TeyvatGuide",
|
||||
"version": "0.8.3",
|
||||
"version": "0.8.6",
|
||||
"build": {
|
||||
"beforeDevCommand": "pnpm vite:dev",
|
||||
"beforeBuildCommand": "pnpm vite:build",
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<div v-if="hasNav" class="tgn-nav">
|
||||
<v-icon size="25" @click="tryGetCode" title="查看兑换码">mdi-code-tags-check</v-icon>
|
||||
</div>
|
||||
<ToLivecode v-model="showOverlay" :data="codeData" v-model:actId="actId" />
|
||||
<ToLivecode v-model="showOverlay" :gid="model" :data="codeData" :actId="actId" />
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
|
||||
@@ -112,7 +112,10 @@
|
||||
}"
|
||||
v-else
|
||||
>
|
||||
{{ props.modelValue.post.post_id }}
|
||||
<span>{{ props.modelValue.post.post_id }}</span>
|
||||
<template v-if="isDevEnv">
|
||||
<span data-html2canvas-ignore>[{{ props.modelValue.post.view_type }}]</span>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -154,7 +157,8 @@ export type RenderCard = {
|
||||
topics: Array<TGApp.BBS.Post.Topic>;
|
||||
reasons: Array<TGApp.BBS.Post.RecommendTags>;
|
||||
};
|
||||
|
||||
// @ts-expect-error The import.meta meta-property is not allowed in files which will build into CommonJS output.
|
||||
const isDevEnv = import.meta.env.MODE === "development";
|
||||
const stats: Readonly<Array<RenderStatus>> = [
|
||||
{ stat: 0, label: "未知", color: "var(--tgc-od-red)" },
|
||||
{ stat: 1, label: "进行中", color: "var(--tgc-od-green)" },
|
||||
|
||||
@@ -110,11 +110,6 @@
|
||||
</v-list-item>
|
||||
</template>
|
||||
<v-list class="side-list-menu sub" density="compact" :nav="true">
|
||||
<v-list-item class="side-item-menu" title="深渊数据库" :link="true" href="/wiki/abyss">
|
||||
<template #prepend>
|
||||
<img src="/source/UI/wikiAbyss.webp" alt="abyssIcon" class="side-icon-menu" />
|
||||
</template>
|
||||
</v-list-item>
|
||||
<v-list-item class="side-item-menu" title="角色图鉴" :link="true" href="/wiki/character">
|
||||
<template #prepend>
|
||||
<img src="/source/UI/wikiAvatar.webp" alt="characterIcon" class="side-icon-menu" />
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<TOverlay v-model="visible" class="tolc-overlay">
|
||||
<div class="tolc-box">
|
||||
<div class="tolc-title">
|
||||
<span>兑换码</span>
|
||||
<span>{{ gameInfo?.name ?? "" }}兑换码</span>
|
||||
<v-icon
|
||||
size="18px"
|
||||
title="share"
|
||||
@@ -39,16 +39,28 @@
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import showSnackbar from "@comp/func/snackbar.js";
|
||||
import useBBSStore from "@store/bbs.js";
|
||||
import { generateShareImg } from "@utils/TGShare.js";
|
||||
import { timestampToDate } from "@utils/toolFunc.js";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { computed } from "vue";
|
||||
|
||||
import TMiImg from "./t-mi-img.vue";
|
||||
import TOverlay from "./t-overlay.vue";
|
||||
|
||||
type ToLiveCodeProps = { data: Array<TGApp.BBS.Navigator.CodeData>; actId: string | undefined };
|
||||
type ToLiveCodeProps = {
|
||||
data: Array<TGApp.BBS.Navigator.CodeData>;
|
||||
actId: string | undefined;
|
||||
gid: number;
|
||||
};
|
||||
|
||||
const { gameList } = storeToRefs(useBBSStore());
|
||||
|
||||
const props = defineProps<ToLiveCodeProps>();
|
||||
const visible = defineModel<boolean>({ default: false });
|
||||
const gameInfo = computed<TGApp.BBS.Game.Item | undefined>(() => {
|
||||
return gameList.value.find((i) => i.id === props.gid);
|
||||
});
|
||||
|
||||
function copy(code: string): void {
|
||||
navigator.clipboard.writeText(code);
|
||||
|
||||
@@ -1,40 +1,35 @@
|
||||
<!-- 名片栏组件 -->
|
||||
<template>
|
||||
<div
|
||||
class="top-nc-box"
|
||||
@click="emit('selected', props.data)"
|
||||
:class="props.finish ? '' : 'grey'"
|
||||
:title.attr="props.data.name"
|
||||
:style="{ backgroundImage: `url('/WIKI/nameCard/bg/${props.data.name}.webp')` }"
|
||||
>
|
||||
<v-list-item>
|
||||
<template #title>
|
||||
<div class="title">
|
||||
<TwnTypeTag :type="props.data.type" />
|
||||
<span>{{ props.data.name }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<template #subtitle>
|
||||
<div class="top-nc-bgc" />
|
||||
<div class="top-nc-prepend">
|
||||
<img :src="`/WIKI/nameCard/icon/${props.data.name}.webp`" alt="icon" />
|
||||
</div>
|
||||
<div class="top-nc-info">
|
||||
<div class="top-nc-title">
|
||||
<TwnTypeTag :type="props.data.type" />
|
||||
<span>{{ props.data.name }}</span>
|
||||
</div>
|
||||
<div class="top-nc-desc">
|
||||
<span class="desc" :title="props.data.desc">{{ props.data.desc }}</span>
|
||||
</template>
|
||||
<template #prepend>
|
||||
<img :src="`/WIKI/nameCard/icon/${props.data.name}.webp`" alt="icon" class="icon" />
|
||||
</template>
|
||||
</v-list-item>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import TwnTypeTag from "@comp/pageWiki/twn-type-tag.vue";
|
||||
import { computed } from "vue";
|
||||
|
||||
type TopNameCardProps = { data: TGApp.App.NameCard.Item; finish?: boolean };
|
||||
type TopNameCardEmits = (e: "selected", v: TGApp.App.NameCard.Item) => void;
|
||||
|
||||
const props = withDefaults(defineProps<TopNameCardProps>(), { finish: true });
|
||||
const emit = defineEmits<TopNameCardEmits>();
|
||||
|
||||
const bgImage = computed<string>(() => {
|
||||
if (props.data.name === "原神·印象") return "none;";
|
||||
return `url("/WIKI/nameCard/bg/${props.data.name}.webp")`;
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@use "@styles/github.styles.scss" as github-styles;
|
||||
@@ -42,17 +37,19 @@ const bgImage = computed<string>(() => {
|
||||
.top-nc-box {
|
||||
@include github-styles.github-card-shadow;
|
||||
|
||||
position: relative;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 80px;
|
||||
box-sizing: border-box;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
border: 1px solid var(--common-shadow-1);
|
||||
border-radius: 4px 50px 50px 4px;
|
||||
background-color: var(--box-bg-1);
|
||||
background-image: v-bind(bgImage); /* stylelint-disable-line value-keyword-case */
|
||||
background-position: right;
|
||||
background-repeat: no-repeat;
|
||||
column-gap: 8px;
|
||||
cursor: pointer;
|
||||
font-family: var(--font-title);
|
||||
transition: filter 0.5s ease-in-out;
|
||||
@@ -70,19 +67,60 @@ const bgImage = computed<string>(() => {
|
||||
@include github-styles.github-card-shadow("dark");
|
||||
}
|
||||
|
||||
.icon {
|
||||
height: 60px;
|
||||
margin-right: 12px;
|
||||
aspect-ratio: 23 / 15;
|
||||
.top-nc-bgc {
|
||||
position: absolute;
|
||||
z-index: 0;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(to right, var(--box-bg-1) 40%, transparent 75%);
|
||||
}
|
||||
|
||||
.title {
|
||||
.top-nc-prepend {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
height: 60px;
|
||||
flex-shrink: 0;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-left: 8px;
|
||||
|
||||
img {
|
||||
height: 60px;
|
||||
aspect-ratio: 23/15;
|
||||
}
|
||||
}
|
||||
|
||||
.top-nc-info {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
height: 100%;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.top-nc-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
column-gap: 4px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.desc {
|
||||
text-shadow: 0 0 2px var(--common-shadow-t-8);
|
||||
.top-nc-desc {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
max-width: calc(100% - 16px);
|
||||
font-size: 14px;
|
||||
opacity: 0.75;
|
||||
text-overflow: ellipsis;
|
||||
text-shadow: 0 0 2px var(--box-bg-1);
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
||||
|
||||
105
src/components/pageAnno/tao-iframe.vue
Normal file
105
src/components/pageAnno/tao-iframe.vue
Normal file
@@ -0,0 +1,105 @@
|
||||
<!-- 游戏内公告浮窗 -->
|
||||
<template>
|
||||
<TOverlay v-model="visible">
|
||||
<div class="tao-iframe-box">
|
||||
<!-- TODO:加载完成后修改样式 -->
|
||||
<iframe :src="link" class="tao-iframe" />
|
||||
</div>
|
||||
</TOverlay>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import TOverlay from "@comp/app/t-overlay.vue";
|
||||
import showSnackbar from "@comp/func/snackbar.js";
|
||||
import takumiReq from "@req/takumiReq.js";
|
||||
import useAppStore from "@store/app.js";
|
||||
import useUserStore from "@store/user.js";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { onMounted, ref, watch } from "vue";
|
||||
|
||||
const { lang } = storeToRefs(useAppStore());
|
||||
const { cookie, account } = storeToRefs(useUserStore());
|
||||
const visible = defineModel<boolean>();
|
||||
|
||||
const authkey = ref<string>("");
|
||||
const link = ref<string>("");
|
||||
|
||||
onMounted(async () => await refreshUrl());
|
||||
|
||||
watch(
|
||||
() => lang.value,
|
||||
async () => {
|
||||
if (!visible.value) return;
|
||||
await refreshUrl();
|
||||
},
|
||||
);
|
||||
|
||||
async function refreshUrl(): Promise<void> {
|
||||
const res = await getUrl();
|
||||
if (res === "") return;
|
||||
link.value = res;
|
||||
}
|
||||
|
||||
async function refreshAuthkey(): Promise<void> {
|
||||
if (!cookie.value || !account.value) {
|
||||
visible.value = false;
|
||||
showSnackbar.warn("请先登录账号");
|
||||
return;
|
||||
}
|
||||
const authkeyRes = await takumiReq.bind.authKey(cookie.value, account.value);
|
||||
if (typeof authkeyRes === "string") {
|
||||
authkey.value = authkeyRes;
|
||||
} else {
|
||||
showSnackbar.error("获取authkey失败");
|
||||
visible.value = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
async function getUrl(): Promise<string> {
|
||||
const path = "https://sdk.mihoyo.com/hk4e/announcement/index.html";
|
||||
if (authkey.value === "") await refreshAuthkey();
|
||||
if (authkey.value === "") return "";
|
||||
const param: Record<string, string> = {
|
||||
auth_appid: "announcement",
|
||||
authkey_ver: "1",
|
||||
bundle_id: "hk4e_cn",
|
||||
channel_id: "14",
|
||||
game: "hk4e",
|
||||
game_biz: account.value.gameBiz,
|
||||
lang: lang.value,
|
||||
level: account.value.level,
|
||||
platform: "pc",
|
||||
region: account.value.region,
|
||||
sdk_presentation_style: "fullscreen",
|
||||
sdk_screen_transparent: "true",
|
||||
sign_type: "2",
|
||||
uid: account.value.gameUid,
|
||||
timestamp: Math.floor(Date.now() / 1000).toString(),
|
||||
authkey: authkey.value,
|
||||
};
|
||||
const targetLink = new URL(path);
|
||||
for (const key in param) {
|
||||
targetLink.searchParams.append(key, param[key]);
|
||||
}
|
||||
return targetLink.toString();
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.tao-iframe-box {
|
||||
position: relative;
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
width: 50vw;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 8px;
|
||||
aspect-ratio: 16/9;
|
||||
}
|
||||
|
||||
.tao-iframe {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
}
|
||||
</style>
|
||||
@@ -404,6 +404,9 @@ async function tryGetCaptcha(phone: string, aigis?: string): Promise<string | fa
|
||||
if ("retcode" in captchaResp) {
|
||||
if (!captchaResp.data || captchaResp.data === "") {
|
||||
showSnackbar.error(`[${captchaResp.retcode}] ${captchaResp.message}`);
|
||||
await TGLogger.Error(
|
||||
`[tc-userBadge][tryGetCaptcha] ${captchaResp.retcode} ${captchaResp.message}`,
|
||||
);
|
||||
return false;
|
||||
}
|
||||
const aigisResp: TGApp.BBS.CaptchaLogin.CaptchaAigis = JSON.parse(captchaResp.data);
|
||||
@@ -424,6 +427,9 @@ async function tryLoginByCaptcha(
|
||||
if ("retcode" in loginResp) {
|
||||
if (!loginResp.data || loginResp.data === "") {
|
||||
showSnackbar.error(`[${loginResp.retcode}] ${loginResp.message}`);
|
||||
await TGLogger.Error(
|
||||
`[tc-userBadge][tryLoginByCaptcha] ${loginResp.retcode} ${loginResp.message}`,
|
||||
);
|
||||
return false;
|
||||
}
|
||||
const aigisResp: TGApp.BBS.CaptchaLogin.CaptchaAigis = JSON.parse(loginResp.data);
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
<div class="tucfi-label">
|
||||
<slot name="label">{{ props.label }}</slot>
|
||||
</div>
|
||||
<div v-if="props.data === null"><span class="tucfi-data">暂无数据</span></div>
|
||||
<div v-if="!props.data">
|
||||
<span class="tucfi-data">暂无数据</span>
|
||||
</div>
|
||||
<div v-else-if="!Array.isArray(props.data)" class="tucfi-data">
|
||||
<TItemBox :model-value="getBox(props.data)" />
|
||||
</div>
|
||||
|
||||
144
src/components/userGacha/gbr-data-line.vue
Normal file
144
src/components/userGacha/gbr-data-line.vue
Normal file
@@ -0,0 +1,144 @@
|
||||
<!-- 千星奇域概览单项组件 -->
|
||||
<template>
|
||||
<div class="gbr-dl-box">
|
||||
<div class="gbr-dl-progress" />
|
||||
<div class="gbr-dl-icon">
|
||||
<img :alt="props.data.name" :src="getIcon()" />
|
||||
</div>
|
||||
<div class="gbr-dl-base">
|
||||
<div class="gbr-dl-name">{{ props.data.name }}</div>
|
||||
<div class="gbr-dl-time">{{ props.data.time }}</div>
|
||||
</div>
|
||||
<div class="gbr-dl-info">
|
||||
<div class="gbr-dl-cnt">{{ props.count }}</div>
|
||||
<div class="gbr-dl-hint" v-if="hint !== ''">{{ hint }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { computed } from "vue";
|
||||
|
||||
export type GbrDataLineProps = { data: TGApp.Sqlite.GachaRecords.TableGachaB; count: number };
|
||||
|
||||
const props = defineProps<GbrDataLineProps>();
|
||||
const hint = getEndHint();
|
||||
|
||||
function getIcon(): string {
|
||||
console.log(props.data);
|
||||
// const find = AppGachaBData.find((i) => i.id.toString() === props.data.itemId);
|
||||
// if (!find) return `/source/UI/paimon.webp`;
|
||||
// return `https://api.hakush.in/gi/UI/${find.icon}.webp`;
|
||||
// TODO: 缺失元数据
|
||||
return `/source/UI/paimon.webp`;
|
||||
}
|
||||
|
||||
function getEndHint(): string {
|
||||
if (props.data.gachaType === "1000") return "";
|
||||
if (!props.data.isUp) return "歪";
|
||||
return "";
|
||||
}
|
||||
|
||||
const progressColor = computed<string>(() => {
|
||||
if (hint === "UP" && props.data.rank === "5") return "#d19a66";
|
||||
if (hint === "UP" && props.data.rank === "4") return "#c678dd";
|
||||
if (hint === "歪") return "#e06c75";
|
||||
return "#61afef";
|
||||
});
|
||||
const progressWidth = computed<string>(() => {
|
||||
let final = 10;
|
||||
if (props.data.rank === "5") {
|
||||
if (props.data.gachaType === "302") final = 80;
|
||||
else final = 90;
|
||||
} else if (props.data.rank === "4") final = 10;
|
||||
else return "0%";
|
||||
return ((props.count / final) * 100).toFixed(2) + "%";
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.gbr-dl-box {
|
||||
position: relative;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 48px;
|
||||
box-sizing: border-box;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
padding: 8px;
|
||||
border: 1px solid var(--common-shadow-1);
|
||||
border-radius: 4px;
|
||||
background: var(--box-bg-2);
|
||||
column-gap: 4px;
|
||||
}
|
||||
|
||||
.gbr-dl-progress {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: v-bind(progressWidth); /* stylelint-disable-line value-keyword-case */
|
||||
max-width: 100%;
|
||||
height: 4px;
|
||||
border-radius: 4px;
|
||||
background: v-bind(progressColor); /* stylelint-disable-line value-keyword-case */
|
||||
}
|
||||
|
||||
.gbr-dl-icon {
|
||||
display: flex;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
flex-shrink: 0;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.gbr-dl-base {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.gbr-dl-name {
|
||||
color: var(--common-text-title);
|
||||
font-family: var(--font-title);
|
||||
font-size: 14px;
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
.gbr-dl-time {
|
||||
color: var(--box-text-7);
|
||||
font-size: 12px;
|
||||
line-height: 14px;
|
||||
}
|
||||
|
||||
.gbr-dl-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-left: auto;
|
||||
column-gap: 4px;
|
||||
}
|
||||
|
||||
.gbr-dl-cnt {
|
||||
color: var(--common-text-title);
|
||||
font-family: var(--font-title);
|
||||
}
|
||||
|
||||
.gbr-dl-hint {
|
||||
display: flex;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 4px;
|
||||
border-radius: 50%;
|
||||
background: var(--box-bg-3);
|
||||
color: v-bind(progressColor); /* stylelint-disable-line value-keyword-case */
|
||||
font-family: var(--font-title);
|
||||
transform: rotate(25deg);
|
||||
}
|
||||
</style>
|
||||
263
src/components/userGacha/gbr-data-view.vue
Normal file
263
src/components/userGacha/gbr-data-view.vue
Normal file
@@ -0,0 +1,263 @@
|
||||
<!-- 千星奇域概览数据视图组件 -->
|
||||
<template>
|
||||
<div class="gbr-dv-container">
|
||||
<div class="gbr-dvt-title">
|
||||
<span>{{ title }}</span>
|
||||
<span>{{ props.dataVal.length }}</span>
|
||||
</div>
|
||||
<div class="gbr-dvt-subtitle">
|
||||
<span v-show="props.dataVal.length === 0">暂无数据</span>
|
||||
<span v-show="props.dataVal.length !== 0">{{ startDate }} ~ {{ endDate }}</span>
|
||||
</div>
|
||||
<div class="gbr-mid-list">
|
||||
<div class="gbr-ml-item">
|
||||
<span>4☆已垫</span>
|
||||
<span>{{ reset4count - 1 }}</span>
|
||||
</div>
|
||||
<div class="gbr-ml-item">
|
||||
<span>5☆已垫</span>
|
||||
<span>{{ reset5count - 1 }}</span>
|
||||
</div>
|
||||
<div class="gbr-ml-item">
|
||||
<span>5☆平均</span>
|
||||
<span>{{ star5avg }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="gbr-mid-list">
|
||||
<div class="gbr-ml-item">
|
||||
<span>5☆统计</span>
|
||||
<span>{{ getTitle("5") }}</span>
|
||||
</div>
|
||||
<div class="gbr-ml-item">
|
||||
<span>4☆统计</span>
|
||||
<span>{{ getTitle("4") }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 这边放具体物品的列表 -->
|
||||
<div class="gbr-bottom">
|
||||
<v-tabs v-model="tab" density="compact">
|
||||
<v-tab value="5">5☆</v-tab>
|
||||
<v-tab value="4">4☆</v-tab>
|
||||
<v-tab value="3">3☆</v-tab>
|
||||
</v-tabs>
|
||||
<v-window v-model="tab" class="gbr-bottom-window">
|
||||
<v-window-item value="5" class="gbr-b-window-item">
|
||||
<v-virtual-scroll :items="star5List" :item-height="48">
|
||||
<template #default="{ item }">
|
||||
<GbrDataLine :data="item.data" :count="item.count" />
|
||||
</template>
|
||||
</v-virtual-scroll>
|
||||
</v-window-item>
|
||||
<v-window-item value="4" class="gbr-b-window-item">
|
||||
<v-virtual-scroll :items="star4List" :item-height="48">
|
||||
<template #default="{ item }">
|
||||
<GbrDataLine :data="item.data" :count="item.count" />
|
||||
</template>
|
||||
</v-virtual-scroll>
|
||||
</v-window-item>
|
||||
<v-window-item value="3" class="gbr-b-window-item">
|
||||
<v-virtual-scroll :items="star3List" :item-height="48">
|
||||
<template #default="{ item }">
|
||||
<GbrDataLine :data="item.data" :count="item.count" />
|
||||
</template>
|
||||
</v-virtual-scroll>
|
||||
</v-window-item>
|
||||
</v-window>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, ref, shallowRef, watch } from "vue";
|
||||
|
||||
import GbrDataLine, { type GbrDataLineProps } from "./gbr-data-line.vue";
|
||||
|
||||
type GachaDataViewProps = {
|
||||
dataType: "normal" | "boy" | "girl";
|
||||
dataVal: Array<TGApp.Sqlite.GachaRecords.TableGachaB>;
|
||||
};
|
||||
|
||||
const props = defineProps<GachaDataViewProps>();
|
||||
|
||||
// data
|
||||
const loading = ref<boolean>(true); // 是否加载完
|
||||
const title = ref<string>(""); // 卡片标题
|
||||
const startDate = ref<string>(""); // 最早的时间
|
||||
const endDate = ref<string>(""); // 最晚的时间
|
||||
const star5List = shallowRef<Array<GbrDataLineProps>>([]); // 5星物品数据
|
||||
const star4List = shallowRef<Array<GbrDataLineProps>>([]); // 4星物品数据
|
||||
const star3List = shallowRef<Array<GbrDataLineProps>>([]);
|
||||
const reset5count = ref<number>(1); // 5星垫抽数量
|
||||
const reset4count = ref<number>(1); // 4星垫抽数量
|
||||
const reset3count = ref<number>(1); // 3星垫抽数量
|
||||
const star3count = ref<number>(0); // 3星物品数量
|
||||
const star5avg = ref<string>(""); // 5星平均抽数
|
||||
const tab = ref<string>("5"); // tab
|
||||
|
||||
onMounted(() => {
|
||||
loadData();
|
||||
loading.value = false;
|
||||
});
|
||||
|
||||
function loadData(): void {
|
||||
title.value = getTitle("top");
|
||||
const tempData = props.dataVal;
|
||||
const temp5Data: Array<GbrDataLineProps> = [];
|
||||
const temp4Data: Array<GbrDataLineProps> = [];
|
||||
const temp3Data: Array<GbrDataLineProps> = [];
|
||||
// 按照 id 升序
|
||||
tempData
|
||||
.sort((a, b) => a.id.localeCompare(b.id))
|
||||
.forEach((item) => {
|
||||
// 处理时间
|
||||
if (startDate.value === "" || item.time < startDate.value) startDate.value = item.time;
|
||||
if (endDate.value === "" || item.time > endDate.value) endDate.value = item.time;
|
||||
if (item.rank === "2") {
|
||||
reset3count.value++;
|
||||
reset4count.value++;
|
||||
reset5count.value++;
|
||||
} else if (item.rank === "3") {
|
||||
reset4count.value++;
|
||||
reset5count.value++;
|
||||
temp3Data.push({ data: item, count: reset3count.value });
|
||||
reset3count.value = 1;
|
||||
} else if (item.rank === "4") {
|
||||
reset5count.value++;
|
||||
temp4Data.push({ data: item, count: reset4count.value });
|
||||
reset4count.value = 1;
|
||||
} else if (item.rank === "5") {
|
||||
reset4count.value++;
|
||||
temp5Data.push({ data: item, count: reset5count.value });
|
||||
reset5count.value = 1;
|
||||
}
|
||||
});
|
||||
star5List.value = temp5Data.reverse();
|
||||
star4List.value = temp4Data.reverse();
|
||||
star3List.value = temp3Data.reverse();
|
||||
star5avg.value = getStar5Avg();
|
||||
}
|
||||
|
||||
// 获取标题
|
||||
function getTitle(type: "top" | "5" | "4" | "3"): string {
|
||||
if (type === "top") {
|
||||
if (props.dataType === "normal") return "常驻颂愿";
|
||||
if (props.dataType === "boy") return "活动颂愿(男)";
|
||||
if (props.dataType === "girl") return "活动颂愿(女)";
|
||||
return "";
|
||||
}
|
||||
if (props.dataVal.length === 0) return "暂无数据";
|
||||
if (type === "5") {
|
||||
// 5星物品统计 00.00%
|
||||
return `${star5List.value.length} [${((star5List.value.length * 100) / props.dataVal.length)
|
||||
.toFixed(2)
|
||||
.padStart(5, "0")}%]`;
|
||||
}
|
||||
if (type === "4") {
|
||||
// 4星物品统计
|
||||
return `${star4List.value.length} [${((star4List.value.length * 100) / props.dataVal.length)
|
||||
.toFixed(2)
|
||||
.padStart(5, "0")}%]`;
|
||||
}
|
||||
// 3星物品统计
|
||||
return `${star3count.value} [${((star3count.value * 100) / props.dataVal.length)
|
||||
.toFixed(2)
|
||||
.padStart(5, "0")}%]`;
|
||||
}
|
||||
|
||||
// 获取5星平均抽数
|
||||
function getStar5Avg(): string {
|
||||
const resetList = star5List.value.map((item) => item.count);
|
||||
if (resetList.length === 0) return "0";
|
||||
const total = resetList.reduce((a, b) => a + b);
|
||||
return (total / star5List.value.length).toFixed(2);
|
||||
}
|
||||
|
||||
// 监听数据变化
|
||||
watch(
|
||||
() => props.dataVal,
|
||||
() => {
|
||||
star5List.value = [];
|
||||
star4List.value = [];
|
||||
reset5count.value = 1;
|
||||
reset4count.value = 1;
|
||||
star3count.value = 1;
|
||||
startDate.value = "";
|
||||
endDate.value = "";
|
||||
star5avg.value = "";
|
||||
tab.value = "5";
|
||||
loadData();
|
||||
},
|
||||
);
|
||||
</script>
|
||||
<style lang="css" scoped>
|
||||
.gbr-dv-container {
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 8px;
|
||||
border-radius: 4px;
|
||||
background: var(--box-bg-1);
|
||||
}
|
||||
|
||||
.gbr-dvt-title {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
color: var(--common-text-title);
|
||||
font-family: var(--font-title);
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.gbr-dvt-subtitle {
|
||||
width: 100%;
|
||||
font-family: var(--font-text);
|
||||
font-size: 12px;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.gbr-mid-list {
|
||||
padding-top: 4px;
|
||||
padding-bottom: 4px;
|
||||
border-top: 1px solid var(--common-shadow-4);
|
||||
color: var(--box-text-7);
|
||||
}
|
||||
|
||||
.gbr-ml-item {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-family: var(--font-title);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.gbr-bottom {
|
||||
position: relative;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: calc(100% - 150px);
|
||||
box-sizing: border-box;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.gbr-bottom-window {
|
||||
position: relative;
|
||||
height: calc(100vh - 428px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.gbr-b-window-item {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
padding-right: 4px;
|
||||
}
|
||||
|
||||
/* stylelint-disable selector-class-pattern */
|
||||
|
||||
:deep(.v-virtual-scroll__item + .v-virtual-scroll__item) {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
/* stylelint-enable selector-class-pattern */
|
||||
</style>
|
||||
34
src/components/userGacha/gbr-overview.vue
Normal file
34
src/components/userGacha/gbr-overview.vue
Normal file
@@ -0,0 +1,34 @@
|
||||
<!-- 千星奇域祈愿概览组件 -->
|
||||
<template>
|
||||
<div class="gro-o-container">
|
||||
<GbrDataView :data-val="normalData" data-type="normal" />
|
||||
<GbrDataView :data-val="boyData" data-type="boy" />
|
||||
<GbrDataView :data-val="girlData" data-type="girl" />
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { computed } from "vue";
|
||||
|
||||
import GbrDataView from "./gbr-data-view.vue";
|
||||
|
||||
type GachaOverviewProps = { modelValue: Array<TGApp.Sqlite.GachaRecords.TableGachaB> };
|
||||
|
||||
const props = defineProps<GachaOverviewProps>();
|
||||
const normalData = computed<Array<TGApp.Sqlite.GachaRecords.TableGachaB>>(() =>
|
||||
props.modelValue.filter((item) => item.opGachaType === "1000"),
|
||||
);
|
||||
const girlData = computed<Array<TGApp.Sqlite.GachaRecords.TableGachaB>>(() =>
|
||||
props.modelValue.filter((item) => item.opGachaType === "20011" || item.opGachaType === "20012"),
|
||||
);
|
||||
const boyData = computed<Array<TGApp.Sqlite.GachaRecords.TableGachaB>>(() =>
|
||||
props.modelValue.filter((item) => item.opGachaType === "20021" || item.opGachaType === "20022"),
|
||||
);
|
||||
</script>
|
||||
<style lang="css" scoped>
|
||||
.gro-o-container {
|
||||
display: grid;
|
||||
height: 100%;
|
||||
column-gap: 8px;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
</style>
|
||||
62
src/components/userGacha/gbr-table.vue
Normal file
62
src/components/userGacha/gbr-table.vue
Normal file
@@ -0,0 +1,62 @@
|
||||
<!-- 千星奇域数据表格 -->
|
||||
<template>
|
||||
<v-data-table
|
||||
:headers="headers"
|
||||
:items="props.modelValue"
|
||||
fixed-header
|
||||
fixed-footer
|
||||
class="gbr-t-box"
|
||||
>
|
||||
<template v-slot:item="{ item }">
|
||||
<tr class="gbr-t-tr">
|
||||
<td>{{ item.time }}</td>
|
||||
<td>{{ getPool(item.opGachaType) }}</td>
|
||||
<td>{{ item.type }}</td>
|
||||
<td>{{ item.name }}</td>
|
||||
<td>{{ item.rank }}</td>
|
||||
</tr>
|
||||
</template>
|
||||
</v-data-table>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
type GroTableProps = { modelValue: Array<TGApp.Sqlite.GachaRecords.TableGachaB> };
|
||||
|
||||
const props = defineProps<GroTableProps>();
|
||||
|
||||
const headers = <const>[
|
||||
{ title: "时间", align: "center", key: "time" },
|
||||
{ title: "卡池", align: "center", key: "opGachaType" },
|
||||
{ title: "类型", align: "center", key: "type" },
|
||||
{ title: "名称", align: "center", key: "name" },
|
||||
{ title: "星级", align: "center", key: "rank" },
|
||||
];
|
||||
|
||||
function getPool(type: string) {
|
||||
switch (type) {
|
||||
case "1000":
|
||||
return "常驻颂愿";
|
||||
case "2000":
|
||||
return "活动颂愿";
|
||||
case "20011":
|
||||
case "20012":
|
||||
return "活动颂愿-男";
|
||||
case "20021":
|
||||
case "20022":
|
||||
return "活动颂愿-女";
|
||||
default:
|
||||
return "未知";
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="css" scoped>
|
||||
.gbr-t-box {
|
||||
height: calc(100vh - 200px);
|
||||
padding-right: 5px;
|
||||
border-radius: 5px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.gbr-t-tr {
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
@@ -20,7 +20,7 @@ import { computed } from "vue";
|
||||
|
||||
import { AppGachaData } from "@/data/index.js";
|
||||
|
||||
export type GroDataLineProps = { data: TGApp.Sqlite.GachaRecords.SingleTable; count: number };
|
||||
export type GroDataLineProps = { data: TGApp.Sqlite.GachaRecords.TableGacha; count: number };
|
||||
|
||||
const props = defineProps<GroDataLineProps>();
|
||||
const hint = getEndHint();
|
||||
|
||||
@@ -64,7 +64,7 @@ import GroDataLine, { type GroDataLineProps } from "./gro-data-line.vue";
|
||||
|
||||
type GachaDataViewProps = {
|
||||
dataType: "new" | "avatar" | "weapon" | "normal" | "mix";
|
||||
dataVal: Array<TGApp.Sqlite.GachaRecords.SingleTable>;
|
||||
dataVal: Array<TGApp.Sqlite.GachaRecords.TableGacha>;
|
||||
};
|
||||
|
||||
const props = defineProps<GachaDataViewProps>();
|
||||
|
||||
134
src/components/userGacha/gro-iframe.vue
Normal file
134
src/components/userGacha/gro-iframe.vue
Normal file
@@ -0,0 +1,134 @@
|
||||
<!-- 嵌入游戏内容的iframe组件 -->
|
||||
<template>
|
||||
<div class="gro-iframe-container">
|
||||
<v-tabs class="gro-ic-tabs" v-model="poolTab" align-tabs="start" direction="vertical">
|
||||
<v-tab v-for="(item, index) in tabList" :key="index" :value="item.value">
|
||||
<template v-if="item.beyond">
|
||||
<img src="/icon/nation/千星奇域.webp" title="千星奇域" alt="beyond" />
|
||||
</template>
|
||||
{{ item.label }}
|
||||
</v-tab>
|
||||
</v-tabs>
|
||||
<iframe class="gro-iframe" :src="link" style="width: 100%; height: 100%; border: none" />
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import showSnackbar from "@comp/func/snackbar.js";
|
||||
import takumiReq from "@req/takumiReq.js";
|
||||
import useUserStore from "@store/user.js";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { onMounted, ref, shallowRef, watch } from "vue";
|
||||
|
||||
/**
|
||||
* 卡池类型-ID映射
|
||||
* @remarks
|
||||
* 目前缺失集录&新手池
|
||||
* TODO: 动态获取当前卡池类型&ID映射
|
||||
*/
|
||||
const GachaIdMap: Record<string, string> = {
|
||||
"200": "34ff1a235049182fd199d285110e3e7d292c50cd", // 常驻
|
||||
"301": "182e725d99b742b14839117650d3e79628cc6221", //角色活动
|
||||
"302": "8ff7a7d42bea79b0d54e92fdb58a20f971490372", // 武器活动
|
||||
"400": "bb0486115a7e7c4bd2994135f7d212014b17173b", // 角色活动-2
|
||||
"1000": "f3f5090a8ec0b28f15805c9969aa6c4ec357", // 千星奇域常驻
|
||||
"20011": "a8d0a985efb4ed61eb2e73a86a57237bd116", // 千星奇域角色活动-男
|
||||
"20021": "57016dec6b768231ba1342c01935417a799b", // 千星奇域角色活动-女
|
||||
};
|
||||
|
||||
const tabNormal: ReadonlyArray<GroTab> = [
|
||||
{ label: "常驻祈愿", value: "200" },
|
||||
{ label: "角色活动祈愿", value: "301" },
|
||||
{ label: "武器活动祈愿", value: "302" },
|
||||
{ label: "角色活动祈愿-2", value: "400" },
|
||||
];
|
||||
const tabBeyond: ReadonlyArray<GroTab> = [
|
||||
{ label: "常驻颂愿", value: "1000", beyond: true },
|
||||
{ label: "活动颂愿-男", value: "20011", beyond: true },
|
||||
{ label: "活动颂愿-女", value: "20021", beyond: true },
|
||||
];
|
||||
|
||||
type GroTabKey = keyof typeof GachaIdMap;
|
||||
type GroTab = { label: string; value: string; beyond?: boolean };
|
||||
type GroIframeProps = { mode: "normal" | "beyond" };
|
||||
const props = defineProps<GroIframeProps>();
|
||||
|
||||
const { cookie, account } = storeToRefs(useUserStore());
|
||||
const authkey = ref<string>("");
|
||||
const link = ref<string>("");
|
||||
const poolTab = ref<GroTabKey>("200");
|
||||
const tabList = shallowRef<ReadonlyArray<GroTab>>(props.mode === "beyond" ? tabBeyond : tabNormal);
|
||||
|
||||
onMounted(async () => {
|
||||
link.value = await getUrl();
|
||||
});
|
||||
|
||||
watch(
|
||||
() => poolTab.value,
|
||||
async () => {
|
||||
link.value = await getUrl();
|
||||
},
|
||||
);
|
||||
|
||||
async function getUrl(): Promise<string> {
|
||||
const path = "https://webstatic.mihoyo.com/hk4e/event/e20190909gacha-v3/index.html";
|
||||
const pathB = "https://webstatic.mihoyo.com/hk4e/event/e20250716gacha/index.html";
|
||||
const pathF = poolTab.value.length < 4 ? path : pathB;
|
||||
if (authkey.value === "") await refreshAuthkey();
|
||||
const param: Record<string, string> = {
|
||||
win_mode: "fullscreen",
|
||||
no_joypad_close: "1",
|
||||
authkey_ver: "1",
|
||||
sign_type: "2",
|
||||
auth_appid: "webview_gacha",
|
||||
gacha_id: GachaIdMap[poolTab.value],
|
||||
timestamp: Math.floor(Date.now() / 1000).toString(),
|
||||
lang: "zh-cn",
|
||||
device_type: "pc",
|
||||
region: account.value.region,
|
||||
authkey: authkey.value,
|
||||
game_biz: account.value.gameBiz,
|
||||
};
|
||||
const targetLink = new URL(pathF);
|
||||
for (const key in param) {
|
||||
targetLink.searchParams.append(key, param[key]);
|
||||
}
|
||||
return targetLink.toString();
|
||||
}
|
||||
|
||||
async function refreshAuthkey(): Promise<void> {
|
||||
if (!cookie.value || !account.value) {
|
||||
return;
|
||||
}
|
||||
const authkeyRes = await takumiReq.bind.authKey(cookie.value, account.value);
|
||||
if (typeof authkeyRes === "string") {
|
||||
authkey.value = authkeyRes;
|
||||
} else {
|
||||
showSnackbar.error("获取authkey失败");
|
||||
return;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.gro-iframe-container {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.gro-ic-tabs {
|
||||
height: 100%;
|
||||
|
||||
img {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.gro-ic-window {
|
||||
height: 100%;
|
||||
flex: 1;
|
||||
}
|
||||
</style>
|
||||
@@ -12,22 +12,22 @@ import { computed, ref, watch } from "vue";
|
||||
|
||||
import GroDataView from "./gro-data-view.vue";
|
||||
|
||||
type GachaOverviewProps = { modelValue: Array<TGApp.Sqlite.GachaRecords.SingleTable> };
|
||||
type GachaOverviewProps = { modelValue: Array<TGApp.Sqlite.GachaRecords.TableGacha> };
|
||||
|
||||
const props = defineProps<GachaOverviewProps>();
|
||||
const newData = computed<Array<TGApp.Sqlite.GachaRecords.SingleTable>>(() =>
|
||||
const newData = computed<Array<TGApp.Sqlite.GachaRecords.TableGacha>>(() =>
|
||||
props.modelValue.filter((item) => item.uigfType === "100"),
|
||||
);
|
||||
const normalData = computed<Array<TGApp.Sqlite.GachaRecords.SingleTable>>(() =>
|
||||
const normalData = computed<Array<TGApp.Sqlite.GachaRecords.TableGacha>>(() =>
|
||||
props.modelValue.filter((item) => item.uigfType === "200"),
|
||||
);
|
||||
const avatarData = computed<Array<TGApp.Sqlite.GachaRecords.SingleTable>>(() =>
|
||||
const avatarData = computed<Array<TGApp.Sqlite.GachaRecords.TableGacha>>(() =>
|
||||
props.modelValue.filter((item) => item.uigfType === "301"),
|
||||
);
|
||||
const weaponData = computed<Array<TGApp.Sqlite.GachaRecords.SingleTable>>(() =>
|
||||
const weaponData = computed<Array<TGApp.Sqlite.GachaRecords.TableGacha>>(() =>
|
||||
props.modelValue.filter((item) => item.uigfType === "302"),
|
||||
);
|
||||
const mixData = computed<Array<TGApp.Sqlite.GachaRecords.SingleTable>>(() =>
|
||||
const mixData = computed<Array<TGApp.Sqlite.GachaRecords.TableGacha>>(() =>
|
||||
props.modelValue.filter((item) => item.uigfType === "500"),
|
||||
);
|
||||
|
||||
@@ -56,7 +56,7 @@ watch(
|
||||
.gro-o-container {
|
||||
display: grid;
|
||||
height: 100%;
|
||||
grid-column-gap: 8px;
|
||||
column-gap: 8px;
|
||||
grid-template-columns: v-bind(cnCols); /* stylelint-disable-line value-keyword-case */
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
</v-data-table>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
type GroTableProps = { modelValue: Array<TGApp.Sqlite.GachaRecords.SingleTable> };
|
||||
type GroTableProps = { modelValue: Array<TGApp.Sqlite.GachaRecords.TableGacha> };
|
||||
|
||||
const props = defineProps<GroTableProps>();
|
||||
|
||||
|
||||
@@ -62,9 +62,11 @@ import { computed, onMounted, ref, shallowRef, watch } from "vue";
|
||||
type UgoUidProps = { mode: "import" | "export" };
|
||||
type UgoUidItem = { uid: string; length: number; time: string };
|
||||
|
||||
const fpEmptyText = "点击选择文件路径";
|
||||
|
||||
const props = defineProps<UgoUidProps>();
|
||||
const visible = defineModel<boolean>();
|
||||
const fp = ref<string>("未选择");
|
||||
const fp = ref<string>(fpEmptyText);
|
||||
const dataRaw = shallowRef<TGApp.Plugins.UIGF.Schema4>();
|
||||
const data = shallowRef<Array<UgoUidItem>>([]);
|
||||
const selectedData = shallowRef<Array<UgoUidItem>>([]);
|
||||
@@ -91,7 +93,7 @@ async function refreshData(): Promise<void> {
|
||||
data.value = [];
|
||||
dataRaw.value = undefined;
|
||||
if (props.mode === "import") {
|
||||
fp.value = "未选择";
|
||||
fp.value = fpEmptyText;
|
||||
await handleImportData();
|
||||
} else {
|
||||
fp.value = await getDefaultSavePath();
|
||||
@@ -118,7 +120,7 @@ async function selectFile(): Promise<void> {
|
||||
}
|
||||
|
||||
async function handleImportData(): Promise<void> {
|
||||
if (fp.value === "未选择") return;
|
||||
if (fp.value === fpEmptyText) return;
|
||||
try {
|
||||
await showLoading.start("正在导入数据...", "正在验证数据...");
|
||||
const check = await verifyUigfData(fp.value, true);
|
||||
@@ -161,7 +163,7 @@ async function handleExportData(): Promise<void> {
|
||||
data.value = tmpData;
|
||||
}
|
||||
|
||||
function parseDataRaw(data: TGApp.Sqlite.GachaRecords.SingleTable[]): UgoUidItem {
|
||||
function parseDataRaw(data: TGApp.Sqlite.GachaRecords.TableGacha[]): UgoUidItem {
|
||||
const timeList = data.map((item) => new Date(item.time).getTime());
|
||||
return {
|
||||
uid: data[0].uid,
|
||||
@@ -178,7 +180,7 @@ async function handleSelected(): Promise<void> {
|
||||
async function handleImport(): Promise<void> {
|
||||
if (!dataRaw.value) {
|
||||
showSnackbar.error("未获取到数据!");
|
||||
fp.value = "未选择";
|
||||
fp.value = fpEmptyText;
|
||||
return;
|
||||
}
|
||||
if (selectedData.value.length === 0) {
|
||||
@@ -234,6 +236,7 @@ async function handleExport(): Promise<void> {
|
||||
position: relative;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
flex-wrap: wrap;
|
||||
align-items: flex-end;
|
||||
justify-content: space-between;
|
||||
column-gap: 10px;
|
||||
@@ -249,6 +252,7 @@ async function handleExport(): Promise<void> {
|
||||
color: var(--tgc-od-white);
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.ugo-header {
|
||||
|
||||
@@ -55,10 +55,7 @@ const showOverlay = ref<boolean>(false);
|
||||
const localUrl = ref<string>();
|
||||
const bgColor = ref<string>("transparent");
|
||||
|
||||
const oriUrl = computed<string>(() => {
|
||||
if (typeof props.data.insert.image === "string") return props.data.insert.image;
|
||||
return props.data.insert.image.url;
|
||||
});
|
||||
const oriUrl = ref<string>("");
|
||||
const imgExt = computed<string>(() => getImageExt());
|
||||
const showOri = ref<boolean>(imgExt.value === "gif" || imageQualityPercent.value === 100);
|
||||
|
||||
@@ -71,6 +68,7 @@ const imgWidth = computed<string>(() => {
|
||||
console.log("tp-image", props.data.insert.image, props.data.attributes);
|
||||
|
||||
onMounted(async () => {
|
||||
oriUrl.value = miniImgUrl();
|
||||
const link = appStore.getImageUrl(oriUrl.value, imgExt.value);
|
||||
localUrl.value = await saveImgLocal(link);
|
||||
});
|
||||
@@ -93,6 +91,17 @@ onUnmounted(() => {
|
||||
if (localUrl.value) URL.revokeObjectURL(localUrl.value);
|
||||
});
|
||||
|
||||
function miniImgUrl(): string {
|
||||
let url: string;
|
||||
if (typeof props.data.insert.image === "string") {
|
||||
url = props.data.insert.image;
|
||||
} else {
|
||||
url = props.data.insert.image.url;
|
||||
}
|
||||
const link = new URL(url);
|
||||
return `${link.origin}${link.pathname}`;
|
||||
}
|
||||
|
||||
function getImageTitle(): string {
|
||||
const res: string[] = [];
|
||||
if (props.data.attributes) {
|
||||
@@ -120,11 +129,11 @@ function getImageTitle(): string {
|
||||
|
||||
function getImageExt(): string {
|
||||
if (props.data.attributes && props.data.attributes.ext) return props.data.attributes.ext;
|
||||
if (typeof props.data.insert.image === "string") {
|
||||
const arr = props.data.insert.image.split(".");
|
||||
return arr[arr.length - 1];
|
||||
if (typeof props.data.insert.image !== "string") {
|
||||
return props.data.insert.image.format;
|
||||
}
|
||||
return props.data.insert.image.format;
|
||||
const arr = oriUrl.value.split(".");
|
||||
return arr[arr.length - 1];
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
/>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import TpUgcLevel from "@comp/viewPost/tp-ugc-level.vue";
|
||||
import TpUgcTag from "@comp/viewPost/tp-ugc-tag.vue";
|
||||
import type { Component } from "vue";
|
||||
|
||||
import TpBackupText from "./tp-backupText.vue";
|
||||
@@ -93,9 +95,11 @@ function getParsedText(data: TpTextType): Array<TpTextType> {
|
||||
|
||||
function getTpName(tp: TGApp.BBS.SctPost.Base): Component {
|
||||
if (tp.children) return TpTexts;
|
||||
if (typeof tp.insert === "undefined") return TpUnknown;
|
||||
if (typeof tp.insert === "string") return TpText;
|
||||
// game_user_info属于backup_text的一种,必须放在backup_text判断的前面
|
||||
// 判断特殊backup_text
|
||||
if ("game_user_info" in tp.insert) return TpUid;
|
||||
if ("ugc_master_tag" in tp.insert) return TpUgcTag;
|
||||
if ("backup_text" in tp.insert) {
|
||||
if (tp.insert.backup_text === "[游戏卡片]" && "reception_card" in tp.insert) return TpGameCard;
|
||||
if (tp.insert.backup_text === "[自定义表情]" && "custom_emoticon" in tp.insert) {
|
||||
@@ -105,6 +109,7 @@ function getTpName(tp: TGApp.BBS.SctPost.Base): Component {
|
||||
}
|
||||
if ("divider" in tp.insert) return TpDivider;
|
||||
if ("image" in tp.insert) return TpImage;
|
||||
if ("level" in tp.insert) return TpUgcLevel;
|
||||
if ("link_card" in tp.insert) return TpLinkCard;
|
||||
if ("mention" in tp.insert) return TpMention;
|
||||
if ("video" in tp.insert) return TpVideo;
|
||||
|
||||
151
src/components/viewPost/tp-ugc-level.vue
Normal file
151
src/components/viewPost/tp-ugc-level.vue
Normal file
@@ -0,0 +1,151 @@
|
||||
<!-- UGC关卡组件 TODO:UI调整-->
|
||||
<template>
|
||||
<div class="tul-card-box" @click="console.log(props.data)">
|
||||
<TMiImg
|
||||
@click="toLevel()"
|
||||
class="tul-cover"
|
||||
:src="props.data.insert.level.cover.url"
|
||||
alt="cover"
|
||||
/>
|
||||
<div class="tul-content">
|
||||
<div class="tul-top">
|
||||
<div class="tul-title">{{ props.data.insert.level.level_name }}</div>
|
||||
<div class="tul-sub">
|
||||
<div class="tul-info-item" title="游戏类型">
|
||||
<v-icon size="16" color="yellow">mdi-gamepad-variant</v-icon>
|
||||
<span>{{ props.data.insert.level.play_type }}</span>
|
||||
</div>
|
||||
<div class="tul-info-item" title="游玩人数">
|
||||
<v-icon size="16" color="blue">mdi-account-multiple</v-icon>
|
||||
<span>{{ props.data.insert.level.show_limit_play_num_str }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tul-attach" v-if="props.data.insert.level.level_attachment">
|
||||
<span>“</span>
|
||||
<span>{{ props.data.insert.level.level_attachment.content }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tul-bottom">
|
||||
<div class="tul-info">
|
||||
<div class="tul-info-item" title="热度">
|
||||
<v-icon size="16" color="orange">mdi-fire</v-icon>
|
||||
<span>{{ props.data.insert.level.hot_score }}</span>
|
||||
</div>
|
||||
<div class="tul-info-item" title="点赞率">
|
||||
<v-icon size="16" color="pink">mdi-thumb-up</v-icon>
|
||||
<span>{{ props.data.insert.level.good_rate }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import TMiImg from "@comp/app/t-mi-img.vue";
|
||||
import TGClient from "@utils/TGClient.js";
|
||||
|
||||
type TpUgcLevel = { insert: { level: TGApp.BBS.UGC.Level } };
|
||||
type TpUgcLevelProps = { data: TpUgcLevel };
|
||||
|
||||
const props = defineProps<TpUgcLevelProps>();
|
||||
|
||||
async function toLevel(): Promise<void> {
|
||||
let url = `https://act.miyoushe.com/ys/ugc_community/mx/#/pages/level-detail/index?`;
|
||||
url = `${url}id=${props.data.insert.level.level_id}®ion=${props.data.insert.level.region}`;
|
||||
await TGClient.open("web_act_thin", url.toString());
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.tul-card-box {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 8px;
|
||||
border: 1px solid var(--common-shadow-1);
|
||||
border-radius: 4px;
|
||||
margin-bottom: 8px;
|
||||
background: var(--box-bg-1);
|
||||
column-gap: 8px;
|
||||
}
|
||||
|
||||
.tul-cover {
|
||||
max-width: 50%;
|
||||
max-height: 180px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: all 0.5s;
|
||||
|
||||
&:hover {
|
||||
scale: 0.9;
|
||||
}
|
||||
}
|
||||
|
||||
.tul-content {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-family: var(--font-title);
|
||||
}
|
||||
|
||||
.tul-top {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.tul-title {
|
||||
color: var(--tgc-od-red);
|
||||
font-family: var(--font-title);
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.tul-sub {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
color: var(--tgc-od-white);
|
||||
column-gap: 16px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.tul-attach {
|
||||
margin-top: 8px;
|
||||
color: var(--common-text-secondary);
|
||||
font-size: 16px;
|
||||
|
||||
span {
|
||||
color: var(--tgc-od-blue);
|
||||
|
||||
&:first-child {
|
||||
font-family: Genshin, sans-serif;
|
||||
font-size: 20px;
|
||||
line-height: 14px;
|
||||
vertical-align: top;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tul-bottom {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.tul-info {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.tul-info-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 14px;
|
||||
gap: 4px;
|
||||
}
|
||||
</style>
|
||||
52
src/components/viewPost/tp-ugc-tag.vue
Normal file
52
src/components/viewPost/tp-ugc-tag.vue
Normal file
@@ -0,0 +1,52 @@
|
||||
<!-- 含 ugc_master_tag 的 backup_text -->
|
||||
<template>
|
||||
<span class="tut-box">
|
||||
{{ tagName }}
|
||||
</span>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import useAppStore from "@store/app.js";
|
||||
import { str2Color } from "@utils/toolFunc.js";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { computed } from "vue";
|
||||
|
||||
type TpUgcTag = {
|
||||
insert: {
|
||||
backup_text: string;
|
||||
ugc_master_tag: {
|
||||
id: number;
|
||||
name: string;
|
||||
is_available: boolean;
|
||||
};
|
||||
};
|
||||
};
|
||||
type TpUgcTagProps = { data: TpUgcTag };
|
||||
|
||||
const props = defineProps<TpUgcTagProps>();
|
||||
const { theme } = storeToRefs(useAppStore());
|
||||
const tagName = computed<string>(() => props.data.insert.ugc_master_tag.name);
|
||||
const isDarkMode = computed<boolean>(() => theme.value === "dark");
|
||||
const tagColor = computed<string>(() => tag2Color(tagName.value, isDarkMode.value));
|
||||
const bgColor = computed<string>(() => `rgba(${tagColor.value.slice(4, -1)}, 0.18)`);
|
||||
|
||||
function tag2Color(str: string, isDarkMode: boolean = false): string {
|
||||
const adjust = isDarkMode ? 80 : -40;
|
||||
return str2Color(str, adjust);
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.tut-box {
|
||||
display: inline-flex;
|
||||
width: fit-content;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0 6px;
|
||||
border-radius: 12px;
|
||||
margin-right: 4px;
|
||||
background: v-bind(bgColor); /* stylelint-disable-line value-keyword-case */
|
||||
color: v-bind(tagColor); /* stylelint-disable-line value-keyword-case */
|
||||
font-family: var(--font-title);
|
||||
font-size: 12px;
|
||||
gap: 2px;
|
||||
}
|
||||
</style>
|
||||
@@ -23,6 +23,7 @@
|
||||
import showLoading from "@comp/func/loading.js";
|
||||
import useAppStore from "@store/app.js";
|
||||
import { getCurrentWindow } from "@tauri-apps/api/window";
|
||||
import { openUrl } from "@tauri-apps/plugin-opener";
|
||||
import { getImageBuffer, saveCanvasImg, saveImgLocal } from "@utils/TGShare.js";
|
||||
import { getVideoDuration } from "@utils/toolFunc.js";
|
||||
import Artplayer, { type Option } from "artplayer";
|
||||
@@ -106,7 +107,7 @@ onMounted(async () => {
|
||||
name: "download-cover",
|
||||
index: 0,
|
||||
position: "right",
|
||||
html: `<i class="mdi mdi-download"></i>`,
|
||||
html: `<span class="mdi mdi-image-check"></span>`,
|
||||
tooltip: "下载封面",
|
||||
click: async () => {
|
||||
await showLoading.start("正在下载封面", props.data.insert.vod.cover);
|
||||
@@ -117,6 +118,17 @@ onMounted(async () => {
|
||||
await showLoading.end();
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "download-video",
|
||||
index: 0,
|
||||
position: "right",
|
||||
html: `<span class="mdi mdi-video-check"></span>`,
|
||||
tooltip: "下载视频",
|
||||
click: async () => {
|
||||
if (!container.value) return;
|
||||
await openUrl(container.value.url);
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
container.value = new Artplayer(option);
|
||||
|
||||
@@ -73,10 +73,7 @@ const format = defineModel<string>("format", { default: "png" });
|
||||
const bgMode = ref<number>(0); // 0: transparent, 1: black, 2: white
|
||||
const isOriSize = ref<boolean>(false);
|
||||
const buffer = shallowRef<Uint8Array | null>(null);
|
||||
const oriLink = computed<string>(() => {
|
||||
const image = props.image.insert.image;
|
||||
return typeof image === "string" ? image : image.url;
|
||||
});
|
||||
const oriLink = computed<string>(() => miniImgUrl());
|
||||
const showCopy = computed<boolean>(() => {
|
||||
// 只能显示 png/jpg/jpeg/webp 格式的复制按钮
|
||||
return ["png", "jpg", "jpeg", "webp"].includes(format.value.toLowerCase());
|
||||
@@ -124,6 +121,17 @@ async function onDownload(): Promise<void> {
|
||||
await saveCanvasImg(buffer.value, fileName, format.value);
|
||||
await showLoading.end();
|
||||
}
|
||||
|
||||
function miniImgUrl(): string {
|
||||
let url: string;
|
||||
if (typeof props.image.insert.image === "string") {
|
||||
url = props.image.insert.image;
|
||||
} else {
|
||||
url = props.image.insert.image.url;
|
||||
}
|
||||
const link = new URL(url);
|
||||
return `${link.origin}${link.pathname}`;
|
||||
}
|
||||
</script>
|
||||
<style lang="css" scoped>
|
||||
.tpoi-box {
|
||||
|
||||
@@ -23,7 +23,8 @@
|
||||
"Id": 1223,
|
||||
"Name": "诳言掩虚实之迹",
|
||||
"Description": "元素战技<color=#FFD780FF>{LINK#S11222}弈术·千夜一舞{/LINK}</color>的技能等级提高3级。\n至多提升至15级。",
|
||||
"Icon": "UI_Talent_U_Nefer_01"
|
||||
"Icon": "UI_Talent_U_Nefer_01",
|
||||
"ExtraLevel": { "Index": 2, "Level": 3 }
|
||||
},
|
||||
{
|
||||
"Id": 1224,
|
||||
@@ -35,7 +36,8 @@
|
||||
"Id": 1225,
|
||||
"Name": "见机在忽微之间",
|
||||
"Description": "元素爆发<color=#FFD780FF>{LINK#S11225}圣约·真眸幻戏{/LINK}</color>的技能等级提高3级。\n至多提升至15级。",
|
||||
"Icon": "UI_Talent_U_Nefer_02"
|
||||
"Icon": "UI_Talent_U_Nefer_02",
|
||||
"ExtraLevel": { "Index": 9, "Level": 3 }
|
||||
},
|
||||
{
|
||||
"Id": 1226,
|
||||
|
||||
@@ -657,7 +657,7 @@
|
||||
},
|
||||
{
|
||||
"id": 10000122,
|
||||
"contentId": 0,
|
||||
"contentId": 506676,
|
||||
"dropDays": [2, 5, 7],
|
||||
"name": "奈芙尔",
|
||||
"itemType": "character",
|
||||
@@ -2369,7 +2369,7 @@
|
||||
},
|
||||
{
|
||||
"id": 14521,
|
||||
"contentId": 0,
|
||||
"contentId": 506913,
|
||||
"dropDays": [2, 5, 7],
|
||||
"name": "真语秘匣",
|
||||
"itemType": "weapon",
|
||||
@@ -4337,7 +4337,7 @@
|
||||
},
|
||||
{
|
||||
"id": 14434,
|
||||
"contentId": 0,
|
||||
"contentId": 506911,
|
||||
"dropDays": [1, 4, 7],
|
||||
"name": "霜辰",
|
||||
"itemType": "weapon",
|
||||
@@ -4705,7 +4705,7 @@
|
||||
},
|
||||
{
|
||||
"id": 13434,
|
||||
"contentId": 0,
|
||||
"contentId": 506912,
|
||||
"dropDays": [2, 5, 7],
|
||||
"name": "圣祭者的辉杖",
|
||||
"itemType": "weapon",
|
||||
|
||||
@@ -2990,5 +2990,41 @@
|
||||
"postId": "69786055",
|
||||
"up5List": [14521, 11513],
|
||||
"up4List": [11434, 13434, 14434, 12402, 15401]
|
||||
},
|
||||
{
|
||||
"name": "炉边烬影",
|
||||
"version": "6.1",
|
||||
"order": 2,
|
||||
"banner": "https://sdk.hoyoverse.com/upload/ann/2025/11/04/f1799709dd6e2c1d5807382d0bb46246_8044642901403314243_transformed.jpg",
|
||||
"from": "2025-11-11T18:00:00+08:00",
|
||||
"to": "2025-12-02T14:59:00+08:00",
|
||||
"type": 301,
|
||||
"postId": "70352225",
|
||||
"up5List": [10000096],
|
||||
"up4List": [10000108, 10000045, 10000064]
|
||||
},
|
||||
{
|
||||
"name": "陵薮市朝",
|
||||
"version": "6.1",
|
||||
"order": 2,
|
||||
"banner": "https://sdk.hoyoverse.com/upload/ann/2025/11/04/146ff4ba52605431d0086cb5d18c1440_4528556376432618835_transformed.jpg",
|
||||
"from": "2025-11-11T18:00:00+08:00",
|
||||
"to": "2025-12-02T14:59:00+08:00",
|
||||
"type": 400,
|
||||
"postId": "70352227",
|
||||
"up5List": [10000030],
|
||||
"up4List": [10000108, 10000045, 10000064]
|
||||
},
|
||||
{
|
||||
"name": "神铸赋形",
|
||||
"version": "6.1",
|
||||
"order": 2,
|
||||
"banner": "https://sdk.hoyoverse.com/upload/ann/2025/10/27/54cde6bd8912354aa5b358d42a69dd79_6997260099594855541_transformed.jpg",
|
||||
"from": "2025-11-11T18:00:00+08:00",
|
||||
"to": "2025-12-02T14:59:00+08:00",
|
||||
"type": 302,
|
||||
"postId": "70352228",
|
||||
"up5List": [13512, 13504],
|
||||
"up4List": [11427, 12427, 15427, 13401, 14403]
|
||||
}
|
||||
]
|
||||
|
||||
3327
src/data/app/gachaB.json
Normal file
3327
src/data/app/gachaB.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -9,7 +9,7 @@
|
||||
{ "id": 15503, "contentId": 1682, "name": "终末嗟叹之诗", "star": 5, "weapon": "弓" },
|
||||
{ "id": 15502, "contentId": 219, "name": "阿莫斯之弓", "star": 5, "weapon": "弓" },
|
||||
{ "id": 15501, "contentId": 323, "name": "天空之翼", "star": 5, "weapon": "弓" },
|
||||
{ "id": 14521, "contentId": 0, "name": "真语秘匣", "star": 5, "weapon": "法器" },
|
||||
{ "id": 14521, "contentId": 506913, "name": "真语秘匣", "star": 5, "weapon": "法器" },
|
||||
{ "id": 14520, "contentId": 506390, "name": "纺夜天镜", "star": 5, "weapon": "法器" },
|
||||
{ "id": 14519, "contentId": 504739, "name": "溢彩心念", "star": 5, "weapon": "法器" },
|
||||
{ "id": 14518, "contentId": 504503, "name": "寝正月初晴", "star": 5, "weapon": "法器" },
|
||||
@@ -88,7 +88,7 @@
|
||||
{ "id": 15403, "contentId": 177, "name": "祭礼弓", "star": 4, "weapon": "弓" },
|
||||
{ "id": 15402, "contentId": 176, "name": "绝弦", "star": 4, "weapon": "弓" },
|
||||
{ "id": 15401, "contentId": 195, "name": "西风猎弓", "star": 4, "weapon": "弓" },
|
||||
{ "id": 14434, "contentId": 0, "name": "霜辰", "star": 4, "weapon": "法器" },
|
||||
{ "id": 14434, "contentId": 506911, "name": "霜辰", "star": 4, "weapon": "法器" },
|
||||
{ "id": 14433, "contentId": 506375, "name": "乌髓孑灯", "star": 4, "weapon": "法器" },
|
||||
{ "id": 14432, "contentId": 506378, "name": "天光的纺琴", "star": 4, "weapon": "法器" },
|
||||
{ "id": 14431, "contentId": 501965, "name": "木棉之环", "star": 4, "weapon": "法器" },
|
||||
@@ -113,7 +113,7 @@
|
||||
{ "id": 14403, "contentId": 197, "name": "祭礼残章", "star": 4, "weapon": "法器" },
|
||||
{ "id": 14402, "contentId": 192, "name": "流浪乐章", "star": 4, "weapon": "法器" },
|
||||
{ "id": 14401, "contentId": 185, "name": "西风秘典", "star": 4, "weapon": "法器" },
|
||||
{ "id": 13434, "contentId": 0, "name": "圣祭者的辉杖", "star": 4, "weapon": "长柄武器" },
|
||||
{ "id": 13434, "contentId": 506912, "name": "圣祭者的辉杖", "star": 4, "weapon": "长柄武器" },
|
||||
{ "id": 13433, "contentId": 506374, "name": "掘金之锹", "star": 4, "weapon": "长柄武器" },
|
||||
{ "id": 13432, "contentId": 504502, "name": "且住亭御咄", "star": 4, "weapon": "长柄武器" },
|
||||
{ "id": 13431, "contentId": 501966, "name": "虹的行迹", "star": 4, "weapon": "长柄武器" },
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/**
|
||||
* @file src/data/index.ts
|
||||
* @description 数据文件入口
|
||||
* @since Beta v0.8.0
|
||||
* 数据文件入口
|
||||
* @since Beta v0.8.4
|
||||
*/
|
||||
|
||||
import type { Schema } from "ajv";
|
||||
@@ -11,6 +10,7 @@ import achievementSeries from "./app/achievementSeries.json" with { type: "json"
|
||||
import calendar from "./app/calendar.json" with { type: "json" };
|
||||
import character from "./app/character.json" with { type: "json" };
|
||||
import gacha from "./app/gacha.json" with { type: "json" };
|
||||
import gachaB from "./app/gachaB.json" with { type: "json" };
|
||||
import nameCards from "./app/namecard.json" with { type: "json" };
|
||||
import weapon from "./app/weapon.json" with { type: "json" };
|
||||
import arcBirCalendar from "./archive/birth_calendar.json" with { type: "json" };
|
||||
@@ -28,6 +28,7 @@ export const AppAchievementSeriesData: Array<TGApp.App.Achievement.Series> = ach
|
||||
export const AppCalendarData: Array<TGApp.App.Calendar.Item> = calendar;
|
||||
export const AppCharacterData: Array<TGApp.App.Character.WikiBriefInfo> = character;
|
||||
export const AppGachaData: Array<TGApp.App.Gacha.PoolItem> = gacha;
|
||||
export const AppGachaBData: Array<TGApp.App.Gacha.GachaBMeta> = gachaB;
|
||||
export const AppNameCardsData: Array<TGApp.App.NameCard.Item> = nameCards;
|
||||
export const AppWeaponData: Array<TGApp.App.Weapon.WikiBriefInfo> = weapon;
|
||||
// Schema
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* @file src/enum/anno.ts
|
||||
* @description 游戏内公告相关枚举
|
||||
* @since Beta v0.8.0
|
||||
* @since Beta v0.8.4
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -37,17 +37,18 @@ export function getAnnoLangDesc(lang: TGApp.BBS.Announcement.AnnoLangEnum): stri
|
||||
|
||||
/**
|
||||
* @description 公告类型
|
||||
* @since Beta v0.7.7
|
||||
* @since Beta v0.8.4
|
||||
* @const AnnoTypeEnum
|
||||
*/
|
||||
export const AnnoTypeEnum: typeof TGApp.BBS.Announcement.AnnoType = {
|
||||
ACTIVITY: "activity",
|
||||
GAME: "game",
|
||||
UGC: "ugc",
|
||||
};
|
||||
|
||||
/**
|
||||
* @description 获取公告类型描述
|
||||
* @since Beta v0.7.7
|
||||
* @since Beta v0.8.4
|
||||
* @param {TGApp.BBS.Announcement.AnnoTypeEnum} type 公告类型
|
||||
* @return {string} 公告类型描述
|
||||
*/
|
||||
@@ -57,5 +58,7 @@ export function getAnnoTypeDesc(type: TGApp.BBS.Announcement.AnnoTypeEnum): stri
|
||||
return "活动公告";
|
||||
case AnnoTypeEnum.GAME:
|
||||
return "游戏公告";
|
||||
case AnnoTypeEnum.UGC:
|
||||
return "千星奇域";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,3 +25,15 @@ export const AvatarExtResTypeEnum: typeof TGApp.BBS.User.AvatarExtResType = {
|
||||
GIF: 3,
|
||||
PNG: 4,
|
||||
};
|
||||
|
||||
/**
|
||||
* @description 帖子ViewType
|
||||
* @since Beta v0.8.4
|
||||
* @enum PostViewTypeEnum
|
||||
*/
|
||||
export const PostViewTypeEnum: typeof TGApp.BBS.Post.PostViewType = {
|
||||
NORMAL: 1,
|
||||
PIC: 2,
|
||||
VOD: 5,
|
||||
UGC: 7,
|
||||
};
|
||||
|
||||
@@ -1,25 +1,29 @@
|
||||
/**
|
||||
* @file enum/game.ts
|
||||
* @description 游戏相关枚举
|
||||
* 游戏相关枚举
|
||||
* @since Beta v0.8.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @description 服务器类型
|
||||
* 服务器类型
|
||||
* @since Beta v0.8.0
|
||||
* @const GameServerEnum
|
||||
*/
|
||||
export const GameServerEnum: typeof TGApp.Game.Base.ServerType = {
|
||||
/** 国服-官方服 */
|
||||
CN_GF01: "cn_gf01",
|
||||
/** 国服-渠道服 */
|
||||
CN_QD01: "cn_qd01",
|
||||
/** 国际服-美服 */
|
||||
OS_USA: "os_usa",
|
||||
/** 国际服-欧服 */
|
||||
OS_EURO: "os_euro",
|
||||
/** 国际服-亚服 */
|
||||
OS_ASIA: "os_asia",
|
||||
/** 国际服-港澳台服 */
|
||||
OS_CHT: "os_cht",
|
||||
};
|
||||
|
||||
/**
|
||||
* @description 获取公告服务器描述
|
||||
* 获取公告服务器描述
|
||||
* @since Beta v0.8.0
|
||||
* @param {TGApp.Game.Base.ServerTypeEnum} server 公告服务器
|
||||
* @return {string} 公告服务器描述
|
||||
@@ -42,15 +46,20 @@ export function getGameServerDesc(server: TGApp.Game.Base.ServerTypeEnum): strin
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 近期活动活动类型枚举
|
||||
* 近期活动活动类型枚举
|
||||
* @since Beta v0.8.0
|
||||
* @enum ActCalendarTypeEnum
|
||||
*/
|
||||
export const ActCalendarTypeEnum: typeof TGApp.Game.ActCalendar.ActType = {
|
||||
/** 幽境危战 */
|
||||
HardChallenge: "ActTypeHardChallenge",
|
||||
/** 真境剧诗 */
|
||||
RoleCombat: "ActTypeRoleCombat",
|
||||
/** 深渊螺旋 */
|
||||
Tower: "ActTypeTower",
|
||||
/** 双倍活动 */
|
||||
Double: "ActTypeDouble",
|
||||
/** 探索活动 */
|
||||
Explore: "ActTypeExplore",
|
||||
/** 其他活动 */
|
||||
Other: "ActTypeOther",
|
||||
};
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<!-- 深境螺旋 -->
|
||||
<template>
|
||||
<v-app-bar>
|
||||
<template #prepend>
|
||||
@@ -20,19 +21,9 @@
|
||||
<img src="/source/UI/userChallenge.webp" alt="challenge" />
|
||||
<span>幽境危战</span>
|
||||
</v-btn>
|
||||
<v-btn :rounded="true" class="ua-btn" @click="toWiki()">
|
||||
<img src="/source/UI/wikiAbyss.webp" alt="wiki" />
|
||||
<span>深渊数据库</span>
|
||||
</v-btn>
|
||||
</div>
|
||||
</template>
|
||||
<template #append>
|
||||
<div class="uat-hutao">
|
||||
<span>胡桃云账号:</span>
|
||||
<span @click="editHutaoEmail()">{{ hutaoEmail ?? "未设置" }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<template #extension>
|
||||
<div class="uat-acts">
|
||||
<v-btn
|
||||
class="ua-btn"
|
||||
@@ -43,7 +34,7 @@
|
||||
分享
|
||||
</v-btn>
|
||||
<v-btn class="ua-btn" @click="refreshAbyss()" prepend-icon="mdi-refresh">刷新</v-btn>
|
||||
<v-btn class="ua-btn" @click="uploadAbyss()" prepend-icon="mdi-cloud-upload">上传</v-btn>
|
||||
<v-btn class="ua-btn" @click="tryReadAbyss()" prepend-icon="mdi-download">导入</v-btn>
|
||||
<v-btn class="ua-btn" @click="deleteAbyss()" prepend-icon="mdi-delete">删除</v-btn>
|
||||
</div>
|
||||
</template>
|
||||
@@ -107,26 +98,25 @@ import showLoading from "@comp/func/loading.js";
|
||||
import showSnackbar from "@comp/func/snackbar.js";
|
||||
import TuaDetail from "@comp/userAbyss/tua-detail.vue";
|
||||
import TuaOverview from "@comp/userAbyss/tua-overview.vue";
|
||||
import Hutao from "@Hutao/index.js";
|
||||
import recordReq from "@req/recordReq.js";
|
||||
import TSUserAbyss from "@Sqlm/userAbyss.js";
|
||||
import TSUserAvatar from "@Sqlm/userAvatar.js";
|
||||
import useUserStore from "@store/user.js";
|
||||
import { getVersion } from "@tauri-apps/api/app";
|
||||
import { open } from "@tauri-apps/plugin-dialog";
|
||||
import { readTextFile } from "@tauri-apps/plugin-fs";
|
||||
import TGLogger from "@utils/TGLogger.js";
|
||||
import { generateShareImg } from "@utils/TGShare.js";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { computed, onMounted, ref, shallowRef, watch } from "vue";
|
||||
import { onMounted, ref, shallowRef, watch } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
|
||||
const router = useRouter();
|
||||
const { account, cookie, hutaoEmail } = storeToRefs(useUserStore());
|
||||
const { account, cookie } = storeToRefs(useUserStore());
|
||||
const userTab = ref<number>(0);
|
||||
const version = ref<string>();
|
||||
const uidCur = ref<string>();
|
||||
const uidList = shallowRef<Array<string>>();
|
||||
const localAbyss = shallowRef<TGApp.Sqlite.Abyss.TableData[]>([]);
|
||||
const abyssIdList = computed<Array<number>>(() => localAbyss.value.map((abyss) => abyss.id));
|
||||
|
||||
onMounted(async () => {
|
||||
await showLoading.start("正在加载深渊数据");
|
||||
@@ -148,35 +138,10 @@ watch(() => uidCur.value, loadAbyss);
|
||||
async function toCombat(): Promise<void> {
|
||||
await router.push({ name: "真境剧诗" });
|
||||
}
|
||||
|
||||
async function toChallenge(): Promise<void> {
|
||||
await router.push({ name: "幽境危战" });
|
||||
}
|
||||
async function toWiki(): Promise<void> {
|
||||
await router.push({ name: "深渊数据库" });
|
||||
}
|
||||
|
||||
async function editHutaoEmail(): Promise<void> {
|
||||
if (hutaoEmail.value) {
|
||||
const chgCheck = await showDialog.check("是否更改胡桃云账号", `当前账号:${hutaoEmail.value}`);
|
||||
if (!chgCheck) {
|
||||
showSnackbar.cancel("已取消更改胡桃云账号");
|
||||
return;
|
||||
}
|
||||
}
|
||||
const newEmail = await showDialog.input("请输入胡桃云账号", "胡桃云账号", hutaoEmail.value);
|
||||
if (!newEmail) {
|
||||
showSnackbar.cancel("已取消设置胡桃云账号");
|
||||
return;
|
||||
}
|
||||
// 简单验证邮箱格式
|
||||
const mailReg = /^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/;
|
||||
if (!mailReg.test(newEmail)) {
|
||||
showSnackbar.error("邮箱格式错误");
|
||||
return;
|
||||
}
|
||||
hutaoEmail.value = newEmail;
|
||||
showSnackbar.success("已设置胡桃云账号");
|
||||
}
|
||||
|
||||
async function loadAbyss(): Promise<void> {
|
||||
localAbyss.value = [];
|
||||
@@ -262,62 +227,6 @@ async function shareAbyss(): Promise<void> {
|
||||
await TGLogger.Info(`[UserAbyss][shareAbyss][${userTab.value}] 生成深渊数据分享图片成功`);
|
||||
}
|
||||
|
||||
async function uploadAbyss(): Promise<void> {
|
||||
await TGLogger.Info("[UserAbyss][uploadAbyss] 上传深渊数据");
|
||||
const abyssData = localAbyss.value.find((item) => item.id === Math.max(...abyssIdList.value));
|
||||
if (!abyssData) {
|
||||
showSnackbar.warn("未找到深渊数据");
|
||||
await TGLogger.Warn("[UserAbyss][uploadAbyss] 未找到深渊数据");
|
||||
return;
|
||||
}
|
||||
const maxFloor = Number(abyssData.maxFloor.split("-")[0]);
|
||||
if (isNaN(maxFloor) || maxFloor <= 9) {
|
||||
showSnackbar.warn("尚未完成深渊,请完成深渊后重试!");
|
||||
await TGLogger.Warn(`[UserAbyss][uploadAbyss] 尚未完成深渊 ${abyssData.maxFloor}`);
|
||||
return;
|
||||
}
|
||||
const startTime = new Date(abyssData.startTime).getTime();
|
||||
const endTime = new Date(abyssData.endTime).getTime();
|
||||
const nowTime = new Date().getTime();
|
||||
if (nowTime < startTime || nowTime > endTime) {
|
||||
showSnackbar.warn("非最新深渊数据,请刷新深渊数据后重试!");
|
||||
await TGLogger.Warn("[UserAbyss][uploadAbyss] 非最新深渊数据");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await showLoading.start(`正在上传${account.value.gameUid}的深渊数据`, `期数:${abyssData.id}`);
|
||||
const transAbyss = Hutao.Abyss.utils.transData(abyssData);
|
||||
if (hutaoEmail.value) transAbyss.ReservedUserName = hutaoEmail.value;
|
||||
await showLoading.update("正在获取角色数据");
|
||||
const roles = await TSUserAvatar.getAvatars(Number(account.value.gameUid));
|
||||
if (!roles) {
|
||||
await showLoading.end();
|
||||
showSnackbar.warn("未找到角色数据");
|
||||
return;
|
||||
}
|
||||
await showLoading.update("正在转换角色数据");
|
||||
transAbyss.Avatars = Hutao.Abyss.utils.transAvatars(roles);
|
||||
await showLoading.update("正在上传深渊数据");
|
||||
const res = await Hutao.Abyss.upload(transAbyss);
|
||||
if (res.retcode !== 0) {
|
||||
showSnackbar.error(`[${res.retcode}]${res.message}`);
|
||||
await TGLogger.Error("[UserAbyss][uploadAbyss] 上传深渊数据失败");
|
||||
await TGLogger.Error(`[UserAbyss][uploadAbyss] ${res.retcode} ${res.message}`);
|
||||
return;
|
||||
}
|
||||
showSnackbar.success(res.message ?? "上传深渊数据成功");
|
||||
await TGLogger.Info("[UserAbyss][uploadAbyss] 上传深渊数据成功");
|
||||
await TGLogger.Info(`[${res.retcode}] ${res.message}`);
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
showSnackbar.error(e.message);
|
||||
await TGLogger.Error("[UserAbyss][uploadAbyss] 上传深渊数据失败");
|
||||
await TGLogger.Error(`[UserAbyss][uploadAbyss] ${e.message}`);
|
||||
}
|
||||
}
|
||||
await showLoading.end();
|
||||
}
|
||||
|
||||
async function deleteAbyss(): Promise<void> {
|
||||
if (uidCur.value === undefined || uidCur.value === "") {
|
||||
showSnackbar.warn("未选择游戏UID");
|
||||
@@ -337,6 +246,47 @@ async function deleteAbyss(): Promise<void> {
|
||||
else uidCur.value = undefined;
|
||||
await loadAbyss();
|
||||
}
|
||||
|
||||
/**
|
||||
* 尝试读取胡桃工具箱导出的深渊数据
|
||||
* @since Beta v0.8.6
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
async function tryReadAbyss(): Promise<void> {
|
||||
const file = await open({
|
||||
multiple: false,
|
||||
title: "选择胡桃工具箱导出的深渊数据文件",
|
||||
filters: [{ name: "JSON 文件", extensions: ["json"] }],
|
||||
directory: false,
|
||||
});
|
||||
if (file === null) {
|
||||
showSnackbar.cancel("已取消文件选择");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await showLoading.start("正在导入深渊数据文件", file);
|
||||
const fileData = JSON.parse(await readTextFile(file));
|
||||
if (!Array.isArray(fileData)) {
|
||||
await showLoading.end();
|
||||
showSnackbar.warn("文件数据格式错误");
|
||||
return;
|
||||
}
|
||||
// TODO:数据结构
|
||||
for (const item of fileData) {
|
||||
await showLoading.update(`Uid: ${item["uid"]},ScheduleId: ${item["schedule_id"]}`);
|
||||
await TSUserAbyss.saveAbyss(item["uid"], item["data"]);
|
||||
}
|
||||
await showLoading.end();
|
||||
showSnackbar.success(`成功导入 ${fileData.length} 条深渊数据,即将刷新页面`);
|
||||
await new Promise<void>((resolve) => setTimeout(resolve, 1000));
|
||||
window.location.reload();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
await TGLogger.Error(`[UserAbyss][tryReadAbyss] 导入深渊数据失败: ${e}`);
|
||||
await showLoading.end();
|
||||
showSnackbar.error("导入深渊数据失败,请检查文件格式是否正确");
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="css" scoped>
|
||||
.uat-left {
|
||||
@@ -361,21 +311,6 @@ async function deleteAbyss(): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
.uat-hutao {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 12px;
|
||||
font-family: var(--font-text);
|
||||
font-size: 16px;
|
||||
|
||||
:last-child {
|
||||
color: var(--tgc-pink-1);
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.uat-acts {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -404,7 +339,7 @@ async function deleteAbyss(): Promise<void> {
|
||||
|
||||
.ua-box {
|
||||
display: flex;
|
||||
height: calc(100vh - 144px);
|
||||
height: calc(100vh - 96px);
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
border: 1px solid var(--common-shadow-2);
|
||||
@@ -470,7 +405,7 @@ async function deleteAbyss(): Promise<void> {
|
||||
.uaw-o-box {
|
||||
display: grid;
|
||||
width: 100%;
|
||||
grid-gap: 8px;
|
||||
gap: 8px;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
|
||||
|
||||
@@ -50,6 +50,9 @@
|
||||
分享
|
||||
</v-btn>
|
||||
<v-btn class="ucp-btn" @click="refreshChallenge()" prepend-icon="mdi-refresh">刷新</v-btn>
|
||||
<v-btn class="ucp-btn" @click="tryReadChallenge()" prepend-icon="mdi-download">
|
||||
导入
|
||||
</v-btn>
|
||||
<v-btn class="ucp-btn" @click="deleteChallenge()" prepend-icon="mdi-delete">删除</v-btn>
|
||||
</div>
|
||||
<div class="pop-list">
|
||||
@@ -123,6 +126,8 @@ import recordReq from "@req/recordReq.js";
|
||||
import TSUserChallenge from "@Sqlm/userChallenge.js";
|
||||
import useUserStore from "@store/user.js";
|
||||
import { getVersion } from "@tauri-apps/api/app";
|
||||
import { open } from "@tauri-apps/plugin-dialog";
|
||||
import { readTextFile } from "@tauri-apps/plugin-fs";
|
||||
import TGLogger from "@utils/TGLogger.js";
|
||||
import { generateShareImg } from "@utils/TGShare.js";
|
||||
import { storeToRefs } from "pinia";
|
||||
@@ -337,6 +342,47 @@ async function refreshPopList(hint: boolean = true): Promise<void> {
|
||||
`已刷新 ${getGameServerDesc(server.value)} 的 ${popList.value.length} 位赋光之人`,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 尝试读取胡桃工具箱导出的危战数据
|
||||
* @since Beta v0.8.6
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
async function tryReadChallenge(): Promise<void> {
|
||||
const file = await open({
|
||||
multiple: false,
|
||||
title: "选择胡桃工具箱导出的危战数据文件",
|
||||
filters: [{ name: "JSON 文件", extensions: ["json"] }],
|
||||
directory: false,
|
||||
});
|
||||
if (file === null) {
|
||||
showSnackbar.cancel("已取消文件选择");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await showLoading.start("正在导入危战数据文件", file);
|
||||
const fileData = JSON.parse(await readTextFile(file));
|
||||
if (!Array.isArray(fileData)) {
|
||||
await showLoading.end();
|
||||
showSnackbar.warn("文件数据格式错误");
|
||||
return;
|
||||
}
|
||||
// TODO:数据结构
|
||||
for (const item of fileData) {
|
||||
await showLoading.update(`Uid: ${item["uid"]},ScheduleId: ${item["schedule_id"]}`);
|
||||
await TSUserChallenge.saveChallenge(item["uid"], item["data"]);
|
||||
}
|
||||
await showLoading.end();
|
||||
showSnackbar.success(`成功导入 ${fileData.length} 条危战数据,即将刷新页面`);
|
||||
await new Promise<void>((resolve) => setTimeout(resolve, 1000));
|
||||
window.location.reload();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
await TGLogger.Error(`[UserChallenge][tryReadChallenge] 导入危战数据失败: ${e}`);
|
||||
await showLoading.end();
|
||||
showSnackbar.error("导入危战数据失败,请检查文件格式是否正确");
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.ucp-top-prepend {
|
||||
|
||||
@@ -21,10 +21,6 @@
|
||||
<img src="/source/UI/userChallenge.webp" alt="challenge" />
|
||||
<span>幽境危战</span>
|
||||
</v-btn>
|
||||
<v-btn :rounded="true" class="uc-btn" @click="loadWiki()">
|
||||
<img src="/source/UI/wikiAbyss.webp" alt="abyss" />
|
||||
<span>统计数据</span>
|
||||
</v-btn>
|
||||
</div>
|
||||
</template>
|
||||
<template #append>
|
||||
@@ -41,13 +37,8 @@
|
||||
<v-btn class="uc-btn" @click="refreshCombat()" :rounded="true" prepend-icon="mdi-refresh">
|
||||
刷新
|
||||
</v-btn>
|
||||
<v-btn
|
||||
class="uc-btn"
|
||||
@click="uploadCombat()"
|
||||
:rounded="true"
|
||||
prepend-icon="mdi-cloud-upload"
|
||||
>
|
||||
上传
|
||||
<v-btn class="uc-btn" @click="tryReadCombat()" :rounded="true" prepend-icon="mdi-download">
|
||||
导入
|
||||
</v-btn>
|
||||
<v-btn class="uc-btn" @click="deleteCombat()" :rounded="true" prepend-icon="mdi-delete">
|
||||
删除
|
||||
@@ -93,7 +84,6 @@
|
||||
<span>暂无数据,请尝试刷新</span>
|
||||
</div>
|
||||
</div>
|
||||
<TucOverlay v-model="showData" :data="cloudCombat" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import TSubLine from "@comp/app/t-subline.vue";
|
||||
@@ -101,30 +91,27 @@ import showDialog from "@comp/func/dialog.js";
|
||||
import showLoading from "@comp/func/loading.js";
|
||||
import showSnackbar from "@comp/func/snackbar.js";
|
||||
import TucAvatars from "@comp/userCombat/tuc-avatars.vue";
|
||||
import TucOverlay from "@comp/userCombat/tuc-overlay.vue";
|
||||
import TucOverview from "@comp/userCombat/tuc-overview.vue";
|
||||
import TucRound from "@comp/userCombat/tuc-round.vue";
|
||||
import Hutao from "@Hutao/index.js";
|
||||
import recordReq from "@req/recordReq.js";
|
||||
import TSUserCombat from "@Sqlm/userCombat.js";
|
||||
import useUserStore from "@store/user.js";
|
||||
import { getVersion } from "@tauri-apps/api/app";
|
||||
import { open } from "@tauri-apps/plugin-dialog";
|
||||
import { readTextFile } from "@tauri-apps/plugin-fs";
|
||||
import TGLogger from "@utils/TGLogger.js";
|
||||
import { generateShareImg } from "@utils/TGShare.js";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { computed, onMounted, ref, shallowRef, watch } from "vue";
|
||||
import { onMounted, ref, shallowRef, watch } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
|
||||
const router = useRouter();
|
||||
const { account, cookie } = storeToRefs(useUserStore());
|
||||
const userTab = ref<number>(0);
|
||||
const showData = ref<boolean>(false);
|
||||
const version = ref<string>();
|
||||
const uidCur = ref<string>();
|
||||
const uidList = shallowRef<Array<string>>();
|
||||
const localCombat = shallowRef<Array<TGApp.Sqlite.Combat.SingleTable>>([]);
|
||||
const cloudCombat = shallowRef<TGApp.Plugins.Hutao.Combat.Data>();
|
||||
const combatIdList = computed<Array<number>>(() => localCombat.value.map((combat) => combat.id));
|
||||
|
||||
onMounted(async () => {
|
||||
await showLoading.start("正在加载剧诗数据");
|
||||
@@ -160,16 +147,6 @@ async function loadCombat(): Promise<void> {
|
||||
if (localCombat.value.length > 0) userTab.value = localCombat.value[0].id;
|
||||
}
|
||||
|
||||
async function loadWiki(): Promise<void> {
|
||||
await showLoading.start("正在加载统计数据");
|
||||
const res = await Hutao.Combat.data();
|
||||
if (res === undefined) showSnackbar.error("未获取到剧诗数据");
|
||||
else cloudCombat.value = <TGApp.Plugins.Hutao.Combat.Data>res;
|
||||
await showLoading.end();
|
||||
showSnackbar.success("成功获取统计数据");
|
||||
showData.value = true;
|
||||
}
|
||||
|
||||
async function refreshCombat(): Promise<void> {
|
||||
if (!cookie.value) {
|
||||
showSnackbar.error("未登录");
|
||||
@@ -238,49 +215,6 @@ async function shareCombat(): Promise<void> {
|
||||
await TGLogger.Info(`[UserCombat][shareCombat][${userTab.value}] 生成剧诗数据分享图片成功`);
|
||||
}
|
||||
|
||||
async function uploadCombat(): Promise<void> {
|
||||
await TGLogger.Info("[UserCombat][uploadCombat] 上传剧诗数据");
|
||||
const combatData = localCombat.value.find((item) => item.id === Math.max(...combatIdList.value));
|
||||
if (!combatData) {
|
||||
showSnackbar.error("未找到剧诗数据");
|
||||
await TGLogger.Warn("[UserCombat][uploadCombat] 未找到深渊数据");
|
||||
return;
|
||||
}
|
||||
if (!combatData.hasDetailData) {
|
||||
showSnackbar.error("未获取到详情数据");
|
||||
await TGLogger.Warn(`[UserCombat][uploadCombat] 未获取到详细数据`);
|
||||
return;
|
||||
}
|
||||
const startTime = new Date(combatData.startTime).getTime();
|
||||
const endTime = new Date(combatData.endTime).getTime();
|
||||
const nowTime = new Date().getTime();
|
||||
if (nowTime < startTime || nowTime > endTime) {
|
||||
showSnackbar.warn("非最新剧诗数据,请刷新剧诗数据后重试!");
|
||||
await TGLogger.Warn("[UserCombat][uploadCombat] 非最新剧诗数据");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await showLoading.start("正在上传剧诗数据");
|
||||
const transCombat = Hutao.Combat.trans(combatData);
|
||||
const res = await Hutao.Combat.upload(transCombat);
|
||||
if (res.retcode === 0) {
|
||||
showSnackbar.success(res.message ?? "上传剧诗数据成功");
|
||||
await TGLogger.Info("[UserCombat][uploadCombat] 上传剧诗数据成功");
|
||||
} else {
|
||||
showSnackbar.error(`[${res.retcode}]${res.message}`);
|
||||
await TGLogger.Error("[UserCombat][uploadCombat] 上传剧诗数据失败");
|
||||
await TGLogger.Error(`[UserCombat][uploadCombat] ${res.retcode} ${res.message}`);
|
||||
}
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
showSnackbar.error(e.message);
|
||||
await TGLogger.Error("[UserCombat][uploadCombat] 上传剧诗数据失败");
|
||||
await TGLogger.Error(`[UserCombat][uploadCombat] ${e.message}`);
|
||||
}
|
||||
}
|
||||
await showLoading.end();
|
||||
}
|
||||
|
||||
async function deleteCombat(): Promise<void> {
|
||||
if (uidCur.value === undefined || uidCur.value === "") {
|
||||
showSnackbar.error("未找到符合条件的数据!");
|
||||
@@ -301,6 +235,47 @@ async function deleteCombat(): Promise<void> {
|
||||
await loadCombat();
|
||||
await showLoading.end();
|
||||
}
|
||||
|
||||
/**
|
||||
* 尝试读取胡桃工具箱导出的剧诗数据
|
||||
* @since Beta v0.8.6
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function tryReadCombat(): Promise<void> {
|
||||
const file = await open({
|
||||
multiple: false,
|
||||
title: "选择胡桃工具箱导出的剧诗数据文件",
|
||||
filters: [{ name: "JSON 文件", extensions: ["json"] }],
|
||||
directory: false,
|
||||
});
|
||||
if (file === null) {
|
||||
showSnackbar.cancel("已取消文件选择");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await showLoading.start("正在导入剧诗数据文件", file);
|
||||
const fileData = JSON.parse(await readTextFile(file));
|
||||
if (!Array.isArray(fileData)) {
|
||||
await showLoading.end();
|
||||
showSnackbar.warn("文件数据格式错误");
|
||||
return;
|
||||
}
|
||||
// TODO:数据结构
|
||||
for (const item of fileData) {
|
||||
await showLoading.update(`Uid: ${item["uid"]},ScheduleId: ${item["schedule_id"]}`);
|
||||
await TSUserCombat.saveCombat(item["uid"], item["data"]);
|
||||
}
|
||||
await showLoading.end();
|
||||
showSnackbar.success(`成功导入 ${fileData.length} 条剧诗数据,即将刷新页面`);
|
||||
await new Promise<void>((resolve) => setTimeout(resolve, 1000));
|
||||
window.location.reload();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
await TGLogger.Error(`[UserCombat][tryReadCombat] 导入剧诗数据失败: ${e}`);
|
||||
await showLoading.end();
|
||||
showSnackbar.error("导入剧诗数据失败,请检查文件格式是否正确");
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.uct-left {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<!-- 祈愿记录页面 -->
|
||||
<template>
|
||||
<v-app-bar>
|
||||
<template #prepend>
|
||||
@@ -12,6 +13,7 @@
|
||||
variant="outlined"
|
||||
label="游戏UID"
|
||||
/>
|
||||
<img src="/icon/nation/千星奇域.webp" alt="byd" @click="toBeyond()" title="千星奇域" />
|
||||
</div>
|
||||
</template>
|
||||
<template #extension>
|
||||
@@ -40,6 +42,7 @@
|
||||
<v-tab value="echarts">图表概览</v-tab>
|
||||
<v-tab value="table">数据表格</v-tab>
|
||||
<v-tab value="history">过往祈愿</v-tab>
|
||||
<v-tab value="iframe" v-if="isLogin">祈愿详情</v-tab>
|
||||
</v-tabs>
|
||||
<v-window v-model="tab" class="gacha-window">
|
||||
<v-window-item value="overview" class="gacha-window-item">
|
||||
@@ -54,6 +57,9 @@
|
||||
<v-window-item value="history" class="gacha-window-item">
|
||||
<gro-history />
|
||||
</v-window-item>
|
||||
<v-window-item value="iframe" class="gacha-window-item">
|
||||
<gro-iframe mode="normal" />
|
||||
</v-window-item>
|
||||
</v-window>
|
||||
</div>
|
||||
<UgoUid v-model="ovShow" :mode="ovMode" />
|
||||
@@ -64,12 +70,14 @@ import showLoading from "@comp/func/loading.js";
|
||||
import showSnackbar from "@comp/func/snackbar.js";
|
||||
import GroEcharts from "@comp/userGacha/gro-echarts.vue";
|
||||
import GroHistory from "@comp/userGacha/gro-history.vue";
|
||||
import GroIframe from "@comp/userGacha/gro-iframe.vue";
|
||||
import GroOverview from "@comp/userGacha/gro-overview.vue";
|
||||
import GroTable from "@comp/userGacha/gro-table.vue";
|
||||
import UgoUid from "@comp/userGacha/ugo-uid.vue";
|
||||
import hk4eReq from "@req/hk4eReq.js";
|
||||
import takumiReq from "@req/takumiReq.js";
|
||||
import TSUserGacha from "@Sqlm/userGacha.js";
|
||||
import useAppStore from "@store/app.js";
|
||||
import useUserStore from "@store/user.js";
|
||||
import { path } from "@tauri-apps/api";
|
||||
import { open, save } from "@tauri-apps/plugin-dialog";
|
||||
@@ -77,17 +85,22 @@ import TGLogger from "@utils/TGLogger.js";
|
||||
import { exportUigfData, readUigfData, verifyUigfData } from "@utils/UIGF.js";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { onMounted, ref, shallowRef, watch } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
|
||||
import { AppCharacterData, AppWeaponData } from "@/data/index.js";
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const { isLogin } = storeToRefs(useAppStore());
|
||||
const { account, cookie } = storeToRefs(useUserStore());
|
||||
|
||||
const authkey = ref<string>("");
|
||||
const uidCur = ref<string>();
|
||||
const tab = ref<string>("overview");
|
||||
const ovShow = ref<boolean>(false);
|
||||
const ovMode = ref<"export" | "import">("import");
|
||||
const selectItem = shallowRef<Array<string>>([]);
|
||||
const gachaListCur = shallowRef<Array<TGApp.Sqlite.GachaRecords.SingleTable>>([]);
|
||||
const gachaListCur = shallowRef<Array<TGApp.Sqlite.GachaRecords.TableGacha>>([]);
|
||||
|
||||
onMounted(async () => {
|
||||
await showLoading.start("正在加载祈愿数据", "正在获取祈愿 UID 列表");
|
||||
@@ -122,6 +135,10 @@ watch(
|
||||
},
|
||||
);
|
||||
|
||||
async function toBeyond(): Promise<void> {
|
||||
await router.push({ name: "千星奇域祈愿记录" });
|
||||
}
|
||||
|
||||
// 刷新按钮点击事件
|
||||
async function confirmRefresh(force: boolean): Promise<void> {
|
||||
await TGLogger.Info(`[UserGacha][${account.value.gameUid}][confirmRefresh] 刷新祈愿数据`);
|
||||
@@ -376,6 +393,10 @@ async function deleteGacha(): Promise<void> {
|
||||
img {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
|
||||
&:last-child {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
span {
|
||||
@@ -416,6 +437,12 @@ async function deleteGacha(): Promise<void> {
|
||||
height: 40px;
|
||||
color: var(--box-text-4);
|
||||
font-family: var(--font-title);
|
||||
|
||||
img {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.gacha-window {
|
||||
|
||||
341
src/pages/User/GachaB.vue
Normal file
341
src/pages/User/GachaB.vue
Normal file
@@ -0,0 +1,341 @@
|
||||
<!-- 千星奇域祈愿记录页面 -->
|
||||
<template>
|
||||
<v-app-bar>
|
||||
<template #prepend>
|
||||
<div class="gb-top-title">
|
||||
<img src="/icon/nation/千星奇域.webp" alt="gacha" />
|
||||
<span>祈愿记录</span>
|
||||
<v-select
|
||||
:hide-details="true"
|
||||
density="compact"
|
||||
v-model="uidCur"
|
||||
:items="selectItem"
|
||||
variant="outlined"
|
||||
label="游戏UID"
|
||||
/>
|
||||
<img src="/source/UI/userGacha.webp" alt="byd" @click="toGacha()" title="祈愿" />
|
||||
</div>
|
||||
</template>
|
||||
<template #extension>
|
||||
<div class="gb-top-btns">
|
||||
<v-btn prepend-icon="mdi-refresh" class="gb-top-btn" @click="confirmRefresh(false)">
|
||||
增量刷新
|
||||
</v-btn>
|
||||
<v-btn prepend-icon="mdi-refresh" class="gb-top-btn" @click="confirmRefresh(true)">
|
||||
全量刷新
|
||||
</v-btn>
|
||||
<v-btn prepend-icon="mdi-delete" class="gb-top-btn" @click="deleteGacha()">删除</v-btn>
|
||||
</div>
|
||||
</template>
|
||||
</v-app-bar>
|
||||
<div class="gb-container">
|
||||
<v-tabs v-model="tab" align-tabs="start" class="gb-tab" density="compact">
|
||||
<v-tab value="overview">数据概览</v-tab>
|
||||
<v-tab value="table">数据表格</v-tab>
|
||||
<v-tab value="iframe" v-if="isLogin">祈愿详情</v-tab>
|
||||
</v-tabs>
|
||||
<v-window v-model="tab" class="gb-window">
|
||||
<v-window-item value="overview" class="gb-window-item">
|
||||
<gbr-overview v-model="gachaListCur" />
|
||||
</v-window-item>
|
||||
<v-window-item value="table" class="gb-window-item">
|
||||
<gbr-table v-model="gachaListCur" />
|
||||
</v-window-item>
|
||||
<v-window-item value="iframe" class="gb-window-item">
|
||||
<gro-iframe mode="beyond" />
|
||||
</v-window-item>
|
||||
</v-window>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import showDialog from "@comp/func/dialog.js";
|
||||
import showLoading from "@comp/func/loading.js";
|
||||
import showSnackbar from "@comp/func/snackbar.js";
|
||||
import GbrOverview from "@comp/userGacha/gbr-overview.vue";
|
||||
import GbrTable from "@comp/userGacha/gbr-table.vue";
|
||||
import GroIframe from "@comp/userGacha/gro-iframe.vue";
|
||||
import hk4eReq from "@req/hk4eReq.js";
|
||||
import takumiReq from "@req/takumiReq.js";
|
||||
import TSUserGachaB from "@Sqlm/userGachaB.js";
|
||||
import useAppStore from "@store/app.js";
|
||||
import useUserStore from "@store/user.js";
|
||||
import TGLogger from "@utils/TGLogger.js";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { onMounted, ref, shallowRef, watch } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
|
||||
const router = useRouter();
|
||||
const { isLogin } = storeToRefs(useAppStore());
|
||||
const { account, cookie } = storeToRefs(useUserStore());
|
||||
|
||||
const authkey = ref<string>("");
|
||||
const uidCur = ref<string>();
|
||||
const tab = ref<string>("overview");
|
||||
const selectItem = shallowRef<Array<string>>([]);
|
||||
const gachaListCur = shallowRef<Array<TGApp.Sqlite.GachaRecords.TableGachaB>>([]);
|
||||
|
||||
onMounted(async () => {
|
||||
await TGLogger.Info("[UserGachaB][onMounted] 进入千星奇域页面");
|
||||
await showLoading.start("正在加载千星奇域祈愿数据", "正在获取UID列表...");
|
||||
selectItem.value = await TSUserGachaB.getUidList();
|
||||
await TGLogger.Info(`[UserGachaB][onMounted] 总UID数:${selectItem.value.length}`);
|
||||
if (selectItem.value.length === 0) {
|
||||
await showLoading.end();
|
||||
await TGLogger.Info("[UserGachaB][onMounted] UID列表为空");
|
||||
return;
|
||||
}
|
||||
uidCur.value = selectItem.value[0];
|
||||
await TGLogger.Info(`[UserGachaB][onMounted] 当前UID:${uidCur.value}`);
|
||||
await showLoading.update(`UID: ${uidCur.value}`);
|
||||
gachaListCur.value = await TSUserGachaB.getGachaRecords(uidCur.value);
|
||||
await TGLogger.Info(`[UserGachaB][onMounted] 祈愿记录数: ${gachaListCur.value.length}`);
|
||||
await showLoading.end();
|
||||
showSnackbar.success(`加载完成,共 ${gachaListCur.value.length} 条祈愿记录`);
|
||||
});
|
||||
|
||||
watch(
|
||||
() => uidCur.value,
|
||||
async (newUid) => {
|
||||
if (!newUid) return;
|
||||
gachaListCur.value = await TSUserGachaB.getGachaRecords(newUid);
|
||||
showSnackbar.success(`成功获取 ${gachaListCur.value.length} 条祈愿数据`);
|
||||
await TGLogger.Info(
|
||||
`[UserGachaB][${newUid}][watch] 成功获取 ${gachaListCur.value.length} 条祈愿数据`,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* 跳转至祈愿页面
|
||||
*/
|
||||
async function toGacha(): Promise<void> {
|
||||
await router.push({ name: "祈愿记录" });
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新祈愿数据
|
||||
* @param {boolean} force 是否强制刷新
|
||||
* @return {Promise<void>} void
|
||||
*/
|
||||
async function confirmRefresh(force: boolean): Promise<void> {
|
||||
if (!isLogin.value || !cookie.value) {
|
||||
showSnackbar.warn("请先登录账号");
|
||||
return;
|
||||
}
|
||||
await TGLogger.Info(`[UserGachaB][${account.value.gameUid}] 开始刷新千星奇域祈愿数据`);
|
||||
if (uidCur.value && uidCur.value !== account.value.gameUid) {
|
||||
const switchCheck = await showDialog.check(
|
||||
"是否切换游戏账户",
|
||||
`确认则尝试切换至 ${uidCur.value}`,
|
||||
);
|
||||
if (switchCheck) {
|
||||
await useUserStore().switchGameAccount(uidCur.value);
|
||||
await confirmRefresh(force);
|
||||
return;
|
||||
}
|
||||
const freshCheck = await showDialog.check(
|
||||
"确定刷新?",
|
||||
`用户${account.value.gameUid}与当前UID${uidCur.value}不一致`,
|
||||
);
|
||||
if (!freshCheck) {
|
||||
showSnackbar.cancel("已取消祈愿数据刷新");
|
||||
return;
|
||||
}
|
||||
}
|
||||
await showLoading.start(`正在刷新祈愿数据`, `UID:${account.value.gameUid},正在获取 authkey`);
|
||||
const authkeyRes = await takumiReq.bind.authKey(cookie.value, account.value);
|
||||
if (typeof authkeyRes === "string") {
|
||||
authkey.value = authkeyRes;
|
||||
await TGLogger.Info(`[UserGacha][${account.value.gameUid}] 成功获取 authkey`);
|
||||
} else {
|
||||
showSnackbar.error("获取 authkey 失败");
|
||||
await TGLogger.Error(`[UserGacha][${account.value.gameUid}] 获取 authkey 失败`);
|
||||
await TGLogger.Error(
|
||||
`[UserGachaB][${account.value.gameUid}] ${authkeyRes.retcode} ${authkeyRes.message}`,
|
||||
);
|
||||
await showLoading.end();
|
||||
return;
|
||||
}
|
||||
await refreshGachaPool("1000", "常驻颂愿", force);
|
||||
await refreshGachaPool("20011", "活动颂愿·男", force);
|
||||
await refreshGachaPool("20012", "活动颂愿·男2", force);
|
||||
await refreshGachaPool("20021", "活动颂愿·女", force);
|
||||
await refreshGachaPool("20022", "活动颂愿·女2", force);
|
||||
await showLoading.end();
|
||||
await TGLogger.Info(`[UserGacha][${account.value.gameUid}] 刷新祈愿数据完成`);
|
||||
showSnackbar.success("祈愿数据刷新完成,即将刷新页面");
|
||||
await new Promise<void>((resolve) => setTimeout(resolve, 1500));
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新指定祈愿池数据
|
||||
* @param {string} gachaType 祈愿池类型
|
||||
* @param {string} gachaName 祈愿池名称
|
||||
* @param {boolean} force 是否强制刷新
|
||||
* @return {Promise<void>} void
|
||||
*/
|
||||
async function refreshGachaPool(
|
||||
gachaType: string,
|
||||
gachaName: string,
|
||||
force: boolean,
|
||||
): Promise<void> {
|
||||
let endId = "0";
|
||||
let reqId = "0";
|
||||
let page = 0;
|
||||
await showLoading.start(`正在刷新${gachaName}数据`, "");
|
||||
if (!force) {
|
||||
endId = (await TSUserGachaB.getGachaCheck(account.value.gameUid, gachaType)) ?? "0";
|
||||
}
|
||||
while (true) {
|
||||
page++;
|
||||
const gachaRes = await hk4eReq.gachaB(authkey.value, gachaType, reqId);
|
||||
if (!Array.isArray(gachaRes)) {
|
||||
showSnackbar.error(`[${gachaType}][${gachaRes.retcode}] ${gachaRes.message}`);
|
||||
await TGLogger.Error(
|
||||
`[UserGachaB][${account.value.gameUid}][refreshGachaPool] 获取祈愿数据失败`,
|
||||
);
|
||||
await TGLogger.Error(
|
||||
`[UserGachaB][${account.value.gameUid}][refreshGachaPool] ${gachaRes.retcode} ${gachaRes.message}`,
|
||||
);
|
||||
await new Promise<void>((resolve) => setTimeout(resolve, 1000));
|
||||
break;
|
||||
}
|
||||
if (gachaRes.length === 0) {
|
||||
// if (force) {
|
||||
// await showLoading.update(`正在清理${label}数据`);
|
||||
// if (gachaDataMap) {
|
||||
// await TSUserGacha.cleanGachaRecords(account.value.gameUid, type, gachaDataMap);
|
||||
// }
|
||||
// }
|
||||
break;
|
||||
}
|
||||
if (force) await showLoading.update(`[${gachaName}] 第${page}页,${gachaRes.length}条`);
|
||||
for (const item of gachaRes) {
|
||||
if (!force) {
|
||||
await showLoading.update(`[${item.item_type}][${item.time}] ${item.item_name}`);
|
||||
}
|
||||
if (force) {
|
||||
// if (!gachaDataMap) gachaDataMap = {};
|
||||
// if (!gachaDataMap[item.time]) gachaDataMap[item.time] = [];
|
||||
// gachaDataMap[item.time].push(item.id.toString());
|
||||
}
|
||||
}
|
||||
await TSUserGachaB.insertGachaList(gachaRes);
|
||||
if (!force && gachaRes.some((i) => i.id.toString() === endId.toString())) break;
|
||||
reqId = gachaRes[gachaRes.length - 1].id.toString();
|
||||
if (force) await new Promise<void>((resolve) => setTimeout(resolve, 1000));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除当前UID的祈愿数据
|
||||
* @return {Promise<void>} void
|
||||
*/
|
||||
async function deleteGacha(): Promise<void> {
|
||||
if (gachaListCur.value.length === 0 || !uidCur.value) {
|
||||
showSnackbar.error("暂无祈愿数据");
|
||||
return;
|
||||
}
|
||||
await TGLogger.Info(`[UserGachaB][${uidCur.value}][deleteGacha] 删除祈愿数据`);
|
||||
const delCheck = await showDialog.check(
|
||||
"确定删除祈愿数据?",
|
||||
`UID:${uidCur.value},共 ${gachaListCur.value.length} 条数据`,
|
||||
);
|
||||
if (!delCheck) {
|
||||
showSnackbar.cancel("已取消祈愿数据删除");
|
||||
await TGLogger.Info(`[UserGachaB][${uidCur.value}][deleteGacha] 已取消祈愿数据删除`);
|
||||
return;
|
||||
}
|
||||
const uidList = await TSUserGachaB.getUidList();
|
||||
if (uidList.length <= 1) {
|
||||
const forceCheck = await showDialog.check("删除后数据库将为空,确定删除?");
|
||||
if (!forceCheck) {
|
||||
showSnackbar.cancel("已取消祈愿数据删除");
|
||||
return;
|
||||
}
|
||||
}
|
||||
await showLoading.start("正在删除祈愿数据", `UID:${uidCur.value}`);
|
||||
await TSUserGachaB.deleteRecords(uidCur.value);
|
||||
await showLoading.end();
|
||||
showSnackbar.success(`已成功删除 ${uidCur.value} 的祈愿数据,即将刷新页面`);
|
||||
await TGLogger.Info(
|
||||
`[UserGachaB][${uidCur.value}][deleteGacha] 成功删除 ${gachaListCur.value.length} 条祈愿数据`,
|
||||
);
|
||||
await new Promise<void>((resolve) => setTimeout(resolve, 1500));
|
||||
window.location.reload();
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.gb-top-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-left: 12px;
|
||||
column-gap: 8px;
|
||||
|
||||
img {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
|
||||
&:last-child {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
span {
|
||||
color: var(--common-text-title);
|
||||
font-family: var(--font-title);
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.gb-top-btns {
|
||||
display: flex;
|
||||
margin-left: 16px;
|
||||
column-gap: 8px;
|
||||
}
|
||||
|
||||
.gb-top-btn {
|
||||
border-radius: 4px;
|
||||
background: var(--tgc-btn-1);
|
||||
color: var(--btn-text);
|
||||
}
|
||||
|
||||
.dark .gb-top-btn {
|
||||
border: 1px solid var(--common-shadow-2);
|
||||
}
|
||||
|
||||
.gb-container {
|
||||
display: flex;
|
||||
height: calc(100vh - 144px);
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
border: 1px solid var(--common-shadow-2);
|
||||
border-radius: 4px;
|
||||
background: var(--app-page-bg);
|
||||
}
|
||||
|
||||
.gb-tab {
|
||||
height: 40px;
|
||||
color: var(--box-text-4);
|
||||
font-family: var(--font-title);
|
||||
|
||||
img {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.gb-window {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.gb-window-item {
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
@@ -32,9 +32,14 @@
|
||||
</div>
|
||||
</template>
|
||||
<template #append>
|
||||
<v-btn class="anno-switch-btn" @click="switchNews" prepend-icon="mdi-bullhorn">
|
||||
切换米游社咨讯
|
||||
</v-btn>
|
||||
<div class="anno-top-append">
|
||||
<v-btn class="anno-switch-btn" @click="switchNews" prepend-icon="mdi-bullhorn">
|
||||
切换米游社咨讯
|
||||
</v-btn>
|
||||
<v-btn class="anno-switch-btn" v-if="isLogin" @click="showIframe()">
|
||||
<v-icon>mdi-web</v-icon>
|
||||
</v-btn>
|
||||
</div>
|
||||
</template>
|
||||
</v-app-bar>
|
||||
<v-window v-model="tab">
|
||||
@@ -44,11 +49,13 @@
|
||||
</div>
|
||||
</v-window-item>
|
||||
</v-window>
|
||||
<TaoIframe v-if="isLogin" v-model="iframeVisible" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import showLoading from "@comp/func/loading.js";
|
||||
import showSnackbar from "@comp/func/snackbar.js";
|
||||
import TaCard from "@comp/pageAnno/ta-card.vue";
|
||||
import TaoIframe from "@comp/pageAnno/tao-iframe.vue";
|
||||
import { AnnoLangEnum, AnnoTypeEnum, getAnnoLangDesc, getAnnoTypeDesc } from "@enum/anno.js";
|
||||
import { getGameServerDesc, GameServerEnum } from "@enum/game.js";
|
||||
import hk4eReq from "@req/hk4eReq.js";
|
||||
@@ -89,14 +96,16 @@ const langList: ReadonlyArray<AnnoSelect<TGApp.BBS.Announcement.AnnoLangEnum>> =
|
||||
const tabList: ReadonlyArray<AnnoSelect<TGApp.BBS.Announcement.AnnoTypeEnum>> = [
|
||||
AnnoTypeEnum.ACTIVITY,
|
||||
AnnoTypeEnum.GAME,
|
||||
AnnoTypeEnum.UGC,
|
||||
].map((i) => ({ text: getAnnoTypeDesc(i), value: i }));
|
||||
|
||||
const { server, lang } = storeToRefs(useAppStore());
|
||||
const { server, lang, isLogin } = storeToRefs(useAppStore());
|
||||
const router = useRouter();
|
||||
|
||||
const tab = ref<TGApp.BBS.Announcement.AnnoTypeEnum>(AnnoTypeEnum.ACTIVITY);
|
||||
const annoCards = shallowRef<AnnoList>({ activity: [], game: [] });
|
||||
const annoCards = shallowRef<AnnoList>({ activity: [], game: [], ugc: [] });
|
||||
const isReq = ref<boolean>(false);
|
||||
const iframeVisible = ref<boolean>(false);
|
||||
|
||||
watch(
|
||||
() => server.value,
|
||||
@@ -123,6 +132,10 @@ onMounted(async () => {
|
||||
await loadData();
|
||||
});
|
||||
|
||||
function showIframe(): void {
|
||||
iframeVisible.value = true;
|
||||
}
|
||||
|
||||
async function loadData(): Promise<void> {
|
||||
if (isReq.value) return;
|
||||
isReq.value = true;
|
||||
@@ -134,6 +147,7 @@ async function loadData(): Promise<void> {
|
||||
const detailResp = await hk4eReq.anno.detail(server.value, AnnoLangEnum.CHS);
|
||||
const actCards: Array<AnnoCard> = [];
|
||||
const gameCards: Array<AnnoCard> = [];
|
||||
const ugcCards: Array<AnnoCard> = [];
|
||||
for (const list of listResp.list) {
|
||||
for (const item of list.list) {
|
||||
const detail = detailResp.find((i) => i.ann_id === item.ann_id);
|
||||
@@ -143,13 +157,15 @@ async function loadData(): Promise<void> {
|
||||
actCards.push(card);
|
||||
} else if (card.typeLabel === "game") {
|
||||
gameCards.push(card);
|
||||
} else if (card.typeLabel === "ugc") {
|
||||
ugcCards.push(card);
|
||||
}
|
||||
} else {
|
||||
await TGLogger.Warn(`[Announcements][loadData] 未找到公告详情:${item.ann_id}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
annoCards.value = { activity: actCards, game: gameCards };
|
||||
annoCards.value = { activity: actCards, game: gameCards, ugc: ugcCards };
|
||||
await showLoading.end();
|
||||
isReq.value = false;
|
||||
}
|
||||
@@ -177,12 +193,17 @@ function getAnnoCard(
|
||||
const timeStart = anno.start_time.split(" ")[0];
|
||||
const timeEnd = anno.end_time.split(" ")[0];
|
||||
const time = `${timeStart} ~ ${timeEnd}`;
|
||||
const labelMap: Record<string, TGApp.BBS.Announcement.AnnoTypeEnum> = {
|
||||
1: AnnoTypeEnum.ACTIVITY,
|
||||
2: AnnoTypeEnum.GAME,
|
||||
26: AnnoTypeEnum.UGC,
|
||||
};
|
||||
return {
|
||||
id: anno.ann_id,
|
||||
title: anno.title,
|
||||
subtitle: anno.subtitle.replace(/<br \/>/g, " "),
|
||||
banner: anno.banner,
|
||||
typeLabel: anno.type === 2 ? "game" : "activity",
|
||||
typeLabel: labelMap[anno.type],
|
||||
tagIcon: anno.tag_icon,
|
||||
tagLabel: getAnnoTag(anno.tag_label),
|
||||
timeStr: time,
|
||||
@@ -217,9 +238,16 @@ async function switchNews(): Promise<void> {
|
||||
font-family: var(--font-title);
|
||||
}
|
||||
|
||||
.anno-top-append {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 16px;
|
||||
column-gap: 8px;
|
||||
}
|
||||
|
||||
.anno-switch-btn {
|
||||
height: 40px;
|
||||
margin-right: 16px;
|
||||
background: var(--tgc-btn-1);
|
||||
color: var(--btn-text);
|
||||
font-family: var(--font-title);
|
||||
@@ -232,8 +260,8 @@ async function switchNews(): Promise<void> {
|
||||
.anno-grid {
|
||||
display: grid;
|
||||
font-family: var(--font-title);
|
||||
gap: 8px;
|
||||
grid-auto-rows: auto;
|
||||
grid-gap: 8px;
|
||||
grid-template-columns: repeat(auto-fill, minmax(360px, 1fr));
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -166,7 +166,7 @@ import { emit } from "@tauri-apps/api/event";
|
||||
import { open } from "@tauri-apps/plugin-dialog";
|
||||
import { remove } from "@tauri-apps/plugin-fs";
|
||||
import { platform } from "@tauri-apps/plugin-os";
|
||||
import { exit } from "@tauri-apps/plugin-process";
|
||||
import { relaunch } from "@tauri-apps/plugin-process";
|
||||
import { backUpUserData, restoreUserData } from "@utils/dataBS.js";
|
||||
import { getBuildTime } from "@utils/TGBuild.js";
|
||||
import TGLogger from "@utils/TGLogger.js";
|
||||
@@ -391,14 +391,18 @@ async function confirmDelCache(): Promise<void> {
|
||||
}
|
||||
await showLoading.start("正在清除缓存");
|
||||
for (const dir of CacheDir) {
|
||||
await showLoading.update(dir);
|
||||
await remove(dir, { recursive: true });
|
||||
try {
|
||||
await showLoading.update(dir);
|
||||
await remove(dir, { recursive: true });
|
||||
} catch (e) {
|
||||
await TGLogger.Error(`[Config][confirmDelCache] 清除缓存失败 ${dir} ${e}`);
|
||||
}
|
||||
}
|
||||
await showLoading.end();
|
||||
await TGLogger.Info("[Config][confirmDelCache] 缓存清除完成");
|
||||
showSnackbar.success("缓存已清除!即将退出应用!");
|
||||
await new Promise<void>((resolve) => setTimeout(resolve, 1500));
|
||||
await exit();
|
||||
await relaunch();
|
||||
}
|
||||
|
||||
// 恢复默认设置
|
||||
|
||||
@@ -20,20 +20,41 @@
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import showSnackbar from "@comp/func/snackbar.js";
|
||||
import recordReq from "@req/recordReq.js";
|
||||
import hk4eReq from "@req/hk4eReq.js";
|
||||
import takumiReq from "@req/takumiReq.js";
|
||||
import useUserStore from "@store/user.js";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { ref } from "vue";
|
||||
|
||||
const { cookie, account } = storeToRefs(useUserStore());
|
||||
|
||||
const authkey = ref<string>("");
|
||||
|
||||
async function test(): Promise<void> {
|
||||
if (!cookie.value) return;
|
||||
const resp = await recordReq.actCalendar(cookie.value, account.value);
|
||||
console.log(resp);
|
||||
if ("retcode" in resp) {
|
||||
showSnackbar.warn(`[${resp.retcode}] ${resp.message}`);
|
||||
if (!cookie.value || !account.value) {
|
||||
showSnackbar.warn("请先登录账号");
|
||||
return;
|
||||
}
|
||||
const authkeyRes = await takumiReq.bind.authKey(cookie.value, account.value);
|
||||
if (typeof authkeyRes === "string") {
|
||||
authkey.value = authkeyRes;
|
||||
} else {
|
||||
showSnackbar.error("获取authkey失败");
|
||||
return;
|
||||
}
|
||||
const list: Array<TGApp.Game.Gacha.GachaBItem> = [];
|
||||
let endId = "0";
|
||||
while (true) {
|
||||
const res = await hk4eReq.gachaB(authkey.value, "1000", endId);
|
||||
if (Array.isArray(res)) {
|
||||
if (res.length === 0) break;
|
||||
list.push(...res);
|
||||
endId = res[res.length - 1].id;
|
||||
} else {
|
||||
showSnackbar.warn(`[${res.retcode}] 获取祈愿记录失败:${res.message}`);
|
||||
}
|
||||
}
|
||||
console.log(list);
|
||||
}
|
||||
</script>
|
||||
<style lang="css" scoped>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/**
|
||||
* @file plugins/Sqlite/index.ts
|
||||
* @description Sqlite 数据库操作类
|
||||
* @since Beta v0.8.0
|
||||
* Sqlite 数据库操作类
|
||||
* @since Beta v0.8.4
|
||||
*/
|
||||
|
||||
import { app } from "@tauri-apps/api";
|
||||
@@ -17,10 +16,11 @@ class Sqlite {
|
||||
"Achievements",
|
||||
"AppData",
|
||||
"GachaRecords",
|
||||
"GachaBRecords",
|
||||
"GameAccount",
|
||||
"SpiralAbyss",
|
||||
"RoleCombat",
|
||||
"HardChallenge",
|
||||
"RoleCombat",
|
||||
"SpiralAbyss",
|
||||
"UFCollection",
|
||||
"UFMap",
|
||||
"UFPost",
|
||||
@@ -39,7 +39,7 @@ class Sqlite {
|
||||
private constructor() {}
|
||||
|
||||
/**
|
||||
* @description 获取数据库实例
|
||||
* 获取数据库实例
|
||||
* @since Beta v0.3.3
|
||||
* @returns {Promise<Database>}
|
||||
*/
|
||||
@@ -49,7 +49,7 @@ class Sqlite {
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 检测是否需要创建数据库
|
||||
* 检测是否需要创建数据库
|
||||
* @since Beta v0.6.1
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
@@ -70,7 +70,7 @@ class Sqlite {
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 初始化数据库
|
||||
* 初始化数据库
|
||||
* @since Beta v0.4.5
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
@@ -81,7 +81,7 @@ class Sqlite {
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取数据库信息
|
||||
* 获取数据库信息
|
||||
* @since Beta v0.3.3
|
||||
* @returns {Promise<TGApp.Sqlite.AppData.Item[]>}
|
||||
*/
|
||||
@@ -92,7 +92,7 @@ class Sqlite {
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 对比数据判断是否需要更新
|
||||
* 对比数据判断是否需要更新
|
||||
* @since Beta v0.3.3
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
@@ -105,7 +105,7 @@ class Sqlite {
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 保存 appData
|
||||
* 保存 appData
|
||||
* @since Beta v0.3.3
|
||||
* @param {string} key
|
||||
* @param {string} value
|
||||
@@ -118,7 +118,7 @@ class Sqlite {
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 已有数据表跟触发器不变的情况下,更新数据库数据
|
||||
* 已有数据表跟触发器不变的情况下,更新数据库数据
|
||||
* @since Beta v0.3.3
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
@@ -131,7 +131,7 @@ class Sqlite {
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 更新 SpiralAbyss 表
|
||||
* 更新 SpiralAbyss 表
|
||||
* @since Beta v0.6.1
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
@@ -147,7 +147,7 @@ class Sqlite {
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 重置数据库
|
||||
* 重置数据库
|
||||
* @since Beta v0.4.0
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/**
|
||||
* @file plugins/Sqlite/modules/userAbyss.ts
|
||||
* @description Sqlite-用户深渊模块
|
||||
* @since Beta v0.6.8
|
||||
* Sqlite-用户深渊模块
|
||||
* @since Beta v0.8.6
|
||||
*/
|
||||
|
||||
import { path } from "@tauri-apps/api";
|
||||
@@ -50,8 +49,8 @@ function getRestoreSql(tableData: TGApp.Sqlite.Abyss.TableData): string {
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取深渊数据的插入更新Sql
|
||||
* @since Beta v0.6.1
|
||||
* 获取深渊数据的插入更新Sql
|
||||
* @since Beta v0.8.6
|
||||
* @param {string} uid - 用户UID
|
||||
* @param {TGApp.Game.Abyss.FullData} data -深渊数据
|
||||
* @returns {string}
|
||||
@@ -67,7 +66,7 @@ function getInsertSql(uid: string, data: TGApp.Game.Abyss.FullData): string {
|
||||
const normalSkillRank = transCharacterData(data.normal_skill_rank);
|
||||
const energySkillRank = transCharacterData(data.energy_skill_rank);
|
||||
const floors = transFloorData(data.floors);
|
||||
const skippedFloor = data.skipped_floor;
|
||||
const skippedFloor = data.skipped_floor ?? "";
|
||||
const timeNow = timestampToDate(new Date().getTime());
|
||||
return `
|
||||
INSERT INTO SpiralAbyss (uid, id, startTime, endTime, totalBattleTimes, totalWinTimes, maxFloor,
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
/**
|
||||
* @file plugins/Sqlite/modules/userGacha.ts
|
||||
* @description 用户祈愿模块
|
||||
* @since Beta v0.7.5
|
||||
* 用户祈愿模块
|
||||
* @since Beta v0.8.6
|
||||
*/
|
||||
|
||||
import showLoading from "@comp/func/loading.js";
|
||||
import showSnackbar from "@comp/func/snackbar.js";
|
||||
import { path } from "@tauri-apps/api";
|
||||
import { exists, mkdir, readDir } from "@tauri-apps/plugin-fs";
|
||||
@@ -115,9 +115,9 @@ async function getGachaCheck(uid: string, type: string): Promise<string | undefi
|
||||
* @description 获取用户祈愿记录
|
||||
* @since Beta v0.4.7
|
||||
* @param {string} uid - UID
|
||||
* @return {Promise<TGApp.Sqlite.GachaRecords.SingleTable[]>}
|
||||
* @return {Promise<TGApp.Sqlite.GachaRecords.TableGacha[]>}
|
||||
*/
|
||||
async function getGachaRecords(uid: string): Promise<TGApp.Sqlite.GachaRecords.SingleTable[]> {
|
||||
async function getGachaRecords(uid: string): Promise<TGApp.Sqlite.GachaRecords.TableGacha[]> {
|
||||
const db = await TGSqlite.getDB();
|
||||
return await db.select("SELECT * FROM GachaRecords WHERE uid = ?;", [uid]);
|
||||
}
|
||||
@@ -132,9 +132,9 @@ async function getGachaRecords(uid: string): Promise<TGApp.Sqlite.GachaRecords.S
|
||||
async function getGachaRecordsGroupByDate(
|
||||
uid: string,
|
||||
type?: string,
|
||||
): Promise<Record<string, TGApp.Sqlite.GachaRecords.SingleTable[]>> {
|
||||
): Promise<Record<string, TGApp.Sqlite.GachaRecords.TableGacha[]>> {
|
||||
const db = await TGSqlite.getDB();
|
||||
type resType = Array<TGApp.Sqlite.GachaRecords.SingleTable>;
|
||||
type resType = Array<TGApp.Sqlite.GachaRecords.TableGacha>;
|
||||
let res: resType;
|
||||
if (type) {
|
||||
res = await db.select<resType>(
|
||||
@@ -146,7 +146,7 @@ async function getGachaRecordsGroupByDate(
|
||||
uid,
|
||||
]);
|
||||
}
|
||||
const map: Record<string, TGApp.Sqlite.GachaRecords.SingleTable[]> = {};
|
||||
const map: Record<string, TGApp.Sqlite.GachaRecords.TableGacha[]> = {};
|
||||
for (const item of res) {
|
||||
// key 是 yyyy-MM-dd hh:mm:ss,按照日期分组
|
||||
const key = item.time.split(" ")[0];
|
||||
@@ -198,33 +198,53 @@ async function cleanGachaRecords(
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 合并祈愿数据
|
||||
* @since Beta v0.4.7
|
||||
* 合并祈愿数据
|
||||
* @since Beta v0.8.6
|
||||
* @param {string} uid - UID
|
||||
* @param {TGApp.Plugins.UIGF.GachaItem[]} data - UIGF数据
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
async function mergeUIGF(uid: string, data: TGApp.Plugins.UIGF.GachaItem[]): Promise<void> {
|
||||
const db = await TGSqlite.getDB();
|
||||
let cnt = 0;
|
||||
const len = data.length;
|
||||
let progress = 0;
|
||||
for (const gacha of data) {
|
||||
const trans = transGacha(gacha);
|
||||
if (cnt % 20 === 0) {
|
||||
progress = Math.round((cnt / len) * 100 * 100) / 100;
|
||||
await showLoading.update(
|
||||
`[${progress}%][${trans.time}] ${"⭐".repeat(Number(trans.rank_type))}-${trans.name}`,
|
||||
);
|
||||
cnt++;
|
||||
}
|
||||
const sql = getInsertSql(uid, trans);
|
||||
await db.execute(sql);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 合并祈愿数据(v4.0)
|
||||
* @since Beta v0.5.0
|
||||
* 合并祈愿数据(v4.0)
|
||||
* @since Beta v0.8.6
|
||||
* @param {TGApp.Plugins.UIGF.GachaHk4e} data - UIGF数据
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
async function mergeUIGF4(data: TGApp.Plugins.UIGF.GachaHk4e): Promise<void> {
|
||||
const db = await TGSqlite.getDB();
|
||||
let cnt: number = 0;
|
||||
const len = data.list.length;
|
||||
let progress: number = 0;
|
||||
for (const gacha of data.list) {
|
||||
const trans = transGacha(gacha, data.timezone);
|
||||
if (cnt % 20 === 0) {
|
||||
progress = Math.round((cnt / len) * 100 * 100) / 100;
|
||||
await showLoading.update(
|
||||
`[${progress}%][${trans.time}] ${"⭐".repeat(Number(trans.rank_type))}-${trans.name}`,
|
||||
);
|
||||
}
|
||||
const sql = getInsertSql(data.uid.toString(), trans);
|
||||
await db.execute(sql);
|
||||
cnt++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
161
src/plugins/Sqlite/modules/userGachaB.ts
Normal file
161
src/plugins/Sqlite/modules/userGachaB.ts
Normal file
@@ -0,0 +1,161 @@
|
||||
/**
|
||||
* 千星奇域祈愿模块
|
||||
* @since Beta v0.8.4
|
||||
*/
|
||||
|
||||
import showSnackbar from "@comp/func/snackbar.js";
|
||||
import TGSqlite from "@Sql/index.js";
|
||||
import { exists, mkdir } from "@tauri-apps/plugin-fs";
|
||||
import TGLogger from "@utils/TGLogger.js";
|
||||
|
||||
/**
|
||||
* 获取导入 Sql
|
||||
* @since Beta v0.8.4
|
||||
* @param {TGApp.Game.Gacha.GachaBItem} gacha - 抽卡记录数据
|
||||
* @returns {string}
|
||||
*/
|
||||
function getInsertSql(gacha: TGApp.Game.Gacha.GachaBItem): string {
|
||||
return `
|
||||
INSERT INTO GachaBRecords(id, uid, region, scheduleId, gachaType,
|
||||
opGachaType, time, itemId, name, type,
|
||||
rank, isUp, updated)
|
||||
VALUES ('${gacha.id}', '${gacha.uid}', '${gacha.region}', '${gacha.schedule_id}',
|
||||
'${gacha.op_gacha_type === "1000" ? "1000" : "2000"}', '${gacha.op_gacha_type}', '${gacha.time}',
|
||||
'${gacha.item_id}', '${gacha.item_name}', '${gacha.item_type}',
|
||||
'${gacha.rank_type}', '${gacha.is_up}', datetime('now', 'localtime'))
|
||||
ON CONFLICT (id)
|
||||
DO UPDATE
|
||||
SET uid = '${gacha.uid}',
|
||||
region = '${gacha.region}',
|
||||
scheduleId = '${gacha.schedule_id}',
|
||||
gachaType = '${gacha.op_gacha_type === "1000" ? "1000" : "2000"}',
|
||||
opGachaType = '${gacha.op_gacha_type}',
|
||||
time = '${gacha.time}',
|
||||
itemId = '${gacha.item_id}',
|
||||
name = '${gacha.item_name}',
|
||||
type = '${gacha.item_type}',
|
||||
rank = '${gacha.rank_type}',
|
||||
isUp = '${gacha.is_up}',
|
||||
updated = datetime('now', 'localtime');
|
||||
`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 插入列表数据
|
||||
* @since Beta v0.8.4
|
||||
* @param {Array<TGApp.Game.Gacha.GachaBItem>} list - 抽卡记录列表
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function insertGachaList(list: Array<TGApp.Game.Gacha.GachaBItem>): Promise<void> {
|
||||
const db = await TGSqlite.getDB();
|
||||
for (const gacha of list) {
|
||||
const sql = getInsertSql(gacha);
|
||||
await db.execute(sql);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数据库UID列表
|
||||
* @since Beta v0.8.4
|
||||
* @returns {Promise<Array<string>>}
|
||||
*/
|
||||
async function getUidList(): Promise<Array<string>> {
|
||||
const db = await TGSqlite.getDB();
|
||||
type resType = Array<{ uid: string }>;
|
||||
const res = await db.select<resType>("SELECT DISTINCT uid FROM GachaBRecords;");
|
||||
return res.map((i) => i.uid);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取增量更新的记录 ID
|
||||
* @since Beta v0.8.4
|
||||
* @param {string} uid - UID
|
||||
* @param {string} type - 类型
|
||||
* @returns {Promise<string|undefined>}
|
||||
*/
|
||||
async function getGachaCheck(uid: string, type: string): Promise<string | undefined> {
|
||||
const db = await TGSqlite.getDB();
|
||||
type resType = Array<{ id: string }>;
|
||||
const res = await db.select<resType>(
|
||||
"SELECT id FROM GachaBRecords WHERE uid = ? AND opGachaType = ? ORDER BY id DESC LIMIT 1;",
|
||||
[uid, type],
|
||||
);
|
||||
if (res.length === 0) return undefined;
|
||||
return res[0].id;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户祈愿记录
|
||||
* @since Beta v0.8.4
|
||||
* @param {string} uid - UID
|
||||
* @param {string} [type] - 类型
|
||||
* @return {Promise<Array<TGApp.Sqlite.GachaRecords.TableGachaB>>}
|
||||
*/
|
||||
async function getGachaRecords(
|
||||
uid: string,
|
||||
type?: string,
|
||||
): Promise<Array<TGApp.Sqlite.GachaRecords.TableGachaB>> {
|
||||
const db = await TGSqlite.getDB();
|
||||
if (type) {
|
||||
return await db.select("SELECT * FROM GachaBRecords WHERE uid = ? AND opGachaType = ?;", [
|
||||
uid,
|
||||
type,
|
||||
]);
|
||||
}
|
||||
return await db.select("SELECT * FROM GachaBRecords WHERE uid = ?;", [uid]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 备份祈愿数据
|
||||
* @since Beta v0.8.4
|
||||
* @param {string} dir - 备份目录
|
||||
* @remarks 等UIGF标准最终确定后与TSUserGacha合并
|
||||
*/
|
||||
async function backUpUigf(dir: string): Promise<void> {
|
||||
if (!(await exists(dir))) {
|
||||
await TGLogger.Warn("不存在指定的祈愿备份目录,即将创建");
|
||||
await mkdir(dir, { recursive: true });
|
||||
}
|
||||
showSnackbar.error(`千星奇域祈愿数据备份功能尚未实现,请耐心等待后续版本更新。`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 恢复祈愿数据
|
||||
* @since Beta v0.8.4
|
||||
* @param {string} dir - 恢复目录
|
||||
* @remarks 等UIGF标准最终确定后与TSUserGacha合并
|
||||
*/
|
||||
async function restoreUigf(dir: string): Promise<boolean> {
|
||||
if (!(await exists(dir))) {
|
||||
await TGLogger.Warn("不存在指定的祈愿备份目录");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除用户祈愿数据
|
||||
* @since Beta v0.8.4
|
||||
* @param {string} uid - UID
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function deleteRecords(uid: string): Promise<void> {
|
||||
const db = await TGSqlite.getDB();
|
||||
await db.execute("DELETE FROM GachaBRecords WHERE uid = ?;", [uid]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 千星奇域祈愿模块
|
||||
* @since Beta v0.8.4
|
||||
*/
|
||||
const TSUserGachaB = {
|
||||
getUidList,
|
||||
getGachaCheck,
|
||||
getGachaRecords,
|
||||
insertGachaList,
|
||||
backUpUigf,
|
||||
restoreUigf,
|
||||
deleteRecords,
|
||||
};
|
||||
|
||||
export default TSUserGachaB;
|
||||
@@ -1,6 +1,5 @@
|
||||
-- @file plugins/Sqlite/sql/createTable.sql
|
||||
-- @brief sqlite数据库创建表语句
|
||||
-- @since Beta v0.8.0
|
||||
-- @since Beta v0.8.4
|
||||
|
||||
-- @brief 创建成就数据表
|
||||
create table if not exists Achievements
|
||||
@@ -148,6 +147,24 @@ create table if not exists GachaRecords
|
||||
updated text
|
||||
);
|
||||
|
||||
-- @brief 创建千星奇域祈愿数据表
|
||||
create table if not exists GachaBRecords
|
||||
(
|
||||
id text primary key not null,
|
||||
uid text,
|
||||
region text,
|
||||
scheduleId text,
|
||||
gachaType text,
|
||||
opGachaType text,
|
||||
time text,
|
||||
itemId text,
|
||||
name text,
|
||||
type text,
|
||||
rank text,
|
||||
isUp text,
|
||||
updated text
|
||||
);
|
||||
|
||||
-- @brief 创建用户帖子收藏
|
||||
create table if not exists UFPost
|
||||
(
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* @file request/hk4eReq.ts
|
||||
* @description Hk4eApi 请求模块
|
||||
* @since Beta v0.8.0
|
||||
* @since Beta v0.8.4
|
||||
*/
|
||||
|
||||
import { AnnoLangEnum } from "@enum/anno.js";
|
||||
@@ -122,7 +122,7 @@ async function getGachaLog(
|
||||
size: "20",
|
||||
end_id: endId,
|
||||
};
|
||||
const resp = await TGHttp<TGApp.Game.Gacha.GachaLogResponse | TGApp.BBS.Response.Base>(
|
||||
const resp = await TGHttp<TGApp.Game.Gacha.GachaLogResp | TGApp.BBS.Response.Base>(
|
||||
"https://public-operation-hk4e.mihoyo.com/gacha_info/api/getGachaLog",
|
||||
{ method: "GET", query: params },
|
||||
);
|
||||
@@ -130,6 +130,37 @@ async function getGachaLog(
|
||||
return resp.data.list;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取千星奇域抽卡记录
|
||||
* @since Beta v0.8.4
|
||||
* @param {string} authKey authKey
|
||||
* @param {string} gachaType 抽卡类型
|
||||
* @param {string} endId 结束 id,默认为 0
|
||||
* @returns {Promise<Array<TGApp.Game.Gacha.GachaBItem>|TGApp.BBS.Response.Base>} 抽卡记录
|
||||
*/
|
||||
async function getBeyondGachaLog(
|
||||
authKey: string,
|
||||
gachaType: string,
|
||||
endId: string = "0",
|
||||
): Promise<Array<TGApp.Game.Gacha.GachaBItem> | TGApp.BBS.Response.Base> {
|
||||
const params = {
|
||||
lang: "zh-cn",
|
||||
auth_appid: "webview_gacha",
|
||||
authkey: authKey,
|
||||
authkey_ver: "1",
|
||||
sign_type: "2",
|
||||
gacha_type: gachaType,
|
||||
size: "5",
|
||||
end_id: endId,
|
||||
};
|
||||
const resp = await TGHttp<TGApp.Game.Gacha.GachaBLogResp | TGApp.BBS.Response.Base>(
|
||||
"https://public-operation-hk4e.mihoyo.com/gacha_info/api/getBeyondGachaLog",
|
||||
{ method: "GET", query: params },
|
||||
);
|
||||
if (resp.retcode !== 0) return <TGApp.BBS.Response.Base>resp;
|
||||
return resp.data.list;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取登录二维码
|
||||
* @since Beta v0.7.2
|
||||
@@ -171,6 +202,7 @@ async function queryPandaQr(
|
||||
const hk4eReq = {
|
||||
anno: { list: getAnnoList, detail: getAnnoDetail },
|
||||
gacha: getGachaLog,
|
||||
gachaB: getBeyondGachaLog,
|
||||
loginQr: { create: fetchPandaQr, state: queryPandaQr },
|
||||
};
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ async function genAuthKey(
|
||||
game_uid: account.gameUid,
|
||||
region: account.region,
|
||||
};
|
||||
const resp = await TGHttp<TGApp.Game.Gacha.AuthkeyResponse | TGApp.BBS.Response.Base>(
|
||||
const resp = await TGHttp<TGApp.Game.Gacha.AuthkeyResp | TGApp.BBS.Response.Base>(
|
||||
`${taBu}binding/api/genAuthKey`,
|
||||
{
|
||||
method: "POST",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* @file router modules user.ts
|
||||
* @file router/modules/user.ts
|
||||
* @description user 路由模块
|
||||
* @since Beta v0.8.0
|
||||
* @since Beta v0.8.4
|
||||
*/
|
||||
import type { RouteRecordRaw } from "vue-router";
|
||||
|
||||
@@ -31,6 +31,11 @@ const userRoutes = (<const>[
|
||||
name: "祈愿记录",
|
||||
component: async () => await import("@/pages/User/Gacha.vue"),
|
||||
},
|
||||
{
|
||||
path: "/user/gachaB",
|
||||
name: "千星奇域祈愿记录",
|
||||
component: async () => await import("@/pages/User/GachaB.vue"),
|
||||
},
|
||||
{
|
||||
path: "/user/record",
|
||||
name: "原神战绩",
|
||||
|
||||
@@ -1,16 +1,10 @@
|
||||
/**
|
||||
* @file router/modules/wiki.ts
|
||||
* @description wiki 路由模块
|
||||
* @since Beta v0.6.7
|
||||
* wiki 路由模块
|
||||
* @since Beta v0.8.6
|
||||
*/
|
||||
import type { RouteRecordRaw } from "vue-router";
|
||||
|
||||
const wikiRoutes = (<const>[
|
||||
{
|
||||
path: "/wiki/abyss",
|
||||
name: "深渊数据库",
|
||||
component: async () => await import("@/pages/WIKI/Abyss.vue"),
|
||||
},
|
||||
{
|
||||
path: "/wiki/character/:id?",
|
||||
name: "角色图鉴",
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/**
|
||||
* @file router routes.ts
|
||||
* @description 路由配置
|
||||
* @since Beta v0.4.4
|
||||
* 路由配置
|
||||
* @since Beta v0.8.6
|
||||
*/
|
||||
|
||||
import { RouteRecordRaw } from "vue-router";
|
||||
@@ -18,6 +17,7 @@ const routes = (<const>[
|
||||
...archiveRoutes,
|
||||
...wikiRoutes,
|
||||
...userRoutes,
|
||||
{ path: "/:pathMatch(.*)*", redirect: "/" },
|
||||
]) satisfies Array<RouteRecordRaw>;
|
||||
|
||||
export default routes;
|
||||
|
||||
106
src/types/App/Gacha.d.ts
vendored
106
src/types/App/Gacha.d.ts
vendored
@@ -1,36 +1,59 @@
|
||||
/**
|
||||
* @file types/App/Gacha.d.ts
|
||||
* @description 本应用的祈愿相关类型定义
|
||||
* @since Beta v0.4.4
|
||||
* 本应用的祈愿相关类型定义
|
||||
* @since Beta v0.8.4
|
||||
*/
|
||||
|
||||
/**
|
||||
* @description 祈愿记录命名空间
|
||||
* @namespace Gacha
|
||||
* @since Beta v0.4.4
|
||||
* @memberof TGApp.App
|
||||
*/
|
||||
declare namespace TGApp.App.Gacha {
|
||||
/**
|
||||
* @description 祈愿类型枚举
|
||||
* 祈愿类型枚举
|
||||
* @since Beta v0.4.4
|
||||
* @enum {number}
|
||||
* @property {number} Newbie 新手祈愿 = 100
|
||||
* @property {number} Normal 常驻祈愿 = 200
|
||||
* @property {number} CharacterUp 角色活动祈愿 = 301
|
||||
* @property {number} CharacterUp2 角色活动祈愿2 = 400
|
||||
* @property {number} WeaponUp 武器活动祈愿 = 302
|
||||
* @property {number} MixUp 集录祈愿 = 500
|
||||
* @return WishType
|
||||
*/
|
||||
const enum WishType {
|
||||
const WishType = <const>{
|
||||
/** 新手祈愿 */
|
||||
Newbie = 100,
|
||||
/** 常驻祈愿 */
|
||||
Normal = 200,
|
||||
/** 角色活动祈愿 */
|
||||
CharacterUp = 301,
|
||||
/** 角色活动祈愿2 */
|
||||
CharacterUp2 = 400,
|
||||
/** 武器活动祈愿 */
|
||||
WeaponUp = 302,
|
||||
/** 集录祈愿 */
|
||||
MixUp = 500,
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 祈愿类型枚举
|
||||
* @since Beta v0.8.4
|
||||
*/
|
||||
type WishTypeEnum = (typeof WishType)[keyof typeof WishType];
|
||||
|
||||
/**
|
||||
* 千星奇域祈愿类型
|
||||
* @since Beta v0.8.4
|
||||
*/
|
||||
const WishTypeB = <const>{
|
||||
/** 常驻祈愿 */
|
||||
Normal: "1000",
|
||||
/** 活动祈愿 */
|
||||
Event: "2000",
|
||||
/** 男性活动祈愿1 */
|
||||
EventBoy1: "20011",
|
||||
/** 男性活动祈愿2 */
|
||||
EventBoy2: "20012",
|
||||
/** 女性活动祈愿1 */
|
||||
EventGirl1: "20021",
|
||||
/** 女性活动祈愿2 */
|
||||
EventGirl2: "20022",
|
||||
};
|
||||
|
||||
/**
|
||||
* 千星奇域祈愿类型
|
||||
* @since Beta v0.8.4
|
||||
*/
|
||||
type WishTypeBEnum = (typeof WishTypeB)[keyof typeof WishTypeB];
|
||||
|
||||
/**
|
||||
* @description 祈愿记录项
|
||||
* @interface PoolItem
|
||||
@@ -47,16 +70,49 @@ declare namespace TGApp.App.Gacha {
|
||||
* @property {number[]} up4List up四星
|
||||
* @return PoolItem
|
||||
*/
|
||||
interface PoolItem {
|
||||
type PoolItem = {
|
||||
/** 卡池名称 */
|
||||
name: string;
|
||||
/** 卡池版本 */
|
||||
version: string;
|
||||
/** 卡池排序 */
|
||||
order: number;
|
||||
/** 卡池横幅 */
|
||||
banner: string;
|
||||
/** 卡池开始时间 yyyy-MM-ddTHH:mm:ss+08:00 */
|
||||
from: string;
|
||||
/** 卡池结束时间 yyyy-MM-ddTHH:mm:ss+08:00 */
|
||||
to: string;
|
||||
type: WishType;
|
||||
/** 卡池类型 */
|
||||
type: number;
|
||||
/** 卡池帖子ID */
|
||||
postId: string;
|
||||
up5List: number[];
|
||||
up4List: number[];
|
||||
}
|
||||
/** up五星 */
|
||||
up5List: Array<number>;
|
||||
/** up四星 */
|
||||
up4List: Array<number>;
|
||||
};
|
||||
|
||||
/**
|
||||
* 千星奇域套装类型
|
||||
* @since Beta v0.8.4
|
||||
*/
|
||||
type GachaBSetType = "装扮部件" | "装扮套装";
|
||||
|
||||
/**
|
||||
* 千星奇域祈愿元数据
|
||||
* @since Beta v0.8.4
|
||||
*/
|
||||
type GachaBMeta = {
|
||||
/** ID */
|
||||
id: string;
|
||||
/** 名称 */
|
||||
name: string;
|
||||
/** 图标 */
|
||||
icon: string;
|
||||
/** 稀有度 */
|
||||
rank: number;
|
||||
/** 类型 */
|
||||
type: GachaBSetType;
|
||||
};
|
||||
}
|
||||
|
||||
6
src/types/BBS/Announcement.d.ts
vendored
6
src/types/BBS/Announcement.d.ts
vendored
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* @file types/BBS/Announcement.d.ts
|
||||
* @description 从 BBS 获取到的游戏内公告类型定义文件
|
||||
* @since Beta v0.8.0
|
||||
* @since Beta v0.8.4
|
||||
*/
|
||||
|
||||
declare namespace TGApp.BBS.Announcement {
|
||||
@@ -30,14 +30,16 @@ declare namespace TGApp.BBS.Announcement {
|
||||
|
||||
/**
|
||||
* @description 公告类型
|
||||
* @since Beta v0.7.7
|
||||
* @since Beta v0.8.4
|
||||
* @const AnnoType
|
||||
* @property {string} "activity" - 活动公告
|
||||
* @property {string} "game" - 游戏公告
|
||||
* @property {string} "ugc" - 千星奇域公告
|
||||
*/
|
||||
const AnnoType = <const>{
|
||||
ACTIVITY: "activity",
|
||||
GAME: "game",
|
||||
UGC: "ugc",
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
16
src/types/BBS/Forum.d.ts
vendored
16
src/types/BBS/Forum.d.ts
vendored
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* @file types/BBS/Forum.d.ts
|
||||
* @description BBS 版块类型定义
|
||||
* @since Beta v0.7.1
|
||||
* @since Beta v0.8.4
|
||||
*/
|
||||
|
||||
declare namespace TGApp.BBS.Forum {
|
||||
@@ -11,7 +11,6 @@ declare namespace TGApp.BBS.Forum {
|
||||
* @interface GameForumResp
|
||||
* @extends TGApp.BBS.Response.BaseWithData
|
||||
* @property {Array<GameForum>} data.list 所有版块信息
|
||||
* @return GameForumResp
|
||||
*/
|
||||
type GameForumResp = TGApp.BBS.Response.BaseWithData & { data: { list: Array<GameForum> } };
|
||||
|
||||
@@ -21,7 +20,6 @@ declare namespace TGApp.BBS.Forum {
|
||||
* @interface PostForumResp
|
||||
* @extends TGApp.BBS.Response.BaseWithData
|
||||
* @property {PostForumRes} data 版块帖子列表
|
||||
* @return PostForumResp
|
||||
*/
|
||||
type PostForumResp = TGApp.BBS.Response.BaseWithData<PostForumRes>;
|
||||
|
||||
@@ -31,7 +29,6 @@ declare namespace TGApp.BBS.Forum {
|
||||
* @interface GameForum
|
||||
* @property {number} game_id 游戏 ID
|
||||
* @property {Array<GameForumItem>} forums 版块信息
|
||||
* @return GameForum
|
||||
*/
|
||||
type GameForum = { game_id: number; forums: Array<GameForumItem> };
|
||||
|
||||
@@ -66,7 +63,6 @@ declare namespace TGApp.BBS.Forum {
|
||||
* @property {string} read_me 说明
|
||||
* @property {Array<ForumCate>} forum_cate_list 分类列表
|
||||
* @property {Array<ForumCate>} video_cat_list 视频分类列表
|
||||
* @return GameForumItem
|
||||
*/
|
||||
type GameForumItem = {
|
||||
id: number;
|
||||
@@ -100,16 +96,15 @@ declare namespace TGApp.BBS.Forum {
|
||||
|
||||
/**
|
||||
* @description 视频分类
|
||||
* @since Beta v0.6.8
|
||||
* @since Beta v0.8.4
|
||||
* @interface ForumCate
|
||||
* @property {number} id 分类 ID
|
||||
* @property {string} name 分类名称
|
||||
* @property {number} forum_id 版块 ID
|
||||
* @property {string} desc 描述
|
||||
* @property {string} remark 备注
|
||||
* @return ForumCate
|
||||
* @property {string} [desc] 描述
|
||||
* @property {string} [remark] 备注
|
||||
*/
|
||||
type ForumCate = { id: number; name: string; forum_id: number; desc: string; remark: string };
|
||||
type ForumCate = { id: number; name: string; forum_id: number; desc?: string; remark?: string };
|
||||
|
||||
/**
|
||||
* @description 版块帖子列表
|
||||
@@ -121,7 +116,6 @@ declare namespace TGApp.BBS.Forum {
|
||||
* @property {number} page 页码
|
||||
* @property {unknown} databox 数据盒子
|
||||
* @property {Array<TGApp.BBS.Post.FullData>} list 帖子列表
|
||||
* @return PostForumRes
|
||||
*/
|
||||
type PostForumRes = {
|
||||
last_id: string;
|
||||
|
||||
731
src/types/BBS/Post.d.ts
vendored
731
src/types/BBS/Post.d.ts
vendored
File diff suppressed because it is too large
Load Diff
41
src/types/BBS/Response.d.ts
vendored
41
src/types/BBS/Response.d.ts
vendored
@@ -1,29 +1,38 @@
|
||||
/**
|
||||
* @file types/BBS/Response.d.ts
|
||||
* @description BBS 返回数据类型定义文件
|
||||
* BBS 返回数据类型定义文件
|
||||
* @since Beta v0.7.1
|
||||
*/
|
||||
|
||||
declare namespace TGApp.BBS.Response {
|
||||
/**
|
||||
* @description 基础返回类型,设计米游社接口请求都是这个类型
|
||||
* @interface Base
|
||||
* 基础返回响应
|
||||
* @since Beta v0.3.9
|
||||
* @property {never} retcode - 响应代码
|
||||
* @property {string} message - 响应消息
|
||||
* @property {never} data - 响应数据
|
||||
* @return Base
|
||||
*/
|
||||
type Base = { retcode: number; message: string; data: never };
|
||||
type Base = {
|
||||
/**
|
||||
* 响应代码
|
||||
* @remarks
|
||||
* 为0表示请求成功,非0表示请求失败
|
||||
* 请求失败时,data 恒为 null
|
||||
*/
|
||||
retcode: number;
|
||||
/** 响应消息 */
|
||||
message: string;
|
||||
/** 响应数据 */
|
||||
data: never;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description 基础返回类型-带有 data 的
|
||||
* @interface BaseWithData
|
||||
* 成功返回响应
|
||||
* @since Beta v0.7.1
|
||||
* @property {0} retcode - 响应代码
|
||||
* @property {string} message - 响应消息
|
||||
* @property {any} data - 响应数据
|
||||
* @return BaseWithData
|
||||
* @remarks 仅用于表示请求成功数据,retcode 恒为 0
|
||||
*/
|
||||
type BaseWithData<T = unknown> = { retcode: 0; message: string; data: T };
|
||||
type BaseWithData<T = unknown> = {
|
||||
/** 响应代码 */
|
||||
retcode: 0;
|
||||
/** 响应消息 */
|
||||
message: string;
|
||||
/** 响应数据 */
|
||||
data: T;
|
||||
};
|
||||
}
|
||||
|
||||
56
src/types/BBS/SctPost.d.ts
vendored
56
src/types/BBS/SctPost.d.ts
vendored
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* @file types/BBS/SctPost.d.ts
|
||||
* @description 结构化帖子类型声明文件
|
||||
* @since Beta v0.7.2
|
||||
* @since Beta v0.8.4
|
||||
*/
|
||||
|
||||
declare namespace TGApp.BBS.SctPost {
|
||||
@@ -31,14 +31,60 @@ declare namespace TGApp.BBS.SctPost {
|
||||
type Empty = { insert: never; attributes?: never };
|
||||
|
||||
/**
|
||||
* @description 帖子结构化数据-其他类型
|
||||
* @since Beta v0.6.7
|
||||
* @description 帖子结构化数据-viewType为2
|
||||
* @since Beta v0.8.4
|
||||
* @property {string} describe - 描述
|
||||
* @property {Array<string>} imgs - 图片链接
|
||||
* @property {Array<string>} link_card_ids - 关联卡片ID
|
||||
* @return Other
|
||||
* @return Pic
|
||||
*/
|
||||
type Other = { describe: string; imgs: Array<string>; link_card_ids?: Array<string> } & {
|
||||
type Pic = { describe: string; imgs: Array<string>; link_card_ids?: Array<string> } & {
|
||||
[key: string]: unknown;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description 帖子结构化数据-viewType为7
|
||||
* @since Beta v0.8.4
|
||||
* @description 下面详细结构参见相关组件
|
||||
* @todo 为简便起见,所有字段可能为null,但是目前没遇到text&level为null的情况
|
||||
* @property {Array<Base>} text - 文字内容
|
||||
* @property {Array<UgcImage>} images - 图片内容
|
||||
* @property {Array<UgcVod>} vods - 视频内容
|
||||
* @property {Array<UgcLevel>} levels - 等级内容
|
||||
*/
|
||||
type Ugc = {
|
||||
text: Array<Base> | null;
|
||||
images: Array<UgcImage> | null;
|
||||
vods: Array<UgcVod> | null;
|
||||
levels: Array<UgcLevel> | null;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Ugc结构下的图片内容
|
||||
* @since Beta v0.8.4
|
||||
* @interface UgcImage
|
||||
* @property {number} image_id - 图片ID
|
||||
* @property {string} image_url - 图片链接
|
||||
* @property {TGApp.BBS.Post.Image} image - 图片信息
|
||||
*/
|
||||
type UgcImage = { image_id: number; image_url: string; image: TGApp.BBS.Post.Image };
|
||||
|
||||
/**
|
||||
* @description Ugc结构下的视频内容
|
||||
* @since Beta v0.8.4
|
||||
* @interface UgcVod
|
||||
* @property {string} vod_id - 视频ID
|
||||
* @property {TGApp.BBS.Post.Vod} vod - 视频信息
|
||||
*/
|
||||
type UgcVod = { vod_id: string; vod: TGApp.BBS.Post.Vod };
|
||||
|
||||
/**
|
||||
* @description Ugc结构下的关卡内容
|
||||
* @since Beta v0.8.4
|
||||
* @interface UgcLevel
|
||||
* @property {string} level_id - 关卡ID
|
||||
* @property {string} region - 关卡服务器
|
||||
* @property {TGApp.BBS.UGC.Level} level - 关卡信息
|
||||
*/
|
||||
type UgcLevel = { level_id: string; region: string; level: TGApp.BBS.UGC.Level };
|
||||
}
|
||||
|
||||
133
src/types/BBS/UGC.d.ts
vendored
Normal file
133
src/types/BBS/UGC.d.ts
vendored
Normal file
@@ -0,0 +1,133 @@
|
||||
/**
|
||||
* @file types/BBS/UGC.d.ts
|
||||
* @description 千星奇域类型定义文件
|
||||
* @since Beta v0.8.4
|
||||
*/
|
||||
|
||||
declare namespace TGApp.BBS.UGC {
|
||||
/**
|
||||
* @description UGC 游戏角色信息
|
||||
* @since Beta v0.8.4
|
||||
* @interface Character
|
||||
* @property {string} region 游戏区服
|
||||
* @property {string} game_uid 游戏 UID
|
||||
* @property {string} nickname 游戏昵称
|
||||
* @property {string} user_label 用户标签
|
||||
* @property {string} region_name 区服名称
|
||||
*/
|
||||
type Character = {
|
||||
region: string;
|
||||
game_uid: string;
|
||||
nickname: string;
|
||||
user_label: string;
|
||||
region_name: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description 关卡信息
|
||||
* @since Beta v0.8.4
|
||||
* @interface Level
|
||||
* @property {string} level_id 关卡 ID
|
||||
* @property {string} region 区域
|
||||
* @property {string} level_name 关卡名称
|
||||
* @property {Cover} cover 关卡封面
|
||||
* @property {string} desc 关卡描述
|
||||
* @property {number} limit_play_num_min 最小游玩人数
|
||||
* @property {number} limit_play_num_max 最大游玩人数
|
||||
* @property {string} play_type 游玩类型
|
||||
* @property {string} good_rate 好评率
|
||||
* @property {string} hot_score 热度分数
|
||||
* @property {string} creator_uid 创建者 UID
|
||||
* @property {InteractInfo} interact_info 交互信息
|
||||
* @property {LevelAttachment} level_attachment 关卡附件
|
||||
* @property {UserPlayInfo} user_play_info 用户游玩信息
|
||||
* @property {Extra} extra 额外信息
|
||||
* @property {boolean} level_info_has_released 关卡信息是否已发布
|
||||
* @property {string} level_source_type 关卡来源类型
|
||||
* @property {string} data_box 数据盒
|
||||
* @property {string} show_limit_play_num_str 显示的游玩人数限制字符串
|
||||
* @property {string} level_intro 关卡介绍
|
||||
*/
|
||||
type Level = {
|
||||
level_id: string;
|
||||
region: string;
|
||||
level_name: string;
|
||||
cover: Cover;
|
||||
desc: string;
|
||||
limit_play_num_min: number;
|
||||
limit_play_num_max: number;
|
||||
play_type: string;
|
||||
good_rate: string;
|
||||
hot_score: string;
|
||||
creator_uid: string;
|
||||
interact_info: InteractInfo;
|
||||
level_attachment: LevelAttachment | null;
|
||||
user_play_info: UserPlayInfo;
|
||||
extra: Extra;
|
||||
level_info_has_released: boolean;
|
||||
level_source_type: string;
|
||||
data_box: string;
|
||||
show_limit_play_num_str: string;
|
||||
level_intro: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description 关卡封面
|
||||
* @since Beta v0.8.4
|
||||
* @interface Cover
|
||||
* @property {string} url 封面链接
|
||||
*/
|
||||
type Cover = { url: string };
|
||||
|
||||
/**
|
||||
* @description 用户交互信息
|
||||
* @since Beta v0.8.4
|
||||
* @interface InteractInfo
|
||||
* @property {boolean} has_fav 是否已收藏
|
||||
*/
|
||||
type InteractInfo = { has_fav: boolean };
|
||||
|
||||
/**
|
||||
* @description 关卡附件
|
||||
* @since Beta v0.8.4
|
||||
* @interface LevelAttachment
|
||||
* @property {string} type 信息类型
|
||||
* @property {string} content 信息内容
|
||||
*/
|
||||
type LevelAttachment = { type: string; content: string };
|
||||
|
||||
/**
|
||||
* @description 游玩信息
|
||||
* @since Beta v0.8.4
|
||||
* @interface UserPlayInfo
|
||||
* @property {boolean} has_played 是否已游玩
|
||||
* @property {string} played_time 游玩时间
|
||||
* @property {number} played_count 游玩次数
|
||||
*/
|
||||
type UserPlayInfo = { has_played: boolean; played_time: string; played_count: number };
|
||||
|
||||
/**
|
||||
* @description 额外信息
|
||||
* @since Beta v0.8.4
|
||||
* @interface Extra
|
||||
* @property {Array<PlayLink>} play_link 游玩链接
|
||||
* @property {boolean} friends_played 好友游玩过
|
||||
* @property {Array<unknown>} friends_played_list 好友游玩列表 // TODO: 类型待确定
|
||||
* @property {string} first_online_time 首次上线时间
|
||||
*/
|
||||
type Extra = {
|
||||
play_link: Array<PlayLink>;
|
||||
friends_played: boolean;
|
||||
friends_played_list: Array<Character>;
|
||||
first_online_time: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description 游玩链接
|
||||
* @since Beta v0.8.4
|
||||
* @interface PlayLink
|
||||
* @property {string} link_content 链接内容
|
||||
* @property {string} link_type 链接类型
|
||||
*/
|
||||
type PlayLink = { link_content: string; link_type: string };
|
||||
}
|
||||
301
src/types/Game/ActCalendar.d.ts
vendored
301
src/types/Game/ActCalendar.d.ts
vendored
@@ -1,147 +1,136 @@
|
||||
/**
|
||||
* @file src/types/Game/ActCalendar.d.ts
|
||||
* @description 游戏-活动日历相关类型定义文件
|
||||
* 游戏-活动日历相关类型定义文件
|
||||
* @since Beta v0.8.3
|
||||
*/
|
||||
|
||||
declare namespace TGApp.Game.ActCalendar {
|
||||
/**
|
||||
* @description 获取活动日历返回响应
|
||||
* 获取活动日历返回响应
|
||||
* @since Beta v0.8.0
|
||||
* @interface Response
|
||||
* @extends TGApp.BBS.Response.BaseWithData
|
||||
* @property {ActRes} data - 返回数据
|
||||
*/
|
||||
type Response = TGApp.BBS.Response.BaseWithData<ActRes>;
|
||||
|
||||
/**
|
||||
* @description 活动日历返回数据
|
||||
* 活动日历返回数据
|
||||
* @since Beta v0.8.0
|
||||
* @interface ActRes
|
||||
* @property {Array<ActPool>} avatar_card_pool_list 角色卡池列表
|
||||
* @property {Array<ActPool>} weapon_card_pool_list 武器卡池列表
|
||||
* @property {Array<ActPool>} mixed_card_pool_list 混合卡池列表
|
||||
* @property {Array<ActPool>} selected_avatar_card_pool_list 选中角色卡池列表 // TODO:未知用途
|
||||
* @property {Array<ActPool>} selected_mixed_card_pool_list 选中混合卡池列表 // TODO:未知用途
|
||||
* @property {Array<ActItem>} act_list 活动列表
|
||||
* @property {Array<ActItem>} fixed_act_list 固定活动列表
|
||||
* @property {Array<ActItem>} selected_act_list 选中活动列表 // TODO:未知用途
|
||||
*/
|
||||
type ActRes = {
|
||||
/** 角色卡池列表 */
|
||||
avatar_card_pool_list: Array<ActPool>;
|
||||
/** 武器卡池列表 */
|
||||
weapon_card_pool_list: Array<ActPool>;
|
||||
/** 混合卡池列表 */
|
||||
mixed_card_pool_list: Array<ActPool>;
|
||||
/** 选中角色卡池列表 */
|
||||
selected_avatar_card_pool_list: Array<ActPool>;
|
||||
/** 选中混合卡池列表 */
|
||||
selected_mixed_card_pool_list: Array<ActPool>;
|
||||
/** 活动列表 */
|
||||
act_list: Array<ActItem>;
|
||||
/** 固定活动列表 */
|
||||
fixed_act_list: Array<ActItem>;
|
||||
/** 选中活动列表 */
|
||||
selected_act_list: Array<ActItem>;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description 活动卡池信息
|
||||
* 活动卡池信息
|
||||
* @since Beta v0.8.0
|
||||
* @interface ActPool
|
||||
* @property {number} pool_id 卡池id
|
||||
* @property {string} version_name 游戏版本 - 5.8
|
||||
* @property {string} pool_name 卡池名称 - 角色活动祈愿
|
||||
* @property {number} pool_type 卡池类型 - 1:角色活动祈愿, 2:武器活动祈愿, 3:
|
||||
* @property {Array<ActPoolAvatar>} avatars 角色列表
|
||||
* @property {Array<ActPoolWeapon>} weapons 武器列表
|
||||
* @property {string} start_timestamp 开始时间戳(秒)
|
||||
* @property {TGApp.Game.Base.DateTime} start_time 开始时间
|
||||
* @property {string} end_timestamp 结束时间戳(秒)
|
||||
* @property {TGApp.Game.Base.DateTime} end_time 结束时间
|
||||
* @property {string} jump_url 跳转链接
|
||||
* @property {number} pool_status 卡池状态 // 1:未开始 2:进行中 3:已结束
|
||||
* @property {number} countdown_seconds 距离结束倒计时(秒)
|
||||
*/
|
||||
type ActPool = {
|
||||
/** 卡池id */
|
||||
pool_id: number;
|
||||
/** 游戏版本 - 5.8 */
|
||||
version_name: string;
|
||||
/** 卡池名称 - 角色活动祈愿 */
|
||||
pool_name: string;
|
||||
/** 卡池类型 - 1:角色活动祈愿, 2:武器活动祈愿, 3:混合活动祈愿 */
|
||||
pool_type: number;
|
||||
/** 角色列表 */
|
||||
avatars: Array<ActPoolAvatar>;
|
||||
/** 武器列表 */
|
||||
weapons: Array<ActPoolWeapon>;
|
||||
/** 开始时间戳(秒) */
|
||||
start_timestamp: string;
|
||||
/** 开始时间 */
|
||||
start_time: TGApp.Game.Base.DateTime;
|
||||
/** 结束时间戳(秒) */
|
||||
end_timestamp: string;
|
||||
/** 结束时间 */
|
||||
end_time: TGApp.Game.Base.DateTime;
|
||||
/** 跳转链接 */
|
||||
jump_url: string;
|
||||
/** 卡池状态 // 1:未开始 2:进行中 3:已结束 */
|
||||
pool_status: number;
|
||||
/** 距离结束倒计时(秒) */
|
||||
countdown_seconds: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description 角色卡池数据
|
||||
* 角色卡池数据
|
||||
* @since Beta v0.8.0
|
||||
* @interface ActPoolAvatar
|
||||
* @property {number} id 角色id
|
||||
* @property {string} icon 角色图标
|
||||
* @property {string} name 角色名称
|
||||
* @property {string} element 角色元素(英文)
|
||||
* @property {number} rarity 角色星级
|
||||
* @property {boolean} is_invisible 是否隐藏
|
||||
*/
|
||||
type ActPoolAvatar = {
|
||||
/** 角色id */
|
||||
id: number;
|
||||
/** 角色图标 */
|
||||
icon: string;
|
||||
/** 角色名称 */
|
||||
name: string;
|
||||
/** 角色元素(英文) */
|
||||
element: string;
|
||||
/** 角色星级 */
|
||||
rarity: number;
|
||||
/** 是否隐藏 */
|
||||
is_invisible: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description 武器卡池数据
|
||||
* 武器卡池数据
|
||||
* @since Beta v0.8.0
|
||||
* @interface ActPoolWeapon
|
||||
* @property {number} id 武器id
|
||||
* @property {string} icon 武器图标
|
||||
* @property {number} rarity 武器星级
|
||||
* @property {string} name 武器名称
|
||||
* @property {string} wiki_url 百科链接
|
||||
*/
|
||||
type ActPoolWeapon = {
|
||||
/** 武器id */
|
||||
id: number;
|
||||
/** 武器图标 */
|
||||
icon: string;
|
||||
/** 武器星级 */
|
||||
rarity: number;
|
||||
/** 武器名称 */
|
||||
name: string;
|
||||
/** 百科链接 */
|
||||
wiki_url: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description 活动类型枚举
|
||||
* 活动类型枚举
|
||||
* @since Beta v0.8.0
|
||||
* @const ActType
|
||||
* @todo 可能不完整,等待补充
|
||||
* @property {string} "ActTypeHardChallenge" 幽境危战
|
||||
* @property {string} "ActTypeRoleCombat" 真境剧诗
|
||||
* @property {string} "ActTypeTower" 深渊螺旋
|
||||
* @property {string} "ActTypeDouble" 双倍活动
|
||||
* @property {string} "ActTypeExplore" 探索活动
|
||||
* @property {string} "ActTypeOther" 其他活动
|
||||
* @remarks TODO:可能不完整,等待补充
|
||||
*/
|
||||
const ActType = <const>{
|
||||
/** 幽境危战 */
|
||||
HardChallenge: "ActTypeHardChallenge",
|
||||
/** 真境剧诗 */
|
||||
RoleCombat: "ActTypeRoleCombat",
|
||||
/** 深渊螺旋 */
|
||||
Tower: "ActTypeTower",
|
||||
/** 双倍活动 */
|
||||
Double: "ActTypeDouble",
|
||||
/** 探索活动 */
|
||||
Explore: "ActTypeExplore",
|
||||
/** 其他活动 */
|
||||
Other: "ActTypeOther",
|
||||
};
|
||||
|
||||
/**
|
||||
* @description 活动类型枚举
|
||||
* 活动类型枚举
|
||||
* @since Beta v0.8.0
|
||||
* @enum ActTypeEnum
|
||||
*/
|
||||
type ActTypeEnum = (typeof ActType)[keyof typeof ActType] | string;
|
||||
|
||||
/**
|
||||
* @description 活动信息
|
||||
* 活动信息
|
||||
* @since Beta v0.8.0
|
||||
* @interface ActItem
|
||||
*/
|
||||
type ActItem =
|
||||
| ActItemHardChallenge
|
||||
@@ -152,190 +141,200 @@ declare namespace TGApp.Game.ActCalendar {
|
||||
| ActItemOther;
|
||||
|
||||
/**
|
||||
* @description 活动信息-通用
|
||||
* 活动信息-通用
|
||||
* @since Beta v0.8.0
|
||||
* @interface ActItemBase
|
||||
* @template T
|
||||
* @property {number} id 活动id
|
||||
* @property {string} name 活动名称
|
||||
* @property {T} type 活动类型
|
||||
* @property {string} start_timestamp 开始时间戳(秒)
|
||||
* @property {TGApp.Game.Base.DateTime} start_time 开始时间
|
||||
* @property {string} end_timestamp 结束时间戳(秒)
|
||||
* @property {TGApp.Game.Base.DateTime} end_time 结束时间
|
||||
* @property {string} desc 活动描述
|
||||
* @property {string} strategy 活动攻略
|
||||
* @property {number} countdown_seconds 距离结束倒计时(秒)
|
||||
* @property {number} status 活动状态 // 1:未开始 2:进行中 3:已结束
|
||||
* @property {Array<ActReward>} reward_list 活动奖励列表
|
||||
* @property {boolean} is_finished 是否完成
|
||||
*/
|
||||
type ActItemBase<T extends ActTypeEnum> = {
|
||||
/** 活动id */
|
||||
id: number;
|
||||
/** 活动名称 */
|
||||
name: string;
|
||||
/** 活动类型 */
|
||||
type: T;
|
||||
/** 开始时间戳(秒) */
|
||||
start_timestamp: string;
|
||||
/** 开始时间 */
|
||||
start_time: TGApp.Game.Base.DateTime;
|
||||
/** 结束时间戳(秒) */
|
||||
end_timestamp: string;
|
||||
/** 结束时间 */
|
||||
end_time: TGApp.Game.Base.DateTime;
|
||||
/** 活动描述 */
|
||||
desc: string;
|
||||
/** 活动攻略 */
|
||||
strategy: string;
|
||||
/** 距离结束倒计时(秒) */
|
||||
countdown_seconds: number;
|
||||
/**
|
||||
* 活动状态
|
||||
* @remarks
|
||||
* 1:未开始
|
||||
* 2:进行中
|
||||
* 3:已结束
|
||||
*/
|
||||
status: number;
|
||||
/** 活动奖励列表 */
|
||||
reward_list: Array<ActReward>;
|
||||
/** 是否完成 */
|
||||
is_finished: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description 活动奖励信息
|
||||
* 活动奖励信息
|
||||
* @since Beta v0.8.0
|
||||
* @interface ActReward
|
||||
* @property {number} item_id 物品id
|
||||
* @property {string} name 物品名称
|
||||
* @property {string} icon 物品图标
|
||||
* @property {string} wiki_url 物品百科链接
|
||||
* @property {number} num 物品数量
|
||||
* @property {string} rarity 物品稀有度
|
||||
* @property {boolean} homepage_show 是否首页展示
|
||||
*/
|
||||
type ActReward = {
|
||||
/** 物品id */
|
||||
item_id: number;
|
||||
/** 物品名称 */
|
||||
name: string;
|
||||
/** 物品图标 */
|
||||
icon: string;
|
||||
/** 物品百科链接 */
|
||||
wiki_url: string;
|
||||
/** 物品数量 */
|
||||
num: number;
|
||||
/** 物品稀有度 */
|
||||
rarity: string;
|
||||
/** 是否首页展示 */
|
||||
homepage_show: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description 活动信息-幽境危战
|
||||
* 活动信息-幽境危战
|
||||
* @since Beta v0.8.0
|
||||
* @interface ActItemHardChallenge
|
||||
* @extends ActItemBase<"ActTypeHardChallenge">
|
||||
* @property {ActHardChallenge} hard_challenge_detail 幽境危战活动详情
|
||||
*/
|
||||
type ActItemHardChallenge = ActItemBase<"ActTypeHardChallenge"> & {
|
||||
/** 幽境危战活动详情 */
|
||||
hard_challenge_detail: ActHardChallenge;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description 幽境危战活动详情
|
||||
* 幽境危战活动详情
|
||||
* @since Beta v0.8.0
|
||||
* @interface ActHardChallenge
|
||||
* @property {boolean} is_unlock 是否解锁
|
||||
* @property {number} difficulty 当前难度
|
||||
* @property {number} second 挑战耗时(秒)
|
||||
* @property {string} icon 活动图标
|
||||
* @property {ActHardChallengeSub} sub 子信息
|
||||
*/
|
||||
type ActHardChallenge = {
|
||||
/** 是否解锁 */
|
||||
is_unlock: boolean;
|
||||
/** 当前难度 */
|
||||
difficulty: number;
|
||||
/** 挑战耗时(秒) */
|
||||
second: number;
|
||||
/** 活动图标 */
|
||||
icon: string;
|
||||
/** 子信息 */
|
||||
sub: ActHardChallengeSub;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description 幽境危战子信息
|
||||
* 幽境危战子信息
|
||||
* @since Beta v0.8.0
|
||||
* @interface ActHardChallengeSub
|
||||
* @property {number} seconds 挑战耗时(秒)
|
||||
* @property {number} x 未知参数
|
||||
* @property {number} y 未知参数
|
||||
*/
|
||||
type ActHardChallengeSub = { seconds: number; x: number; y: number };
|
||||
type ActHardChallengeSub = {
|
||||
/** 挑战耗时(秒) */
|
||||
seconds: number;
|
||||
/** 未知参数 */
|
||||
x: number;
|
||||
/** 未知参数 */
|
||||
y: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description 活动信息-真境剧诗
|
||||
* @since Beta v0.8.0
|
||||
* @interface ActItemRoleCombat
|
||||
* @extends ActItemBase<"ActTypeRoleCombat">
|
||||
* @property {ActRoleCombat} role_combat_detail 真境剧诗活动详情
|
||||
* 活动信息-真境剧诗
|
||||
* @since Beta v0.8.0剧诗活动详情
|
||||
*/
|
||||
type ActItemRoleCombat = ActItemBase<"ActTypeRoleCombat"> & { role_combat_detail: ActRoleCombat };
|
||||
type ActItemRoleCombat = ActItemBase<"ActTypeRoleCombat"> & {
|
||||
/** 真境剧诗活动详情 */
|
||||
role_combat_detail: ActRoleCombat;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description 真境剧诗活动详情
|
||||
* 真境剧诗活动详情
|
||||
* @since Beta v0.8.3
|
||||
* @interface ActRoleCombat
|
||||
* @property {number} difficulty_id 难度id
|
||||
* @property {boolean} has_data 是否有数据
|
||||
* @property {boolean} is_unlock 是否解锁
|
||||
* @property {number} max_round_id 最大回合数
|
||||
* @property {number} tarot_finished_cnt 塔罗牌完成数
|
||||
*/
|
||||
type ActRoleCombat = {
|
||||
/** 难度id */
|
||||
difficulty_id: number;
|
||||
/** 是否有数据 */
|
||||
has_data: boolean;
|
||||
/** 是否解锁 */
|
||||
is_unlock: boolean;
|
||||
/** 最大回合数 */
|
||||
max_round_id: number;
|
||||
/** 塔罗牌完成数 */
|
||||
tarot_finished_cnt: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description 活动信息-深渊螺旋
|
||||
* 活动信息-深渊螺旋
|
||||
* @since Beta v0.8.0
|
||||
* @interface ActItemTower
|
||||
* @extends ActItemBase<"ActTypeTower">
|
||||
* @property {ActTower} tower_detail 深渊螺旋活动详情
|
||||
*/
|
||||
type ActItemTower = ActItemBase<"ActTypeTower"> & { tower_detail: ActTower };
|
||||
type ActItemTower = ActItemBase<"ActTypeTower"> & {
|
||||
/** 深渊螺旋活动详情 */
|
||||
tower_detail: ActTower;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description 深渊螺旋活动详情
|
||||
* 深渊螺旋活动详情
|
||||
* @since Beta v0.8.0
|
||||
* @interface ActTower
|
||||
* @property {boolean} is_unlock 是否解锁
|
||||
* @property {number} max_star 最大星数
|
||||
* @property {number} total_star 总星数
|
||||
* @property {boolean} has_data 是否有数据
|
||||
*/
|
||||
type ActTower = { is_unlock: boolean; max_star: number; total_star: number; has_data: boolean };
|
||||
type ActTower = {
|
||||
/** 是否解锁 */
|
||||
is_unlock: boolean;
|
||||
/** 最大星数 */
|
||||
max_star: number;
|
||||
/** 总星数 */
|
||||
total_star: number;
|
||||
/** 是否有数据 */
|
||||
has_data: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description 活动信息-双倍活动
|
||||
* 活动信息-双倍活动
|
||||
* @since Beta v0.8.0
|
||||
* @interface ActItemDouble
|
||||
* @extends ActItemBase<"ActTypeDouble">
|
||||
* @property {ActDouble} double_detail 双倍活动详情
|
||||
*/
|
||||
type ActItemDouble = ActItemBase<"ActTypeDouble"> & { double_detail: ActDouble };
|
||||
type ActItemDouble = ActItemBase<"ActTypeDouble"> & {
|
||||
/** 双倍活动详情 */
|
||||
double_detail: ActDouble;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description 双倍活动详情
|
||||
* 双倍活动详情
|
||||
* @since Beta v0.8.0
|
||||
* @interface ActDouble
|
||||
* @property {number} total 总次数
|
||||
* @property {number} left 剩余次数
|
||||
*/
|
||||
type ActDouble = { total: number; left: number };
|
||||
type ActDouble = {
|
||||
/** 总次数 */
|
||||
total: number;
|
||||
/** 剩余次数 */
|
||||
left: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description 活动信息-探索活动
|
||||
* 活动信息-探索活动
|
||||
* @since Beta v0.8.0
|
||||
* @interface ActItemExplore
|
||||
* @extends ActItemBase<"ActTypeExplore">
|
||||
* @property {ActExplore} explore_detail 探索活动详情
|
||||
*/
|
||||
type ActItemExplore = ActItemBase<"ActTypeExplore"> & { explore_detail: ActExplore };
|
||||
type ActItemExplore = ActItemBase<"ActTypeExplore"> & {
|
||||
/** 探索活动详情 */
|
||||
explore_detail: ActExplore;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description 探索活动详情
|
||||
* 探索活动详情
|
||||
* @since Beta v0.8.0
|
||||
* @interface ActExplore
|
||||
* @property {number} explore_percent 探索进度百分比
|
||||
* @property {boolean} is_finished 是否完成
|
||||
*/
|
||||
type ActExplore = { explore_percent: number; is_finished: boolean };
|
||||
type ActExplore = {
|
||||
/** 探索进度百分比 */
|
||||
explore_percent: number;
|
||||
/** 是否完成 */
|
||||
is_finished: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description 活动信息-其他活动
|
||||
* 活动信息-其他活动
|
||||
* @since Beta v0.8.0
|
||||
* @interface ActItemOther
|
||||
* @extends ActItemBase<"ActTypeOther">
|
||||
* @property {boolean} is_finished 是否完成
|
||||
*/
|
||||
type ActItemOther = ActItemBase<"ActTypeOther"> & { is_finished: boolean };
|
||||
type ActItemOther = ActItemBase<"ActTypeOther"> & {
|
||||
/** 是否完成 */
|
||||
is_finished: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
36
src/types/Game/Base.d.ts
vendored
36
src/types/Game/Base.d.ts
vendored
@@ -1,54 +1,50 @@
|
||||
/**
|
||||
* @file types/Game/Base.d.ts
|
||||
* @description 游戏相关基础类型定义文件
|
||||
* 游戏相关基础类型定义文件
|
||||
* @since Beta v0.8.0
|
||||
*/
|
||||
|
||||
declare namespace TGApp.Game.Base {
|
||||
/**
|
||||
* @description 服务器类型
|
||||
* 服务器类型
|
||||
* @since Beta v0.8.0
|
||||
* @const ServerType
|
||||
* @property {string} "cn_gf01" - 国内-国服
|
||||
* @property {string} "cn_qd01" - 国内-渠道服
|
||||
* @property {string} "os_usa" - 海外-美国
|
||||
* @property {string} "os_euro" - 海外-欧洲
|
||||
* @property {string} "os_asia" - 海外-亚洲
|
||||
* @property {string} "os_cht" - 海外-繁体中文
|
||||
*/
|
||||
const ServerType = <const>{
|
||||
/** 国服-官方服 */
|
||||
CN_GF01: "cn_gf01",
|
||||
/** 国服-渠道服 */
|
||||
CN_QD01: "cn_qd01",
|
||||
/** 国际服-美服 */
|
||||
OS_USA: "os_usa",
|
||||
/** 国际服-欧服 */
|
||||
OS_EURO: "os_euro",
|
||||
/** 国际服-亚服 */
|
||||
OS_ASIA: "os_asia",
|
||||
/** 国际服-港澳台服 */
|
||||
OS_CHT: "os_cht",
|
||||
};
|
||||
|
||||
/**
|
||||
* @description 公告服务器类型枚举
|
||||
* 公告服务器类型枚举
|
||||
* @since Beta v0.8.0
|
||||
* @enum ServerTypeEnum
|
||||
*/
|
||||
type ServerTypeEnum = (typeof ServerType)[keyof typeof ServerType];
|
||||
|
||||
/**
|
||||
* @description 时间类型
|
||||
* 时间类型
|
||||
* @since Beta v0.8.0
|
||||
* @interface DateTime
|
||||
* @property {number} year - 年份
|
||||
* @property {number} month - 月份(1-12)
|
||||
* @property {number} day - 日(1-31)
|
||||
* @property {number} hour - 小时(0-23)
|
||||
* @property {number} minute - 分钟(0-59)
|
||||
* @property {number} second - 秒(0-59)
|
||||
*/
|
||||
type DateTime = {
|
||||
/** 年份 */
|
||||
year: number;
|
||||
/** 月份(1-12) */
|
||||
month: number;
|
||||
/** 日(1-31) */
|
||||
day: number;
|
||||
/** 小时(0-23) */
|
||||
hour: number;
|
||||
/** 分钟(0-59) */
|
||||
minute: number;
|
||||
/** 秒(0-59) */
|
||||
second: number;
|
||||
};
|
||||
}
|
||||
|
||||
119
src/types/Game/Gacha.d.ts
vendored
119
src/types/Game/Gacha.d.ts
vendored
@@ -1,53 +1,59 @@
|
||||
/**
|
||||
* @file types/Game/Gacha.d.ts
|
||||
* @description 游戏抽卡相关类型定义文件
|
||||
* @since Beta v0.3.0
|
||||
* @since Beta v0.8.4
|
||||
*/
|
||||
|
||||
/**
|
||||
* @description 游戏抽卡相关类型定义命名空间
|
||||
* @since Beta v0.3.0
|
||||
* @namespace TGApp.Game.Gacha
|
||||
* @memberof TGApp.Game
|
||||
*/
|
||||
declare namespace TGApp.Game.Gacha {
|
||||
/**
|
||||
* @description 获取 authkey 返回类型
|
||||
* @interface AuthkeyResponse
|
||||
* @since Beta v0.3.0
|
||||
* @interface AuthkeyResp
|
||||
* @since Beta v0.8.4
|
||||
* @extends TGApp.BBS.Response.BaseWithData
|
||||
* @property {number} data.sign_type - 签名类型
|
||||
* @property {number} data.authkey_ver - authkey 版本
|
||||
* @property {string} data.authkey - authkey
|
||||
* @return AuthkeyResponse
|
||||
*/
|
||||
interface AuthkeyResponse extends TGApp.BBS.Response.BaseWithData {
|
||||
data: {
|
||||
sign_type: number;
|
||||
authkey_ver: number;
|
||||
authkey: string;
|
||||
};
|
||||
}
|
||||
type AuthkeyResp = TGApp.BBS.Response.BaseWithData<AuthkeyRes>;
|
||||
|
||||
/**
|
||||
* @description 获取 authkey 数据类型
|
||||
* @interface AuthkeyRes
|
||||
* @since Beta v0.8.4
|
||||
* @property {number} sign_type - 签名类型
|
||||
* @property {number} authkey_ver - authkey 版本
|
||||
* @property {string} authkey - authkey
|
||||
*/
|
||||
type AuthkeyRes = { sign_type: number; authkey_ver: number; authkey: string };
|
||||
|
||||
/**
|
||||
* @description 获取抽卡记录返回类型
|
||||
* @interface GachaLogResponse
|
||||
* @since Beta v0.3.0
|
||||
* @interface GachaLogResp
|
||||
* @since Beta v0.8.4
|
||||
* @extends TGApp.BBS.Response.BaseWithData
|
||||
* @property {number} data.page - 页码
|
||||
* @property {number} data.size - 每页大小
|
||||
* @property {number} data.total - 总数
|
||||
* @property {GachaItem[]} data.list - 抽卡记录列表
|
||||
* @return GachaLogResponse
|
||||
*/
|
||||
interface GachaLogResponse extends TGApp.BBS.Response.BaseWithData {
|
||||
data: {
|
||||
page: number;
|
||||
size: number;
|
||||
total: number;
|
||||
list: GachaItem[];
|
||||
};
|
||||
}
|
||||
type GachaLogResp = TGApp.BBS.Response.BaseWithData<GachaLogRes>;
|
||||
|
||||
/**
|
||||
* @description 获取千星奇域抽卡记录返回类型
|
||||
* @interface GachaBLogResp
|
||||
* @since Beta v0.8.4
|
||||
* @extends TGApp.BBS.Response.BaseWithData
|
||||
*/
|
||||
type GachaBLogResp = TGApp.BBS.Response.BaseWithData<GachaBLogRes>;
|
||||
|
||||
/**
|
||||
* @description 抽卡记录返回数据类型
|
||||
* @interface GachaLogRes
|
||||
* @since Beta v0.8.4
|
||||
* @property {number} page - 页码
|
||||
* @property {number} size - 每页大小
|
||||
* @property {number} total - 总数
|
||||
* @property {Array<GachaItem>} list - 抽卡记录列表
|
||||
*/
|
||||
type GachaLogRes = {
|
||||
page: number;
|
||||
size: number;
|
||||
total: number;
|
||||
list: Array<GachaItem>;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description 抽卡记录类型
|
||||
@@ -65,7 +71,7 @@ declare namespace TGApp.Game.Gacha {
|
||||
* @property {string} id - 抽卡记录 id
|
||||
* @return GachaItem
|
||||
*/
|
||||
interface GachaItem {
|
||||
type GachaItem = {
|
||||
uid: string;
|
||||
gacha_type: string;
|
||||
item_id: string;
|
||||
@@ -76,5 +82,44 @@ declare namespace TGApp.Game.Gacha {
|
||||
item_type: string;
|
||||
rank_type: string;
|
||||
id: number;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @description 千星奇域抽卡记录返回数据类型
|
||||
* @interface GachaBLogRes
|
||||
* @since Beta v0.8.4
|
||||
* @property {Array<GachaBItem>} list - 页码
|
||||
* @property {number} total - 总数
|
||||
*/
|
||||
type GachaBLogRes = { list: Array<GachaBItem>; total: number };
|
||||
|
||||
/**
|
||||
* @description 千星奇域抽卡记录类型
|
||||
* @interface GachaBItem
|
||||
* @since Beta v0.8.4
|
||||
* @property {string} id - 抽卡记录 id
|
||||
* @property {string} is_up - 是否为UP池,0-否,1-是
|
||||
* @property {string} item_id - 物品 id
|
||||
* @property {string} item_name - 物品名称
|
||||
* @property {string} item_type - 物品类型
|
||||
* @property {string} op_gacha_type - 抽卡类型,用于接口请求
|
||||
* @property {string} rank_type - 星级
|
||||
* @property {string} region - 区域
|
||||
* @property {string} schedule_id - 排期ID
|
||||
* @property {string} time - 抽卡时间,格式:yyyy-MM-dd HH:mm:ss
|
||||
* @property {string} uid - 用户 uid
|
||||
*/
|
||||
type GachaBItem = {
|
||||
id: string;
|
||||
is_up: string;
|
||||
item_id: string;
|
||||
item_name: string;
|
||||
item_type: string;
|
||||
op_gacha_type: string;
|
||||
rank_type: string;
|
||||
region: string;
|
||||
schedule_id: string;
|
||||
time: string;
|
||||
uid: string;
|
||||
};
|
||||
}
|
||||
|
||||
277
src/types/Plugins/JSBridge.d.ts
vendored
277
src/types/Plugins/JSBridge.d.ts
vendored
@@ -1,247 +1,246 @@
|
||||
/**
|
||||
* @file types/Plugins/JSBridge.d.ts
|
||||
* @description JSBridge 插件相关类型定义文件
|
||||
* @since Beta v0.6.0
|
||||
* JSBridge 插件相关类型定义文件
|
||||
* @since Beta v0.8.4
|
||||
*/
|
||||
|
||||
/**
|
||||
* @description JSBridge 插件相关类型命名
|
||||
* @since Beta v0.6.0
|
||||
* @namespace TGApp.Plugins.JSBridge
|
||||
* @memberof TGApp.Plugins
|
||||
*/
|
||||
declare namespace TGApp.Plugins.JSBridge {
|
||||
/**
|
||||
* @description JSBridge 通用 arg 参数
|
||||
* JSBridge 通用 arg 参数
|
||||
* @since Beta v0.3.9
|
||||
* @interface Arg
|
||||
* @template T
|
||||
* @property {string} method - 方法名
|
||||
* @property {T} payload - 参数
|
||||
* @property {string} callback - 回调函数名
|
||||
* @return Arg
|
||||
*/
|
||||
interface Arg<T> {
|
||||
type Arg<T> = {
|
||||
/** 方法名 */
|
||||
method: string;
|
||||
/** 参数 */
|
||||
payload: T;
|
||||
/** 回调函数名 */
|
||||
callback: string;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @description 通用 arg 参数-无参数
|
||||
* 通用 arg 参数-无参数
|
||||
* @since Beta v0.3.9
|
||||
* @interface NullArg
|
||||
* @return NullArg
|
||||
*/
|
||||
type NullArg = Arg<null>;
|
||||
|
||||
/**
|
||||
* @description configShare 方法参数
|
||||
* configShare 方法参数
|
||||
* @since Beta v0.3.9
|
||||
* @interface ConfigSharePayload
|
||||
* @property {boolean} enable - 是否启用分享
|
||||
* @return ConfigSharePayload
|
||||
*/
|
||||
interface ConfigSharePayload {
|
||||
type ConfigSharePayload = {
|
||||
/** 是否启用分享 */
|
||||
enable: boolean;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @description eventTrack 方法参数
|
||||
* eventTrack 方法参数
|
||||
* @since Beta v0.3.9
|
||||
* @interface EventTrackPayload
|
||||
* @property {object} pageInfo - 页面信息
|
||||
* @property {string} pageInfo.page_path - 页面路径
|
||||
* @property {string} pageInfo.page_name - 页面名称
|
||||
* @property {string} pageInfo.sub_page_path - 子页面路径
|
||||
* @property {string} pageInfo.sub_page_name - 子页面名称
|
||||
* @property {string} pageInfo.source_path - 来源页面路径
|
||||
* @property {string} pageInfo.source_name - 来源页面名称
|
||||
* @property {string} pageInfo.page_id - 页面 ID
|
||||
* @property {string} pageInfo.page_type - 页面类型
|
||||
* @property {string} pageInfo.source_id - 来源 ID
|
||||
* @property {unknown} pageInfo.extra_info - 额外信息
|
||||
* @property {object} eventInfo - 事件信息
|
||||
* @property {string} eventInfo.time - 事件时间
|
||||
* @property {number} eventInfo.action_id - 事件 ID
|
||||
* @property {string} eventInfo.btn_name - 按钮名称
|
||||
* @property {string} eventInfo.module_id - 模块 ID
|
||||
* @property {string} eventInfo.module_name - 模块名称
|
||||
* @property {unknown} eventInfo.extra_info - 额外信息
|
||||
* @property {object} commonInfo - 公共信息
|
||||
* @property {object} commonInfo.extra_info - 额外信息
|
||||
* @property {string} commonInfo.extra_info.game_id - 游戏 ID
|
||||
* @property {string} commonInfo.extra_info.view_type - 视图类型
|
||||
* @return EventTrackPayload
|
||||
*/
|
||||
interface EventTrackPayload {
|
||||
type EventTrackPayload = {
|
||||
/** 页面信息 */
|
||||
pageInfo: {
|
||||
/** 页面路径 */
|
||||
page_path: string;
|
||||
/** 页面名称 */
|
||||
page_name: string;
|
||||
/** 子页面路径 */
|
||||
sub_page_path: string;
|
||||
/** 子页面名称 */
|
||||
sub_page_name: string;
|
||||
/** 来源页面路径 */
|
||||
source_path: string;
|
||||
/** 来源页面名称 */
|
||||
source_name: string;
|
||||
/** 页面 ID */
|
||||
page_id: string;
|
||||
/** 页面类型 */
|
||||
page_type: string;
|
||||
/** 来源 ID */
|
||||
source_id: string;
|
||||
/** 额外信息 */
|
||||
extra_info: unknown;
|
||||
};
|
||||
/** 事件信息 */
|
||||
eventInfo: {
|
||||
/** 事件发生时间 */
|
||||
time: string;
|
||||
/** 事件 ID */
|
||||
action_id: number;
|
||||
/** 按钮名称 */
|
||||
btn_name: string;
|
||||
/** 模块 ID */
|
||||
module_id: string;
|
||||
/** 模块名称 */
|
||||
module_name: string;
|
||||
/** 额外信息 */
|
||||
extra_info: unknown;
|
||||
};
|
||||
/** 公共信息 */
|
||||
commonInfo: {
|
||||
/** 额外信息 */
|
||||
extra_info: {
|
||||
/** 游戏 ID */
|
||||
game_id: string;
|
||||
/** 视图类型 */
|
||||
view_type: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @description getActionTicket 方法参数
|
||||
* getActionTicket 方法参数
|
||||
* @since Beta v0.3.9
|
||||
* @interface GetActionTicketPayload
|
||||
* @property {string} action_type
|
||||
* @return GetActionTicketPayload
|
||||
*/
|
||||
interface GetActionTicketPayload {
|
||||
type GetActionTicketPayload = {
|
||||
/** 行为类型 */
|
||||
action_type: string;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @description genAuthkey 方法参数
|
||||
* genAuthkey 方法参数
|
||||
* @since Beta v0.3.9
|
||||
* @interface GenAuthkeyPayload
|
||||
* @return GenAuthkeyPayload
|
||||
*/
|
||||
type GenAuthkeyPayload = Record<string, string>;
|
||||
|
||||
/**
|
||||
* @description getCookieToken 方法参数
|
||||
* getCookieToken 方法参数
|
||||
* @since Beta v0.3.9
|
||||
* @interface GetCookieTokenPayload
|
||||
* @property {boolean} forceRefresh - 是否强制刷新
|
||||
* @return GetCookieTokenPayload
|
||||
*/
|
||||
interface GetCookieTokenPayload {
|
||||
type GetCookieTokenPayload = {
|
||||
/** 是否强制刷新 */
|
||||
forceRefresh: boolean;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @description getDS2 方法参数
|
||||
* getDS2 方法参数
|
||||
* @since Beta v0.3.9
|
||||
* @interface GetDS2Payload
|
||||
* @property {Record<string,string|number>|string} query - 查询参数
|
||||
* @property {Record<string,string|number>|string} body - 请求体
|
||||
* @return GetDS2Payload
|
||||
*/
|
||||
interface GetDS2Payload {
|
||||
type GetDS2Payload = {
|
||||
/** 查询参数 */
|
||||
query: Record<string, string | number> | string;
|
||||
/** 请求体 */
|
||||
body: Record<string, string | number> | string;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @description onClickImg 方法参数
|
||||
* onClickImg 方法参数
|
||||
* @since Beta v0.3.9
|
||||
* @interface OnClickImgPayload
|
||||
* @property {Array<object>} image_list - 图片列表
|
||||
* @property {string} image_list[].url - 图片链接
|
||||
* @property {string} image_list[].format - 图片格式
|
||||
* @return OnClickImgPayload
|
||||
*/
|
||||
interface OnClickImgPayload {
|
||||
type OnClickImgPayload = {
|
||||
/** 图片列表 */
|
||||
image_list: Array<{
|
||||
/** 图片链接 */
|
||||
url: string;
|
||||
/** 图片格式 */
|
||||
format: string;
|
||||
}>;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @description openApplication 方法参数
|
||||
* openApplication 方法参数
|
||||
* @since Beta v0.3.9
|
||||
* @interface OpenApplicationPayload
|
||||
* @property {number} gameCenterId - 游戏中心对应 id
|
||||
* @return OpenApplicationPayload
|
||||
*/
|
||||
interface OpenApplicationPayload {
|
||||
type OpenApplicationPayload = {
|
||||
/** 游戏中心对应 id */
|
||||
gameCenterId: number;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @description 打开系统浏览器
|
||||
* openSystemBrowser 方法参数
|
||||
* @since Beta v0.6.0
|
||||
* @interface OpenSystemBrowserPayload
|
||||
* @property {string} open_url - 打开的链接
|
||||
* @return OpenSystemBrowserPayload
|
||||
*/
|
||||
interface OpenSystemBrowserPayload {
|
||||
type OpenSystemBrowserPayload = {
|
||||
/** 打开的链接 */
|
||||
open_url: string;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @description pushPage 方法参数
|
||||
* pushPage 方法参数
|
||||
* @since Beta v0.3.9
|
||||
* @interface PushPagePayload
|
||||
* @property {string} page - 页面地址
|
||||
* @return PushPagePayload
|
||||
*/
|
||||
interface PushPagePayload {
|
||||
type PushPagePayload = {
|
||||
/** 页面地址 */
|
||||
page: string;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @description setPresentationStyle 方法参数
|
||||
* setPresentationStyle 方法参数
|
||||
* @since Beta v0.3.9
|
||||
* @interface SetPresentationStylePayload
|
||||
* @property {string} style - 窗口样式
|
||||
* @property {unknown} navigationBar - 导航栏
|
||||
* @property {string} statusBar.style - 状态栏模式
|
||||
* @return SetPresentationStylePayload
|
||||
*/
|
||||
interface SetPresentationStylePayload {
|
||||
type SetPresentationStylePayload = {
|
||||
/** 窗口样式 */
|
||||
style: string;
|
||||
/** 导航栏 */
|
||||
navigationBar: unknown;
|
||||
/** 状态栏模式 */
|
||||
statusBar: {
|
||||
/** 状态栏模式 */
|
||||
style: string;
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @description share 方法参数
|
||||
* share 方法参数
|
||||
* @since Beta v0.3.9
|
||||
* @interface SharePayload
|
||||
* @property {string} type - 分享类型 // screenshot
|
||||
* @property {object} content - 分享内容
|
||||
* @property {boolean} content?.preview - 是否预览
|
||||
* @return SharePayload
|
||||
*/
|
||||
type SharePayload =
|
||||
| {
|
||||
type: "default";
|
||||
content: {
|
||||
title: string;
|
||||
description: string;
|
||||
link: string;
|
||||
image_url: string;
|
||||
};
|
||||
}
|
||||
| {
|
||||
type: "screenshot";
|
||||
content: {
|
||||
preview: boolean;
|
||||
};
|
||||
}
|
||||
| {
|
||||
type: "image";
|
||||
content: {
|
||||
image_url?: string;
|
||||
image_base64?: string;
|
||||
};
|
||||
};
|
||||
type SharePayload = SharePayloadDefault | SharePayloadScreenshot | SharePayloadImage;
|
||||
|
||||
/**
|
||||
* share 方法参数-默认分享
|
||||
* @since Beta v0.8.4
|
||||
*/
|
||||
type SharePayloadDefault = {
|
||||
/** 分享类型,默认值"default" */
|
||||
type: "default";
|
||||
/** 分享内容 */
|
||||
content: {
|
||||
/** 标题 */
|
||||
title: string;
|
||||
/** 描述 */
|
||||
description: string;
|
||||
/** 链接 */
|
||||
link: string;
|
||||
/** 图片链接 */
|
||||
image_url: string;
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* share 方法参数-截图分享
|
||||
* @since Beta v0.8.4
|
||||
*/
|
||||
type SharePayloadScreenshot = {
|
||||
/** 分享类型,值为"screenshot" */
|
||||
type: "screenshot";
|
||||
/** 分享内容 */
|
||||
content: {
|
||||
/** 是否预览 */
|
||||
preview: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* share 方法参数-图片分享
|
||||
* @since Beta v0.8.4
|
||||
*/
|
||||
type SharePayloadImage = {
|
||||
/** 分享类型,值为"image" */
|
||||
type: "image";
|
||||
/** 分享内容 */
|
||||
content: {
|
||||
/** 图片链接(可选) */
|
||||
image_url?: string;
|
||||
/** 图片的 Base64 编码(可选) */
|
||||
image_base64?: string;
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* getRegionRoleInfo 方法参数
|
||||
* @since Beta v0.8.4
|
||||
*/
|
||||
type GetRegionRoleInfoPayload = {
|
||||
/** 游戏 biz 标识 */
|
||||
game_biz: string;
|
||||
};
|
||||
}
|
||||
|
||||
300
src/types/Plugins/UIGF.d.ts
vendored
300
src/types/Plugins/UIGF.d.ts
vendored
@@ -1,161 +1,165 @@
|
||||
/**
|
||||
* @file types/Plugins/UIGF.d.ts
|
||||
* @description UIGF 插件类型定义文件
|
||||
* @since Beta v0.5.1
|
||||
* @version UIGF v3.0 | UIGF v4.0
|
||||
* UIGF 标准类型定义文件
|
||||
* @since Beta v0.8.4
|
||||
* @version UIGF v3.0 | UIGF v4.1
|
||||
*/
|
||||
|
||||
declare namespace TGApp.Plugins.UIGF {
|
||||
/**
|
||||
* @description UIGF 数据
|
||||
* UIGF 数据
|
||||
* @since Beta v0.5.0
|
||||
* @interface Schema
|
||||
* @property {Info} info - UIGF 头部信息
|
||||
* @property {GachaItem[]} list - UIGF 祈愿列表
|
||||
* @return Schema
|
||||
*/
|
||||
interface Schema {
|
||||
info: Info;
|
||||
list: GachaItem[];
|
||||
}
|
||||
|
||||
/**
|
||||
* @description UIGF 数据, v4.0
|
||||
* @since Beta v0.5.0
|
||||
* @interface Schema4
|
||||
* @property {Info4} info - UIGF 头部信息
|
||||
* @property {GachaHk4e[]} hk4e - UIGF 祈愿列表,原神数据
|
||||
* @return Schema4
|
||||
*/
|
||||
interface Schema4 {
|
||||
info: Info4;
|
||||
hk4e: GachaHk4e[];
|
||||
}
|
||||
|
||||
/**
|
||||
* @description UIGF 头部信息
|
||||
* @since Beta v0.5.0
|
||||
* @interface Info
|
||||
* @see docs\UIGF.md
|
||||
* @property {string} uid - UID
|
||||
* @property {string} lang - 语言
|
||||
* @property {string} uigf_version - UIGF 版本,应用使用的是 2.3.0
|
||||
* @property {number} export_timestamp - 导出时间戳(秒)
|
||||
* @property {string} export_time - 导出时间 yyyy-MM-dd HH:mm:ss
|
||||
* @property {string} export_app - 导出应用
|
||||
* @property {string} export_app_version - 导出应用版本
|
||||
* @property {number} region_time_zone - 时区
|
||||
* @return Info
|
||||
*/
|
||||
interface Info {
|
||||
uid: string;
|
||||
lang: string;
|
||||
uigf_version: string;
|
||||
export_timestamp?: number;
|
||||
export_time?: string;
|
||||
export_app?: string;
|
||||
export_app_version?: string;
|
||||
region_time_zone?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description UIGF 头部信息, v4.0
|
||||
* @since Beta v0.5.1
|
||||
* @interface Info4
|
||||
* @see docs\UIGF4.md
|
||||
* @property {string} export_timestamp - 导出时间戳(秒)
|
||||
* @property {string} export_app - 导出应用
|
||||
* @property {string} export_app_version - 导出应用版本
|
||||
* @property {string} version - UIGF 版本
|
||||
* @property {string} lang - 语言
|
||||
* @return Info4
|
||||
*/
|
||||
interface Info4 {
|
||||
export_timestamp: string;
|
||||
export_app: string;
|
||||
export_app_version: string;
|
||||
version: string;
|
||||
lang?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 祈愿类型
|
||||
* @since Alpha v0.2.3
|
||||
* @enum EnumGachaType
|
||||
* @property {string} CharacterUp - 角色活动祈愿
|
||||
* @property {string} CharacterUp2 - 角色活动祈愿2
|
||||
* @property {string} WeaponUp - 武器活动祈愿
|
||||
* @property {string} Normal - 普通祈愿
|
||||
* @property {string} Newbie - 新手祈愿
|
||||
* @return EnumGachaType
|
||||
*/
|
||||
enum EnumGachaType {
|
||||
CharacterUp = "301",
|
||||
CharacterUp2 = "400",
|
||||
WeaponUp = "302",
|
||||
Normal = "200",
|
||||
Newbie = "100",
|
||||
}
|
||||
|
||||
/**
|
||||
* @description UIGF 祈愿类型
|
||||
* @since Alpha v0.2.3
|
||||
* @enum EnumUigfGachaType
|
||||
* @property {string} CharacterUp - 角色活动祈愿&角色活动祈愿2
|
||||
* @property {string} WeaponUp - 武器活动祈愿
|
||||
* @property {string} Normal - 普通祈愿
|
||||
* @property {string} Newbie - 新手祈愿
|
||||
* @return EnumUigfGachaType
|
||||
*/
|
||||
enum EnumUigfGachaType {
|
||||
CharacterUp = "301",
|
||||
WeaponUp = "302",
|
||||
Normal = "200",
|
||||
Newbie = "100",
|
||||
}
|
||||
|
||||
/**
|
||||
* @description UIGF 祈愿列表
|
||||
* @Beta v0.5.1
|
||||
* @version UIGF v3.0
|
||||
* @interface GachaItem
|
||||
* @property {EnumGachaType} gacha_type - 祈愿类型
|
||||
* @property {string} item_id - 物品ID
|
||||
* @property {string} count - 数量
|
||||
* @property {string} time - 时间 yyyy-MM-dd HH:mm:ss
|
||||
* @property {string} name - 名称
|
||||
* @property {string} item_type - 物品类型
|
||||
* @property {string} rank_type - 稀有度
|
||||
* @property {string} id - ID
|
||||
* @property {EnumUigfGachaType} uigf_gacha_type - UIGF 祈愿类型
|
||||
* @return GachaItem
|
||||
*/
|
||||
interface GachaItem {
|
||||
uigf_gacha_type: string;
|
||||
gacha_type: string;
|
||||
item_id: string;
|
||||
count?: string;
|
||||
time: string;
|
||||
name: string;
|
||||
item_type?: string;
|
||||
rank_type?: string;
|
||||
id: string;
|
||||
}
|
||||
type Schema = {
|
||||
/** 头部信息 */
|
||||
info: Info;
|
||||
/** 祈愿列表 */
|
||||
list: Array<GachaItem>;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description UIGF 祈愿列表, v4.0,原神数据
|
||||
* @since Beta v0.5.0
|
||||
* @interface GachaHk4e
|
||||
* @property {string|number} uid - UID
|
||||
* @property {number} timezone - 时区
|
||||
* @property {string} lang - 语言
|
||||
* @property {GachaItem[]} list - 祈愿列表
|
||||
* @return GachaHk4e
|
||||
* UIGF 数据, v4.0
|
||||
* @since Beta v0.8.4
|
||||
*/
|
||||
interface GachaHk4e {
|
||||
uid: string | number;
|
||||
timezone: number;
|
||||
type Schema4 = {
|
||||
/** 头部信息 */
|
||||
info: Info4;
|
||||
/** 祈愿列表,原神数据 */
|
||||
hk4e: Array<GachaHk4e>;
|
||||
/** 祈愿列表,千星奇域数据 */
|
||||
hk4e_ugc?: Array<GachaUgc>;
|
||||
};
|
||||
|
||||
/**
|
||||
* UIGF 头部信息
|
||||
* @since Beta v0.5.0
|
||||
* @version UIGF v3.0
|
||||
*/
|
||||
type Info = {
|
||||
/** UID */
|
||||
uid: string;
|
||||
/** 语言 */
|
||||
lang: string;
|
||||
/** UIGF 版本,应用使用的是 3.0 */
|
||||
uigf_version: string;
|
||||
/** 导出时间戳(秒) */
|
||||
export_timestamp?: number;
|
||||
/** 导出时间 yyyy-MM-dd HH:mm:ss */
|
||||
export_time?: string;
|
||||
/** 导出应用 */
|
||||
export_app?: string;
|
||||
/** 导出应用版本 */
|
||||
export_app_version?: string;
|
||||
/** 时区 */
|
||||
region_time_zone?: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* UIGF 头部信息
|
||||
* @since Beta v0.5.1
|
||||
* @version v4.0+
|
||||
*/
|
||||
type Info4 = {
|
||||
/** 导出时间戳(秒) */
|
||||
export_timestamp: string;
|
||||
/** 导出应用 */
|
||||
export_app: string;
|
||||
/** 导出应用版本 */
|
||||
export_app_version: string;
|
||||
/** UIGF 版本 */
|
||||
version: string;
|
||||
/** 语言 */
|
||||
lang?: string;
|
||||
list: GachaItem[];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* UIGF4 祈愿项,原神
|
||||
* @since Beta v0.5.0
|
||||
*/
|
||||
type GachaHk4e = {
|
||||
/** UID */
|
||||
uid: string | number;
|
||||
/** 时区 */
|
||||
timezone: number;
|
||||
/** 语言 */
|
||||
lang?: string;
|
||||
list: Array<GachaItem>;
|
||||
};
|
||||
|
||||
/**
|
||||
* UIGF4 祈愿项,千星奇域
|
||||
* @since Beta v0.8.4
|
||||
* @remarks 该标准尚未最终确定
|
||||
*/
|
||||
type GachaUgc = {
|
||||
/** UID */
|
||||
uid: string | number;
|
||||
/** 时区 */
|
||||
timezone: number;
|
||||
/** 语言 */
|
||||
lang?: string;
|
||||
/** 服务器区域 */
|
||||
region: string;
|
||||
/** 祈愿列表 */
|
||||
list: Array<GachaItemB>;
|
||||
};
|
||||
|
||||
/**
|
||||
* UIGF 祈愿项-原神
|
||||
* @since Beta v0.5.1
|
||||
* @version UIGF v3.0
|
||||
*/
|
||||
type GachaItem = {
|
||||
/** UIGF 祈愿类型 */
|
||||
uigf_gacha_type: string;
|
||||
/** 祈愿类型 */
|
||||
gacha_type: string;
|
||||
/** 物品ID */
|
||||
item_id: string;
|
||||
/** 数量 */
|
||||
count?: string;
|
||||
/** 时间 yyyy-MM-dd HH:mm:ss */
|
||||
time: string;
|
||||
/** 名称 */
|
||||
name: string;
|
||||
/** 物品类型 */
|
||||
item_type?: string;
|
||||
/** 稀有度 */
|
||||
rank_type?: string;
|
||||
/** ID */
|
||||
id: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* UIGF 祈愿项-千星奇域
|
||||
* @since Beta v0.8.4
|
||||
* @remarks 该标准尚未最终确定
|
||||
*/
|
||||
type GachaItemB = {
|
||||
/** id */
|
||||
id: string;
|
||||
/** 排期id */
|
||||
schedule_id: string;
|
||||
/** 物品类型 */
|
||||
item_type: string;
|
||||
/** 物品id */
|
||||
item_id: string;
|
||||
/** 名称 */
|
||||
item_name: string;
|
||||
/** 稀有度 */
|
||||
rank_type: string;
|
||||
/** 是否限定 */
|
||||
is_up: string;
|
||||
/** 时间 yyyy-MM-dd HH:mm:ss */
|
||||
time: string;
|
||||
/**
|
||||
* 祈愿类型
|
||||
* @remarks
|
||||
* 1000-常驻池
|
||||
* 2000-活动池
|
||||
*/
|
||||
gacha_type: string;
|
||||
/** 祈愿类型,用于接口请求 */
|
||||
op_gacha_type: string;
|
||||
};
|
||||
}
|
||||
|
||||
109
src/types/Sqlite/GachaRecords.d.ts
vendored
109
src/types/Sqlite/GachaRecords.d.ts
vendored
@@ -1,39 +1,108 @@
|
||||
/**
|
||||
* @file types Sqlite GachaRecords.d.ts
|
||||
* @description 数据库抽卡记录相关类型定义文件
|
||||
* @author BTMuli <bt-muli@outlook.com>
|
||||
* @since Alpha v0.2.3
|
||||
* 数据库抽卡记录相关类型定义文件
|
||||
* @since Beta v0.8.4
|
||||
*/
|
||||
|
||||
declare namespace TGApp.Sqlite.GachaRecords {
|
||||
/**
|
||||
* @description 数据库-抽卡记录表
|
||||
* 原神抽卡记录表类型定义
|
||||
* @since Alpha v0.2.3
|
||||
* @interface SingleTable
|
||||
* @property {string} id - 抽卡记录 ID
|
||||
* @property {string} uid - UID
|
||||
* @property {string} gachaType - 抽卡类型
|
||||
* @property {string} uigfType - UIGF 类型
|
||||
* @property {string} time - 抽卡时间
|
||||
* @property {string} itemId - 抽卡物品 ID
|
||||
* @property {string} name - 抽卡物品名称
|
||||
* @property {string} type - 抽卡物品类型
|
||||
* @property {string} rank - 抽卡物品星级
|
||||
* @property {string} count - 抽卡物品数量
|
||||
* @property {string} updated - 数据库更新时间
|
||||
* @return SingleTable
|
||||
*/
|
||||
interface SingleTable {
|
||||
type TableGacha = {
|
||||
/** 抽卡记录 ID */
|
||||
id: string;
|
||||
/** UID */
|
||||
uid: string;
|
||||
/** 抽卡类型 */
|
||||
gachaType: string;
|
||||
/** UIGF 类型 */
|
||||
uigfType: string;
|
||||
/**
|
||||
* 抽卡时间
|
||||
* @remarks
|
||||
* 从接口获取的数据均为 UTC+8 时间
|
||||
* 从外部导入数据也转换为 UTC+8 时间
|
||||
*/
|
||||
time: string;
|
||||
/** 抽卡物品 ID */
|
||||
itemId: string;
|
||||
/**
|
||||
* 抽卡物品名称
|
||||
* @remarks
|
||||
* 从接口获取的数据均为中文名称
|
||||
* 从外部导入数据从本地字典中获取中文名称
|
||||
*/
|
||||
name: string;
|
||||
/** 抽卡物品类型 */
|
||||
type: string;
|
||||
/** 抽卡物品星级 */
|
||||
rank: string;
|
||||
/**
|
||||
* 抽卡物品数量
|
||||
* @remarks 恒为 "1"
|
||||
*/
|
||||
count: string;
|
||||
/** 数据库更新时间 */
|
||||
updated: string;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 千星奇域抽卡记录表类型定义
|
||||
* @since Beta v0.8.4
|
||||
*/
|
||||
type TableGachaB = {
|
||||
/** 抽卡记录 ID */
|
||||
id: string;
|
||||
/** UID */
|
||||
uid: string;
|
||||
/** 服务器区域 */
|
||||
region: string;
|
||||
/** 排期 ID */
|
||||
scheduleId: string;
|
||||
/**
|
||||
* 抽卡类型
|
||||
* @remarks
|
||||
* 1000-常驻池
|
||||
* 2000-活动池
|
||||
* 20011-男活动池
|
||||
* 20012-男活动池2
|
||||
* 20021-女活动池
|
||||
* 20022-女活动池2
|
||||
*/
|
||||
gachaType: string;
|
||||
/**
|
||||
* 抽卡类型(接口用)
|
||||
* @remarks
|
||||
* 100-常驻池
|
||||
* 200-活动池
|
||||
*/
|
||||
opGachaType: string;
|
||||
/** 抽卡时间 */
|
||||
time: string;
|
||||
/** 抽卡物品 ID */
|
||||
itemId: string;
|
||||
/**
|
||||
* 抽卡物品名称
|
||||
* @remarks
|
||||
* 从接口获取到的为中文名称
|
||||
* 从外部导入数据需要转换为中文名称
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* 抽卡物品类型
|
||||
* @remarks
|
||||
* 从接口获取到的为中文名称
|
||||
* 从外部导入数据需要转换为中文名称
|
||||
*/
|
||||
type: string;
|
||||
/** 抽卡物品星级 */
|
||||
rank: string;
|
||||
/**
|
||||
* 是否是 UP 物品
|
||||
* @remarks 0-否,1-是
|
||||
*/
|
||||
isUp: string;
|
||||
/** 数据库更新时间 */
|
||||
updated: string;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,27 +1,26 @@
|
||||
/**
|
||||
* @file utils/TGBbs.ts
|
||||
* @description 关于 BBS 的工具函数
|
||||
* @since Beta v0.8.0
|
||||
* 关于 BBS 的工具函数
|
||||
* @since Beta v0.8.4
|
||||
*/
|
||||
|
||||
/**
|
||||
* @description salt 类型
|
||||
* @since Beta v0.7.3
|
||||
* salt 类型
|
||||
* @since Beta v0.8.4
|
||||
*/
|
||||
export type SaltKey = "K2" | "LK2" | "X4" | "X6" | "PROD";
|
||||
|
||||
const BBS_VERSION: Readonly<string> = "2.93.0";
|
||||
const BBS_VERSION: Readonly<string> = "2.95.1";
|
||||
const BBS_UA_MOBILE: Readonly<string> = `Mozilla/5.0 (Linux; Android 12) Mobile miHoYoBBS/${BBS_VERSION}`;
|
||||
const BBS_UA_PC: Readonly<string> = `Mozilla/5.0 (Windows NT 10.0; Win64; x64) miHoYoBBS/${BBS_VERSION}`;
|
||||
|
||||
/**
|
||||
* @description salt 值
|
||||
* @version 2.93.0
|
||||
* @since Beta v0.8.0
|
||||
* salt 值
|
||||
* @version 2.95.1
|
||||
* @since Beta v0.8.4
|
||||
*/
|
||||
const BBS_SALT: Readonly<Record<SaltKey, string>> = {
|
||||
K2: "idMMaGYmVgPzh3wxmWudUXKUPGidO7GM",
|
||||
LK2: "G1ktdwFL4IyGkHuuWSmz0wUe9Db9scyK",
|
||||
K2: "sfYPEgpxkOe1I3XVMLdwp1Lyt9ORgZsq",
|
||||
LK2: "sidQFEglajEz7FA0Aj7HQPV88zpf17SO",
|
||||
X4: "xV8v4Qu54lUKrEYFZkJhB8cuOh9Asafs",
|
||||
X6: "t0qEgfub6cvueAPgR5m9aQWWVciEer7v",
|
||||
PROD: "t0qEgfub6cvueAPgR5m9aQWWVciEer7v",
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/**
|
||||
* @file utils/TGClient.ts
|
||||
* @desc 负责米游社客户端的 callback 处理
|
||||
* 负责米游社客户端的 callback 处理
|
||||
* @since Beta v0.8.3
|
||||
*/
|
||||
|
||||
@@ -23,9 +22,6 @@ import TGLogger from "./TGLogger.js";
|
||||
import { createPost } from "./TGWindow.js";
|
||||
import { getDeviceInfo } from "./toolFunc.js";
|
||||
|
||||
// invoke 参数
|
||||
type InvokeArg = { func: string };
|
||||
|
||||
class Client {
|
||||
private listener: UnlistenFn | undefined;
|
||||
private route: string[] = [];
|
||||
@@ -43,10 +39,9 @@ class Client {
|
||||
}
|
||||
|
||||
/**
|
||||
* @func run
|
||||
* 运行米游社客户端
|
||||
* @since Beta v0.3.4
|
||||
* @desc 运行米游社客户端
|
||||
* @returns {void} - 无返回值
|
||||
* @returns Promise<void>
|
||||
*/
|
||||
async run(): Promise<void> {
|
||||
if (this.listener === undefined) {
|
||||
@@ -58,15 +53,12 @@ class Client {
|
||||
}
|
||||
}
|
||||
|
||||
/* 内置函数 */
|
||||
|
||||
/**
|
||||
* @func callback
|
||||
* 回调函数
|
||||
* @since Beta v0.5.0
|
||||
* @desc 回调函数
|
||||
* @param {string} callback - 回调函数名
|
||||
* @param {object} data - 回调数据
|
||||
* @returns {void} - 无返回值
|
||||
* @returns Promise<void>
|
||||
*/
|
||||
async callback(callback: string, data: object): Promise<void> {
|
||||
const response = { retcode: 0, message: "success", data: data ?? {} };
|
||||
@@ -76,12 +68,11 @@ class Client {
|
||||
}
|
||||
|
||||
/**
|
||||
* @func getSaveImgJS
|
||||
* 获取保存图片的 JS
|
||||
* @since Beta v0.5.1
|
||||
* @desc 获取保存图片的 JS
|
||||
* @param {string} url - 图片链接
|
||||
* @param {string} format - 图片格式
|
||||
* @returns {string} - JS
|
||||
* @returns JS代码字符串
|
||||
*/
|
||||
getSaveImgJS(url: string, format: string): string {
|
||||
return `javascript:(async function() {
|
||||
@@ -104,9 +95,8 @@ class Client {
|
||||
}
|
||||
|
||||
/**
|
||||
* @func getUrl
|
||||
* 获取 url
|
||||
* @since Beta v0.5.0
|
||||
* @desc 获取 url
|
||||
* @param {string} func - 方法名
|
||||
* @returns {string} - url
|
||||
*/
|
||||
@@ -130,9 +120,8 @@ class Client {
|
||||
}
|
||||
|
||||
/**
|
||||
* @func handleCallback
|
||||
* 处理米游社客户端的 callback
|
||||
* @since Beta v0.6.1
|
||||
* @desc 处理米游社客户端的 callback
|
||||
* @param {Event<string>} arg - 事件参数
|
||||
* @returns {Promise<void>} - 返回值
|
||||
*/
|
||||
@@ -162,14 +151,16 @@ class Client {
|
||||
<TGApp.Plugins.JSBridge.Arg<TGApp.Plugins.JSBridge.EventTrackPayload>>argParse,
|
||||
);
|
||||
break;
|
||||
case "getStatusBarHeight":
|
||||
await this.getStatusBarHeight(<TGApp.Plugins.JSBridge.NullArg>argParse);
|
||||
break;
|
||||
case "genAuthKey":
|
||||
await this.genAuthKey(
|
||||
<TGApp.Plugins.JSBridge.Arg<TGApp.Plugins.JSBridge.GenAuthkeyPayload>>argParse,
|
||||
);
|
||||
break;
|
||||
case "getActionTicket":
|
||||
await this.getActionTicket(
|
||||
<TGApp.Plugins.JSBridge.Arg<TGApp.Plugins.JSBridge.GetActionTicketPayload>>argParse,
|
||||
);
|
||||
break;
|
||||
case "getCookieInfo":
|
||||
await this.getCookieInfo(<TGApp.Plugins.JSBridge.NullArg>argParse);
|
||||
break;
|
||||
@@ -178,14 +169,6 @@ class Client {
|
||||
<TGApp.Plugins.JSBridge.Arg<TGApp.Plugins.JSBridge.GetCookieTokenPayload>>argParse,
|
||||
);
|
||||
break;
|
||||
case "getActionTicket":
|
||||
await this.getActionTicket(
|
||||
<TGApp.Plugins.JSBridge.Arg<TGApp.Plugins.JSBridge.GetActionTicketPayload>>argParse,
|
||||
);
|
||||
break;
|
||||
case "getHTTPRequestHeaders":
|
||||
await this.getHTTPRequestHeaders(<TGApp.Plugins.JSBridge.NullArg>argParse);
|
||||
break;
|
||||
case "getDS":
|
||||
await this.getDS(<TGApp.Plugins.JSBridge.NullArg>argParse);
|
||||
break;
|
||||
@@ -194,6 +177,17 @@ class Client {
|
||||
<TGApp.Plugins.JSBridge.Arg<TGApp.Plugins.JSBridge.GetDS2Payload>>argParse,
|
||||
);
|
||||
break;
|
||||
case "getHTTPRequestHeaders":
|
||||
await this.getHTTPRequestHeaders(<TGApp.Plugins.JSBridge.NullArg>argParse);
|
||||
break;
|
||||
case "getRegionRoleInfo":
|
||||
await this.getRegionRoleInfo(
|
||||
<TGApp.Plugins.JSBridge.Arg<TGApp.Plugins.JSBridge.GetRegionRoleInfoPayload>>argParse,
|
||||
);
|
||||
break;
|
||||
case "getStatusBarHeight":
|
||||
await this.getStatusBarHeight(<TGApp.Plugins.JSBridge.NullArg>argParse);
|
||||
break;
|
||||
case "getUserInfo":
|
||||
await this.getUserInfo(<TGApp.Plugins.JSBridge.NullArg>argParse);
|
||||
break;
|
||||
@@ -437,7 +431,7 @@ class Client {
|
||||
await TGLogger.Error(`[TGClient][open] ${e}`);
|
||||
}
|
||||
}
|
||||
await core.invoke<InvokeArg>("create_mhy_client", { func, url });
|
||||
await core.invoke("create_mhy_client", { func, url });
|
||||
await this.loadJSBridge();
|
||||
}
|
||||
|
||||
@@ -657,6 +651,26 @@ class Client {
|
||||
await this.callback(arg.callback, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对应区服的角色信息
|
||||
* @since Beta v0.8.4
|
||||
* @param {TGApp.Plugins.JSBridge.Arg<TGApp.Plugins.JSBridge.GetRegionRoleInfoPayload>} arg - 方法参数
|
||||
* @returns {Promise<void>} - 无返回值
|
||||
*/
|
||||
async getRegionRoleInfo(
|
||||
arg: TGApp.Plugins.JSBridge.Arg<TGApp.Plugins.JSBridge.GetRegionRoleInfoPayload>,
|
||||
): Promise<void> {
|
||||
const user = useUserStore();
|
||||
const data = {
|
||||
region: user.account.region,
|
||||
game_uid: user.account.gameUid,
|
||||
nickname: user.account.nickname,
|
||||
user_label: user.account.nickname,
|
||||
region_name: user.account.regionName,
|
||||
};
|
||||
await this.callback(arg.callback, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @func getUserInfo
|
||||
* @since Beta v0.3.9
|
||||
|
||||
@@ -82,12 +82,12 @@ export async function getUigf4Header(): Promise<TGApp.Plugins.UIGF.Info4> {
|
||||
/**
|
||||
* @description 数据转换-数据库到 UIGF
|
||||
* @since Beta v0.7.5
|
||||
* @param {TGApp.Sqlite.GachaRecords.SingleTable[]} data - 数据库数据
|
||||
* @param {TGApp.Sqlite.GachaRecords.TableGacha[]} data - 数据库数据
|
||||
* @param {number} timezone - 时区
|
||||
* @returns {TGApp.Plugins.UIGF.GachaItem[]} UIGF 数据
|
||||
*/
|
||||
function convertDataToUigf(
|
||||
data: TGApp.Sqlite.GachaRecords.SingleTable[],
|
||||
data: TGApp.Sqlite.GachaRecords.TableGacha[],
|
||||
timezone: number,
|
||||
): TGApp.Plugins.UIGF.GachaItem[] {
|
||||
return data.map((gacha) => {
|
||||
@@ -191,13 +191,13 @@ export async function readUigf4Data(userPath: string): Promise<TGApp.Plugins.UIG
|
||||
* @description 导出 UIGF 数据
|
||||
* @since Beta v0.7.5
|
||||
* @param {string} uid - UID
|
||||
* @param {TGApp.Sqlite.GachaRecords.SingleTable[]} gachaList - 祈愿列表
|
||||
* @param {TGApp.Sqlite.GachaRecords.TableGacha[]} gachaList - 祈愿列表
|
||||
* @param {string} savePath - 保存路径
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export async function exportUigfData(
|
||||
uid: string,
|
||||
gachaList: TGApp.Sqlite.GachaRecords.SingleTable[],
|
||||
gachaList: TGApp.Sqlite.GachaRecords.TableGacha[],
|
||||
savePath?: string,
|
||||
): Promise<void> {
|
||||
const timezone = getUigfTimeZone(uid);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/**
|
||||
* @file src/utils/annoParser.ts
|
||||
* @description 解析游戏内公告数据
|
||||
* @since Beta v0.7.6
|
||||
* 解析游戏内公告数据
|
||||
* @since Beta v0.8.4
|
||||
*/
|
||||
|
||||
import TpText from "@comp/viewPost/tp-text.vue";
|
||||
@@ -38,18 +37,22 @@ function handleAnnoContent(data: string): string {
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 解析公告内容,转换为结构化数据
|
||||
* @since Beta v0.5.3
|
||||
* 解析公告内容,转换为结构化数据
|
||||
* @since Beta v0.8.4
|
||||
* @param {TGApp.BBS.Announcement.AnnoDetail} anno - 公告内容
|
||||
* @returns {TGApp.BBS.SctPost.Base[]} 结构化数据
|
||||
* @returns {Array<TGApp.BBS.SctPost.Base>} 结构化数据
|
||||
*/
|
||||
function parseAnnoContent(anno: TGApp.BBS.Announcement.AnnoDetail): Array<TGApp.BBS.SctPost.Base> {
|
||||
const parser = new DOMParser();
|
||||
const first = handleAnnoContent(anno.content);
|
||||
const doc = parser.parseFromString(first, "text/html");
|
||||
const children: Array<TGApp.BBS.SctPost.Base> = [];
|
||||
let children: Array<TGApp.BBS.SctPost.Base> = [];
|
||||
if (anno.banner !== "") children.push({ insert: { image: anno.banner } });
|
||||
doc.body.childNodes.forEach((child) => children.push(...parseAnnoNode(child)));
|
||||
// 剔除 attributes 为空且内容为 "\n\n" 的节点
|
||||
children = children.filter((child) => {
|
||||
return !(child.attributes === undefined && child.insert === "\n\n");
|
||||
});
|
||||
return children;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/**
|
||||
* @file src/utils/linkParser.ts
|
||||
* @description 处理链接
|
||||
* @since Beta v0.7.3
|
||||
* 处理链接
|
||||
* @since Beta v0.8.4
|
||||
*/
|
||||
|
||||
import showDialog from "@comp/func/dialog.js";
|
||||
@@ -12,9 +11,8 @@ import TGClient from "./TGClient.js";
|
||||
import { createPost } from "./TGWindow.js";
|
||||
|
||||
/**
|
||||
* @function parsePost
|
||||
* 处理帖子链接
|
||||
* @since Beta v0.6.9
|
||||
* @description 处理帖子
|
||||
* @param {string} link
|
||||
* @returns {Promise<false|string>} - 处理情况,或者转换后的链接
|
||||
*/
|
||||
@@ -54,9 +52,8 @@ export async function parsePost(link: string): Promise<false | string> {
|
||||
}
|
||||
|
||||
/**
|
||||
* @function parseLink
|
||||
* 处理链接
|
||||
* @since Beta v0.7.2
|
||||
* @description 处理链接
|
||||
* @param {string} link - 链接
|
||||
* @param {boolean} useInner - 是否采用内置 JSBridge 打开
|
||||
* @returns {Promise<boolean|string>} - 处理情况,或者转换后的链接
|
||||
@@ -144,6 +141,7 @@ export async function parseLink(
|
||||
const prefix = [
|
||||
"m.miyoushe.com",
|
||||
"act.mihoyo.com",
|
||||
"act.miyoushe.com",
|
||||
"ys.mihoyo.com",
|
||||
"mhyurl.cn",
|
||||
"webstatic.mihoyo.com",
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
/**
|
||||
* @file utils/toolFunc.ts
|
||||
* @description 一些工具函数
|
||||
* @since Beta v0.8.2
|
||||
* 一些工具函数
|
||||
*/
|
||||
|
||||
import { AvatarExtResTypeEnum, AvatarExtTypeEnum } from "@enum/bbs.js";
|
||||
@@ -122,14 +120,14 @@ export function bytesToSize(bytes: number): string {
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取缓存目录
|
||||
* @since Beta v0.5.0
|
||||
* @returns {string|string[]} 缓存目录
|
||||
* 获取缓存目录
|
||||
* @returns {Array<string>|false} 缓存目录
|
||||
*/
|
||||
export async function getCacheDir(): Promise<string[] | false> {
|
||||
export async function getCacheDir(): Promise<Array<string> | false> {
|
||||
const cacheDir = await path.appCacheDir();
|
||||
const osType = type().toLowerCase();
|
||||
if (osType === "windows") {
|
||||
// TODO: 会报错显示占用
|
||||
const cache = `${cacheDir}${path.sep()}EBWebview${path.sep()}Default${path.sep()}Cache`;
|
||||
const codeCache = `${cacheDir}${path.sep()}EBWebview${path.sep()}Default${path.sep()}Code Cache`;
|
||||
return [cache, codeCache];
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<!-- 公告详情页面,TODO:联动消除红点 -->
|
||||
<template>
|
||||
<TSwitchTheme />
|
||||
<TPinWin />
|
||||
|
||||
@@ -9,6 +9,9 @@
|
||||
</template>
|
||||
<template #text>
|
||||
<div class="tpj-box">
|
||||
<span class="tpj-box-copy" @click="copyContent(jsonData)" title="复制全部内容到剪贴板">
|
||||
<v-icon small>mdi-content-copy</v-icon>
|
||||
</span>
|
||||
<vue-json-pretty
|
||||
:data="JSON.parse(JSON.stringify(jsonData))"
|
||||
:show-icon="true"
|
||||
@@ -30,6 +33,9 @@
|
||||
</template>
|
||||
<template #text>
|
||||
<div class="tpj-box">
|
||||
<span class="tpj-box-copy" @click="copyContent(parseData)" title="复制全部内容到剪贴板">
|
||||
<v-icon small>mdi-content-copy</v-icon>
|
||||
</span>
|
||||
<vue-json-pretty
|
||||
:data="parseData"
|
||||
:show-icon="true"
|
||||
@@ -101,6 +107,16 @@ onMounted(async () => {
|
||||
}
|
||||
await showLoading.end();
|
||||
});
|
||||
|
||||
async function copyContent(jsonData: unknown): Promise<void> {
|
||||
try {
|
||||
await navigator.clipboard.writeText(JSON.stringify(jsonData, null, 2));
|
||||
showSnackbar.success("已复制帖子返回内容 JSON 到剪贴板");
|
||||
} catch (err) {
|
||||
showSnackbar.error("复制失败,请稍后重试");
|
||||
await TGLogger.Error(`[${postId}]复制帖子返回内容 JSON 失败:${err}`);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.tpj-page {
|
||||
@@ -125,4 +141,20 @@ onMounted(async () => {
|
||||
border-radius: 4px;
|
||||
background: var(--box-bg-1);
|
||||
}
|
||||
|
||||
.tpj-box-copy {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
top: 8px;
|
||||
right: 8px;
|
||||
display: flex;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 4px;
|
||||
color: var(--common-text-title);
|
||||
cursor: pointer;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
<v-icon size="16">mdi-comment</v-icon>
|
||||
<span>{{ postData?.stat?.reply_num }}</span>
|
||||
</div>
|
||||
<!-- TODO: 展示不同种类点赞图标&数量 -->
|
||||
<div
|
||||
class="mpm-item"
|
||||
:title="`点赞数:${postData?.stat?.like_num}`"
|
||||
@@ -62,6 +63,15 @@
|
||||
</div>
|
||||
<!-- 一些附加信息,比如 topic、collection 等 -->
|
||||
<div class="tp-post-extra">
|
||||
<div
|
||||
class="tp-post-contribution"
|
||||
v-if="postData.contribution_act"
|
||||
:title="`投稿活动:${postData.contribution_act.title}`"
|
||||
@click="toAct()"
|
||||
>
|
||||
<v-icon size="10">mdi-party-popper</v-icon>
|
||||
<span>{{ postData.contribution_act.title }}</span>
|
||||
</div>
|
||||
<div
|
||||
class="tp-post-collection"
|
||||
:title="`合集ID:${postData.collection.collection_id}`"
|
||||
@@ -98,6 +108,10 @@
|
||||
<v-icon size="16">mdi-copyright</v-icon>
|
||||
<span>{{ getRepublishAuthorization(postData.post.republish_authorization) }}</span>
|
||||
</span>
|
||||
<span v-if="postData.post.post_extra?.minos_aigc_info?.is_aigc" class="tp-post-aigc">
|
||||
<v-icon size="16">mdi-robot</v-icon>
|
||||
<span>疑似含AI生成内容,请谨慎甄别</span>
|
||||
</span>
|
||||
</div>
|
||||
<TpParser v-model:data="renderPost" />
|
||||
</div>
|
||||
@@ -123,6 +137,7 @@ import VpBtnCollect from "@comp/viewPost/vp-btn-collect.vue";
|
||||
import VpBtnReply from "@comp/viewPost/vp-btn-reply.vue";
|
||||
import VpOverlayCollection from "@comp/viewPost/vp-overlay-collection.vue";
|
||||
import VpOverlayUser from "@comp/viewPost/vp-overlay-user.vue";
|
||||
import { PostViewTypeEnum } from "@enum/bbs.js";
|
||||
import apiHubReq from "@req/apiHubReq.js";
|
||||
import postReq from "@req/postReq.js";
|
||||
import useAppStore from "@store/app.js";
|
||||
@@ -130,6 +145,8 @@ import useBBSStore from "@store/bbs.js";
|
||||
import useUserStore from "@store/user.js";
|
||||
import { app, webviewWindow } from "@tauri-apps/api";
|
||||
import { emit, type Event, listen, type UnlistenFn } from "@tauri-apps/api/event";
|
||||
import { openUrl } from "@tauri-apps/plugin-opener";
|
||||
import { parseLink, parsePost } from "@utils/linkParser.js";
|
||||
import TGClient from "@utils/TGClient.js";
|
||||
import TGLogger from "@utils/TGLogger.js";
|
||||
import { createTGWindow } from "@utils/TGWindow.js";
|
||||
@@ -260,13 +277,25 @@ function getRepublishAuthorization(type: number): string {
|
||||
async function getRenderPost(
|
||||
data: TGApp.BBS.Post.FullData,
|
||||
): Promise<Array<TGApp.BBS.SctPost.Base>> {
|
||||
if (
|
||||
data.post.view_type === PostViewTypeEnum.NORMAL ||
|
||||
data.post.view_type === PostViewTypeEnum.VOD
|
||||
) {
|
||||
return JSON.parse(data.post.structured_content);
|
||||
}
|
||||
if (data.post.view_type === PostViewTypeEnum.PIC) {
|
||||
return await parsePostPic(data);
|
||||
}
|
||||
if (data.post.view_type === PostViewTypeEnum.UGC) {
|
||||
return parsePostUgc(data.post);
|
||||
}
|
||||
const postContent = data.post.content;
|
||||
let jsonParse: string;
|
||||
if (postContent.startsWith("<")) {
|
||||
jsonParse = data.post.structured_content;
|
||||
} else {
|
||||
try {
|
||||
jsonParse = await parseContent(data);
|
||||
jsonParse = JSON.stringify(await parsePostPic(data));
|
||||
} catch (e) {
|
||||
if (e instanceof SyntaxError) {
|
||||
await TGLogger.Warn(`[t-post][${postId}] ${e.name}: ${e.message}`);
|
||||
@@ -274,12 +303,16 @@ async function getRenderPost(
|
||||
jsonParse = data.post.structured_content;
|
||||
}
|
||||
}
|
||||
return JSON.parse(jsonParse);
|
||||
const res = JSON.parse(jsonParse);
|
||||
if (!Array.isArray(res) && !res.insert) return [res];
|
||||
return res;
|
||||
}
|
||||
|
||||
async function parseContent(fullData: TGApp.BBS.Post.FullData): Promise<string> {
|
||||
async function parsePostPic(
|
||||
fullData: TGApp.BBS.Post.FullData,
|
||||
): Promise<Array<TGApp.BBS.SctPost.Base>> {
|
||||
const content = fullData.post.content;
|
||||
const data: TGApp.BBS.SctPost.Other = JSON.parse(content);
|
||||
const data: TGApp.BBS.SctPost.Pic = JSON.parse(content);
|
||||
const result: TGApp.BBS.SctPost.Base[] = [];
|
||||
for (const key of Object.keys(data)) {
|
||||
switch (key) {
|
||||
@@ -311,7 +344,35 @@ async function parseContent(fullData: TGApp.BBS.Post.FullData): Promise<string>
|
||||
break;
|
||||
}
|
||||
}
|
||||
return JSON.stringify(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
function parsePostUgc(post: TGApp.BBS.Post.Post): Array<TGApp.BBS.SctPost.Base> {
|
||||
const data: TGApp.BBS.SctPost.Ugc = JSON.parse(post.structured_content);
|
||||
const result: Array<TGApp.BBS.SctPost.Base> = [];
|
||||
if (Array.isArray(data.text)) {
|
||||
for (const text of data.text) {
|
||||
result.push(text);
|
||||
}
|
||||
// 手动添加换行以对齐解析逻辑
|
||||
if (data.text.length > 0) result.push({ insert: "\n" });
|
||||
}
|
||||
if (Array.isArray(data.images)) {
|
||||
for (const image of data.images) {
|
||||
result.push({ insert: { image: image.image } });
|
||||
}
|
||||
}
|
||||
if (Array.isArray(data.vods)) {
|
||||
for (const vod of data.vods) {
|
||||
result.push({ insert: { vod: vod.vod } });
|
||||
}
|
||||
}
|
||||
if (Array.isArray(data.levels)) {
|
||||
for (const level of data.levels) {
|
||||
result.push({ insert: { level: level.level } });
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
async function createPostJson(postId: number): Promise<void> {
|
||||
@@ -365,6 +426,23 @@ async function toForum(forum: TGApp.BBS.Post.Forum): Promise<void> {
|
||||
await emit("active_deep_link", `router?path=/posts/forum/${forum.game_id}/${forum.id}`);
|
||||
}
|
||||
|
||||
async function toAct(): Promise<void> {
|
||||
if (!postData.value || !postData.value.external_link) return;
|
||||
const link = postData.value.external_link.external_link;
|
||||
const isPost = await parsePost(link);
|
||||
if (isPost !== false) {
|
||||
location.href = `/post_detail/${isPost}`;
|
||||
return;
|
||||
}
|
||||
const res = await parseLink(link);
|
||||
if (res === true) return;
|
||||
if (res === false) {
|
||||
showSnackbar.error(`未知链接:${link}`, 3000);
|
||||
return;
|
||||
}
|
||||
await openUrl(res);
|
||||
}
|
||||
|
||||
function handleUser(user: TGApp.BBS.Post.User): void {
|
||||
curUid.value = user.uid;
|
||||
if (showCollection.value) showCollection.value = false;
|
||||
@@ -416,7 +494,8 @@ function handleUser(user: TGApp.BBS.Post.User): void {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.tp-post-copyright {
|
||||
.tp-post-copyright,
|
||||
.tp-post-aigc {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@@ -517,6 +596,26 @@ function handleUser(user: TGApp.BBS.Post.User): void {
|
||||
gap: 8px 4px;
|
||||
}
|
||||
|
||||
.tp-post-contribution {
|
||||
@include github-styles.github-tag-dark-gen(#e06c75);
|
||||
|
||||
display: flex;
|
||||
height: 20px;
|
||||
box-sizing: border-box;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0 6px;
|
||||
border-radius: 4px;
|
||||
column-gap: 2px;
|
||||
cursor: pointer;
|
||||
font-family: var(--font-title);
|
||||
font-size: 12px;
|
||||
|
||||
&:hover {
|
||||
@include github-styles.github-tag-dark-gen(#c678dd);
|
||||
}
|
||||
}
|
||||
|
||||
.tp-post-collection {
|
||||
@include github-styles.github-tag-dark-gen(#3572a5);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user