mirror of
https://github.com/BTMuli/TeyvatGuide.git
synced 2026-03-22 04:59:45 +08:00
Compare commits
46 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1f814bc8b5 | ||
|
|
885fa3b19e | ||
|
|
82ba88a2c9 | ||
|
|
1a4d175e3d | ||
|
|
6a39aa127b | ||
|
|
5c0d5f50fe | ||
|
|
27501acfd2 | ||
|
|
69157ea008 | ||
|
|
db00765f7b | ||
|
|
49854367b1 | ||
|
|
cab497f573 | ||
|
|
2c5c205566 | ||
|
|
447ee3b329 | ||
|
|
e049508ab6 | ||
|
|
9bfb7c4108 | ||
|
|
ea3a88ecb4 | ||
|
|
b502cf4025 | ||
|
|
942e63654e | ||
|
|
baf8c3f794 | ||
|
|
2abfcdc050 | ||
|
|
d22203e7b4 | ||
|
|
8f116c954f | ||
|
|
a8b1ff4588 | ||
|
|
5d036ddeee | ||
|
|
b2843dbf13 | ||
|
|
672fa2e536 | ||
|
|
a7a0a8b0e0 | ||
|
|
e9252bbfcd | ||
|
|
e3e5b757e4 | ||
|
|
6b18d63fb0 | ||
|
|
948db203bd | ||
|
|
f0b3a311d7 | ||
|
|
289620922d | ||
|
|
20ad79f08b | ||
|
|
db3cb2fa29 | ||
|
|
f822c0b32d | ||
|
|
632576de94 | ||
|
|
0b8dc2ef60 | ||
|
|
047f9eaf3a | ||
|
|
792d337e45 | ||
|
|
de89aa5159 | ||
|
|
5c8f2d5e57 | ||
|
|
7391752247 | ||
|
|
27a2e93efc | ||
|
|
5bf2521938 | ||
|
|
41ee27c74e |
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
@@ -43,11 +43,11 @@ jobs:
|
||||
- name: setup node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 22.0.0
|
||||
node-version: 22.3.0
|
||||
- name: setup pnpm
|
||||
uses: pnpm/action-setup@v2
|
||||
with:
|
||||
version: 9.5.0
|
||||
version: 9.6.0
|
||||
- name: remove lockfile
|
||||
run: rm pnpm-lock.yaml
|
||||
- name: Install frontend dependencies
|
||||
|
||||
@@ -1,4 +1 @@
|
||||
#!/bin/sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
npx lint-staged
|
||||
pnpm lint-staged
|
||||
|
||||
37
CHANGELOG.md
37
CHANGELOG.md
@@ -2,14 +2,45 @@
|
||||
Author: 目棃
|
||||
Description: CHANGELOG
|
||||
Date: 2024-07-09
|
||||
Update: 2024-07-16
|
||||
Update: 2024-08-11
|
||||
---
|
||||
|
||||
> 本文档 [`Frontmatter`](https://github.com/BTMuli/MuCli#Frontmatter) 由 [MuCli](https://github.com/BTMuli/Mucli) 自动生成于 `2024-07-09 14:16:16`
|
||||
>
|
||||
> 更新于 `2024-07-16 22:03:11`
|
||||
> 更新于 `2024-08-11 15:43:34`
|
||||
|
||||
## [0.5.0](https://github.com/BTMuli/TeyvatGuide/releases/v0.5.0) (2024-07-09)
|
||||
## [0.5.2](https://github.com/BTMuli/TeyvatGuide/releases/v0.5.2) (2024-08-11)
|
||||
|
||||
- 🐛 修复日志目录异常
|
||||
- ♻️ 重构窗体/缩放调整逻辑
|
||||
- 🐛 修复公告时间获取异常&内容渲染异常
|
||||
- 🔥 移除扫码登录模块
|
||||
👽️ 调整更新日志链接
|
||||
- 🍱 更新4.8下半数据
|
||||
|
||||
## [0.5.1](https://github.com/BTMuli/TeyvatGuide/releases/v0.5.1) (2024-07-30)
|
||||
|
||||
- ⚡️ 不允许低于 UIGF v2.3 版本的数据导入
|
||||
- 🐛 修复网页小工具数据获取异常
|
||||
- ⚡️ 咨讯页刷新时记忆 tab 状态,顶部按钮进行调整
|
||||
- ⚡️ 调整收藏页面分类删除逻辑,优化卡片样式
|
||||
- ⚡️ 设置页数据目录添加按钮显式表示相关操作
|
||||
- ⚡️ 优化帖子搜索逻辑,不会自动弹出浮窗
|
||||
- 💄 帖子页面调整刷新数量,20→12
|
||||
- 💄 角色/武器图鉴材料支持左右切换
|
||||
- ✨ 支持短信验证码登录 [`#118`](https://github.com/BTMuli/TeyvatGuide/issues/118)
|
||||
- 💄 调整首页日历组件不同状态下的背景色、边缘、文本色
|
||||
- ♻️ 重构米游社相关链接解析处理
|
||||
- 💄 分享色背景设为透明
|
||||
- 🐛 修复特定情况下的米游社子窗口分享功能异常
|
||||
- 💄 设置页显示设备信息,支持复制cookie
|
||||
- 💄 帖子顶部添加分区图标
|
||||
- 🐛 修复祈愿页面导出按钮逻辑异常
|
||||
- 🐛 修复公告页部分公告时间解析异常
|
||||
- 🐛 调整祈愿页面抽数计算规则
|
||||
- 💄 根据屏幕缩放调整应用缩放
|
||||
|
||||
## [0.5.0](https://github.com/BTMuli/TeyvatGuide/releases/v0.5.0) (2024-07-17)
|
||||
|
||||
- ⬆️ 底层架构更新,升级至 `Tauri v2` [`#92`](https://github.com/BTMuli/TeyvatGuide/issues/92)
|
||||
- 🐛 修复数据恢复异常
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
Author: 目棃
|
||||
Description: 说明文档
|
||||
Date: 2023-03-05
|
||||
Update: 2024-07-14
|
||||
Update: 2024-07-31
|
||||
---
|
||||
|
||||
> 本文档 [`Frontmatter`](https://github.com/BTMuli/MuCli#Frontmatter) 由 [MuCli](https://github.com/BTMuli/Mucli) 自动生成于 `2023-03-05 14:41:55`
|
||||
>
|
||||
> 更新于 `2024-07-14 08:50:43`
|
||||
> 更新于 `2024-07-31 18:21:43`
|
||||
|
||||
 
|
||||
|
||||
@@ -33,7 +33,7 @@ Game Tool for Genshin Impact player, supports Windows and macOS.
|
||||
|
||||
> macOS 用户可以通过 Github Release 下载
|
||||
|
||||
[](https://github.com/BTMuli/TeyvatGuide/releases/latest)
|
||||
[](https://github.com/BTMuli/TeyvatGuide/releases/latest)
|
||||
|
||||
## 仓库概况 / Repo Stats
|
||||
|
||||
@@ -48,7 +48,7 @@ Game Tool for Genshin Impact player, supports Windows and macOS.
|
||||
- [x] 米游社官方帖获取(支持通过 ID 获取)
|
||||
- [x] 米游社各分区帖子获取(支持通过 ID 获取)
|
||||
- [x] 成就管理(UIAF v1.1),支持 [`YaeAchievement`](https://github.com/HolographicHat/YaeAchievement) 导入
|
||||
- [x] 祈愿管理(UIGF v3.0,0.5.0 起支持 UIGF v4.0)
|
||||
- [x] 祈愿管理(UIGF v3.0,UIGF v4.0)
|
||||
- [x] 留影叙佳期画片查看
|
||||
- [x] 帖子收藏
|
||||
|
||||
@@ -66,6 +66,7 @@ Game Tool for Genshin Impact player, supports Windows and macOS.
|
||||
- [x] 武器图鉴
|
||||
- [x] 名片图鉴
|
||||
- [x] 卡牌图鉴
|
||||
- [x] 材料图鉴
|
||||
|
||||
- 应用功能:
|
||||
- [x] 浅色/深色主题切换
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
<link rel="icon" type="image/svg+xml" href="/icon.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>TeyvatGuide</title>
|
||||
<script src="https://static.geetest.com/v4/gt4.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
72
package.json
72
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "TeyvatGuide",
|
||||
"version": "0.5.0",
|
||||
"version": "0.5.2",
|
||||
"description": "Game Tool for Genshin Impact player",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
@@ -66,73 +66,75 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@mdi/font": "7.4.47",
|
||||
"@tauri-apps/api": "2.0.0-beta.14",
|
||||
"@tauri-apps/plugin-deep-link": "2.0.0-beta.8",
|
||||
"@tauri-apps/plugin-dialog": "2.0.0-beta.6",
|
||||
"@tauri-apps/plugin-fs": "2.0.0-beta.6",
|
||||
"@tauri-apps/plugin-http": "2.0.0-beta.7",
|
||||
"@tauri-apps/plugin-log": "2.0.0-beta.7",
|
||||
"@tauri-apps/plugin-os": "2.0.0-beta.6",
|
||||
"@tauri-apps/plugin-process": "2.0.0-beta.6",
|
||||
"@tauri-apps/plugin-shell": "2.0.0-beta.7",
|
||||
"@tauri-apps/plugin-sql": "2.0.0-beta.6",
|
||||
"ajv": "^8.16.0",
|
||||
"@tauri-apps/api": "2.0.0-rc.0",
|
||||
"@tauri-apps/plugin-deep-link": "2.0.0-beta.10",
|
||||
"@tauri-apps/plugin-dialog": "2.0.0-beta.8",
|
||||
"@tauri-apps/plugin-fs": "2.0.0-beta.8",
|
||||
"@tauri-apps/plugin-http": "2.0.0-beta.9",
|
||||
"@tauri-apps/plugin-log": "2.0.0-beta.9",
|
||||
"@tauri-apps/plugin-os": "2.0.0-beta.8",
|
||||
"@tauri-apps/plugin-process": "2.0.0-beta.8",
|
||||
"@tauri-apps/plugin-shell": "2.0.0-beta.9",
|
||||
"@tauri-apps/plugin-sql": "2.0.0-beta.7",
|
||||
"ajv": "^8.17.1",
|
||||
"artplayer": "^5.1.6",
|
||||
"clipboard": "^2.0.11",
|
||||
"color-convert": "^2.0.1",
|
||||
"echarts": "^5.5.1",
|
||||
"html2canvas": "^1.4.1",
|
||||
"js-md5": "^0.8.3",
|
||||
"pinia": "^2.1.7",
|
||||
"jsencrypt": "^3.3.2",
|
||||
"pinia": "^2.2.0",
|
||||
"pinia-plugin-persistedstate": "^3.2.1",
|
||||
"qrcode.vue": "^3.4.1",
|
||||
"uuid": "^10.0.0",
|
||||
"vue": "^3.4.31",
|
||||
"vue": "^3.4.35",
|
||||
"vue-echarts": "^6.7.3",
|
||||
"vue-json-viewer": "^3.0.4",
|
||||
"vue-router": "^4.4.0",
|
||||
"vuetify": "^3.6.11",
|
||||
"vue-router": "^4.4.2",
|
||||
"vuetify": "^3.6.14",
|
||||
"wcag-color": "^1.1.1",
|
||||
"xml-js": "^1.6.11"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/eslintrc": "^3.1.0",
|
||||
"@eslint/js": "^9.6.0",
|
||||
"@eslint/js": "^9.8.0",
|
||||
"@tauri-apps/cli": "2.0.0-beta.21",
|
||||
"@types/color-convert": "^2.0.3",
|
||||
"@types/js-md5": "^0.7.2",
|
||||
"@types/node": "^20.14.10",
|
||||
"@types/node": "^22.1.0",
|
||||
"@types/uuid": "^10.0.0",
|
||||
"@typescript-eslint/parser": "^7.15.0",
|
||||
"@vitejs/plugin-vue": "^5.0.5",
|
||||
"@typescript-eslint/parser": "^8.0.0",
|
||||
"@vitejs/plugin-vue": "^5.1.2",
|
||||
"concurrently": "^8.2.2",
|
||||
"eslint": "^9.6.0",
|
||||
"eslint-config-love": "^53.0.0",
|
||||
"eslint": "^9.8.0",
|
||||
"eslint-config-love": "^62.0.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-plugin-import": "^2.29.1",
|
||||
"eslint-plugin-jsonc": "^2.16.0",
|
||||
"eslint-plugin-n": "^17.9.0",
|
||||
"eslint-plugin-prettier": "^5.1.3",
|
||||
"eslint-plugin-promise": "^6.4.0",
|
||||
"eslint-plugin-n": "^17.10.1",
|
||||
"eslint-plugin-prettier": "^5.2.1",
|
||||
"eslint-plugin-promise": "^7.0.0",
|
||||
"eslint-plugin-vue": "^9.27.0",
|
||||
"eslint-plugin-yml": "^1.14.0",
|
||||
"globals": "^15.8.0",
|
||||
"husky": "^9.0.11",
|
||||
"globals": "^15.9.0",
|
||||
"husky": "^9.1.4",
|
||||
"jsonc-eslint-parser": "^2.4.0",
|
||||
"lint-staged": "^15.2.7",
|
||||
"oxlint": "^0.5.2",
|
||||
"prettier": "3.3.2",
|
||||
"stylelint": "^16.6.1",
|
||||
"oxlint": "^0.6.1",
|
||||
"prettier": "3.3.3",
|
||||
"stylelint": "^16.8.1",
|
||||
"stylelint-config-idiomatic-order": "^10.0.0",
|
||||
"stylelint-config-standard-vue": "^1.0.0",
|
||||
"stylelint-declaration-block-no-ignored-properties": "^2.8.0",
|
||||
"stylelint-high-performance-animation": "^1.10.0",
|
||||
"stylelint-order": "^6.0.4",
|
||||
"stylelint-prettier": "^5.0.0",
|
||||
"typescript": "^5.5.3",
|
||||
"typescript-eslint": "^7.15.0",
|
||||
"vite": "^5.3.3",
|
||||
"vite-plugin-vue-devtools": "^7.3.5",
|
||||
"stylelint-prettier": "^5.0.2",
|
||||
"typescript": "^5.5.4",
|
||||
"typescript-eslint": "^8.0.0",
|
||||
"vite": "^5.3.5",
|
||||
"vite-plugin-node-polyfills": "^0.22.0",
|
||||
"vite-plugin-vue-devtools": "^7.3.7",
|
||||
"vite-plugin-vuetify": "^2.0.3",
|
||||
"vue-eslint-parser": "^9.4.3",
|
||||
"yaml-eslint-parser": "^1.2.3"
|
||||
|
||||
2079
pnpm-lock.yaml
generated
2079
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
513
src-tauri/Cargo.lock
generated
513
src-tauri/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "TeyvatGuide"
|
||||
version = "0.5.0"
|
||||
version = "0.5.2"
|
||||
description = "Game Tool for Genshin Impact player"
|
||||
authors = ["BTMuli <bt-muli@outlook.com>"]
|
||||
license = "MIT"
|
||||
@@ -10,15 +10,15 @@ edition = "2021"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[build-dependencies]
|
||||
tauri-build = { version = "2.0.0-beta.18", features = [] }
|
||||
tauri-build = { version = "2.0.0-beta.19", features = [] }
|
||||
|
||||
[dependencies]
|
||||
chrono = "0.4.38"
|
||||
log = "0.4.22"
|
||||
serde = { version = "1.0.204", features = ["derive"] }
|
||||
serde_json = "1.0.120"
|
||||
tauri = { version = "2.0.0-beta.23", features = [] }
|
||||
tauri-utils = "2.0.0-beta.18"
|
||||
serde_json = "1.0.122"
|
||||
tauri = { version = "2.0.0-rc.0", features = [] }
|
||||
tauri-utils = "2.0.0-rc.0"
|
||||
url = "2.5.2"
|
||||
walkdir = "2.5.0"
|
||||
|
||||
|
||||
@@ -4,23 +4,28 @@
|
||||
"description": "Capability for the dev json window",
|
||||
"windows": ["Dev_JSON"],
|
||||
"permissions": [
|
||||
"app:allow-version",
|
||||
"app:default",
|
||||
"event:allow-listen",
|
||||
"event:default",
|
||||
"core:app:allow-version",
|
||||
"core:app:default",
|
||||
"core:event:allow-listen",
|
||||
"core:event:default",
|
||||
"http:allow-fetch",
|
||||
"log:allow-log",
|
||||
"log:default",
|
||||
"path:allow-resolve-directory",
|
||||
"path:default",
|
||||
"core:path:allow-resolve-directory",
|
||||
"core:path:default",
|
||||
"sql:allow-load",
|
||||
"sql:allow-execute",
|
||||
"sql:default",
|
||||
"window:allow-close",
|
||||
"window:allow-destroy",
|
||||
"window:allow-set-title",
|
||||
"window:allow-set-fullscreen",
|
||||
"window:allow-show",
|
||||
"core:webview:allow-set-webview-zoom",
|
||||
"core:webview:default",
|
||||
"core:window:allow-center",
|
||||
"core:window:allow-close",
|
||||
"core:window:allow-destroy",
|
||||
"core:window:allow-set-size",
|
||||
"core:window:allow-set-title",
|
||||
"core:window:allow-set-fullscreen",
|
||||
"core:window:allow-show",
|
||||
"core:window:default",
|
||||
{
|
||||
"identifier": "http:default",
|
||||
"allow": [
|
||||
|
||||
@@ -6,36 +6,80 @@
|
||||
"permissions": [
|
||||
"dialog:allow-message",
|
||||
"dialog:default",
|
||||
"event:allow-emit",
|
||||
"path:allow-resolve-directory",
|
||||
"path:default",
|
||||
"core:event:allow-emit",
|
||||
"http:allow-fetch",
|
||||
"core:webview:allow-set-webview-zoom",
|
||||
"core:webview:default",
|
||||
"core:window:allow-center",
|
||||
"core:window:default",
|
||||
"core:path:allow-resolve-directory",
|
||||
"core:path:default",
|
||||
{
|
||||
"identifier": "fs:allow-exists",
|
||||
"allow": [{ "path": "**" }]
|
||||
"allow": [
|
||||
{
|
||||
"path": "**"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"identifier": "fs:allow-mkdir",
|
||||
"allow": [{ "path": "**" }]
|
||||
"allow": [
|
||||
{
|
||||
"path": "**"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"identifier": "fs:allow-read-dir",
|
||||
"allow": [{ "path": "**" }]
|
||||
"allow": [
|
||||
{
|
||||
"path": "**"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"identifier": "fs:allow-read-text-file",
|
||||
"allow": [{ "path": "**" }]
|
||||
"allow": [
|
||||
{
|
||||
"path": "**"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"identifier": "fs:allow-remove",
|
||||
"allow": [{ "path": "**" }]
|
||||
"allow": [
|
||||
{
|
||||
"path": "**"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"identifier": "fs:allow-write-file",
|
||||
"allow": [{ "path": "**" }]
|
||||
"allow": [
|
||||
{
|
||||
"path": "**"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"identifier": "fs:allow-write-text-file",
|
||||
"allow": [{ "path": "**" }]
|
||||
"allow": [
|
||||
{
|
||||
"path": "**"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"identifier": "http:default",
|
||||
"allow": [
|
||||
{
|
||||
"url": "https://*.miyoushe.com/*"
|
||||
},
|
||||
{
|
||||
"url": "https://*.mihoyo.com/*"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"remote": {
|
||||
|
||||
@@ -4,28 +4,33 @@
|
||||
"description": "Capability for the sub window",
|
||||
"windows": ["Sub_window"],
|
||||
"permissions": [
|
||||
"app:allow-version",
|
||||
"app:default",
|
||||
"core:app:allow-version",
|
||||
"core:app:default",
|
||||
"dialog:allow-save",
|
||||
"dialog:default",
|
||||
"event:allow-listen",
|
||||
"event:default",
|
||||
"core:event:allow-listen",
|
||||
"core:event:default",
|
||||
"fs:default",
|
||||
"http:allow-fetch",
|
||||
"log:allow-log",
|
||||
"log:default",
|
||||
"path:allow-resolve-directory",
|
||||
"path:default",
|
||||
"core:path:allow-resolve-directory",
|
||||
"core:path:default",
|
||||
"shell:allow-open",
|
||||
"shell:default",
|
||||
"sql:allow-load",
|
||||
"sql:allow-execute",
|
||||
"sql:default",
|
||||
"window:allow-close",
|
||||
"window:allow-destroy",
|
||||
"window:allow-set-title",
|
||||
"window:allow-set-fullscreen",
|
||||
"window:allow-show",
|
||||
"core:webview:allow-set-webview-zoom",
|
||||
"core:webview:default",
|
||||
"core:window:allow-center",
|
||||
"core:window:allow-close",
|
||||
"core:window:allow-destroy",
|
||||
"core:window:allow-set-size",
|
||||
"core:window:allow-set-title",
|
||||
"core:window:allow-set-fullscreen",
|
||||
"core:window:allow-show",
|
||||
"core:window:default",
|
||||
{
|
||||
"identifier": "fs:allow-exists",
|
||||
"allow": [{ "path": "**" }]
|
||||
|
||||
@@ -4,18 +4,18 @@
|
||||
"description": "Capability for the main window",
|
||||
"windows": ["TeyvatGuide"],
|
||||
"permissions": [
|
||||
"app:allow-version",
|
||||
"app:default",
|
||||
"core:app:allow-version",
|
||||
"core:app:default",
|
||||
"dialog:allow-save",
|
||||
"dialog:default",
|
||||
"event:allow-listen",
|
||||
"event:default",
|
||||
"core:event:allow-listen",
|
||||
"core:event:default",
|
||||
"fs:default",
|
||||
"http:allow-fetch",
|
||||
"log:allow-log",
|
||||
"log:default",
|
||||
"path:allow-resolve-directory",
|
||||
"path:default",
|
||||
"core:path:allow-resolve-directory",
|
||||
"core:path:default",
|
||||
"process:allow-exit",
|
||||
"process:default",
|
||||
"shell:allow-execute",
|
||||
@@ -24,16 +24,19 @@
|
||||
"sql:allow-load",
|
||||
"sql:allow-execute",
|
||||
"sql:default",
|
||||
"webview:allow-create-webview-window",
|
||||
"webview:default",
|
||||
"window:allow-close",
|
||||
"window:allow-destroy",
|
||||
"window:default",
|
||||
"window:allow-is-minimized",
|
||||
"window:allow-set-title",
|
||||
"window:allow-set-focus",
|
||||
"window:allow-show",
|
||||
"window:allow-unminimize",
|
||||
"core:webview:allow-create-webview-window",
|
||||
"core:webview:allow-set-webview-zoom",
|
||||
"core:webview:default",
|
||||
"core:window:allow-center",
|
||||
"core:window:allow-close",
|
||||
"core:window:allow-destroy",
|
||||
"core:window:allow-set-size",
|
||||
"core:window:default",
|
||||
"core:window:allow-is-minimized",
|
||||
"core:window:allow-set-title",
|
||||
"core:window:allow-set-focus",
|
||||
"core:window:allow-show",
|
||||
"core:window:allow-unminimize",
|
||||
{
|
||||
"identifier": "fs:allow-exists",
|
||||
"allow": [{ "path": "**" }]
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,23 +5,28 @@
|
||||
"local": true,
|
||||
"windows": ["Dev_JSON"],
|
||||
"permissions": [
|
||||
"app:allow-version",
|
||||
"app:default",
|
||||
"event:allow-listen",
|
||||
"event:default",
|
||||
"core:app:allow-version",
|
||||
"core:app:default",
|
||||
"core:event:allow-listen",
|
||||
"core:event:default",
|
||||
"http:allow-fetch",
|
||||
"log:allow-log",
|
||||
"log:default",
|
||||
"path:allow-resolve-directory",
|
||||
"path:default",
|
||||
"core:path:allow-resolve-directory",
|
||||
"core:path:default",
|
||||
"sql:allow-load",
|
||||
"sql:allow-execute",
|
||||
"sql:default",
|
||||
"window:allow-close",
|
||||
"window:allow-destroy",
|
||||
"window:allow-set-title",
|
||||
"window:allow-set-fullscreen",
|
||||
"window:allow-show",
|
||||
"core:webview:allow-set-webview-zoom",
|
||||
"core:webview:default",
|
||||
"core:window:allow-center",
|
||||
"core:window:allow-close",
|
||||
"core:window:allow-destroy",
|
||||
"core:window:allow-set-size",
|
||||
"core:window:allow-set-title",
|
||||
"core:window:allow-set-fullscreen",
|
||||
"core:window:allow-show",
|
||||
"core:window:default",
|
||||
{
|
||||
"identifier": "http:default",
|
||||
"allow": [{ "url": "https://*.miyoushe.com/*" }, { "url": "https://*.mihoyo.com/*" }]
|
||||
@@ -38,16 +43,25 @@
|
||||
"permissions": [
|
||||
"dialog:allow-message",
|
||||
"dialog:default",
|
||||
"event:allow-emit",
|
||||
"path:allow-resolve-directory",
|
||||
"path:default",
|
||||
"core:event:allow-emit",
|
||||
"http:allow-fetch",
|
||||
"core:webview:allow-set-webview-zoom",
|
||||
"core:webview:default",
|
||||
"core:window:allow-center",
|
||||
"core:window:default",
|
||||
"core:path:allow-resolve-directory",
|
||||
"core:path:default",
|
||||
{ "identifier": "fs:allow-exists", "allow": [{ "path": "**" }] },
|
||||
{ "identifier": "fs:allow-mkdir", "allow": [{ "path": "**" }] },
|
||||
{ "identifier": "fs:allow-read-dir", "allow": [{ "path": "**" }] },
|
||||
{ "identifier": "fs:allow-read-text-file", "allow": [{ "path": "**" }] },
|
||||
{ "identifier": "fs:allow-remove", "allow": [{ "path": "**" }] },
|
||||
{ "identifier": "fs:allow-write-file", "allow": [{ "path": "**" }] },
|
||||
{ "identifier": "fs:allow-write-text-file", "allow": [{ "path": "**" }] }
|
||||
{ "identifier": "fs:allow-write-text-file", "allow": [{ "path": "**" }] },
|
||||
{
|
||||
"identifier": "http:default",
|
||||
"allow": [{ "url": "https://*.miyoushe.com/*" }, { "url": "https://*.mihoyo.com/*" }]
|
||||
}
|
||||
],
|
||||
"platforms": ["windows", "macOS"]
|
||||
},
|
||||
@@ -57,28 +71,33 @@
|
||||
"local": true,
|
||||
"windows": ["Sub_window"],
|
||||
"permissions": [
|
||||
"app:allow-version",
|
||||
"app:default",
|
||||
"core:app:allow-version",
|
||||
"core:app:default",
|
||||
"dialog:allow-save",
|
||||
"dialog:default",
|
||||
"event:allow-listen",
|
||||
"event:default",
|
||||
"core:event:allow-listen",
|
||||
"core:event:default",
|
||||
"fs:default",
|
||||
"http:allow-fetch",
|
||||
"log:allow-log",
|
||||
"log:default",
|
||||
"path:allow-resolve-directory",
|
||||
"path:default",
|
||||
"core:path:allow-resolve-directory",
|
||||
"core:path:default",
|
||||
"shell:allow-open",
|
||||
"shell:default",
|
||||
"sql:allow-load",
|
||||
"sql:allow-execute",
|
||||
"sql:default",
|
||||
"window:allow-close",
|
||||
"window:allow-destroy",
|
||||
"window:allow-set-title",
|
||||
"window:allow-set-fullscreen",
|
||||
"window:allow-show",
|
||||
"core:webview:allow-set-webview-zoom",
|
||||
"core:webview:default",
|
||||
"core:window:allow-center",
|
||||
"core:window:allow-close",
|
||||
"core:window:allow-destroy",
|
||||
"core:window:allow-set-size",
|
||||
"core:window:allow-set-title",
|
||||
"core:window:allow-set-fullscreen",
|
||||
"core:window:allow-show",
|
||||
"core:window:default",
|
||||
{ "identifier": "fs:allow-exists", "allow": [{ "path": "**" }] },
|
||||
{ "identifier": "fs:allow-mkdir", "allow": [{ "path": "**" }] },
|
||||
{ "identifier": "fs:allow-read-dir", "allow": [{ "path": "**" }] },
|
||||
@@ -99,18 +118,18 @@
|
||||
"local": true,
|
||||
"windows": ["TeyvatGuide"],
|
||||
"permissions": [
|
||||
"app:allow-version",
|
||||
"app:default",
|
||||
"core:app:allow-version",
|
||||
"core:app:default",
|
||||
"dialog:allow-save",
|
||||
"dialog:default",
|
||||
"event:allow-listen",
|
||||
"event:default",
|
||||
"core:event:allow-listen",
|
||||
"core:event:default",
|
||||
"fs:default",
|
||||
"http:allow-fetch",
|
||||
"log:allow-log",
|
||||
"log:default",
|
||||
"path:allow-resolve-directory",
|
||||
"path:default",
|
||||
"core:path:allow-resolve-directory",
|
||||
"core:path:default",
|
||||
"process:allow-exit",
|
||||
"process:default",
|
||||
"shell:allow-execute",
|
||||
@@ -119,16 +138,19 @@
|
||||
"sql:allow-load",
|
||||
"sql:allow-execute",
|
||||
"sql:default",
|
||||
"webview:allow-create-webview-window",
|
||||
"webview:default",
|
||||
"window:allow-close",
|
||||
"window:allow-destroy",
|
||||
"window:default",
|
||||
"window:allow-is-minimized",
|
||||
"window:allow-set-title",
|
||||
"window:allow-set-focus",
|
||||
"window:allow-show",
|
||||
"window:allow-unminimize",
|
||||
"core:webview:allow-create-webview-window",
|
||||
"core:webview:allow-set-webview-zoom",
|
||||
"core:webview:default",
|
||||
"core:window:allow-center",
|
||||
"core:window:allow-close",
|
||||
"core:window:allow-destroy",
|
||||
"core:window:allow-set-size",
|
||||
"core:window:default",
|
||||
"core:window:allow-is-minimized",
|
||||
"core:window:allow-set-title",
|
||||
"core:window:allow-set-focus",
|
||||
"core:window:allow-show",
|
||||
"core:window:allow-unminimize",
|
||||
{ "identifier": "fs:allow-exists", "allow": [{ "path": "**" }] },
|
||||
{ "identifier": "fs:allow-mkdir", "allow": [{ "path": "**" }] },
|
||||
{ "identifier": "fs:allow-read-dir", "allow": [{ "path": "**" }] },
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,8 @@
|
||||
//! @file src/client/menu.rs
|
||||
//! @desc 客户端菜单模块,负责操作米游社客户端菜单
|
||||
//! @since Beta v0.5.0
|
||||
//! @since Beta v0.5.2
|
||||
|
||||
use crate::client::utils;
|
||||
use tauri::menu::{Menu, MenuBuilder, MenuEvent, MenuItemBuilder, Submenu, SubmenuBuilder};
|
||||
use tauri::{AppHandle, LogicalSize, Manager, Size, Window, Wry};
|
||||
use tauri_utils::config::WebviewUrl;
|
||||
@@ -21,7 +22,7 @@ pub fn get_mhy_client_url(func: String) -> WebviewUrl {
|
||||
.parse()
|
||||
.unwrap();
|
||||
}
|
||||
return WebviewUrl::External(url_res);
|
||||
WebviewUrl::External(url_res)
|
||||
}
|
||||
|
||||
// 创建子菜单-工具
|
||||
@@ -39,7 +40,7 @@ fn create_utils_menu(app: AppHandle) -> Submenu<Wry> {
|
||||
.item(&rotate_window_submenu)
|
||||
.build()
|
||||
.expect("failed to create utils_menu");
|
||||
return utils_menu;
|
||||
utils_menu
|
||||
}
|
||||
|
||||
// 创建米游社客户端菜单
|
||||
@@ -48,13 +49,13 @@ pub fn create_mhy_menu(app: AppHandle) -> Menu<Wry> {
|
||||
let cancel_top_menu = MenuItemBuilder::with_id("cancel_top", "取消置顶").build(&app).unwrap();
|
||||
let open_post_menu = MenuItemBuilder::with_id("open_post", "打开帖子").build(&app).unwrap();
|
||||
let utils_menu = create_utils_menu(app.clone());
|
||||
return MenuBuilder::new(&app)
|
||||
MenuBuilder::new(&app)
|
||||
.item(&top_menu)
|
||||
.item(&cancel_top_menu)
|
||||
.item(&open_post_menu)
|
||||
.item(&utils_menu)
|
||||
.build()
|
||||
.expect("failed to create mhy_menu");
|
||||
.expect("failed to create mhy_menu")
|
||||
}
|
||||
|
||||
// 菜单栏事件处理
|
||||
@@ -181,10 +182,17 @@ fn handle_menu_rotate_window(app_handle: &Window) {
|
||||
let window = window_get.unwrap();
|
||||
// 获取窗口宽高比
|
||||
let cur_size = window.inner_size().ok().unwrap();
|
||||
let monitor = window.primary_monitor().ok().unwrap().unwrap();
|
||||
if cur_size.width > cur_size.height {
|
||||
window.set_size(Size::Logical(LogicalSize { width: 400.0, height: 800.0 })).unwrap();
|
||||
let trans_size = utils::get_window_size2(monitor, 400.0, 800.0);
|
||||
window
|
||||
.set_size(Size::Logical(LogicalSize { width: trans_size.0, height: trans_size.1 }))
|
||||
.unwrap();
|
||||
} else {
|
||||
window.set_size(Size::Logical(LogicalSize { width: 1280.0, height: 720.0 })).unwrap();
|
||||
let trans_size = utils::get_window_size2(monitor, 1280.0, 720.0);
|
||||
window
|
||||
.set_size(Size::Logical(LogicalSize { width: trans_size.0, height: trans_size.1 }))
|
||||
.unwrap();
|
||||
}
|
||||
window.center().unwrap();
|
||||
window.set_focus().unwrap();
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
//! @file src/client/mod.rs
|
||||
//! @desc 客户端模块,负责操作米游社客户端
|
||||
//! @since Beta v0.5.0
|
||||
//! @since Beta v0.5.2
|
||||
|
||||
mod menu;
|
||||
mod utils;
|
||||
|
||||
use tauri::{AppHandle, Manager, WebviewWindowBuilder};
|
||||
use tauri_utils::config::WebviewUrl;
|
||||
|
||||
@@ -29,8 +31,9 @@ pub async fn create_mhy_client(handle: AppHandle, func: String, url: String) {
|
||||
window_find.unwrap().destroy().unwrap();
|
||||
return;
|
||||
}
|
||||
let trans_size = utils::get_window_size(handle.clone(), win_width, win_height);
|
||||
WebviewWindowBuilder::new(&handle, "mhy_client", url_parse)
|
||||
.inner_size(win_width, win_height)
|
||||
.inner_size(trans_size.0, trans_size.1)
|
||||
.title("米游社")
|
||||
.center()
|
||||
.user_agent(win_ua)
|
||||
|
||||
22
src-tauri/src/client/utils.rs
Normal file
22
src-tauri/src/client/utils.rs
Normal file
@@ -0,0 +1,22 @@
|
||||
//! @file src/client/utils.rs
|
||||
//! @desc 结合屏幕分辨率获取窗口大小
|
||||
//! @since Beta v0.5.2
|
||||
|
||||
use tauri::{AppHandle, Manager, Monitor};
|
||||
|
||||
pub fn get_window_size(app: AppHandle, width: f64, height: f64) -> (f64, f64) {
|
||||
let mhy_window = app.get_webview_window("TeyvatGuide").unwrap();
|
||||
let monitor = mhy_window.primary_monitor().unwrap().expect("failed to get primary monitor");
|
||||
get_window_size2(monitor, width, height)
|
||||
}
|
||||
|
||||
pub fn get_window_size2(monitor: Monitor, width: f64, height: f64) -> (f64, f64) {
|
||||
let monitor_size = monitor.size();
|
||||
let monitor_width = monitor_size.width as f64;
|
||||
let monitor_height = monitor_size.height as f64;
|
||||
let width_scale = monitor_width / 1920.0;
|
||||
let height_scale = monitor_height / 1080.0;
|
||||
let get_width = (width * width_scale).round();
|
||||
let get_height = (height * height_scale).round();
|
||||
(get_width, get_height)
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
//! @file src/commands.rs
|
||||
//! @desc 命令模块,负责处理命令
|
||||
//! @since Beta v0.5.0
|
||||
//! @since Beta v0.5.1
|
||||
|
||||
use tauri::{AppHandle, Manager, WebviewWindowBuilder};
|
||||
use tauri::{AppHandle, Emitter, Manager, WebviewWindowBuilder};
|
||||
use tauri_utils::config::{WebviewUrl, WindowConfig};
|
||||
|
||||
// 放一个常数,用来判断应用是否初始化
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
//! @file src/main.rs
|
||||
//! @desc 主模块,用于启动应用
|
||||
//! @since Beta v0.5.0
|
||||
//! @since Beta v0.5.2
|
||||
|
||||
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
|
||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||
|
||||
use tauri::Manager;
|
||||
use tauri::{Emitter, Manager};
|
||||
mod client;
|
||||
mod commands;
|
||||
mod plugins;
|
||||
@@ -45,7 +45,7 @@ fn main() {
|
||||
.plugin(tauri_plugin_os::init())
|
||||
.plugin(tauri_plugin_process::init())
|
||||
.plugin(tauri_plugin_shell::init())
|
||||
.plugin(plugins::build_sql_plugin())
|
||||
.plugin(tauri_plugin_sql::Builder::default().build())
|
||||
.plugin(plugins::build_log_plugin())
|
||||
.setup(|_app| {
|
||||
let _window = _app.get_webview_window("TeyvatGuide");
|
||||
|
||||
@@ -1,18 +1,12 @@
|
||||
//! @file src/plugins.rs
|
||||
//! @desc 插件模块,用于注册插件
|
||||
//! @since Beta v0.5.0
|
||||
//! @since Beta v0.5.2
|
||||
|
||||
use super::utils;
|
||||
use log::LevelFilter;
|
||||
use tauri::plugin::TauriPlugin;
|
||||
use tauri::Runtime;
|
||||
use tauri_plugin_log::{Target, TargetKind, TimezoneStrategy};
|
||||
use tauri_plugin_sql::PluginConfig;
|
||||
|
||||
// sqlite 插件
|
||||
pub fn build_sql_plugin<R: Runtime>() -> TauriPlugin<R, Option<PluginConfig>> {
|
||||
tauri_plugin_sql::Builder::default().build()
|
||||
}
|
||||
|
||||
// 日志插件
|
||||
pub fn build_log_plugin<R: Runtime>() -> TauriPlugin<R> {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"productName": "TeyvatGuide",
|
||||
"identifier": "TeyvatGuide",
|
||||
"version": "0.5.0",
|
||||
"version": "0.5.2",
|
||||
"build": {
|
||||
"beforeDevCommand": "pnpm vite:dev",
|
||||
"beforeBuildCommand": "pnpm vite:build",
|
||||
|
||||
38
src/App.vue
38
src/App.vue
@@ -11,7 +11,8 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { app, event, core, webviewWindow } from "@tauri-apps/api";
|
||||
import { app, event, core, webviewWindow, window as TauriWindow } from "@tauri-apps/api";
|
||||
import { PhysicalSize } from "@tauri-apps/api/dpi";
|
||||
import { UnlistenFn, Event } from "@tauri-apps/api/event";
|
||||
import { mkdir } from "@tauri-apps/plugin-fs";
|
||||
import { storeToRefs } from "pinia";
|
||||
@@ -42,7 +43,7 @@ let themeListener: UnlistenFn;
|
||||
let urlListener: UnlistenFn;
|
||||
|
||||
onBeforeMount(async () => {
|
||||
const win = webviewWindow.getCurrent();
|
||||
const win = webviewWindow.getCurrentWebviewWindow();
|
||||
isMain.value = win.label === "TeyvatGuide";
|
||||
if (isMain.value) {
|
||||
const title = "Teyvat Guide v" + (await app.getVersion()) + " Beta";
|
||||
@@ -51,9 +52,42 @@ onBeforeMount(async () => {
|
||||
await core.invoke("init_app");
|
||||
urlListener = await getDeepLink();
|
||||
}
|
||||
await checkResize();
|
||||
await win.show();
|
||||
});
|
||||
|
||||
async function checkResize(): Promise<void> {
|
||||
const screen = await TauriWindow.currentMonitor();
|
||||
if (screen === null) {
|
||||
showSnackbar({
|
||||
text: "获取屏幕信息失败!",
|
||||
color: "error",
|
||||
timeout: 3000,
|
||||
});
|
||||
return;
|
||||
}
|
||||
const windowCur = await webviewWindow.getCurrentWebviewWindow();
|
||||
if (await windowCur.isMaximized()) return;
|
||||
const designSize = getSize(windowCur.label);
|
||||
const widthScale = screen.size.width / 1920;
|
||||
const heightScale = screen.size.height / 1080;
|
||||
await windowCur.setSize(
|
||||
new PhysicalSize(
|
||||
Math.round(designSize.width * widthScale),
|
||||
Math.round(designSize.height * heightScale),
|
||||
),
|
||||
);
|
||||
await windowCur.setZoom((1 / screen.scaleFactor) * Math.min(widthScale, heightScale));
|
||||
await windowCur.center();
|
||||
return;
|
||||
}
|
||||
|
||||
function getSize(label: string): PhysicalSize {
|
||||
if (label === "TeyvatGuide") return new PhysicalSize(1600, 900);
|
||||
if (label === "Sub_Window" || label === "Dev_JSON") return new PhysicalSize(960, 720);
|
||||
return new PhysicalSize(1280, 720);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
document.documentElement.className = theme.value;
|
||||
themeListener = event.listen("readTheme", async (e: Event<string>) => {
|
||||
|
||||
@@ -301,7 +301,7 @@ onMounted(async () => {
|
||||
const theme = e.payload;
|
||||
themeGet.value = theme === "default" ? "default" : "dark";
|
||||
});
|
||||
if (webviewWindow.getCurrent().label === "TeyvatGuide") {
|
||||
if (webviewWindow.getCurrentWebviewWindow().label === "TeyvatGuide") {
|
||||
await mhyClient.run();
|
||||
}
|
||||
if (userStore.briefInfo.value && userStore.briefInfo.value.nickname) {
|
||||
|
||||
@@ -52,7 +52,7 @@ function toStore() {
|
||||
}
|
||||
|
||||
function toSite() {
|
||||
window.open("https://app.btmuli.ink/docs/Changelogs.html");
|
||||
window.open("https://app.btmuli.ink/docs/TeyvatGuide/changelogs.html");
|
||||
}
|
||||
</script>
|
||||
<style lang="css" scoped>
|
||||
|
||||
@@ -2,41 +2,60 @@
|
||||
<v-list class="config-list">
|
||||
<v-list-subheader :inset="true" class="config-header" title="路径" />
|
||||
<v-divider :inset="true" class="border-opacity-75" />
|
||||
<v-list-item>
|
||||
<v-list-item title="用户数据目录" :subtitle="appStore.userDir">
|
||||
<template #prepend>
|
||||
<div class="config-icon">
|
||||
<v-icon>mdi-folder-key</v-icon>
|
||||
</div>
|
||||
</template>
|
||||
<v-list-item-title style="cursor: pointer" @click="confirmCUD"
|
||||
>用户数据目录
|
||||
</v-list-item-title>
|
||||
<v-list-item-subtitle @click="openPath('user')">{{ appStore.userDir }}</v-list-item-subtitle>
|
||||
<template #append>
|
||||
<v-icon @click="copyPath('user')">mdi-content-copy</v-icon>
|
||||
<v-icon @click="confirmCUD()" style="cursor: pointer" title="修改用户数据目录"
|
||||
>mdi-pencil
|
||||
</v-icon>
|
||||
 
|
||||
<v-icon @click="openPath('user')" style="cursor: pointer" title="打开用户数据目录"
|
||||
>mdi-folder-open
|
||||
</v-icon>
|
||||
 
|
||||
<v-icon @click="copyPath('user')" style="cursor: pointer" title="复制用户数据目录路径"
|
||||
>mdi-content-copy
|
||||
</v-icon>
|
||||
</template>
|
||||
</v-list-item>
|
||||
<v-list-item title="应用数据库路径">
|
||||
<v-list-item title="应用数据库路径" :subtitle="appStore.dbPath">
|
||||
<template #prepend>
|
||||
<div class="config-icon">
|
||||
<v-icon>mdi-folder-account</v-icon>
|
||||
</div>
|
||||
</template>
|
||||
<v-list-item-subtitle @click="openPath('db')">{{ appStore.dbPath }}</v-list-item-subtitle>
|
||||
<template #append>
|
||||
<v-icon @click="copyPath('db')">mdi-content-copy</v-icon>
|
||||
<v-icon @click="openPath('db')" style="cursor: pointer" title="打开数据库目录"
|
||||
>mdi-folder-open
|
||||
</v-icon>
|
||||
 
|
||||
<v-icon @click="copyPath('db')" style="cursor: pointer" title="复制数据库目录路径"
|
||||
>mdi-content-copy
|
||||
</v-icon>
|
||||
</template>
|
||||
</v-list-item>
|
||||
<v-list-item>
|
||||
<v-list-item title="日志目录" :subtitle="appStore.logDir">
|
||||
<template #prepend>
|
||||
<div class="config-icon">
|
||||
<v-icon>mdi-folder-multiple</v-icon>
|
||||
</div>
|
||||
</template>
|
||||
<v-list-item-title style="cursor: pointer" @click="confirmCLD">日志目录</v-list-item-title>
|
||||
<v-list-item-subtitle @click="openPath('log')">{{ appStore.logDir }}</v-list-item-subtitle>
|
||||
<template #append>
|
||||
<v-icon @click="copyPath('log')">mdi-content-copy</v-icon>
|
||||
<v-icon @click="confirmCLD()" style="cursor: pointer" title="清理日志文件"
|
||||
>mdi-delete
|
||||
</v-icon>
|
||||
 
|
||||
<v-icon @click="openPath('log')" style="cursor: pointer" title="打开日志目录"
|
||||
>mdi-folder-open
|
||||
</v-icon>
|
||||
 
|
||||
<v-icon @click="copyPath('log')" style="cursor: pointer" title="复制日志目录路径"
|
||||
>mdi-content-copy
|
||||
</v-icon>
|
||||
</template>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
@@ -45,6 +64,7 @@
|
||||
import { path } from "@tauri-apps/api";
|
||||
import { open } from "@tauri-apps/plugin-dialog";
|
||||
import { readDir, remove } from "@tauri-apps/plugin-fs";
|
||||
import { onMounted } from "vue";
|
||||
|
||||
import TGSqlite from "../../plugins/Sqlite/index.js";
|
||||
import { useAppStore } from "../../store/modules/app.js";
|
||||
@@ -55,12 +75,30 @@ import showSnackbar from "../func/snackbar.js";
|
||||
|
||||
const appStore = useAppStore();
|
||||
|
||||
onMounted(async () => {
|
||||
const logDir = await path.appLogDir();
|
||||
const dbPath = `${await path.appConfigDir()}${path.sep()}TeyvatGuide.db`;
|
||||
let message = "";
|
||||
if (appStore.dbPath !== dbPath) {
|
||||
appStore.dbPath = dbPath;
|
||||
await TGSqlite.saveAppData("dbPath", dbPath);
|
||||
message += "数据库路径 ";
|
||||
}
|
||||
if (appStore.logDir !== logDir) {
|
||||
appStore.logDir = logDir;
|
||||
message += "日志路径 ";
|
||||
}
|
||||
if (message !== "") {
|
||||
showSnackbar({
|
||||
text: `${message}已更新!`,
|
||||
color: "success",
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
async function confirmCUD(): Promise<void> {
|
||||
const oriDir = appStore.userDir;
|
||||
const check = await showConfirm({
|
||||
title: "确认修改用户数据路径吗?",
|
||||
text: "祈愿数据需修改后重新手动备份!",
|
||||
});
|
||||
const check = await showConfirm({ title: "确认修改用户数据路径吗?" });
|
||||
if (!check) {
|
||||
showSnackbar({
|
||||
color: "cancel",
|
||||
@@ -90,11 +128,21 @@ async function confirmCUD(): Promise<void> {
|
||||
appStore.userDir = dir;
|
||||
await TGSqlite.saveAppData("userDir", dir);
|
||||
await backUpUserData(dir);
|
||||
await remove(oriDir, { recursive: true });
|
||||
showSnackbar({
|
||||
text: "已重新备份数据!即将刷新页面!",
|
||||
timeout: 3000,
|
||||
text: "已重新备份数据!",
|
||||
color: "success",
|
||||
});
|
||||
const confirm = await showConfirm({
|
||||
title: "是否删除原用户数据目录?",
|
||||
text: "删除后不可恢复!",
|
||||
});
|
||||
if (confirm) {
|
||||
await remove(oriDir, { recursive: true });
|
||||
showSnackbar({
|
||||
text: "已删除原用户数据目录!",
|
||||
color: "success",
|
||||
});
|
||||
}
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 4000);
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<template>
|
||||
<ToGameLogin v-model="scan" @success="refreshUser" />
|
||||
<v-card class="tcu-box">
|
||||
<template #prepend>
|
||||
<v-avatar :image="userInfo.avatar" />
|
||||
@@ -9,16 +8,29 @@
|
||||
<template #text>{{ userInfo.desc }}</template>
|
||||
<template #actions>
|
||||
<v-spacer />
|
||||
<v-btn variant="outlined" @click="scan = true" icon="mdi-qrcode-scan" />
|
||||
<v-btn variant="outlined" @click="confirmRefreshUser" icon="mdi-refresh" :loading="loading" />
|
||||
<v-btn
|
||||
variant="outlined"
|
||||
@click="tryCaptchaLogin()"
|
||||
icon="mdi-cellphone"
|
||||
title="验证码登录"
|
||||
/>
|
||||
<v-btn
|
||||
variant="outlined"
|
||||
@click="confirmRefreshUser"
|
||||
icon="mdi-refresh"
|
||||
:loading="loading"
|
||||
title="刷新用户信息"
|
||||
/>
|
||||
<v-btn variant="outlined" @click="confirmCopyCookie" icon="mdi-cookie" title="复制Cookie" />
|
||||
</template>
|
||||
</v-card>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import type { UnlistenFn } from "@tauri-apps/api/event";
|
||||
import { UnlistenFn } from "@tauri-apps/api/event";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { onMounted, onUnmounted, ref, watch } from "vue";
|
||||
|
||||
import Mys from "../../plugins/Mys/index.js";
|
||||
import TGSqlite from "../../plugins/Sqlite/index.js";
|
||||
import { useAppStore } from "../../store/modules/app.js";
|
||||
import { useUserStore } from "../../store/modules/user.js";
|
||||
@@ -26,8 +38,8 @@ import TGLogger from "../../utils/TGLogger.js";
|
||||
import { getDeviceFp } from "../../web/request/getDeviceFp.js";
|
||||
import TGRequest from "../../web/request/TGRequest.js";
|
||||
import showConfirm from "../func/confirm.js";
|
||||
import showGeetest from "../func/geetest.js";
|
||||
import showSnackbar from "../func/snackbar.js";
|
||||
import ToGameLogin from "../overlay/to-gameLogin.vue";
|
||||
|
||||
interface TcUserBadgeEmits {
|
||||
(e: "loadOuter", v: TGApp.Component.Loading.EmitParams): void;
|
||||
@@ -39,7 +51,6 @@ const appStore = useAppStore();
|
||||
const userStore = storeToRefs(useUserStore());
|
||||
|
||||
const loading = ref<boolean>(false);
|
||||
const scan = ref<boolean>(false);
|
||||
const userInfo = ref<TGApp.App.Account.BriefInfo>({
|
||||
nickname: "未登录",
|
||||
uid: "-1",
|
||||
@@ -61,13 +72,72 @@ watch(userStore.briefInfo, (v) => {
|
||||
}
|
||||
});
|
||||
|
||||
async function tryCaptchaLogin(): Promise<void> {
|
||||
const phone = await showConfirm({
|
||||
mode: "input",
|
||||
title: "请输入手机号",
|
||||
text: "+86",
|
||||
});
|
||||
if (!phone) {
|
||||
showSnackbar({
|
||||
color: "cancel",
|
||||
text: "已取消验证码登录",
|
||||
});
|
||||
return;
|
||||
}
|
||||
const phoneReg = /^1[3-9]\d{9}$/;
|
||||
if (!phoneReg.test(phone)) {
|
||||
showSnackbar({
|
||||
color: "error",
|
||||
text: "请输入正确的手机号",
|
||||
});
|
||||
return;
|
||||
}
|
||||
const actionType = await tryGetCaptcha(phone);
|
||||
if (!actionType) return;
|
||||
showSnackbar({
|
||||
text: `已发送验证码到 ${phone}`,
|
||||
});
|
||||
const captcha = await showConfirm({
|
||||
mode: "input",
|
||||
title: "请输入验证码",
|
||||
text: "验证码:",
|
||||
otcancel: false,
|
||||
});
|
||||
if (!captcha) {
|
||||
showSnackbar({
|
||||
color: "error",
|
||||
text: "输入验证码为空",
|
||||
});
|
||||
return;
|
||||
}
|
||||
const loginResp = await tryLoginByCaptcha(phone, captcha, actionType);
|
||||
if (!loginResp) return;
|
||||
userStore.cookie.value = {
|
||||
account_id: loginResp.user_info.aid,
|
||||
ltuid: loginResp.user_info.aid,
|
||||
stuid: loginResp.user_info.aid,
|
||||
mid: loginResp.user_info.mid,
|
||||
cookie_token: "",
|
||||
stoken: loginResp.token.token,
|
||||
ltoken: "",
|
||||
};
|
||||
showSnackbar({
|
||||
text: "登录成功,即将刷新用户信息",
|
||||
color: "success",
|
||||
});
|
||||
setTimeout(() => {
|
||||
refreshUser();
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
async function refreshUser() {
|
||||
const ck = userStore.cookie.value;
|
||||
if (ck === undefined || JSON.stringify(ck) === "{}") {
|
||||
await TGLogger.Error("[tc-userBadge][refreshUser] cookie 不存在");
|
||||
showSnackbar({
|
||||
color: "error",
|
||||
text: "扫码登录后才能刷新用户信息!",
|
||||
text: "登录后才能刷新用户信息!",
|
||||
});
|
||||
appStore.isLogin = false;
|
||||
return;
|
||||
@@ -147,8 +217,8 @@ async function refreshUser() {
|
||||
emits("loadOuter", { show: false });
|
||||
}
|
||||
|
||||
async function refreshUserInfo(cnt: number = 0): Promise<number> {
|
||||
let failCount = cnt;
|
||||
async function refreshUserInfo(): Promise<number> {
|
||||
let failCount = 0;
|
||||
const ck = userStore.cookie.value;
|
||||
if (ck === undefined) {
|
||||
showSnackbar({
|
||||
@@ -218,6 +288,74 @@ async function confirmRefreshUser(): Promise<void> {
|
||||
await refreshUser();
|
||||
}
|
||||
|
||||
async function confirmCopyCookie(): Promise<void> {
|
||||
const res = await showConfirm({
|
||||
title: "确认复制 Cookie 吗?",
|
||||
text: "将会复制当前登录的 Cookie",
|
||||
});
|
||||
if (!res) {
|
||||
showSnackbar({
|
||||
color: "cancel",
|
||||
text: "已取消复制",
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (!userStore.cookie) {
|
||||
showSnackbar({
|
||||
color: "error",
|
||||
text: "请先登录",
|
||||
});
|
||||
return;
|
||||
}
|
||||
const ckText = useUserStore().getAllCookie();
|
||||
await navigator.clipboard.writeText(ckText);
|
||||
showSnackbar({
|
||||
text: "已复制 Cookie!",
|
||||
color: "success",
|
||||
});
|
||||
}
|
||||
|
||||
async function tryGetCaptcha(phone: string, aigis?: string): Promise<string | false> {
|
||||
const captchaResp = await Mys.User.getCaptcha(phone, aigis);
|
||||
if ("retcode" in captchaResp) {
|
||||
if (!captchaResp.data || captchaResp.data === "") {
|
||||
showSnackbar({
|
||||
text: `[${captchaResp.retcode}] ${captchaResp.message}`,
|
||||
color: "error",
|
||||
});
|
||||
return false;
|
||||
}
|
||||
const aigisResp: TGApp.Plugins.Mys.CaptchaLogin.CaptchaAigis = JSON.parse(captchaResp.data);
|
||||
const resp = await showGeetest(JSON.parse(aigisResp.data));
|
||||
const aigisStr = `${aigisResp.session_id};${btoa(JSON.stringify(resp))}`;
|
||||
return await tryGetCaptcha(phone, aigisStr);
|
||||
}
|
||||
return captchaResp.action_type;
|
||||
}
|
||||
|
||||
async function tryLoginByCaptcha(
|
||||
phone: string,
|
||||
captcha: string,
|
||||
actionType: string,
|
||||
aigis?: string,
|
||||
): Promise<TGApp.Plugins.Mys.CaptchaLogin.LoginData | false> {
|
||||
const loginResp = await Mys.User.login(phone, captcha, actionType, aigis);
|
||||
if ("retcode" in loginResp) {
|
||||
if (!loginResp.data || loginResp.data === "") {
|
||||
showSnackbar({
|
||||
text: `[${loginResp.retcode}] ${loginResp.message}`,
|
||||
color: "error",
|
||||
});
|
||||
return false;
|
||||
}
|
||||
const aigisResp: TGApp.Plugins.Mys.CaptchaLogin.CaptchaAigis = JSON.parse(loginResp.data);
|
||||
const resp = await showGeetest(JSON.parse(aigisResp.data));
|
||||
const aigisStr = `${aigisResp.session_id};${btoa(JSON.stringify(resp))}`;
|
||||
return await tryLoginByCaptcha(phone, captcha, actionType, aigisStr);
|
||||
}
|
||||
return loginResp;
|
||||
}
|
||||
|
||||
onUnmounted(() => {
|
||||
if (signListener) signListener();
|
||||
});
|
||||
|
||||
59
src/components/func/geetest.ts
Normal file
59
src/components/func/geetest.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* @file component/func/geetest.ts
|
||||
* @description 封装自定义 geetest 组件,通过函数调用的方式,简化 geetest 的使用
|
||||
* @since Beta v0.5.1
|
||||
*/
|
||||
|
||||
import { h, render } from "vue";
|
||||
import type { ComponentInternalInstance, VNode } from "vue";
|
||||
|
||||
import geetest from "./geetest.vue";
|
||||
|
||||
const geetestId = "tg-func-geetest";
|
||||
|
||||
/**
|
||||
* @description 自定义 geetest 组件
|
||||
* @since Beta v0.5.1
|
||||
* @extends ComponentInternalInstance
|
||||
* @property {Function} exposeProxy.displayBox 弹出 geetest 验证
|
||||
* @return GeetestInstance
|
||||
*/
|
||||
interface GeetestInstance extends ComponentInternalInstance {
|
||||
exposeProxy: {
|
||||
displayBox: (
|
||||
props: TGApp.Plugins.Mys.Geetest.reqResp,
|
||||
) => Promise<TGApp.Plugins.Mys.Geetest.validateResp | false>;
|
||||
};
|
||||
}
|
||||
|
||||
const renderBox = (props: TGApp.Plugins.Mys.Geetest.reqResp): VNode => {
|
||||
const container = document.createElement("div");
|
||||
container.id = geetestId;
|
||||
const boxVNode: VNode = h(geetest, props);
|
||||
render(boxVNode, container);
|
||||
document.body.appendChild(container);
|
||||
return boxVNode;
|
||||
};
|
||||
|
||||
let geetestInstance: VNode;
|
||||
|
||||
/**
|
||||
* @function showGeetest
|
||||
* @since Beta v0.5.1
|
||||
* @description 弹出 geetest 验证
|
||||
* @param {TGApp.Plugins.Mys.Geetest.reqResp} props geetest 验证的参数
|
||||
* @return {Promise<TGApp.Plugins.Mys.Geetest.validateResp>} 验证成功返回验证数据
|
||||
*/
|
||||
async function showGeetest(
|
||||
props: TGApp.Plugins.Mys.Geetest.reqResp,
|
||||
): Promise<TGApp.Plugins.Mys.Geetest.validateResp | false> {
|
||||
if (geetestInstance !== undefined) {
|
||||
const boxVue = <GeetestInstance>geetestInstance.component;
|
||||
return boxVue.exposeProxy.displayBox(props);
|
||||
} else {
|
||||
geetestInstance = renderBox(props);
|
||||
return await showGeetest(props);
|
||||
}
|
||||
}
|
||||
|
||||
export default showGeetest;
|
||||
165
src/components/func/geetest.vue
Normal file
165
src/components/func/geetest.vue
Normal file
@@ -0,0 +1,165 @@
|
||||
<template>
|
||||
<transition name="func-geetest-outer">
|
||||
<div v-show="show || showOuter" class="geetest-overlay" @click.self.prevent>
|
||||
<transition name="func-geetest-inner">
|
||||
<div v-show="showInner" class="geetest-box">
|
||||
<div class="geetest-top">
|
||||
<div class="geetest-title">请完成如下极验测试</div>
|
||||
</div>
|
||||
<div id="verify" class="geetest-mid">
|
||||
<div id="geetest" ref="geetestRef"></div>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</transition>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, watch } from "vue";
|
||||
|
||||
const show = ref<boolean>(false);
|
||||
const showOuter = ref<boolean>(false);
|
||||
const showInner = ref<boolean>(false);
|
||||
|
||||
const geetestRef = ref<HTMLElement>(<HTMLElement>document.getElementById("geetest"));
|
||||
|
||||
watch(show, () => {
|
||||
if (show.value) {
|
||||
showOuter.value = true;
|
||||
setTimeout(() => {
|
||||
showInner.value = true;
|
||||
}, 100);
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
showInner.value = false;
|
||||
}, 100);
|
||||
setTimeout(() => {
|
||||
showOuter.value = false;
|
||||
}, 300);
|
||||
}
|
||||
});
|
||||
|
||||
async function displayBox(
|
||||
props: TGApp.Plugins.Mys.Geetest.reqResp,
|
||||
): Promise<TGApp.Plugins.Mys.Geetest.validateResp | false> {
|
||||
return await new Promise<TGApp.Plugins.Mys.Geetest.validateResp>((resolve) => {
|
||||
// eslint-disable-next-line no-undef
|
||||
initGeetest(
|
||||
{
|
||||
gt: props.gt,
|
||||
challenge: props.challenge,
|
||||
offline: false,
|
||||
new_captcha: true,
|
||||
product: "custom",
|
||||
area: "#verify",
|
||||
width: "250px",
|
||||
},
|
||||
(captchaObj: TGApp.Plugins.Mys.Geetest.GeetestCaptcha) => {
|
||||
geetestRef.value.innerHTML = "";
|
||||
captchaObj.appendTo("#geetest");
|
||||
captchaObj.onReady(() => {
|
||||
show.value = true;
|
||||
});
|
||||
captchaObj.onSuccess(async () => {
|
||||
const validate = captchaObj.getValidate();
|
||||
resolve(validate);
|
||||
});
|
||||
captchaObj.onClose(() => {
|
||||
show.value = false;
|
||||
});
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
displayBox,
|
||||
});
|
||||
</script>
|
||||
<style lang="css" scoped>
|
||||
.func-geetest-outer-enter-active,
|
||||
.func-geetest-outer-leave-active,
|
||||
.func-geetest-inner-enter-active {
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.func-geetest-inner-leave-active {
|
||||
transition: all 0.5s ease-in-out;
|
||||
}
|
||||
|
||||
.func-geetest-inner-enter-from {
|
||||
opacity: 0;
|
||||
transform: scale(1.5);
|
||||
}
|
||||
|
||||
.func-geetest-inner-enter-to,
|
||||
.func-geetest-inner-leave-from {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
.func-geetest-outer-enter-to,
|
||||
.func-geetest-outer-leave-from {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.func-geetest-outer-enter-from,
|
||||
.func-geetest-outer-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.func-geetest-inner-leave-to {
|
||||
opacity: 0;
|
||||
transform: scale(0);
|
||||
}
|
||||
|
||||
.geetest-overlay {
|
||||
position: fixed;
|
||||
z-index: 100;
|
||||
top: 0;
|
||||
left: 0;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
backdrop-filter: blur(20px);
|
||||
background: rgb(0 0 0 / 50%);
|
||||
}
|
||||
|
||||
.geetest-box {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
background-color: var(--box-bg-1);
|
||||
color: var(--app-page-content);
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.geetest-top {
|
||||
border-bottom: 1px solid var(--common-shadow-4);
|
||||
font-family: var(--font-title);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.geetest-title {
|
||||
color: var(--common-text-title);
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.geetest-mid {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
background: var(--box-bg-2);
|
||||
}
|
||||
|
||||
#verify {
|
||||
width: 256px;
|
||||
height: 320px;
|
||||
}
|
||||
</style>
|
||||
@@ -125,7 +125,7 @@ function loadData(): void {
|
||||
gachaCount: reset4count.value,
|
||||
icon: getIcon(item.itemId),
|
||||
});
|
||||
reset4count.value = 0;
|
||||
reset4count.value = 1;
|
||||
} else if (item.rank === "5") {
|
||||
reset4count.value++;
|
||||
star5List.value.push({
|
||||
@@ -133,7 +133,7 @@ function loadData(): void {
|
||||
gachaCount: reset5count.value,
|
||||
icon: getIcon(item.itemId),
|
||||
});
|
||||
reset5count.value = 0;
|
||||
reset5count.value = 1;
|
||||
}
|
||||
});
|
||||
star5avg.value = getStar5Avg();
|
||||
|
||||
@@ -16,9 +16,19 @@
|
||||
:key="text.week"
|
||||
rounded
|
||||
:style="{
|
||||
border: text.week === weekNow ? '1px solid var(--common-shadow-4)' : 'none',
|
||||
backgroundColor: text.week === btnNow ? 'var(--tgc-yellow-1)' : 'var(--tgc-btn-1)',
|
||||
color: text.week === btnNow ? 'var(--box-text-4)' : 'var(--btn-text)',
|
||||
border: text.week === weekNow ? '1px solid var(--tgc-yellow-1)' : 'none',
|
||||
backgroundColor:
|
||||
text.week === btnNow
|
||||
? 'var(--tgc-yellow-1)'
|
||||
: text.week === weekNow
|
||||
? 'transparent'
|
||||
: 'var(--tgc-btn-1)',
|
||||
color:
|
||||
text.week === btnNow
|
||||
? 'var(--box-text-4)'
|
||||
: text.week === weekNow
|
||||
? 'var(--tgc-yellow-1)'
|
||||
: 'var(--btn-text)',
|
||||
}"
|
||||
@click="getContents(text.week)"
|
||||
>{{ text.text }}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { emit } from "@tauri-apps/api/event";
|
||||
import { onMounted, ref, watch } from "vue";
|
||||
|
||||
import Mys from "../../plugins/Mys/index.js";
|
||||
@@ -95,8 +96,15 @@ async function toBBS(link: URL): Promise<void> {
|
||||
}
|
||||
if (link.pathname.startsWith("//forum")) {
|
||||
const forumId = link.pathname.split("/").pop();
|
||||
const url = `https://www.miyoushe.com/ys/home/${forumId}`;
|
||||
window.open(url);
|
||||
const localPath = getLocalPath(forumId);
|
||||
if (localPath === "") {
|
||||
showSnackbar({
|
||||
text: `不支持的链接:${link.href}`,
|
||||
color: "warn",
|
||||
});
|
||||
return;
|
||||
}
|
||||
await emit("active_deep_link", `router?path=${localPath}`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -105,12 +113,40 @@ async function toBBS(link: URL): Promise<void> {
|
||||
color: "warn",
|
||||
});
|
||||
}
|
||||
|
||||
function getLocalPath(forum?: string): string {
|
||||
if (!forum) return "";
|
||||
const forumLocalMap: Record<string, string> = {
|
||||
"31": "/news/3", // 崩坏2官方
|
||||
"6": "/news/1", // 崩坏3官方
|
||||
"28": "/news/2", // 原神官方
|
||||
"33": "/news/4", // 未定官方
|
||||
"58": "/news/8", // 绝区零官方
|
||||
"36": "/news/5", // 大别野公告
|
||||
};
|
||||
if (forumLocalMap[forum]) return forumLocalMap[forum];
|
||||
const ysForums = ["26", "43", "29", "49", "50"];
|
||||
const srForums = ["52", "61", "56", "62"];
|
||||
const bh3Forums = ["1", "14", "4", "41"];
|
||||
const bh2Forums = ["30", "51", "40"];
|
||||
const wdForums = ["37", "60", "42", "38"];
|
||||
const zzzForums = ["57", "59", "64", "65"];
|
||||
const dbyForums = ["54", "35", "34", "39", "47", "48", "55", "36"];
|
||||
if (ysForums.includes(forum)) return `/posts/2/${forum}`;
|
||||
if (srForums.includes(forum)) return `/posts/6/${forum}`;
|
||||
if (bh3Forums.includes(forum)) return `/posts/1/${forum}`;
|
||||
if (bh2Forums.includes(forum)) return `/posts/3/${forum}`;
|
||||
if (wdForums.includes(forum)) return `/posts/4/${forum}`;
|
||||
if (zzzForums.includes(forum)) return `/posts/8/${forum}`;
|
||||
if (dbyForums.includes(forum)) return `/posts/5/${forum}`;
|
||||
return "";
|
||||
}
|
||||
</script>
|
||||
<style lang="css" scoped>
|
||||
.tgn-container {
|
||||
display: flex;
|
||||
padding: 5px;
|
||||
gap: 10px 10px;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.tgn-nav {
|
||||
|
||||
@@ -38,13 +38,20 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tpc-forum" v-if="card.forum" :title="`频道: ${card.forum.name}`">
|
||||
<div
|
||||
class="tpc-forum"
|
||||
v-if="card.forum && card.forum.name !== ''"
|
||||
:title="`频道: ${card.forum.name}`"
|
||||
>
|
||||
<img :src="card.forum.icon" :alt="card.forum.name" />
|
||||
<span>{{ card.forum.name }}</span>
|
||||
</div>
|
||||
<div v-if="props.selectMode" class="tpc-select">
|
||||
<v-checkbox-btn v-model="selectedList" :value="props.modelValue.post.post_id" />
|
||||
</div>
|
||||
<v-checkbox-btn
|
||||
v-if="props.selectMode"
|
||||
class="tpc-select"
|
||||
v-model="selectedList"
|
||||
:value="props.modelValue.post.post_id"
|
||||
/>
|
||||
</v-card>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
@@ -254,15 +261,23 @@ function getPostCard(item: TGApp.Plugins.Mys.Post.FullData): TGApp.Plugins.Mys.N
|
||||
border-bottom-left-radius: 5px;
|
||||
box-shadow: 0 0 10px var(--tgc-dark-1);
|
||||
color: var(--tgc-white-1);
|
||||
text-shadow: 0 0 5px var(--tgc-dark-1);
|
||||
}
|
||||
|
||||
.tpc-select {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
top: 0;
|
||||
left: 0;
|
||||
display: flex;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
backdrop-filter: blur(20px);
|
||||
background: var(--tgc-yellow-2);
|
||||
border-bottom-right-radius: 4px;
|
||||
box-shadow: 0 0 10px var(--tgc-dark-1);
|
||||
}
|
||||
|
||||
.tpc-forum img {
|
||||
|
||||
@@ -30,6 +30,8 @@ import { computed } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
|
||||
import { useAppStore } from "../../store/modules/app.js";
|
||||
import { ToChannelItem } from "../../web/constant/bbs.js";
|
||||
import TGConstant from "../../web/constant/TGConstant.js";
|
||||
import showSnackbar from "../func/snackbar.js";
|
||||
import TOverlay from "../main/t-overlay.vue";
|
||||
|
||||
@@ -41,12 +43,6 @@ interface ToChannelProps {
|
||||
|
||||
type ToChannelEmits = (e: "update:modelValue", value: boolean) => void;
|
||||
|
||||
interface ToChannelItem {
|
||||
title: string;
|
||||
icon: string;
|
||||
gid: string;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<ToChannelProps>(), {
|
||||
modelValue: false,
|
||||
});
|
||||
@@ -60,44 +56,7 @@ const visible = computed({
|
||||
});
|
||||
const router = useRouter();
|
||||
const appStore = useAppStore();
|
||||
|
||||
const channelList: ToChannelItem[] = [
|
||||
{
|
||||
title: "原神",
|
||||
icon: "/platforms/mhy/ys.webp",
|
||||
gid: "2",
|
||||
},
|
||||
{
|
||||
title: "崩坏:星穹铁道",
|
||||
icon: "/platforms/mhy/sr.webp",
|
||||
gid: "6",
|
||||
},
|
||||
{
|
||||
title: "绝区零",
|
||||
icon: "/platforms/mhy/zzz.webp",
|
||||
gid: "8",
|
||||
},
|
||||
{
|
||||
title: "崩坏3",
|
||||
icon: "/platforms/mhy/bh3.webp",
|
||||
gid: "1",
|
||||
},
|
||||
{
|
||||
title: "崩坏2",
|
||||
icon: "/platforms/mhy/bh2.webp",
|
||||
gid: "3",
|
||||
},
|
||||
{
|
||||
title: "未定事件簿",
|
||||
icon: "/platforms/mhy/wd.webp",
|
||||
gid: "4",
|
||||
},
|
||||
{
|
||||
title: "大别野",
|
||||
icon: "/platforms/mhy/dby.webp",
|
||||
gid: "5",
|
||||
},
|
||||
];
|
||||
const channelList = TGConstant.BBS.CHANNELS;
|
||||
|
||||
function onCancel(): void {
|
||||
visible.value = false;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<TOverlay v-model="visible" hide blur-val="20px" :to-click="onCancel">
|
||||
<div class="tog-box">
|
||||
<div class="tog-top">
|
||||
<div class="tog-title">请使用米游社或原神进行操作</div>
|
||||
<div class="tog-title">请使用米游社APP进行操作</div>
|
||||
<div class="tog-subtitle">所需米游社版本 >= 2.57.1</div>
|
||||
</div>
|
||||
<div class="tog-mid">
|
||||
@@ -135,7 +135,7 @@ async function cycleGetData() {
|
||||
if (res.payload.proto !== "OpenToken" && res.payload.proto !== "Account") {
|
||||
await TGLogger.Warn(`[to-gameLogin] 检测到意外协议:${res.payload.proto}`);
|
||||
showSnackbar({
|
||||
text: "请使用米游社或原神进行扫码操作",
|
||||
text: "请使用米游社APP进行扫码操作",
|
||||
color: "error",
|
||||
});
|
||||
visible.value = false;
|
||||
|
||||
@@ -89,13 +89,11 @@ watch(
|
||||
async (value) => {
|
||||
if (search.value === "" && value !== "") {
|
||||
search.value = value;
|
||||
await searchPosts();
|
||||
} else if (search.value !== value && value !== "") {
|
||||
search.value = value;
|
||||
results.value = [];
|
||||
lastId.value = "";
|
||||
isLast.value = false;
|
||||
await searchPosts();
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
@@ -63,6 +63,7 @@ onMounted(async () => {
|
||||
const width = highestResolution.width;
|
||||
const height = highestResolution.height;
|
||||
if (width && height) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
||||
width > height
|
||||
? (vodAspectRatio.value = width / height)
|
||||
: (vodAspectRatio.value = height / width);
|
||||
@@ -118,7 +119,7 @@ onMounted(async () => {
|
||||
};
|
||||
container.value = new Artplayer(option);
|
||||
container.value?.on("fullscreen", async (state) => {
|
||||
await TauriWindow.getCurrent().setFullscreen(state);
|
||||
await TauriWindow.getCurrentWindow().setFullscreen(state);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
class="twc-material-box"
|
||||
v-for="(item, index) in props.data"
|
||||
:key="index"
|
||||
@click="checkData(item)"
|
||||
@click="checkData(item, index)"
|
||||
>
|
||||
<div class="twc-material-left">
|
||||
<div class="twc-material-bg">
|
||||
@@ -19,7 +19,18 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<TwoMaterial :data="curData" v-model="showOverlay" />
|
||||
<TwoMaterial :data="curData" v-model="showOverlay">
|
||||
<template #left>
|
||||
<div class="card-arrow left" @click="switchMaterial(false)">
|
||||
<img src="../../assets/icons/arrow-right.svg" alt="right" />
|
||||
</div>
|
||||
</template>
|
||||
<template #right>
|
||||
<div class="card-arrow" @click="switchMaterial(true)">
|
||||
<img src="../../assets/icons/arrow-right.svg" alt="right" />
|
||||
</div>
|
||||
</template>
|
||||
</TwoMaterial>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref } from "vue";
|
||||
@@ -35,6 +46,7 @@ interface TwcMaterialsProp {
|
||||
|
||||
const props = defineProps<TwcMaterialsProp>();
|
||||
const showOverlay = ref(false);
|
||||
const curIndex = ref(0);
|
||||
const curData = ref<TGApp.App.Material.WikiItem>({
|
||||
id: 0,
|
||||
name: "",
|
||||
@@ -45,11 +57,12 @@ const curData = ref<TGApp.App.Material.WikiItem>({
|
||||
convert: [],
|
||||
});
|
||||
|
||||
function checkData(item: TGApp.App.Calendar.Material) {
|
||||
function checkData(item: TGApp.App.Calendar.Material, index: number) {
|
||||
if (showOverlay.value) showOverlay.value = false;
|
||||
const material = WikiMaterialData.find((m) => m.id === item.id);
|
||||
if (material) {
|
||||
curData.value = material;
|
||||
curIndex.value = index;
|
||||
showOverlay.value = true;
|
||||
} else {
|
||||
showSnackbar({
|
||||
@@ -58,6 +71,39 @@ function checkData(item: TGApp.App.Calendar.Material) {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function switchMaterial(isNext: boolean) {
|
||||
if (isNext) {
|
||||
if (curIndex.value === props.data.length - 1) {
|
||||
showSnackbar({
|
||||
text: "已经是最后一个材料了",
|
||||
color: "warn",
|
||||
});
|
||||
return;
|
||||
}
|
||||
curIndex.value++;
|
||||
} else {
|
||||
if (curIndex.value === 0) {
|
||||
showSnackbar({
|
||||
text: "已经是第一个材料了",
|
||||
color: "warn",
|
||||
});
|
||||
return;
|
||||
}
|
||||
curIndex.value--;
|
||||
}
|
||||
const curItem = props.data[curIndex.value];
|
||||
const material = WikiMaterialData.find((m) => m.id === curItem.id);
|
||||
if (material) {
|
||||
curData.value = material;
|
||||
return;
|
||||
}
|
||||
showSnackbar({
|
||||
text: `材料 ${curItem.name} 暂无详细信息`,
|
||||
color: "warn",
|
||||
});
|
||||
isNext ? curIndex.value-- : curIndex.value++;
|
||||
}
|
||||
</script>
|
||||
<style lang="css" scoped>
|
||||
.twc-materials-grid {
|
||||
@@ -111,4 +157,27 @@ function checkData(item: TGApp.App.Calendar.Material) {
|
||||
color: var(--box-text-2);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.card-arrow {
|
||||
position: relative;
|
||||
display: flex;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.dark .card-arrow {
|
||||
filter: invert(11%) sepia(73%) saturate(11%) hue-rotate(139deg) brightness(97%) contrast(81%);
|
||||
}
|
||||
|
||||
.card-arrow img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.card-arrow.left img {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -416,7 +416,7 @@
|
||||
},
|
||||
{
|
||||
"id": 1313,
|
||||
"contentId": 0,
|
||||
"contentId": 501569,
|
||||
"name": "夏沃蕾",
|
||||
"type": "角色牌",
|
||||
"icon": "/WIKI/GCG/normal/夏沃蕾.webp",
|
||||
@@ -754,7 +754,7 @@
|
||||
},
|
||||
{
|
||||
"id": 1608,
|
||||
"contentId": 0,
|
||||
"contentId": 501547,
|
||||
"name": "娜维娅",
|
||||
"type": "角色牌",
|
||||
"icon": "/WIKI/GCG/normal/娜维娅.webp",
|
||||
@@ -899,7 +899,7 @@
|
||||
},
|
||||
{
|
||||
"id": 2104,
|
||||
"contentId": 0,
|
||||
"contentId": 501558,
|
||||
"name": "愚人众·霜役人",
|
||||
"type": "角色牌",
|
||||
"icon": "/WIKI/GCG/normal/愚人众·霜役人.webp",
|
||||
@@ -1991,7 +1991,7 @@
|
||||
},
|
||||
{
|
||||
"id": 213131,
|
||||
"contentId": 0,
|
||||
"contentId": 501564,
|
||||
"name": "尖兵协同战法",
|
||||
"type": "行动牌",
|
||||
"icon": "/WIKI/GCG/normal/尖兵协同战法.webp",
|
||||
@@ -2297,7 +2297,7 @@
|
||||
},
|
||||
{
|
||||
"id": 216081,
|
||||
"contentId": 0,
|
||||
"contentId": 501548,
|
||||
"name": "不明流通渠道",
|
||||
"type": "行动牌",
|
||||
"icon": "/WIKI/GCG/normal/不明流通渠道.webp",
|
||||
@@ -2427,7 +2427,7 @@
|
||||
},
|
||||
{
|
||||
"id": 221041,
|
||||
"contentId": 0,
|
||||
"contentId": 501559,
|
||||
"name": "冰雅刺剑",
|
||||
"type": "行动牌",
|
||||
"icon": "/WIKI/GCG/normal/冰雅刺剑.webp",
|
||||
@@ -2904,7 +2904,7 @@
|
||||
},
|
||||
{
|
||||
"id": 311308,
|
||||
"contentId": 0,
|
||||
"contentId": 501561,
|
||||
"name": "「究极霸王超级魔剑」",
|
||||
"type": "行动牌",
|
||||
"icon": "/WIKI/GCG/normal/「究极霸王超级魔剑」.webp",
|
||||
@@ -3003,7 +3003,7 @@
|
||||
},
|
||||
{
|
||||
"id": 311409,
|
||||
"contentId": 0,
|
||||
"contentId": 501562,
|
||||
"name": "勘探钻机",
|
||||
"type": "行动牌",
|
||||
"icon": "/WIKI/GCG/normal/勘探钻机.webp",
|
||||
@@ -3382,7 +3382,7 @@
|
||||
},
|
||||
{
|
||||
"id": 312029,
|
||||
"contentId": 0,
|
||||
"contentId": 501566,
|
||||
"name": "角斗士的凯旋",
|
||||
"type": "行动牌",
|
||||
"icon": "/WIKI/GCG/normal/角斗士的凯旋.webp",
|
||||
@@ -4002,7 +4002,7 @@
|
||||
},
|
||||
{
|
||||
"id": 322027,
|
||||
"contentId": 0,
|
||||
"contentId": 501567,
|
||||
"name": "瑟琳",
|
||||
"type": "行动牌",
|
||||
"icon": "/WIKI/GCG/normal/瑟琳.webp",
|
||||
@@ -4618,7 +4618,7 @@
|
||||
},
|
||||
{
|
||||
"id": 332037,
|
||||
"contentId": 0,
|
||||
"contentId": 501563,
|
||||
"name": "噔噔!",
|
||||
"type": "行动牌",
|
||||
"icon": "/WIKI/GCG/normal/噔噔!.webp",
|
||||
|
||||
@@ -1409,7 +1409,7 @@
|
||||
},
|
||||
{
|
||||
"id": 10000099,
|
||||
"contentId": 0,
|
||||
"contentId": 501441,
|
||||
"dropDays": [3, 6, 7],
|
||||
"name": "艾梅莉埃",
|
||||
"itemType": "character",
|
||||
@@ -6094,7 +6094,7 @@
|
||||
},
|
||||
{
|
||||
"id": 13513,
|
||||
"contentId": 0,
|
||||
"contentId": 501725,
|
||||
"dropDays": [3, 6, 7],
|
||||
"name": "柔灯挽歌",
|
||||
"itemType": "weapon",
|
||||
|
||||
@@ -2141,5 +2141,41 @@
|
||||
"postId": "55147896",
|
||||
"up5List": [12512, 11511],
|
||||
"up4List": [11418, 12402, 13401, 14409, 15405]
|
||||
},
|
||||
{
|
||||
"name": "凝露轻芳",
|
||||
"version": "4.8",
|
||||
"order": 2,
|
||||
"banner": "https://sdk.hoyoverse.com/upload/ann/2024/07/19/a613355dfdca28e391d39905d3d0879b_5070315245511088722.jpg",
|
||||
"from": "2024-08-06T18:00:00+08:00",
|
||||
"to": "2024-08-27T14:59:00+08:00",
|
||||
"type": 301,
|
||||
"postId": "56072207",
|
||||
"up5List": [10000099],
|
||||
"up4List": [10000048, 10000023, 10000020]
|
||||
},
|
||||
{
|
||||
"name": "素霓伣天",
|
||||
"version": "4.8",
|
||||
"order": 2,
|
||||
"banner": "https://sdk.hoyoverse.com/upload/ann/2024/07/19/4dff39a5df5b0978653f46b92fa512f8_7130552963756568493.jpg",
|
||||
"from": "2024-08-06T18:00:00+08:00",
|
||||
"to": "2024-08-27T14:59:00+08:00",
|
||||
"type": 400,
|
||||
"postId": "56072208",
|
||||
"up5List": [10000060],
|
||||
"up4List": [10000048, 10000023, 10000020]
|
||||
},
|
||||
{
|
||||
"name": "神铸赋形",
|
||||
"version": "4.8",
|
||||
"order": 2,
|
||||
"banner": "https://sdk.hoyoverse.com/upload/ann/2024/07/19/3cecd09457f7158fad2d9c140dc09fdb_8057167364819822333.jpg",
|
||||
"from": "2024-08-06T18:00:00+08:00",
|
||||
"to": "2024-08-27T14:59:00+08:00",
|
||||
"type": 302,
|
||||
"postId": "56072210",
|
||||
"up5List": [13513, 15508],
|
||||
"up4List": [12415, 14416, 11402, 13407, 15401]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -181,7 +181,7 @@
|
||||
},
|
||||
{
|
||||
"id": 13513,
|
||||
"contentId": 0,
|
||||
"contentId": 501725,
|
||||
"name": "柔灯挽歌",
|
||||
"star": 5,
|
||||
"bg": "/icon/bg/5-Star.webp",
|
||||
|
||||
@@ -10,6 +10,7 @@ import { createVuetify } from "vuetify";
|
||||
import App from "./App.vue";
|
||||
import router from "./router/index.js";
|
||||
import store from "./store/index.js";
|
||||
import "https://static.geetest.com/static/js/gt.0.4.9.js";
|
||||
|
||||
import "@mdi/font/css/materialdesignicons.css";
|
||||
import "vuetify/styles";
|
||||
|
||||
@@ -399,7 +399,7 @@ async function exportUigf(): Promise<void> {
|
||||
});
|
||||
return;
|
||||
}
|
||||
const res = showConfirm({
|
||||
const res = await showConfirm({
|
||||
title: "是否导出祈愿数据?",
|
||||
text: `UID:${uidCur.value},共 ${gachaList.length} 条数据`,
|
||||
});
|
||||
|
||||
@@ -184,11 +184,10 @@ async function loadData(): Promise<void> {
|
||||
function getAnnoTime(content: string): string | false {
|
||||
const regexes = [
|
||||
/〓活动时间〓.*?\d\.\d版本期间持续开放/,
|
||||
/(?:〓活动时间〓|〓任务开放时间〓).*?(?:(\d\.\d版本更新(?:完成|))|(\d{4}\/\d{2}\/\d{2} \d{2}:\d{2}:\d{2}).*?)后永久开放/,
|
||||
/(?:〓(?:活动|折扣)时间〓|祈愿时间|【上架时间】).*?(\d\.\d版本更新后).*?~.*?<t class="t_(?:gl|lc)".*?>(.*?)<\/t>/,
|
||||
/(?:〓(?:活动|折扣)时间〓|祈愿时间|【上架时间】).*?<t class="t_(?:gl|lc)".*?>(.*?)<\/t>.*?~.*?<t class="t_(?:gl|lc)".*?>(.*?)<\/t>/,
|
||||
/(?:〓活动时间〓|〓任务开放时间〓).*?(?:(\d\.\d)版本更新(?:完成|)|<t class="t_(?:gl|lc)".*?>(.*?)<\/t> *?)后永久开放/s,
|
||||
/(?:〓活动时间〓|祈愿时间|【上架时间】|〓折扣时间〓).*?(\d\.\d)版本更新后.*?~.*?<t class="t_(?:gl|lc)".*?>(.*?)<\/t>/s,
|
||||
/(?:〓(?:活动|折扣)时间〓|祈愿时间|【上架时间】).*?<t class="t_(?:gl|lc)".*?>(.*?)<\/t>.*?~.*?<t class="t_(?:gl|lc)".*?>(.*?)<\/t>/s,
|
||||
/〓活动时间〓.*?(\d{4}\/\d{2}\/\d{2} \d{2}:\d{2}:\d{2}).*?(\d\.\d版本结束)/,
|
||||
/〓更新时间〓.+?<t class="t_(?:gl|lc)".*?>(.*?)&;lt;\/t>/,
|
||||
];
|
||||
if (content.match(regexes[0])) {
|
||||
const res = content.match(regexes[0]);
|
||||
@@ -206,10 +205,22 @@ function getAnnoTime(content: string): string | false {
|
||||
}
|
||||
if (content.match(regexes[2])) {
|
||||
const res = content.match(regexes[2]);
|
||||
if (res?.[1]?.match(/\d\.\d/)) {
|
||||
return `${res?.[1]}版本更新后 ~ ${res?.[2]}`;
|
||||
}
|
||||
return `${res?.[1]} ~ ${res?.[2]}`;
|
||||
}
|
||||
if (content.match(regexes[3])) {
|
||||
const res = content.match(regexes[3]);
|
||||
try {
|
||||
const span1 = document.createElement("span");
|
||||
span1.innerHTML = res?.[1] ?? "";
|
||||
const span2 = document.createElement("span");
|
||||
span2.innerHTML = res?.[2] ?? "";
|
||||
return `${span1.innerText} ~ ${span2.innerText}`;
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
return `${res?.[1]} ~ ${res?.[2]}`;
|
||||
}
|
||||
if (content.match(regexes[4])) {
|
||||
@@ -220,11 +231,6 @@ function getAnnoTime(content: string): string | false {
|
||||
}
|
||||
return `${res?.[1]} ~ ${res?.[2]}`;
|
||||
}
|
||||
if (content.match(regexes[5])) {
|
||||
console.log("verUpdateTime");
|
||||
// todo 待处理
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -346,6 +352,7 @@ async function createAnno(item: TGApp.App.Announcement.ListCard): Promise<void>
|
||||
border-bottom-left-radius: 5px;
|
||||
box-shadow: 0 0 10px var(--tgc-dark-1);
|
||||
color: var(--tgc-white-1);
|
||||
text-shadow: 0 0 5px var(--tgc-dark-1);
|
||||
}
|
||||
|
||||
.anno-label img {
|
||||
|
||||
@@ -52,6 +52,10 @@
|
||||
</div>
|
||||
</template>
|
||||
<v-list-item-title @click="confirmUpdateDevice()">刷新设备信息</v-list-item-title>
|
||||
<v-list-item-subtitle>
|
||||
{{ appStore.deviceInfo.device_name }}({{ appStore.deviceInfo.product }}) -
|
||||
{{ appStore.deviceInfo.device_fp }}
|
||||
</v-list-item-subtitle>
|
||||
<template #append>
|
||||
<v-icon @click="confirmUpdateDevice(true)">mdi-bug</v-icon>
|
||||
</template>
|
||||
|
||||
@@ -11,19 +11,21 @@
|
||||
label="请输入帖子 ID 或搜索词"
|
||||
:single-line="true"
|
||||
hide-details
|
||||
@click:append="searchPost"
|
||||
@keyup.enter="searchPost"
|
||||
@keyup.enter="searchPost()"
|
||||
/>
|
||||
<v-spacer />
|
||||
<v-btn class="news-switch-btn" @click="switchAnno">
|
||||
<v-btn class="news-top-btn" @click="firstLoad(tab, true)">
|
||||
<v-icon>mdi-refresh</v-icon>
|
||||
</v-btn>
|
||||
<v-btn class="news-top-btn" @click="showList = true">
|
||||
<v-icon>mdi-view-list</v-icon>
|
||||
</v-btn>
|
||||
<v-btn class="news-top-btn" @click="switchAnno" v-if="gid === '2'">
|
||||
<template #prepend>
|
||||
<v-icon>mdi-bullhorn</v-icon>
|
||||
</template>
|
||||
切换游戏内公告
|
||||
</v-btn>
|
||||
<v-btn class="news-switch-btn" @click="showList = true">
|
||||
<v-icon>mdi-view-list</v-icon>
|
||||
</v-btn>
|
||||
</v-tabs>
|
||||
<v-window v-model="tab">
|
||||
<v-window-item v-for="(value, index) in tabValues" :key="index" :value="value">
|
||||
@@ -33,7 +35,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="load-news">
|
||||
<v-btn class="news-switch-btn" rounded :loading="loadingSub" @click="loadMore(value)">
|
||||
<v-btn class="news-top-btn" rounded :loading="loadingSub" @click="loadMore(value)">
|
||||
已加载:{{ rawData[value].lastId }},加载更多
|
||||
</v-btn>
|
||||
</div>
|
||||
@@ -44,7 +46,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { nextTick, onMounted, ref, watch } from "vue";
|
||||
import { computed, nextTick, onMounted, ref } from "vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
|
||||
import showSnackbar from "../../components/func/snackbar.js";
|
||||
@@ -79,7 +81,6 @@ type RawData = {
|
||||
// 路由
|
||||
const router = useRouter();
|
||||
const gid = <string>useRoute().params.gid;
|
||||
let type = <string | undefined>useRoute().params.type;
|
||||
// loading
|
||||
const loading = ref<boolean>(true);
|
||||
const loadingTitle = ref<string>("正在加载");
|
||||
@@ -87,10 +88,21 @@ const loadingSub = ref<boolean>(false);
|
||||
|
||||
// UI 数据
|
||||
const appStore = useAppStore();
|
||||
const tab = ref<NewsKey>("notice");
|
||||
const showList = ref<boolean>(false);
|
||||
const showSearch = ref<boolean>(false);
|
||||
const tabValues = ref<Array<NewsKey>>(["notice", "activity", "news"]);
|
||||
const tabList = ["notice", "activity", "news"];
|
||||
const tab = computed({
|
||||
get: () => {
|
||||
if (appStore.recentNewsType === "" || !tabList.includes(appStore.recentNewsType)) {
|
||||
return "notice";
|
||||
}
|
||||
return <NewsKey>appStore.recentNewsType;
|
||||
},
|
||||
set: (val) => {
|
||||
appStore.recentNewsType = val;
|
||||
},
|
||||
});
|
||||
|
||||
// 渲染数据
|
||||
const search = ref<string>("");
|
||||
@@ -117,29 +129,13 @@ const rawData = ref<RawData>({
|
||||
},
|
||||
});
|
||||
|
||||
onMounted(async () => {
|
||||
await TGLogger.Info(`[News][${gid}][onMounted] 打开咨讯页面`);
|
||||
const typeList = ["notice", "activity", "news"];
|
||||
if (type != undefined) {
|
||||
appStore.recentNewsType = type;
|
||||
}
|
||||
const curType = appStore.recentNewsType;
|
||||
if (typeList.includes(curType)) {
|
||||
tab.value = <NewsKey>curType;
|
||||
} else {
|
||||
tab.value = "notice";
|
||||
appStore.recentNewsType = "notice";
|
||||
}
|
||||
await firstLoad(tab.value);
|
||||
});
|
||||
onMounted(async () => await firstLoad(tab.value));
|
||||
|
||||
watch(tab, (val) => {
|
||||
appStore.recentNewsType = val;
|
||||
});
|
||||
|
||||
async function firstLoad(key: NewsKey): Promise<void> {
|
||||
async function firstLoad(key: NewsKey, refresh: boolean = false): Promise<void> {
|
||||
if (rawData.value[key].lastId !== 0) {
|
||||
return;
|
||||
if (!refresh) return;
|
||||
postData.value[key] = [];
|
||||
rawData.value[key].lastId = 0;
|
||||
}
|
||||
loadingTitle.value = `正在获取${rawData.value[key].name}数据...`;
|
||||
loading.value = true;
|
||||
@@ -191,7 +187,7 @@ async function loadMore(key: NewsKey): Promise<void> {
|
||||
});
|
||||
}
|
||||
|
||||
function searchPost(): void {
|
||||
async function searchPost(): Promise<void> {
|
||||
if (search.value === "") {
|
||||
showSnackbar({
|
||||
text: "请输入搜索内容",
|
||||
@@ -203,7 +199,7 @@ function searchPost(): void {
|
||||
if (isNaN(numCheck)) {
|
||||
showSearch.value = true;
|
||||
} else {
|
||||
createPost(search.value);
|
||||
await createPost(search.value);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -215,7 +211,7 @@ function searchPost(): void {
|
||||
font-family: var(--font-title);
|
||||
}
|
||||
|
||||
.news-switch-btn {
|
||||
.news-top-btn {
|
||||
height: 40px;
|
||||
margin-left: 15px;
|
||||
background: var(--btn-bg-1);
|
||||
@@ -223,7 +219,7 @@ function searchPost(): void {
|
||||
font-family: var(--font-title);
|
||||
}
|
||||
|
||||
.dark .news-switch-btn {
|
||||
.dark .news-top-btn {
|
||||
border: 1px solid var(--common-shadow-2);
|
||||
}
|
||||
|
||||
|
||||
@@ -317,11 +317,11 @@ async function toEdit(): Promise<void> {
|
||||
await load();
|
||||
}
|
||||
|
||||
async function deleteOper(forever: boolean): Promise<void> {
|
||||
async function deleteOper(force: boolean): Promise<void> {
|
||||
if (selectedMode.value) {
|
||||
await deletePost(forever);
|
||||
await deletePost(force);
|
||||
} else {
|
||||
await deleteCollect(forever);
|
||||
await deleteCollect(force);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -377,7 +377,10 @@ async function deleteCollect(force: boolean): Promise<void> {
|
||||
return;
|
||||
}
|
||||
const title = force ? "删除分类" : "清空分类";
|
||||
const res = await showConfirm({ title: `确定${title}?` });
|
||||
const res = await showConfirm({
|
||||
title: `确定${title}?`,
|
||||
text: `该分类下${selected.value.length}条帖子将被${force ? "删除" : "移除分类(未分类将被删除)"}`,
|
||||
});
|
||||
if (!res) {
|
||||
showSnackbar({
|
||||
text: "取消删除",
|
||||
|
||||
@@ -49,7 +49,8 @@
|
||||
<ToPostSearch :gid="curGid.toString()" v-model="showSearch" :keyword="search" />
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { nextTick, onMounted, ref, watch } from "vue";
|
||||
import { nextTick, onBeforeMount, onMounted, ref, watch } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
|
||||
import showSnackbar from "../../components/func/snackbar.js";
|
||||
import TGameNav from "../../components/main/t-gamenav.vue";
|
||||
@@ -134,6 +135,10 @@ const gameList = {
|
||||
大别野: 5,
|
||||
};
|
||||
|
||||
// 路由
|
||||
const gid = useRoute().params.gid;
|
||||
const forum = useRoute().params.forum;
|
||||
|
||||
// 渲染参数
|
||||
const curForumLabel = ref<string>("酒馆");
|
||||
const forumItem = ref<string[]>(["酒馆", "攻略", "同人图", "COS", "硬核"]);
|
||||
@@ -162,12 +167,44 @@ const posts = ref<TGApp.Plugins.Mys.Post.FullData[]>([]);
|
||||
const search = ref<string>("");
|
||||
const showSearch = ref<boolean>(false);
|
||||
|
||||
onBeforeMount(async () => {
|
||||
if (gid && typeof gid === "string") {
|
||||
const gameKeys = Object.values(gameList);
|
||||
if (!gameKeys.includes(Number(gid))) {
|
||||
showSnackbar({
|
||||
text: `不存在GID为${gid}的游戏`,
|
||||
color: "error",
|
||||
});
|
||||
return;
|
||||
}
|
||||
curGameLabel.value = <keyof typeof gameList>(
|
||||
Object.keys(gameList)[gameKeys.indexOf(Number(gid))]
|
||||
);
|
||||
curGid.value = Number(gid);
|
||||
}
|
||||
if (forum && typeof forum === "string") {
|
||||
const forumKeys = Object.keys(forumList[curGameLabel.value]);
|
||||
if (!forumKeys.includes(forum)) {
|
||||
showSnackbar({
|
||||
text: `${curGameLabel.value}不存在ID为${forum}的版块`,
|
||||
color: "error",
|
||||
});
|
||||
return;
|
||||
}
|
||||
curForumLabel.value = forum;
|
||||
}
|
||||
});
|
||||
|
||||
onMounted(async () => {
|
||||
await TGLogger.Info(
|
||||
`[Posts][${curGameLabel.value}][onMounted][${curForumLabel.value}] 打开帖子列表`,
|
||||
);
|
||||
loading.value = true;
|
||||
await freshPostData();
|
||||
if (gid && forum) {
|
||||
freshCurForum(curForumLabel.value);
|
||||
} else {
|
||||
await freshPostData();
|
||||
}
|
||||
loading.value = false;
|
||||
});
|
||||
|
||||
@@ -202,7 +239,7 @@ async function freshPostData(): Promise<void> {
|
||||
);
|
||||
loading.value = true;
|
||||
loadingTitle.value = `正在加载 ${curGameLabel.value}-${curForumLabel.value}-${curSortLabel.value} 的数据`;
|
||||
const postsGet = await Mys.Posts.get(curForum.value, curSortType.value);
|
||||
const postsGet = await Mys.Posts.get(curForum.value, curSortType.value, 12);
|
||||
posts.value = postsGet.list;
|
||||
await nextTick();
|
||||
loading.value = false;
|
||||
|
||||
@@ -36,6 +36,11 @@
|
||||
}
|
||||
|
||||
.test-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
background: var(--tgc-btn-1);
|
||||
color: var(--btn-text);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
/**
|
||||
* @file plugins/Mys/index.ts
|
||||
* @description Mys plugin index
|
||||
* @since Beta v0.4.5
|
||||
* @since Beta v0.5.1
|
||||
*/
|
||||
|
||||
import MysApi from "./api/index.js";
|
||||
import { getCaptcha, doCaptchaLogin } from "./request/doCaptchaLogin.js";
|
||||
import { getLoginQr, getLoginStatus } from "./request/doGameLogin.js";
|
||||
import { getCollectionData, getCollectionPosts } from "./request/getCollectionData.js";
|
||||
import getForumList from "./request/getForumList.js";
|
||||
@@ -50,6 +51,8 @@ const Mys = {
|
||||
User: {
|
||||
getQr: getLoginQr,
|
||||
getData: getLoginStatus,
|
||||
getCaptcha,
|
||||
login: doCaptchaLogin,
|
||||
},
|
||||
Vote: {
|
||||
get: getVoteInfo,
|
||||
|
||||
140
src/plugins/Mys/request/doCaptchaLogin.ts
Normal file
140
src/plugins/Mys/request/doCaptchaLogin.ts
Normal file
@@ -0,0 +1,140 @@
|
||||
/**
|
||||
* @file plugins/Mys/request/doCaptchaLogin.ts
|
||||
* @description 通过短信验证码登录账号获取 stoken
|
||||
* @since Beta v0.5.1
|
||||
*/
|
||||
|
||||
import { JSEncrypt } from "jsencrypt";
|
||||
|
||||
import showSnackbar from "../../../components/func/snackbar.js";
|
||||
import TGHttp from "../../../utils/TGHttp.js";
|
||||
import { getDeviceInfo } from "../../../utils/toolFunc.js";
|
||||
import TGConstant from "../../../web/constant/TGConstant.js";
|
||||
|
||||
const PUB_KEY_STR = `-----BEGIN PUBLIC KEY-----
|
||||
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDDvekdPMHN3AYhm/vktJT+YJr7cI5DcsNKqdsx5DZX0gDuWFuIjzdwButrIYPNmRJ1G8ybDIF7oDW2eEpm5sMbL9zs
|
||||
9ExXCdvqrn51qELbqj0XxtMTIpaCHFSI50PfPpTFV9Xt/hmyVwokoOXFlAEgCn+Q
|
||||
CgGs52bFoYMtyi+xEQIDAQAB
|
||||
-----END PUBLIC KEY-----`;
|
||||
const encrypt = new JSEncrypt();
|
||||
encrypt.setPublicKey(PUB_KEY_STR);
|
||||
|
||||
/**
|
||||
* @description rsa 加密
|
||||
* @since Beta v0.5.1
|
||||
* @param {string} data - 待加密数据
|
||||
* @returns {string} 加密后数据
|
||||
*/
|
||||
function rsaEncrypt(data: string): string {
|
||||
const res = encrypt.encrypt(data.toString());
|
||||
if (res === false) {
|
||||
showSnackbar({
|
||||
text: "RSA 加密失败",
|
||||
color: "error",
|
||||
});
|
||||
return "";
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取短信验证码
|
||||
* @since Beta v0.5.1
|
||||
* @param {string} phone - 手机号
|
||||
* @param {string} [aigis] - 验证数据
|
||||
* @returns {Promise<TGApp.Plugins.Mys.CaptchaLogin.CaptchaData | TGApp.BBS.Response.Base>}
|
||||
*/
|
||||
export async function getCaptcha(
|
||||
phone: string,
|
||||
aigis?: string,
|
||||
): Promise<TGApp.Plugins.Mys.CaptchaLogin.CaptchaData | TGApp.BBS.Response.BaseWithData> {
|
||||
const url = "https://passport-api.mihoyo.com/account/ma-cn-verifier/verifier/createLoginCaptcha";
|
||||
const device_fp = getDeviceInfo("device_fp");
|
||||
const device_name = getDeviceInfo("device_name");
|
||||
const device_id = getDeviceInfo("device_id");
|
||||
const device_model = getDeviceInfo("product");
|
||||
const body = { area_code: rsaEncrypt("+86"), mobile: rsaEncrypt(phone) };
|
||||
const header: Record<string, string> = {
|
||||
"x-rpc-aigis": aigis || "",
|
||||
"x-rpc-app_version": TGConstant.BBS.VERSION,
|
||||
"x-rpc-client_type": "2",
|
||||
"x-rpc-app_id": TGConstant.BBS.APP_ID,
|
||||
"x-rpc-device_fp": device_fp,
|
||||
"x-rpc-device_name": device_name,
|
||||
"x-rpc-device_id": device_id,
|
||||
"x-rpc-device_model": device_model,
|
||||
"user-agent": TGConstant.BBS.UA_MOBILE,
|
||||
"content-type": "application/json",
|
||||
referer: "https://user.miyoushe.com/",
|
||||
"x-rpc-game_biz": TGConstant.Utils.GAME_BIZ,
|
||||
};
|
||||
const resp = await TGHttp<
|
||||
TGApp.Plugins.Mys.CaptchaLogin.CaptchaResponse | TGApp.BBS.Response.Base
|
||||
>(
|
||||
url,
|
||||
{
|
||||
method: "POST",
|
||||
headers: header,
|
||||
body: JSON.stringify(body),
|
||||
},
|
||||
true,
|
||||
);
|
||||
const data = await resp.data;
|
||||
if (data.retcode !== 0) {
|
||||
return <TGApp.BBS.Response.BaseWithData>{
|
||||
retcode: data.retcode,
|
||||
message: data.message,
|
||||
data: resp.resp.headers.get("x-rpc-aigis"),
|
||||
};
|
||||
}
|
||||
return <TGApp.Plugins.Mys.CaptchaLogin.CaptchaData>data.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 通过短信验证码登录
|
||||
* @since Beta v0.5.1
|
||||
* @param {string} phone - 手机号
|
||||
* @param {string} captcha - 验证码
|
||||
* @param {string} action_type - 操作类型
|
||||
* @param {string} [aigis] - 验证数据
|
||||
* @returns {Promise<TGApp.Plugins.Mys.CaptchaLogin.LoginData | TGApp.BBS.Response.Base>}
|
||||
*/
|
||||
export async function doCaptchaLogin(
|
||||
phone: string,
|
||||
captcha: string,
|
||||
action_type: string,
|
||||
aigis?: string,
|
||||
): Promise<TGApp.Plugins.Mys.CaptchaLogin.LoginData | TGApp.BBS.Response.Base> {
|
||||
const url = "https://passport-api.mihoyo.com/account/ma-cn-passport/app/loginByMobileCaptcha";
|
||||
const device_fp = getDeviceInfo("device_fp");
|
||||
const device_name = getDeviceInfo("device_name");
|
||||
const device_id = getDeviceInfo("device_id");
|
||||
const device_model = getDeviceInfo("product");
|
||||
const body = {
|
||||
area_code: rsaEncrypt("+86"),
|
||||
mobile: rsaEncrypt(phone),
|
||||
action_type,
|
||||
captcha,
|
||||
};
|
||||
const header = {
|
||||
"x-rpc-aigis": aigis || "",
|
||||
"x-rpc-app_version": TGConstant.BBS.VERSION,
|
||||
"x-rpc-client_type": "2",
|
||||
"x-rpc-app_id": TGConstant.BBS.APP_ID,
|
||||
"x-rpc-device_fp": device_fp,
|
||||
"x-rpc-device_name": device_name,
|
||||
"x-rpc-device_id": device_id,
|
||||
"x-rpc-device_model": device_model,
|
||||
"user-agent": TGConstant.BBS.UA_MOBILE,
|
||||
};
|
||||
const resp = await TGHttp<TGApp.Plugins.Mys.CaptchaLogin.LoginResponse | TGApp.BBS.Response.Base>(
|
||||
url,
|
||||
{
|
||||
method: "POST",
|
||||
headers: header,
|
||||
body: JSON.stringify(body),
|
||||
},
|
||||
);
|
||||
if (resp.retcode !== 0) return <TGApp.BBS.Response.Base>resp;
|
||||
return resp.data;
|
||||
}
|
||||
@@ -1,24 +1,30 @@
|
||||
/**
|
||||
* @file plugins/Mys/request/getForumList.ts
|
||||
* @description Mys 插件特定论坛请求
|
||||
* @since Beta v0.5.0
|
||||
* @since Beta v0.5.1
|
||||
*/
|
||||
|
||||
import TGHttp from "../../../utils/TGHttp.js";
|
||||
|
||||
/**
|
||||
* @description 获取特定论坛列表
|
||||
* @since Beta v0.5.0
|
||||
* @since Beta v0.5.1
|
||||
* @param {number} forumId 特定论坛 ID
|
||||
* @param {number} type 排序方式: 0-按热度排序,1-最新回复,2-按时间排序
|
||||
* @param {number} page_size 每页数量
|
||||
* @return {Promise<TGApp.Plugins.Mys.Forum.FullData>}
|
||||
*/
|
||||
async function getForumList(
|
||||
forumId: number,
|
||||
type: number = 0,
|
||||
page_size: number = 20,
|
||||
): Promise<TGApp.Plugins.Mys.Forum.FullData> {
|
||||
const url = "https://bbs-api.miyoushe.com/post/wapi/getForumPostList";
|
||||
const params = { forum_id: forumId.toString(), sort_type: type.toString() };
|
||||
const params = {
|
||||
forum_id: forumId.toString(),
|
||||
sort_type: type.toString(),
|
||||
page_size: page_size.toString(),
|
||||
};
|
||||
const resp = await TGHttp<TGApp.Plugins.Mys.Forum.Response>(url, {
|
||||
method: "GET",
|
||||
query: params,
|
||||
|
||||
@@ -1,23 +1,24 @@
|
||||
/**
|
||||
* @file plugins/Mys/request/getHomeNavigator.ts
|
||||
* @description Mys 插件首页导航请求
|
||||
* @since Beta v0.5.0
|
||||
* @since Beta v0.5.1
|
||||
*/
|
||||
|
||||
import TGHttp from "../../../utils/TGHttp.js";
|
||||
|
||||
/**
|
||||
* @description 获取首页导航列表
|
||||
* @since Beta v0.5.0
|
||||
* @since Beta v0.5.1
|
||||
* @param {number} gid GID
|
||||
* @return {Promise<TGApp.BBS.Navigator.Navigator[]>}
|
||||
*/
|
||||
async function getHomeNavigator(gid: number = 2): Promise<TGApp.BBS.Navigator.Navigator[]> {
|
||||
const url = "https://bbs-api.miyoushe.com/apihub/api/home/new";
|
||||
const params = { gids: gid.toString() };
|
||||
const header = { "x-rpc-client_type": "2" };
|
||||
const resp = await TGHttp<TGApp.BBS.Navigator.HomeResponse>(url, {
|
||||
method: "GET",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
headers: header,
|
||||
query: params,
|
||||
});
|
||||
return resp.data.navigator;
|
||||
|
||||
179
src/plugins/Mys/types/CaptchaLogin.d.ts
vendored
Normal file
179
src/plugins/Mys/types/CaptchaLogin.d.ts
vendored
Normal file
@@ -0,0 +1,179 @@
|
||||
/**
|
||||
* @file plugins/Mys/types/CaptchaLogin.d.ts
|
||||
* @description Mys 插件 Captcha 登录类型定义文件
|
||||
* @since Beta v0.5.1
|
||||
*/
|
||||
|
||||
/**
|
||||
* @description Mys 插件 Captcha 登录类型
|
||||
* @since Beta v0.5.1
|
||||
* @namespace TGApp.Plugins.Mys.CaptchaLogin
|
||||
* @memberof TGApp.Plugins.Mys
|
||||
*/
|
||||
declare namespace TGApp.Plugins.Mys.CaptchaLogin {
|
||||
/**
|
||||
* @description 获取短信验证码返回数据
|
||||
* @since Beta v0.5.1
|
||||
* @interface CaptchaResponse
|
||||
* @extends TGApp.BBS.Response.BaseWithData
|
||||
* @property {CaptchaData} data 数据
|
||||
* @return GetCaptchaResponse
|
||||
*/
|
||||
interface CaptchaResponse extends TGApp.BBS.Response.BaseWithData {
|
||||
data: CaptchaData;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取短信验证码返回数据
|
||||
* @since Beta v0.5.1
|
||||
* @interface CaptchaData
|
||||
* @property {string} sent_new 是否发送新验证码
|
||||
* @property {string} countdown 倒计时
|
||||
* @property {string} action_type 操作类型
|
||||
* @return CaptchaData
|
||||
*/
|
||||
interface CaptchaData {
|
||||
sent_new: string;
|
||||
countdown: string;
|
||||
action_type: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 触发验证的序列化数据
|
||||
* @since Beta v0.5.1
|
||||
* @interface CaptchaAigis
|
||||
* @property {string} session_id 会话 id
|
||||
* @property {number} mmt_type mmt 类型
|
||||
* @see TGApp.Plugins.Mys.Geetest.validateResp
|
||||
* @property {string} data 数据,为上面的序列化数据
|
||||
* @return CaptchaBody
|
||||
*/
|
||||
interface CaptchaAigis {
|
||||
session_id: string;
|
||||
mmt_type: number;
|
||||
data: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 短信验证码登录返回数据
|
||||
* @since Beta v0.5.1
|
||||
* @interface LoginResponse
|
||||
* @extends TGApp.BBS.Response.BaseWithData
|
||||
* @property {LoginData} data 数据
|
||||
* @return LoginResponse
|
||||
*/
|
||||
interface LoginResponse extends TGApp.BBS.Response.BaseWithData {
|
||||
data: LoginData;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 短信验证码登录返回数据
|
||||
* @since Beta v0.5.1
|
||||
* @interface LoginData
|
||||
* @property {Token} token token数据
|
||||
* @property {UserInfo} user_info 用户信息
|
||||
* @property {ReactivateInfo} reactivate_info 重新激活信息
|
||||
* @property {string} login_ticket 登录 ticket
|
||||
* @property {boolean} new_user 是否新用户
|
||||
* @property {RealnameInfo} realname_info 实名信息
|
||||
* @property {boolean} need_realperson 是否需要实名认证
|
||||
* @property {string} oauth_hw_open_id 华为 open id
|
||||
* @return LoginData
|
||||
*/
|
||||
interface LoginData {
|
||||
token: Token;
|
||||
user_info: UserInfo;
|
||||
reactivate_info: ReactivateInfo;
|
||||
login_ticket: string;
|
||||
new_user: boolean;
|
||||
realname_info: RealnameInfo;
|
||||
need_realperson: boolean;
|
||||
oauth_hw_open_id: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description token 数据
|
||||
* @since Beta v0.5.1
|
||||
* @interface Token
|
||||
* @property {number} token_type token 类型
|
||||
* @property {string} token token
|
||||
* @return Token
|
||||
*/
|
||||
interface Token {
|
||||
token_type: number;
|
||||
token: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 用户信息
|
||||
* @since Beta v0.5.1
|
||||
* @interface UserInfo
|
||||
* @property {string} aid 账号 id
|
||||
* @property {string} mid mid
|
||||
* @property {string} account_name 账号名称
|
||||
* @property {string} email 邮箱
|
||||
* @property {number} is_email_verify 是否验证邮箱
|
||||
* @property {string} area_code 手机区号
|
||||
* @property {string} mobile 手机号
|
||||
* @property {string} safe_area_code 安全手机区号
|
||||
* @property {string} safe_mobile 安全手机号
|
||||
* @property {string} realname 真实姓名
|
||||
* @property {string} identity_code 身份证号
|
||||
* @property {string} rebind_area_code 重新绑定手机区号
|
||||
* @property {string} rebind_mobile 重新绑定手机号
|
||||
* @property {string} rebind_mobile_time 重新绑定手机时间
|
||||
* @property {string[]} links 账号绑定信息
|
||||
* @property {string} country 国家
|
||||
* @property {string} unmasked_email 邮箱
|
||||
* @property {number} unmasked_email_type 邮箱类型
|
||||
* @return UserInfo
|
||||
*/
|
||||
interface UserInfo {
|
||||
aid: string;
|
||||
mid: string;
|
||||
account_name: string;
|
||||
email: string;
|
||||
is_email_verify: number;
|
||||
area_code: string;
|
||||
mobile: string;
|
||||
safe_area_code: string;
|
||||
safe_mobile: string;
|
||||
realname: string;
|
||||
identity_code: string;
|
||||
rebind_area_code: string;
|
||||
rebind_mobile: string;
|
||||
rebind_mobile_time: string;
|
||||
links: string[];
|
||||
country: string;
|
||||
unmasked_email: string;
|
||||
unmasked_email_type: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 重新激活信息
|
||||
* @since Beta v0.5.1
|
||||
* @interface ReactivateInfo
|
||||
* @property {boolean} required 是否需要重新激活
|
||||
* @property {string} ticket 重新激活 ticket
|
||||
* @return ReactivateInfo
|
||||
*/
|
||||
interface ReactivateInfo {
|
||||
required: boolean;
|
||||
ticket: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 实名信息
|
||||
* @since Beta v0.5.1
|
||||
* @interface RealnameInfo
|
||||
* @property {boolean} required 是否需要实名认证
|
||||
* @property {string} action_type 操作类型
|
||||
* @property {string} action_ticket 操作 ticket
|
||||
* @return RealnameInfo
|
||||
*/
|
||||
interface RealnameInfo {
|
||||
required: boolean;
|
||||
action_type: string;
|
||||
action_ticket: string;
|
||||
}
|
||||
}
|
||||
100
src/plugins/Mys/types/Geetest.d.ts
vendored
Normal file
100
src/plugins/Mys/types/Geetest.d.ts
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
/**
|
||||
* @file plugins/Mys/types/Geetest.d.ts
|
||||
* @description Mys 插件 Geetest 类型定义文件
|
||||
* @since Beta v0.5.2
|
||||
*/
|
||||
|
||||
/**
|
||||
* @description Mys 插件 Geetest 类型
|
||||
* @since Beta v0.5.2
|
||||
* @namespace TGApp.Plugins.Mys.Geetest
|
||||
* @memberof TGApp.Plugins.Mys
|
||||
*/
|
||||
declare namespace TGApp.Plugins.Mys.Geetest {
|
||||
/**
|
||||
* @description 极验验证的响应数据
|
||||
* @since Beta v0.5.1
|
||||
* @interface reqResp
|
||||
* @property {string} gt - 极验验证 gt
|
||||
* @property {string} challenge - 极验验证 challenge
|
||||
* @property {number} new_captcha - 极验验证 new_captcha
|
||||
* @property {number} success - 极验验证 success
|
||||
* @return reqResp
|
||||
*/
|
||||
interface reqResp {
|
||||
gt: string;
|
||||
challenge: string;
|
||||
new_captcha: number;
|
||||
success: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 极验验证的请求数据
|
||||
* @since Beta v0.5.1
|
||||
* @interface postData
|
||||
* @property {string} challenge - 极验验证 challenge
|
||||
* @property {string} validate - 极验验证 validate
|
||||
* @return postData
|
||||
*/
|
||||
interface postData {
|
||||
challenge: string;
|
||||
validate: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 极验验证的请求方法-请求参数
|
||||
* @since Beta v0.5.1
|
||||
* @interface InitGeetestParams
|
||||
* @property {string} gt - 极验验证 gt
|
||||
* @property {string} challenge - 极验验证 challenge
|
||||
* @property {boolean} offline - 极验验证 offline
|
||||
* @property {boolean} new_captcha - 极验验证 new_captcha
|
||||
* @property {string} product - 极验验证 product
|
||||
* @property {string} width - 极验验证 width
|
||||
* @property {string} area - 极验验证 area
|
||||
* @return InitGeetestParams
|
||||
*/
|
||||
interface InitGeetestParams {
|
||||
gt: string;
|
||||
challenge: string;
|
||||
offline: boolean;
|
||||
new_captcha: boolean;
|
||||
product: string;
|
||||
width: string;
|
||||
area: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Geetest 插件 captchaObj
|
||||
* @since Beta v0.5.2
|
||||
* @interface GeetestCaptcha
|
||||
* @property {Function} appendTo
|
||||
* @property {Function} getValidate
|
||||
* @property {Function} onSuccess
|
||||
* @property {Function} onClose
|
||||
* @property {Function} onReady
|
||||
* @return GeetestCaptcha
|
||||
*/
|
||||
interface GeetestCaptcha {
|
||||
appendTo: (selector: string) => void;
|
||||
getValidate: () => validateResp;
|
||||
onSuccess: (callback: () => void) => void;
|
||||
onClose: (callback: () => void) => void;
|
||||
onReady: (callback: () => void) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Geetest 插件 validate
|
||||
* @since Beta v0.5.1
|
||||
* @interface validateResp
|
||||
* @property {string} geetest_challenge
|
||||
* @property {string} geetest_validate
|
||||
* @property {string} geetest_seccode
|
||||
* @return validateResp
|
||||
*/
|
||||
interface validateResp {
|
||||
geetest_challenge: string;
|
||||
geetest_validate: string;
|
||||
geetest_seccode: string;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* @file plugins/Sqlite/modules/userCollect.ts
|
||||
* @description 用户收藏模块
|
||||
* @since Beta v0.4.5
|
||||
* @since Beta v0.5.1
|
||||
*/
|
||||
|
||||
import TGSqlite from "../index.js";
|
||||
@@ -93,7 +93,7 @@ async function createCollect(title: string, desc: string): Promise<boolean> {
|
||||
|
||||
/**
|
||||
* @description 删除收藏合集
|
||||
* @since Beta v0.4.5
|
||||
* @since Beta v0.5.1
|
||||
* @param {string} title 收藏合集标题
|
||||
* @param {boolean} force 是否强制删除
|
||||
* @return {Promise<boolean>} 返回是否删除成功
|
||||
@@ -108,6 +108,9 @@ async function deleteCollect(title: string, force: boolean): Promise<boolean> {
|
||||
if (force) {
|
||||
const deleteSql = "DELETE FROM UFCollection WHERE title = ?";
|
||||
await db.execute(deleteSql, [title]);
|
||||
const deletePostSql =
|
||||
"DELETE FROM UFPost WHERE id IN (SELECT postId FROM UFMap WHERE collectionId = ?)";
|
||||
await db.execute(deletePostSql, [res[0].id]);
|
||||
}
|
||||
const deleteRefSql = "DELETE FROM UFMap WHERE collectionId = ?";
|
||||
await db.execute(deleteRefSql, [res[0].id]);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* @file plugins/Sqlite/modules/userGacha.ts
|
||||
* @description 用户祈愿模块
|
||||
* @since Beta v0.5.0
|
||||
* @since Beta v0.5.1
|
||||
*/
|
||||
|
||||
import { AppCharacterData, AppWeaponData } from "../../../data/index.js";
|
||||
@@ -29,12 +29,12 @@ function getGachaItemType(item_id: string): gachaItemTypeRes {
|
||||
|
||||
/**
|
||||
* @description 转换祈愿数据,防止多语言
|
||||
* @since Beta v0.4.7
|
||||
* @since Beta v0.5.1
|
||||
* @param {TGApp.Plugins.UIGF.GachaItem} gacha - UIGF数据
|
||||
* @return {TGApp.Plugins.UIGF.GachaItem} 转换后的数据
|
||||
*/
|
||||
function transGacha(gacha: TGApp.Plugins.UIGF.GachaItem): TGApp.Plugins.UIGF.GachaItem {
|
||||
const type = getGachaItemType(gacha.item_id!);
|
||||
const type = getGachaItemType(gacha.item_id);
|
||||
let res = gacha;
|
||||
res.item_type = type[0];
|
||||
if (type[0] === "角色") {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* @file router/modules/main.ts
|
||||
* @description 主路由模块
|
||||
* @since Beta v0.4.5
|
||||
* @since Beta v0.5.1
|
||||
*/
|
||||
|
||||
const mainRoutes = [
|
||||
@@ -21,7 +21,7 @@ const mainRoutes = [
|
||||
component: async () => await import("../../pages/common/News.vue"),
|
||||
},
|
||||
{
|
||||
path: "/posts",
|
||||
path: "/posts/:gid?/:forum?",
|
||||
name: "酒馆",
|
||||
component: async () => await import("../../pages/common/Posts.vue"),
|
||||
},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* @file store/modules/app.ts
|
||||
* @description App store module
|
||||
* @since Beta v0.5.0
|
||||
* @since Beta v0.5.2
|
||||
*/
|
||||
|
||||
import { path } from "@tauri-apps/api";
|
||||
@@ -16,7 +16,7 @@ const userDataDir = `${await path.appLocalDataDir()}${path.sep()}userData`;
|
||||
// 用于存放数据库的路径
|
||||
const dbDataPath = `${await path.appConfigDir()}${path.sep()}TeyvatGuide.db`;
|
||||
// 用于存放日志的路径
|
||||
const logDataDir = `${await path.appConfigDir()}${path.sep()}logs`;
|
||||
const logDataDir = await path.appLogDir();
|
||||
|
||||
export const useAppStore = defineStore(
|
||||
"app",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* @file store/modules/user.ts
|
||||
* @description 用户信息模块
|
||||
* @since Beta v0.3.9
|
||||
* @since Beta v0.5.1
|
||||
*/
|
||||
|
||||
import { defineStore } from "pinia";
|
||||
@@ -28,10 +28,38 @@ export const useUserStore = defineStore(
|
||||
});
|
||||
const cookie = ref<TGApp.User.Account.Cookie>();
|
||||
|
||||
function getAllCookie(): string {
|
||||
let res = "";
|
||||
if (!cookie.value) return res;
|
||||
if (cookie.value.ltuid && cookie.value.ltuid !== "") {
|
||||
res += `ltuid=${cookie.value.ltuid};`;
|
||||
}
|
||||
if (cookie.value.ltoken && cookie.value.ltoken !== "") {
|
||||
res += `ltoken=${cookie.value.ltoken};`;
|
||||
}
|
||||
if (cookie.value.mid && cookie.value.mid !== "") {
|
||||
res += `mid=${cookie.value.mid};`;
|
||||
}
|
||||
if (cookie.value.cookie_token && cookie.value.cookie_token !== "") {
|
||||
res += `cookie_token=${cookie.value.cookie_token};`;
|
||||
}
|
||||
if (cookie.value.stoken && cookie.value.stoken !== "") {
|
||||
res += `stoken=${cookie.value.stoken};`;
|
||||
}
|
||||
if (cookie.value.stuid && cookie.value.stuid !== "") {
|
||||
res += `stuid=${cookie.value.stuid};`;
|
||||
}
|
||||
if (cookie.value.account_id && cookie.value.account_id !== "") {
|
||||
res += `account_id=${cookie.value.account_id};`;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
return {
|
||||
cookie,
|
||||
briefInfo,
|
||||
account,
|
||||
getAllCookie,
|
||||
};
|
||||
},
|
||||
{
|
||||
|
||||
11
src/types/Plugins/UIGF.d.ts
vendored
11
src/types/Plugins/UIGF.d.ts
vendored
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* @file types/Plugins/UIGF.d.ts
|
||||
* @description UIGF 插件类型定义文件
|
||||
* @since Beta v0.5.0
|
||||
* @since Beta v0.5.1
|
||||
* @version UIGF v3.0 | UIGF v4.0
|
||||
*/
|
||||
|
||||
@@ -60,13 +60,14 @@ declare namespace TGApp.Plugins.UIGF {
|
||||
|
||||
/**
|
||||
* @description UIGF 头部信息, v4.0
|
||||
* @since Beta v0.5.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 {
|
||||
@@ -74,6 +75,7 @@ declare namespace TGApp.Plugins.UIGF {
|
||||
export_app: string;
|
||||
export_app_version: string;
|
||||
version: string;
|
||||
lang?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -114,7 +116,8 @@ declare namespace TGApp.Plugins.UIGF {
|
||||
|
||||
/**
|
||||
* @description UIGF 祈愿列表
|
||||
* @since Alpha v0.2.3
|
||||
* @Beta v0.5.1
|
||||
* @version UIGF v3.0
|
||||
* @interface GachaItem
|
||||
* @property {EnumGachaType} gacha_type - 祈愿类型
|
||||
* @property {string} item_id - 物品ID
|
||||
@@ -130,7 +133,7 @@ declare namespace TGApp.Plugins.UIGF {
|
||||
interface GachaItem {
|
||||
uigf_gacha_type: string;
|
||||
gacha_type: string;
|
||||
item_id?: string;
|
||||
item_id: string;
|
||||
count?: string;
|
||||
time: string;
|
||||
name: string;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* @file utils/TGClient.ts
|
||||
* @desc 负责米游社客户端的 callback 处理
|
||||
* @since Beta v0.5.0
|
||||
* @since Beta v0.5.1
|
||||
*/
|
||||
|
||||
import { event, core, webviewWindow } from "@tauri-apps/api";
|
||||
@@ -99,7 +99,7 @@ class TGClient {
|
||||
|
||||
/**
|
||||
* @func getSaveImgJS
|
||||
* @since Beta v0.3.7
|
||||
* @since Beta v0.5.1
|
||||
* @desc 获取保存图片的 JS
|
||||
* @param {string} url - 图片链接
|
||||
* @param {string} format - 图片格式
|
||||
@@ -107,23 +107,19 @@ class TGClient {
|
||||
*/
|
||||
getSaveImgJS(url: string, format: string): string {
|
||||
return `javascript:(async function() {
|
||||
const _t = window.__TAURI__;
|
||||
const defaultPath = await _t.path.downloadDir() + Date.now() + '.${format}';
|
||||
const savePath = await _t.dialog.save({
|
||||
var _path = window.__TAURI__.path;
|
||||
var saveDefault = await _path.downloadDir() + _path.sep() + Date.now() + '.${format}';
|
||||
var savePath = await window.__TAURI_PLUGIN_DIALOG__.save({
|
||||
title: '保存图片',
|
||||
filters: [{ name: '图片', extensions: ['png'] }],
|
||||
defaultPath: defaultPath,
|
||||
filters: [{ name: '图片', extensions: ['${format}'] }],
|
||||
defaultPath: saveDefault,
|
||||
});
|
||||
if (savePath) {
|
||||
const resBlob = await _t.http.fetch('${url}',{
|
||||
method: 'GET',
|
||||
responseType: _t.http.ResponseType.Binary
|
||||
});
|
||||
const buffer = new Uint8Array(resBlob.data);
|
||||
await _t.fs.writeBinaryFile({
|
||||
contents: buffer,
|
||||
path: savePath,
|
||||
});
|
||||
if(savePath !== null) {
|
||||
var resp = await window.__TAURI_PLUGIN_HTTP__.fetch('${url}',{
|
||||
method: 'GET'
|
||||
}).then(res => res.arrayBuffer());
|
||||
var buffer = new Uint8Array(resp);
|
||||
await window.__TAURI_PLUGIN_FS__.writeFile(savePath, buffer);
|
||||
alert('保存成功');
|
||||
}
|
||||
})();`;
|
||||
@@ -814,14 +810,13 @@ class TGClient {
|
||||
|
||||
/**
|
||||
* @func share
|
||||
* @since Beta v0.5.0
|
||||
* @since Beta v0.5.1
|
||||
* @desc 分享
|
||||
* @param {TGApp.Plugins.JSBridge.Arg<TGApp.Plugins.JSBridge.SharePayload>} arg - 方法参数
|
||||
* @returns {Promise<void>} - 无返回值
|
||||
*/
|
||||
async share(arg: TGApp.Plugins.JSBridge.Arg<TGApp.Plugins.JSBridge.SharePayload>): Promise<void> {
|
||||
// 如果有数据
|
||||
if (arg.payload.type === "image" && arg.payload.content?.image_url !== undefined) {
|
||||
if (arg.payload.type === "default") {
|
||||
const image = arg.payload.content.image_url;
|
||||
const format = image.split(".").pop();
|
||||
const executeJS = this.getSaveImgJS(image, format ?? "png");
|
||||
@@ -859,7 +854,7 @@ class TGClient {
|
||||
var scale = 1.5;
|
||||
var option = {
|
||||
debug: true,
|
||||
backgroundColor: "white",
|
||||
backgroundColor: "transparent",
|
||||
height: shareDom.scrollHeight * scale,
|
||||
width: shareDom.scrollWidth * scale,
|
||||
style: {
|
||||
@@ -903,6 +898,14 @@ class TGClient {
|
||||
return;
|
||||
}
|
||||
if (arg.payload.type === "image") {
|
||||
if (arg.payload.content?.image_url !== undefined) {
|
||||
const image = arg.payload.content.image_url;
|
||||
const format = image.split(".").pop();
|
||||
const executeJS = this.getSaveImgJS(image, format ?? "png");
|
||||
await core.invoke("execute_js", { label: "mhy_client", js: executeJS });
|
||||
await this.callback(arg.callback, {});
|
||||
return;
|
||||
}
|
||||
if (arg.payload.content?.image_base64 !== undefined) {
|
||||
let image = arg.payload.content.image_base64;
|
||||
image = `data:image/png;base64,${image}`;
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
/**
|
||||
* @file utils/TGHttp.ts
|
||||
* @description 封装HTTP请求
|
||||
* @since Beta v0.5.0
|
||||
* @since Beta v0.5.1
|
||||
*/
|
||||
|
||||
import { fetch } from "@tauri-apps/plugin-http";
|
||||
|
||||
/**
|
||||
* @description 请求参数
|
||||
* @since Beta v0.5.0
|
||||
* @since Beta v0.5.1
|
||||
* @property {"GET"|"POST"} method 请求方法
|
||||
* @property {Record<string,string>} headers 请求头
|
||||
* @property {Record<string,string>} query 请求参数
|
||||
@@ -26,13 +26,23 @@ type TGHttpParams = {
|
||||
|
||||
/**
|
||||
* @description 发送请求
|
||||
* @since Beta v0.5.0
|
||||
* @since Beta v0.5.1
|
||||
* @template T
|
||||
* @param {string} url 请求地址
|
||||
* @param {TGHttpParams} options 请求参数
|
||||
* @returns {Promise<T>}
|
||||
* @returns {Promise<T>} 请求结果
|
||||
*/
|
||||
async function TGHttp<T>(url: string, options: TGHttpParams): Promise<T> {
|
||||
async function TGHttp<T>(url: string, options: TGHttpParams): Promise<T>;
|
||||
async function TGHttp<T>(
|
||||
url: string,
|
||||
options: TGHttpParams,
|
||||
fullResponse: true,
|
||||
): Promise<{ data: Promise<T>; resp: Response }>;
|
||||
async function TGHttp<T>(
|
||||
url: string,
|
||||
options: TGHttpParams,
|
||||
fullResponse?: true,
|
||||
): Promise<T | { data: Promise<T>; resp: Response }> {
|
||||
const httpHeaders = new Headers();
|
||||
if (options.headers) {
|
||||
for (const key in options.headers) {
|
||||
@@ -55,8 +65,11 @@ async function TGHttp<T>(url: string, options: TGHttpParams): Promise<T> {
|
||||
return await fetch(url, fetchOptions)
|
||||
.then((res) => {
|
||||
if (res.ok) {
|
||||
if (options.isBlob) return res.arrayBuffer();
|
||||
return res.json();
|
||||
const data = options.isBlob ? res.arrayBuffer() : res.json();
|
||||
if (fullResponse) {
|
||||
return { data, resp: res };
|
||||
}
|
||||
return data;
|
||||
}
|
||||
throw new Error(`HTTP error! status: ${res.status}`);
|
||||
})
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* @file utils/UIGF.ts
|
||||
* @description UIGF工具类
|
||||
* @since Beta v0.5.0
|
||||
* @since Beta v0.5.1
|
||||
*/
|
||||
|
||||
import { app, path } from "@tauri-apps/api";
|
||||
@@ -50,7 +50,7 @@ async function getUigfHeader(uid: string): Promise<TGApp.Plugins.UIGF.Info> {
|
||||
|
||||
/**
|
||||
* @description 获取 UIGF v4.0 头部信息
|
||||
* @since Beta v0.5.0
|
||||
* @since Beta v0.5.1
|
||||
* @returns {TGApp.Plugins.UIGF.Info4} UIGF v4.0 头部信息
|
||||
*/
|
||||
async function getUigf4Header(): Promise<TGApp.Plugins.UIGF.Info4> {
|
||||
@@ -60,6 +60,7 @@ async function getUigf4Header(): Promise<TGApp.Plugins.UIGF.Info4> {
|
||||
export_app: "TeyvatGuide",
|
||||
export_app_version: await app.getVersion(),
|
||||
version: "v4.0",
|
||||
lang: "zh-cn",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -110,7 +111,7 @@ export async function verifyUigfData(path: string, isVersion4: boolean = false):
|
||||
|
||||
/**
|
||||
* @description 验证 UIGF 数据
|
||||
* @since Beta v0.5.0
|
||||
* @since Beta v0.5.1
|
||||
* @param {object} data - UIGF 数据
|
||||
* @returns {boolean} 是否验证通过
|
||||
*/
|
||||
@@ -126,6 +127,14 @@ function validateUigfData(data: object): boolean {
|
||||
});
|
||||
return false;
|
||||
}
|
||||
const parsedData: TGApp.Plugins.UIGF.Schema = <TGApp.Plugins.UIGF.Schema>data;
|
||||
if (parsedData.info.uigf_version < "v2.3") {
|
||||
showSnackbar({
|
||||
text: "UIGF 版本过低,请使用 v2.3 或以上版本",
|
||||
color: "error",
|
||||
});
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* @file src/utils/linkParser.ts
|
||||
* @description 处理链接
|
||||
* @since Beta v0.5.0
|
||||
* @since Beta v0.5.1
|
||||
*/
|
||||
|
||||
import { emit } from "@tauri-apps/api/event";
|
||||
@@ -52,7 +52,7 @@ export async function parsePost(link: string): Promise<false | string> {
|
||||
|
||||
/**
|
||||
* @function parseLink
|
||||
* @since Beta v0.5.0
|
||||
* @since Beta v0.5.1
|
||||
* @description 处理链接
|
||||
* @param {string} link - 链接
|
||||
* @param {boolean} useInner - 是否采用内置 JSBridge 打开
|
||||
@@ -83,16 +83,24 @@ export async function parseLink(
|
||||
console.log(url.pathname, url.search);
|
||||
// 处理特定路径
|
||||
if (url.pathname.startsWith("//discussion")) {
|
||||
await emit("active_deep_link", "router?path=/posts");
|
||||
const gid = url.pathname.split("/").pop();
|
||||
const forum = url.searchParams.get("forum_id");
|
||||
await emit("active_deep_link", `router?path=/posts/${gid}/${forum}`);
|
||||
return true;
|
||||
}
|
||||
if (link === "mihoyobbs://homeForum?game_id=2&tab_type=2") {
|
||||
await emit("active_deep_link", "router?path=/news/2/news");
|
||||
return true;
|
||||
}
|
||||
if (link === "mihoyobbs://homeForum?game_id=8&tab_type=2") {
|
||||
await emit("active_deep_link", "router?path=/news/8/news");
|
||||
return true;
|
||||
if (url.pathname.startsWith("//homeForum")) {
|
||||
const game_id = url.searchParams.get("game_id");
|
||||
const tab_type = url.searchParams.get("tab_type");
|
||||
if (game_id && tab_type) {
|
||||
const tabList = ["", "notice", "activity", "news"];
|
||||
if (["1", "2", "3"].includes(tab_type)) {
|
||||
await emit(
|
||||
"active_deep_link",
|
||||
`router?path=/news/${game_id}/${tabList[parseInt(tab_type)]}`,
|
||||
);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -81,7 +81,7 @@ export function getInitDeviceInfo(): TGApp.App.Device.DeviceInfo {
|
||||
* @param {string} key - 设备信息 key
|
||||
* @returns {string} 设备信息
|
||||
*/
|
||||
export function getDeviceInfo(key: "device_id" | "device_fp"): string {
|
||||
export function getDeviceInfo(key: keyof TGApp.App.Device.DeviceInfo): string {
|
||||
const localDevice = localStorage.getItem("deviceInfo");
|
||||
let deviceInfo: TGApp.App.Device.DeviceInfo;
|
||||
if (localDevice === null) {
|
||||
|
||||
@@ -75,7 +75,9 @@ onMounted(async () => {
|
||||
annoHtml.value = await TGUtils.Anno.parseContent(annoData.value.content);
|
||||
if (annoData.value.banner !== "") annoBanner.value = await saveImgLocal(annoData.value.banner);
|
||||
annoTitle.value = `Anno_${annoId}`;
|
||||
await webviewWindow.getCurrent().setTitle(`Anno_${annoId} ${annoData.value.title}`);
|
||||
await webviewWindow
|
||||
.getCurrentWebviewWindow()
|
||||
.setTitle(`Anno_${annoId} ${annoData.value.title}`);
|
||||
annoRef.value = <HTMLElement>document.querySelector(".anno-body");
|
||||
} catch (error) {
|
||||
if (error instanceof Error)
|
||||
@@ -83,7 +85,7 @@ onMounted(async () => {
|
||||
else console.error(error);
|
||||
loadingEmpty.value = true;
|
||||
loadingTitle.value = "公告不存在或解析失败";
|
||||
await webviewWindow.getCurrent().setTitle(`Anno_${annoId} Parsing Error`);
|
||||
await webviewWindow.getCurrentWebviewWindow().setTitle(`Anno_${annoId} Parsing Error`);
|
||||
return;
|
||||
}
|
||||
// 打开 json
|
||||
|
||||
@@ -15,31 +15,32 @@
|
||||
</div>
|
||||
<div class="tp-post-meta">
|
||||
<div class="mpm-forum" v-if="postData.forum">
|
||||
<img :src="getGameIcon(postData.forum.game_id)" alt="gameIcon" />
|
||||
<img :src="postData.forum.icon" alt="forumIcon" />
|
||||
<span>{{ postData.forum.name }}</span>
|
||||
</div>
|
||||
<div class="mpm-item" :title="`浏览数:${postData.stat.view_num}`">
|
||||
<div class="mpm-item" :title="`浏览数:${postData?.stat?.view_num}`">
|
||||
<v-icon>mdi-eye</v-icon>
|
||||
<span>{{ postData.stat.view_num }}</span>
|
||||
<span>{{ postData?.stat?.view_num }}</span>
|
||||
</div>
|
||||
<div class="mpm-item" :title="`收藏数:${postData.stat.bookmark_num}`">
|
||||
<div class="mpm-item" :title="`收藏数:${postData?.stat?.bookmark_num}`">
|
||||
<v-icon>mdi-star</v-icon>
|
||||
<span>{{ postData.stat.bookmark_num }}</span>
|
||||
<span>{{ postData?.stat?.bookmark_num }}</span>
|
||||
</div>
|
||||
<div class="mpm-item" :title="`回复数:${postData.stat.reply_num}`">
|
||||
<div class="mpm-item" :title="`回复数:${postData?.stat?.reply_num}`">
|
||||
<v-icon>mdi-comment</v-icon>
|
||||
<span>{{ postData.stat.reply_num }}</span>
|
||||
<span>{{ postData?.stat?.reply_num }}</span>
|
||||
</div>
|
||||
<div class="mpm-item" :title="`点赞数:${postData.stat.like_num}`">
|
||||
<div class="mpm-item" :title="`点赞数:${postData?.stat?.like_num}`">
|
||||
<v-icon>mdi-thumb-up</v-icon>
|
||||
<span>{{ postData.stat.like_num }}</span>
|
||||
<span>{{ postData?.stat?.like_num }}</span>
|
||||
</div>
|
||||
<div class="mpm-item" :title="`转发数:${postData.stat.forward_num}`">
|
||||
<div class="mpm-item" :title="`转发数:${postData?.stat?.forward_num}`">
|
||||
<v-icon>mdi-share-variant</v-icon>
|
||||
<span>{{ postData.stat.forward_num }}</span>
|
||||
<span>{{ postData?.stat?.forward_num }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<TpAvatar :data="postData.user" position="right" />
|
||||
<TpAvatar :data="postData.user" position="right" v-if="postData.user" />
|
||||
</div>
|
||||
<div class="tp-post-title" @click="toPost()" title="点击查看评论">
|
||||
<span class="mpt-official" v-if="postData.post.post_status.is_official">官</span>
|
||||
@@ -98,6 +99,7 @@ import { useAppStore } from "../store/modules/app.js";
|
||||
import TGClient from "../utils/TGClient.js";
|
||||
import TGLogger from "../utils/TGLogger.js";
|
||||
import { createTGWindow } from "../utils/TGWindow.js";
|
||||
import TGConstant from "../web/constant/TGConstant.js";
|
||||
|
||||
// loading
|
||||
const loading = ref<boolean>(true);
|
||||
@@ -122,13 +124,19 @@ const shareTimeTimer = ref<any>();
|
||||
// 合集
|
||||
const showCollection = ref<boolean>(false);
|
||||
|
||||
function getGameIcon(gameId: number): string {
|
||||
const find = TGConstant.BBS.CHANNELS.find((item) => item.gid === gameId.toString());
|
||||
if (find) return find.icon;
|
||||
return "/platforms/mhy/mys.webp";
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
appVersion.value = await app.getVersion();
|
||||
// 检查数据
|
||||
if (!postId) {
|
||||
loadingEmpty.value = true;
|
||||
loadingTitle.value = "未找到数据";
|
||||
await webviewWindow.getCurrent().setTitle("未找到数据");
|
||||
await webviewWindow.getCurrentWebviewWindow().setTitle("未找到数据");
|
||||
await TGLogger.Error("[t-post][onMounted] PostID 不存在");
|
||||
return;
|
||||
}
|
||||
@@ -139,7 +147,9 @@ onMounted(async () => {
|
||||
loadingTitle.value = "正在渲染数据...";
|
||||
renderPost.value = getRenderPost(postData.value);
|
||||
shareTitle.value = `Post_${postId}`;
|
||||
await webviewWindow.getCurrent().setTitle(`Post_${postId} ${postData.value.post.subject}`);
|
||||
await webviewWindow
|
||||
.getCurrentWebviewWindow()
|
||||
.setTitle(`Post_${postId} ${postData.value.post.subject}`);
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
await TGLogger.Error(`[t-post][${postId}] ${error.name}: ${error.message}`);
|
||||
@@ -151,7 +161,7 @@ onMounted(async () => {
|
||||
loadingSub.value = "请检查帖子是否存在或者是否为合法的帖子";
|
||||
}
|
||||
loadingEmpty.value = true;
|
||||
await webviewWindow.getCurrent().setTitle(`Post_${postId} Parsing Error`);
|
||||
await webviewWindow.getCurrentWebviewWindow().setTitle(`Post_${postId} Parsing Error`);
|
||||
return;
|
||||
}
|
||||
await TGLogger.Info(`[t-post][${postId}][onMounted] ${postData.value.post.subject}`);
|
||||
@@ -357,6 +367,11 @@ onUnmounted(() => {
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.mpm-forum img:first-child {
|
||||
border-radius: 5px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.mpm-forum span {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
19
src/vite-env.d.ts
vendored
19
src/vite-env.d.ts
vendored
@@ -1,10 +1,12 @@
|
||||
/**
|
||||
* @file vite-env.d.ts
|
||||
* @description vite-env.d.ts
|
||||
* @author BTMuli<bt-muli@outlook.com>
|
||||
* @since Beta v0.3.3
|
||||
* @description 全局类型定义文件
|
||||
* @since Beta v0.5.1
|
||||
*/
|
||||
|
||||
/**
|
||||
* @description vue 文件类型声明
|
||||
*/
|
||||
declare module "*.vue" {
|
||||
import type { DefineComponent } from "vue";
|
||||
const component: DefineComponent<object, object, any>;
|
||||
@@ -48,3 +50,14 @@ interface ImportMetaEnv {
|
||||
declare interface ImportMeta {
|
||||
readonly env: ImportMetaEnv;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 极验验证的请求方法-请求参数
|
||||
* @param {TGApp.BBS.Geetest.InitGeetestParams} params
|
||||
* @param {(captchaObj: TGApp.BBS.Geetest.GeetestCaptcha) => void} callback
|
||||
* @return void
|
||||
*/
|
||||
declare function initGeetest(
|
||||
params: TGApp.Plugins.Mys.Geetest.InitGeetestParams,
|
||||
callback: (captchaObj: TGApp.Plugins.Mys.Geetest.GeetestCaptcha) => void,
|
||||
): void;
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
/**
|
||||
* @file web/constant/TGConstant.ts
|
||||
* @description 常量
|
||||
* @since Beta v0.3.6
|
||||
* @since Beta v0.5.1
|
||||
*/
|
||||
|
||||
import { BBS_APP_ID, BBS_SALT, BBS_UA_MOBILE, BBS_UA_PC, BBS_VERSION } from "./bbs.js";
|
||||
import {
|
||||
BBS_APP_ID,
|
||||
BBS_SALT,
|
||||
BBS_UA_MOBILE,
|
||||
BBS_UA_PC,
|
||||
BBS_VERSION,
|
||||
CHANNEL_LIST,
|
||||
} from "./bbs.js";
|
||||
import SERVER from "./server.js";
|
||||
import { GAME_BIZ } from "./utils.js";
|
||||
|
||||
@@ -14,6 +21,7 @@ const TGConstant = {
|
||||
UA_PC: BBS_UA_PC,
|
||||
UA_MOBILE: BBS_UA_MOBILE,
|
||||
APP_ID: BBS_APP_ID,
|
||||
CHANNELS: CHANNEL_LIST,
|
||||
},
|
||||
Salt: BBS_SALT,
|
||||
Server: SERVER,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* @file web/constant/bbs.ts
|
||||
* @description 常量-应用数据
|
||||
* @since Beta v0.5.0
|
||||
* @since Beta v0.5.1
|
||||
*/
|
||||
|
||||
export const BBS_VERSION = "2.72.2";
|
||||
@@ -21,3 +21,63 @@ export const BBS_SALT = {
|
||||
X6: "t0qEgfub6cvueAPgR5m9aQWWVciEer7v",
|
||||
PROD: "t0qEgfub6cvueAPgR5m9aQWWVciEer7v",
|
||||
};
|
||||
|
||||
/**
|
||||
* @description 频道列表
|
||||
* @version 2.72.2
|
||||
* @since Beta v0.5.1
|
||||
* @interface ToChannelItem
|
||||
* @property {string} title - 频道名称
|
||||
* @property {string} icon - 频道图标
|
||||
* @property {string} gid - 频道 gid
|
||||
* @return ToChannelItem
|
||||
*/
|
||||
export interface ToChannelItem {
|
||||
title: string;
|
||||
icon: string;
|
||||
gid: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 渠道列表
|
||||
* @version 2.72.2
|
||||
* @since Beta v0.5.1
|
||||
* @type {Array<ToChannelItem>}
|
||||
*/
|
||||
export const CHANNEL_LIST: ToChannelItem[] = [
|
||||
{
|
||||
title: "原神",
|
||||
icon: "/platforms/mhy/ys.webp",
|
||||
gid: "2",
|
||||
},
|
||||
{
|
||||
title: "崩坏:星穹铁道",
|
||||
icon: "/platforms/mhy/sr.webp",
|
||||
gid: "6",
|
||||
},
|
||||
{
|
||||
title: "绝区零",
|
||||
icon: "/platforms/mhy/zzz.webp",
|
||||
gid: "8",
|
||||
},
|
||||
{
|
||||
title: "崩坏3",
|
||||
icon: "/platforms/mhy/bh3.webp",
|
||||
gid: "1",
|
||||
},
|
||||
{
|
||||
title: "崩坏2",
|
||||
icon: "/platforms/mhy/bh2.webp",
|
||||
gid: "3",
|
||||
},
|
||||
{
|
||||
title: "未定事件簿",
|
||||
icon: "/platforms/mhy/wd.webp",
|
||||
gid: "4",
|
||||
},
|
||||
{
|
||||
title: "大别野",
|
||||
icon: "/platforms/mhy/dby.webp",
|
||||
gid: "5",
|
||||
},
|
||||
];
|
||||
|
||||
@@ -12,7 +12,7 @@ import TGConstant from "../constant/TGConstant.js";
|
||||
* @description 获取设备指纹
|
||||
* @since Beta v0.5.0
|
||||
* @param {TGApp.App.Device.DeviceInfo} Info - 设备信息
|
||||
* @returns {Promise<string>} 设备指纹
|
||||
* @returns {Promise<TGApp.App.Device.DeviceInfo>} 设备指纹
|
||||
*/
|
||||
export async function getDeviceFp(
|
||||
Info?: TGApp.App.Device.DeviceInfo,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* @file web/utils/parseAnno.ts
|
||||
* @description 解析游戏内公告数据
|
||||
* @since Beta v0.5.0
|
||||
* @since Beta v0.5.2
|
||||
*/
|
||||
|
||||
import { saveImgLocal } from "../../utils/TGShare.js";
|
||||
@@ -30,7 +30,7 @@ function parseAnnoA(a: HTMLAnchorElement): HTMLAnchorElement {
|
||||
|
||||
/**
|
||||
* @description 解析 p
|
||||
* @since Beta v0.4.7
|
||||
* @since Beta v0.5.2
|
||||
* @param {HTMLParagraphElement} p p 元素
|
||||
* @returns {HTMLParagraphElement} 解析后的 p 元素
|
||||
*/
|
||||
@@ -40,6 +40,7 @@ function parseAnnoP(p: HTMLParagraphElement): HTMLParagraphElement {
|
||||
} else {
|
||||
p.querySelectorAll("*").forEach((child) => {
|
||||
child.innerHTML = decodeRegExp(child.innerHTML);
|
||||
child.querySelectorAll("span").forEach((span) => parseAnnoSpan(span));
|
||||
});
|
||||
}
|
||||
return p;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* @since Beta v0.4.3
|
||||
*/
|
||||
|
||||
import TGConstant from "../constant/TGConstant";
|
||||
import TGConstant from "../constant/TGConstant.js";
|
||||
|
||||
/**
|
||||
* @description 转义正则表达式
|
||||
|
||||
@@ -11,7 +11,13 @@
|
||||
"isolatedModules": true,
|
||||
"esModuleInterop": true,
|
||||
"lib": ["DOM", "ESNext"],
|
||||
"types": ["vite/client"],
|
||||
"types": [
|
||||
"vite/client",
|
||||
"src/types",
|
||||
"src/plugins/Bili/types",
|
||||
"src/plugins/Hutao/types",
|
||||
"src/plugins/Mys/types"
|
||||
],
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"composite": true
|
||||
},
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
/**
|
||||
* @file vite.config.ts
|
||||
* @description vite 配置文件
|
||||
* @since Beta v0.5.0
|
||||
* @since Beta v0.5.1
|
||||
*/
|
||||
|
||||
import vue from "@vitejs/plugin-vue";
|
||||
import { defineConfig } from "vite";
|
||||
import { nodePolyfills } from "vite-plugin-node-polyfills";
|
||||
import VueDevtools from "vite-plugin-vue-devtools";
|
||||
import vuetify from "vite-plugin-vuetify";
|
||||
|
||||
@@ -13,7 +14,7 @@ import buildTimePlugin from "./src/utils/TGBuild.js";
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [vue(), vuetify(), buildTimePlugin(), VueDevtools()],
|
||||
plugins: [vue(), vuetify(), buildTimePlugin(), VueDevtools(), nodePolyfills()],
|
||||
|
||||
// Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build`
|
||||
// prevent vite from obscuring rust errors
|
||||
|
||||
Reference in New Issue
Block a user