Compare commits

...

100 Commits

Author SHA1 Message Date
目棃
1f814bc8b5 🚀 v0.5.2 2024-08-11 15:46:05 +08:00
目棃
885fa3b19e JSBridge分辨率适配 2024-08-11 15:40:17 +08:00
目棃
82ba88a2c9 🍱 更新4.8下半数据 2024-08-09 09:30:23 +08:00
目棃
1a4d175e3d 👽️ 调整更新日志链接 2024-08-09 09:23:16 +08:00
目棃
6a39aa127b 🐛 修复窗口全屏失效 2024-08-03 21:37:55 +08:00
目棃
5c0d5f50fe ⬆️ migrate tauri-rc 2024-08-02 23:09:42 +08:00
目棃
27501acfd2 🔥 移除扫码登录模块 2024-08-02 13:16:35 +08:00
目棃
69157ea008 🐛 修复公告时间获取异常&内容渲染异常 2024-08-02 13:14:25 +08:00
目棃
db00765f7b ♻️ 重构窗体/缩放调整 2024-08-01 22:09:54 +08:00
目棃
49854367b1 🐛 修复日志目录异常 2024-07-31 19:13:16 +08:00
目棃
cab497f573 📝 更新文档 2024-07-31 18:21:56 +08:00
目棃
2c5c205566 🚀 v0.5.1 2024-07-30 10:50:44 +08:00
目棃
447ee3b329 💄 根据屏幕缩放调整应用缩放 2024-07-30 10:38:58 +08:00
目棃
e049508ab6 🐛 调整抽数计算规则 2024-07-29 14:59:15 +08:00
目棃
9bfb7c4108 🐛 完善活动正则 2024-07-28 23:24:37 +08:00
目棃
ea3a88ecb4 🐛 修复未确认便弹出选择窗口 2024-07-28 17:01:47 +08:00
目棃
b502cf4025 ♻️ 帖子显示版块信息 2024-07-27 21:08:15 +08:00
目棃
942e63654e 验证码登录成功
close #118
2024-07-27 20:41:39 +08:00
目棃
baf8c3f794 💄 显示设备信息 2024-07-27 20:31:54 +08:00
目棃
2abfcdc050 验证码登录测试成功 #118 2024-07-27 20:01:32 +08:00
目棃
d22203e7b4 🐛 修复分享功能异常 2024-07-27 14:14:04 +08:00
目棃
8f116c954f 🌱 继续优化验证码登录,尝试引入geetest 2024-07-27 12:51:28 +08:00
目棃
a8b1ff4588 💄 优化label可见度 2024-07-27 11:55:22 +08:00
目棃
5d036ddeee 🐛 修复图片加载失败 2024-07-27 00:17:10 +08:00
目棃
b2843dbf13 💄 分享色背景设为透明 2024-07-26 22:55:44 +08:00
目棃
672fa2e536 ♻️ 重构链接解析处理 2024-07-26 22:36:40 +08:00
目棃
a7a0a8b0e0 🐛 修复过时用法 2024-07-26 22:03:44 +08:00
目棃
e9252bbfcd 💄 调整不同状态下的背景色、边缘、文本色 2024-07-26 21:27:54 +08:00
目棃
e3e5b757e4 ♻️ 重构 http req,捕获aigis 2024-07-26 18:02:58 +08:00
目棃
6b18d63fb0 🐛 修复遗漏token 2024-07-26 17:15:44 +08:00
目棃
948db203bd 支持复制ck 2024-07-26 14:28:21 +08:00
目棃
f0b3a311d7 🐛 修复用户数据目录打开失败 2024-07-26 14:19:48 +08:00
目棃
289620922d 🧪 -3001,参数错误☹ 2024-07-25 19:40:07 +08:00
目棃
20ad79f08b 🌱 验证码登录请求草创 #118 2024-07-25 16:14:20 +08:00
目棃
db3cb2fa29 💄 角色/武器图鉴材料支持左右切换 2024-07-25 14:18:38 +08:00
目棃
f822c0b32d 💄 仅在原神页面提供公告页跳转 2024-07-25 14:03:04 +08:00
目棃
632576de94 💄 帖子一次刷新12条 2024-07-25 13:07:46 +08:00
目棃
0b8dc2ef60 ️ 优化查找逻辑,不会自动弹出 2024-07-25 13:02:08 +08:00
目棃
047f9eaf3a ️ 按钮显式表示相关操作 2024-07-24 13:01:22 +08:00
目棃
792d337e45 ️ 调整分类删除逻辑,优化卡片样式 2024-07-24 12:49:33 +08:00
目棃
de89aa5159 ️ 调整按钮顺序,添加刷新按钮 2024-07-24 12:02:14 +08:00
目棃
5c8f2d5e57 ️ 咨询页刷新时记忆 tab 状态 2024-07-23 20:03:28 +08:00
目棃
7391752247 🐛 修复nav返回数据为空或者缺失 2024-07-23 13:07:43 +08:00
目棃
27a2e93efc ✏️ UIGF4导出添加lang 2024-07-18 18:13:12 +08:00
目棃
5bf2521938 ️ 不允许低于 UIGF v2.3 版本的数据导入 2024-07-18 18:04:12 +08:00
目棃
41ee27c74e 🔨 调整 pre-commit 脚本 2024-07-18 18:03:31 +08:00
目棃
3041185128 🚀 v0.5.0
close #113
2024-07-16 22:04:29 +08:00
目棃
ecbf8209bd 材料 wiki 页面草创 2024-07-16 21:59:57 +08:00
目棃
9f40e28eca 🍱 更新 4.8 版本资源
close #115
2024-07-16 21:21:07 +08:00
目棃
462335b20b ️ 调整v4 hint 2024-07-16 10:41:48 +08:00
目棃
cc46e727d1 🐛 修复未装配圣遗物时的渲染异常(2周目) 2024-07-14 20:37:37 +08:00
目棃
b566d32eb8 🐛 修复未装配圣遗物时的渲染异常 2024-07-14 20:20:42 +08:00
目棃
1fbbcf29b9 💄 角色详情overlay样式迭代 2024-07-14 16:23:57 +08:00
目棃
34a4aa4449 先写计划,要赶飞机了( 2024-07-14 08:54:10 +08:00
目棃
008b0f4b75 ✏️ 添加UIGFv4提醒 2024-07-14 08:54:07 +08:00
目棃
e23473862b ✏️ add missing await 2024-07-14 08:54:03 +08:00
目棃
704efbb52f 📝 0.5.0 CHANGELOG 起草 2024-07-14 08:53:52 +08:00
目棃
d646c20b21 ️背景色默认暗色 2024-07-14 08:51:28 +08:00
目棃
46627f4a54 📝 添加 UIGF v4 注释 2024-07-14 08:51:16 +08:00
Mccree Lee
59e4012ced ️ 窗体初始化后显示 (#117)
* perf: show window after initializing

* perf: remove redundant show() in sub windows
2024-07-14 08:36:09 +08:00
目棃
cf66a7e128 UIGFv4支持 2024-07-13 14:41:20 +08:00
目棃
5d054c309a ️ 调整zzz位置 2024-07-13 12:10:30 +08:00
目棃
3347482164 视频支持封面下载 2024-07-12 11:14:03 +08:00
目棃
ea5e568525 ️ 角色分享图添加水印 2024-07-11 22:57:11 +08:00
目棃
cee7d192ae 🐛 修复公告链接无法打开,调整链接样式 2024-07-11 14:37:03 +08:00
目棃
3f93be29b6 🐛 修复帖子内图片无法保存到本地 2024-07-11 14:15:07 +08:00
目棃
012be32ffe 🐛 修复绝区零战绩分享图渲染异常 2024-07-10 17:13:41 +08:00
目棃
26f8aabcc3 ♻️ 顶部工具添加到首页 2024-07-10 16:50:04 +08:00
目棃
bd124e46df 🧑‍💻 调整 chunk warn size 2024-07-09 17:18:25 +08:00
目棃
5e32cce930 🐛 修复导出路径异常 2024-07-09 17:10:23 +08:00
目棃
7baa8050d7 📝 更新日志归档 2024-07-09 14:21:57 +08:00
目棃
d0ed35b2bb Merge pull request #114 from BTMuli/92-todo-tauri-v2
tauri v1 → v2 迁移
2024-07-09 14:12:03 +08:00
目棃
c365cb4906 ⬆️ 更新依赖 2024-07-09 14:07:45 +08:00
目棃
ba3885b637 ️ 链接解析调整 2024-07-08 14:50:14 +08:00
目棃
5816f5c825 👽️ 适应米社返回数据变更 2024-07-08 14:47:59 +08:00
目棃
d9ab24cb14 🐛 修复数据路径错误 2024-07-07 17:10:57 +08:00
目棃
2ba4596f50 ♻️ 收尾,基本测完了 2024-07-07 17:07:11 +08:00
目棃
efcf16fe93 🐛 修复咨询页渲染异常 2024-07-06 01:05:41 +08:00
目棃
012efb03ea ♻️ 重构米社窗口创建,现在默认只创建一个窗口了 2024-07-06 00:28:13 +08:00
目棃
f4de7552e1 ♻️ 感觉差不多了,剩下的就靠测试了 #92 2024-07-03 22:33:37 +08:00
目棃
8a2c7d13c6 ♻️ 姑且没登录的功能都给试了下 2024-07-03 17:49:15 +08:00
目棃
367307029b ♻️ 姑且能跑 dev,尚需调试功能 2024-07-02 23:08:54 +08:00
目棃
1214501691 ♻️ 初步更改,cargo build 2024-07-02 19:29:53 +08:00
目棃
ac63baed2c 🔧 移除打包压缩 2024-07-01 14:42:26 +08:00
目棃
834a8da696 ⬆️ 更新绝区零版块列表 2024-07-01 14:26:21 +08:00
目棃
3c6c3cffaf ⬆️ 更新 salt 2024-07-01 14:21:18 +08:00
目棃
bdd5e99f95 ✏️ 修正剩余的import 2024-07-01 14:17:13 +08:00
目棃
d48e39e580 幻想真境剧诗适配-战绩页面 #113 2024-07-01 13:43:15 +08:00
目棃
0061dfa988 ✏️ 继续修正 import 2024-06-26 22:41:25 +08:00
目棃
f2e4186042 🧑‍💻 更新依赖,启用 devtools 2024-06-26 22:15:47 +08:00
目棃
8a4edd29f5 ✏️ 修复 IDE import 报错 2024-06-26 22:12:02 +08:00
目棃
7ea47b4606 🐛 修复数据恢复异常 2024-06-20 21:49:20 +08:00
BTMuli
b87de84a0d ⬆️ 更新依赖 2024-06-20 14:05:35 +08:00
目棃
e4057d96ea 🚀 v0.4.9 热修版本 2024-06-14 22:53:36 +08:00
目棃
d3b24d52c9 ️ 输入空时重置 2024-06-14 22:48:23 +08:00
目棃
711d35360f 🍱 更新卡牌资料 2024-06-14 22:37:58 +08:00
目棃
f0428d0bae ✏️ 修正部分描述 2024-06-14 22:34:20 +08:00
目棃
59a686cace 🐛 修复成就导入异常 2024-06-13 23:22:16 +08:00
目棃
b628df5ae9 💄 调整tag样式 2024-06-12 11:53:16 +08:00
目棃
b22d23f663 ⬆️ 更新依赖 2024-06-12 11:52:33 +08:00
269 changed files with 23959 additions and 5091 deletions

View File

@@ -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.1.0
version: 9.6.0
- name: remove lockfile
run: rm pnpm-lock.yaml
- name: Install frontend dependencies

View File

@@ -1,4 +1 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npx lint-staged
pnpm lint-staged

View File

@@ -15,3 +15,4 @@ qodana.yaml
*.svg
# data
!src/data/**/*.json
src-tauri/gen/*.json

View File

@@ -1,200 +1,58 @@
---
Author: 目棃
Description: CHANGELOG
Date: 2024-01-15
Update: 2024-06-04
Date: 2024-07-09
Update: 2024-08-11
---
> 本文档 [`Frontmatter`](https://github.com/BTMuli/MuCli#Frontmatter) 由 [MuCli](https://github.com/BTMuli/Mucli) 自动生成于 `2024-01-15 17:29:15`
> 本文档 [`Frontmatter`](https://github.com/BTMuli/MuCli#Frontmatter) 由 [MuCli](https://github.com/BTMuli/Mucli) 自动生成于 `2024-07-09 14:16:16`
>
> 更新于 `2024-06-04 20:07:53`
> 更新于 `2024-08-11 15:43:34`
## [0.4.8](https://github.com/BTMuli/TeyvatGuide/releases/v0.4.8) (2024-06-04)
## [0.5.2](https://github.com/BTMuli/TeyvatGuide/releases/v0.5.2) (2024-08-11)
- 🐛 修复名片解析异常 [`#110`](https://github.com/BTMuli/TeyvatGuide/issues/110)
- 💄 调整链接组件样式
- 💄 调整首页生日组件样式
- 🐛 修复版本搜索失效,未完成成就优先
- 🍱 更新 4.7 版本资源 [`#112`](https://github.com/BTMuli/TeyvatGuide/issues/112)
- 🐛 修复日志目录异常
- ♻️ 重构窗体/缩放调整逻辑
- 🐛 修复公告时间获取异常&内容渲染异常
- 🔥 移除扫码登录模块
👽️ 调整更新日志链接
- 🍱 更新4.8下半数据
## [0.4.7](https://github.com/BTMuli/TeyvatGuide/releases/v0.4.7) (2024-05-10)
## [0.5.1](https://github.com/BTMuli/TeyvatGuide/releases/v0.5.1) (2024-07-30)
- 🐛 修复 mention 类型渲染异常
- ⚡️ 对签到链接跳转进行处理,优化部分网页活动打开
- 重构用户祈愿数据库相关处理
- 🔥 隐藏网页登录模块 [`#108`](https://github.com/BTMuli/TeyvatGuide/issues/108)
- ✨ 采用 ajv 验证 UIGF [`#109`](https://github.com/BTMuli/TeyvatGuide/issues/109)
- 💄 完善公告`table`&`p`部分的渲染
- 💄 调整帖子文本部分的样式
- ♻️ UIAF重构支持祈愿备份/恢复
- ⚡️ 完善公告正则
- ♻️ 名片组件抽离wiki添加名片信息
- ⚡️ 不允许低于 UIGF v2.3 版本的数据导入
- 🐛 修复网页小工具数据获取异常
- 咨讯页刷新时记忆 tab 状态,顶部按钮进行调整
- ⚡️ 调整收藏页面分类删除逻辑,优化卡片样式
- ⚡️ 设置页数据目录添加按钮显式表示相关操作
- ⚡️ 优化帖子搜索逻辑,不会自动弹出浮窗
- 💄 帖子页面调整刷新数量20→12
- 💄 角色/武器图鉴材料支持左右切换
- ✨ 支持短信验证码登录 [`#118`](https://github.com/BTMuli/TeyvatGuide/issues/118)
- 💄 调整首页日历组件不同状态下的背景色、边缘、文本色
- ♻️ 重构米游社相关链接解析处理
- 💄 分享色背景设为透明
- 🐛 修复特定情况下的米游社子窗口分享功能异常
- 💄 设置页显示设备信息支持复制cookie
- 💄 帖子顶部添加分区图标
- 🐛 修复祈愿页面导出按钮逻辑异常
- 🐛 修复公告页部分公告时间解析异常
- 🐛 调整祈愿页面抽数计算规则
- 💄 根据屏幕缩放调整应用缩放
## [0.4.6](https://github.com/BTMuli/TeyvatGuide/releases/v0.4.6) (2024-04-24)
## [0.5.0](https://github.com/BTMuli/TeyvatGuide/releases/v0.5.0) (2024-07-17)
### Feat
- 帖子:重构文本解析,现在更加贴近应用内的渲染效果
- 应用:侧边栏点击咨讯时的参数也支持记忆了
- 应用:更新 4.6 版本的游戏资源
### Fix
- 留影叙佳期:修复侧边栏点击时初始画片数异常
- 首页:修复特定情况下日历组件左侧切换日期按钮点击无效
- 帖子:修复 `align:right` 不生效的问题
- 首页:修复特定情况下首页卡池渲染异常
### Change
- 应用:数据库更新后弹出更新日志
## [0.4.5](https://github.com/BTMuli/TeyvatGuide/releases/v0.4.5) (2024-04-05)
### Feat
- 添加收藏页面,支持导入用户收藏 [`#100`](https://github.com/BTMuli/TeyvatGuide/issues/100)
- 设置页添加更新日志跳转
- 首页:组件样式迭代
- 帖子:替换默认封面
- 帖子:支持关键词搜索 [`#103`](https://github.com/BTMuli/TeyvatGuide/issues/103)
### Fix
- 成就:修正部分成就版本信息 [`3501590f`](https://github.com/BTMuli/TeyvatGuide/commit/3501590f)
- 帖子:链接卡片添加间距
- 图鉴:修复埃洛伊背景渲染异常
- 留影叙佳期:补充遗漏数据,支持特定日期查看
### Change
- 帖子:微调解析逻辑
- 重构:通用帖子卡片抽离作为组件,添加 `select` 状态
- 帖子:抽奖详情改为 `overlay`
- 应用:调整默认颜色
## [0.4.4](https://github.com/BTMuli/TeyvatGuide/releases/v0.4.4) (2024-03-13)
### Feat
- 咨讯:优化咨讯版块切换&页面跳转体验
- 公告:在可能的情况下根据公告内容获取对应时间 [`#94`](https://github.com/BTMuli/TeyvatGuide/issues/94)
- 帖子:添加转载声明,支持图片浮窗
- 祈愿:添加 `祈愿历史` 页面,支持查看历史祈愿记录及对应信息
- 祈愿:添加集录祈愿支持,更新 UIGF 版本至 3.0 [`#96`](https://github.com/BTMuli/TeyvatGuide/issues/96)
- 应用:版本更新弹窗确认后将直接更新数据库
- 应用:添加 `留影叙佳期` 页面,汇聚三年间的相关信息
- 应用:再次支持米游社扫码登录,网页登录自动检测数据并刷新 [`#99`](https://github.com/BTMuli/TeyvatGuide/issues/99)
- 应用:添加 4.5 版本的游戏资源 [`#95`](https://github.com/BTMuli/TeyvatGuide/issues/95)
- 祈愿:支持增量更新祈愿数据
### Fix
- 图鉴:修复武器图鉴左侧列表高度异常
- 日志:修正过期日志判断逻辑
- 帖子:修复动图渲染异常
- 深渊当期不存在9层及以上的数据时进行提示
- JSBridge: 修复特定页面的分享图渲染错误
- 帖子:完善文本样式解析,修复特定情况下文本异常渲染为图片
- 应用:修复侧边栏及设置页面用户信息响应式异常
### Change
- 公告:调整样式,顶部添加相关信息
- 设置:数据路径采用系统命令而非文件选择器打开
- 日志:调试模式下不将日志写入文件
- 帖子:移除大别野组件
- 首页:素材日历移除留影叙佳期入口及生日提醒
- 深渊:胡桃数据库角色持有页面重构
- 首页:卡池组件样式重构
## [0.4.3](https://github.com/BTMuli/TeyvatGuide/releases/v0.4.3) (2024-02-09)
### Feat
- 公告:支持服务器&语言的切换 [`#81`](https://github.com/BTMuli/TeyvatGuide/issues/81)
- 战绩:世界探索部分数据结构调整,合并同类数据(如沉玉谷)[`#91`](https://github.com/BTMuli/TeyvatGuide/issues/91)
- 图鉴:角色/武器图鉴支持条件筛选(武器类型、星级等) [`#87`](https://github.com/BTMuli/TeyvatGuide/issues/87)
### Fix
- 应用:完善 UID 与服务器的对应关系 [`#90`](https://github.com/BTMuli/TeyvatGuide/issues/81)
- 应用:修正扫码登录,增加网页登录 [`#89`](https://github.com/BTMuli/TeyvatGuide/issues/89)
- 祈愿:修复 `authkey` 获取失败后无限加载的问题
- 图鉴:修复部分文本错误,增加遗漏数据
- 应用:修复外部唤起失效,**不保证*应用商店版本*的唤起可用性**
- 日志:修复过时日志检测逻辑错误
### Change
- 设置:平台图标跟随平台变更,调整图标样式 [`#88`](https://github.com/BTMuli/TeyvatGuide/issues/88)
- 深渊:调整深渊样式,完善上传错误处理
- 应用:后端代码重构,修复窗口创建时的 `error` 问题
## [0.4.2](https://github.com/BTMuli/TeyvatGuide/releases/v0.4.2) (2024-01-30)
### Feat
- 应用:支持日志记录 [`#83`](https://github.com/BTMuli/TeyvatGuide/issues/83)
- 图鉴:角色/武器图鉴支持查看材料详细信息
- 资源:游戏资源更新至 4.4 版本 [`#86`](https://github.com/BTMuli/TeyvatGuide/issues/86)
- 成就:支持查看成就详细信息,数据来源于 [`amos-data`](https://github.com/yuehaiteam/amos-data)
- 首页:祈愿卡池 Up 角色支持直接跳转到对应图鉴界面
### Fix
- 应用:完善用户账号数据返回,修复部分用户数据获取失败
- 应用:修复深渊页面分享图生成错误,完善分享&上传判断
- 首页:采用 `emit` 替代 `interval`,提升性能
- JSBridge修复特定情况下的 `panic`
### Change
- 应用:设置页面重构
- 应用:首页组件配置移至首页
- 帖子:调整别野卡片、合集 overlay 样式
## [0.4.1](https://github.com/BTMuli/TeyvatGuide/releases/v0.4.1) (2024-01-19)
### Feat
- 组件:首页素材日历添加 wiki 页面跳转
- 应用:完善 fp 获取,添加强制更新入口
- 图鉴:名片图鉴搜索支持搜索来源
- 应用:支持修改数据目录 [`#78`](https://github.com/BTMuli/TeyvatGuide/issues/78)
### Fix
- 应用:修复首页启动卡数据加载 [`#79`](https://github.com/BTMuli/TeyvatGuide/issues/79)
- 应用:修复 macOS 启动崩溃 [`#82`](https://github.com/BTMuli/TeyvatGuide/issues/82)
- 图鉴:完善切换时的底部 hint
### Change
- 图鉴:卡牌图鉴样式重构
- 组件:统一底部弹窗样式
- 应用:调整部分点击跳转逻辑
## [0.4.0](https://github.com/BTMuli/TeyvatGuide/releases/v0.4.0) (2024-01-15)
### Feat
- 应用:实装角色、武器、名片图鉴
- JSBridge添加窗口旋转子菜单
- 应用:更新米社 salt 版本
- 帖子:完成 `video` 类型数据的渲染 [`#77`](https://github.com/BTMuli/TeyvatGuide/issues/77)
### Fix
- wiki修复流浪者数据错误
- 应用:添加夏沃蕾、究极霸王超级魔剑的数据
- 帖子:修复部分格式图片渲染失败
- 公告:优化公告详情在深色模式下的表现
- JSBridge修复登录态错误
- 应用:类型重构 [`#51`](https://github.com/BTMuli/TeyvatGuide/issues/51)
- 应用:完善数据库完整性检测
### Change
- 应用:调整 `snackbar` 样式
- 应用:取消 `transparent` 配置
- ⬆️ 底层架构更新,升级至 `Tauri v2` [`#92`](https://github.com/BTMuli/TeyvatGuide/issues/92)
- 🐛 修复数据恢复异常
- ⬆️ 更新绝区零版块列表
- ⬆️ 适配米游社返回数据
- ⚡️ 链接解析调整
- ✨ 幻想真境剧诗适配 [`#113`](https://github.com/BTMuli/TeyvatGuide/issues/113)
- ♻️ 帖子顶部小工具添加到首页
- ⚡️ 公告链接样式调整
- ⚡️ 角色分享图添加水印
- ✨ 视频支持封面下载
- ⚡️ 咨讯页调整绝区零版块排序
- ✨ 祈愿添加 UIGF v4 支持
- 🍱 更新 4.8 版本资源 [`#115`](https://github.com/BTMuli/TeyvatGuide/issues/115)
- ✨ 材料 wiki 页面草创

View File

@@ -2,16 +2,16 @@
Author: 目棃
Description: 说明文档
Date: 2023-03-05
Update: 2024-04-05
Update: 2024-07-31
---
> 本文档 [`Frontmatter`](https://github.com/BTMuli/MuCli#Frontmatter) 由 [MuCli](https://github.com/BTMuli/Mucli) 自动生成于 `2023-03-05 14:41:55`
>
> 更新于 `2024-04-05 14:25:24`
> 更新于 `2024-07-31 18:21:43`
![](https://img.shields.io/github/last-commit/BTMuli/TeyvatGuide?style=for-the-badge) ![](https://img.shields.io/github/commits-since/BTMuli/TeyvatGuide/latest?include_prereleases&style=for-the-badge)
![](https://img.shields.io/badge/UIAF-v1.1-orange?style=for-the-badge) ![](https://img.shields.io/badge/UIGF-v3.0-red?style=for-the-badge) ![](https://img.shields.io/github/license/BTMuli/TeyvatGuide?style=for-the-badge)
![](https://img.shields.io/badge/UIAF-v1.1-orange?style=for-the-badge) ![](https://img.shields.io/badge/UIGF-v3.0-red?style=for-the-badge) ![](https://img.shields.io/badge/UIGF-v4.0-red?style=for-the-badge) ![](https://img.shields.io/github/license/BTMuli/TeyvatGuide?style=for-the-badge)
<div style="width: 100%; text-align: center; margin: 0 auto;">
<img alt="icon" src="https://s2.loli.net/2023/10/19/Y5DpBQRy3usLHEb.png" />
@@ -33,7 +33,7 @@ Game Tool for Genshin Impact player, supports Windows and macOS.
> macOS 用户可以通过 Github Release 下载
[![GitHub release (latest by date including pre-releases)](https://img.shields.io/github/v/release/BTMuli/TeyvatGuide?include_prereleases&style=for-the-badge)](https://github.com/BTMuli/TeyvatGuide/releases/latest)
[![GitHub release (latest by date)](https://img.shields.io/github/v/release/BTMuli/TeyvatGuide?style=for-the-badge)](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
- [x] 祈愿管理UIGF v3.0UIGF v4.0
- [x] 留影叙佳期画片查看
- [x] 帖子收藏
@@ -66,6 +66,7 @@ Game Tool for Genshin Impact player, supports Windows and macOS.
- [x] 武器图鉴
- [x] 名片图鉴
- [x] 卡牌图鉴
- [x] 材料图鉴
- 应用功能:
- [x] 浅色/深色主题切换

View File

@@ -1,7 +1,7 @@
---
Author: 目棃
Date: 2023-06-18
Description: CHANGELOG
Description: CHANGELOG v0.2.x
Update: 2023-08-31
---

View File

@@ -1,6 +1,6 @@
---
Author: 目棃
Description: CHANGELOG
Description: CHANGELOG v0.3.x
Date: 2023-09-08
Update: 2024-01-15
---

208
docs/CHANGELOG-v0.4.x.md Normal file
View File

@@ -0,0 +1,208 @@
---
Author: 目棃
Description: CHANGELOG v0.4.x
Date: 2024-01-15
Update: 2024-07-09
---
> 本文档 [`Frontmatter`](https://github.com/BTMuli/MuCli#Frontmatter) 由 [MuCli](https://github.com/BTMuli/Mucli) 自动生成于 `2024-01-15 17:29:15`
>
> 更新于 `2024-07-09 14:17:46`
## [0.4.9](https://github.com/BTMuli/TeyvatGuide/releases/v0.4.9) (2024-06-14)
- 💄 调整帖子 `tag` 样式
- 🐛 修复成就导入异常
- ✏️ 修正角色图鉴部分描述
- 🍱 更新卡牌资料
- ⚡️ 成就查询输入为空时重置结果
## [0.4.8](https://github.com/BTMuli/TeyvatGuide/releases/v0.4.8) (2024-06-04)
- 🐛 修复名片解析异常 [`#110`](https://github.com/BTMuli/TeyvatGuide/issues/110)
- 💄 调整链接组件样式
- 💄 调整首页生日组件样式
- 🐛 修复版本搜索失效,未完成成就优先
- 🍱 更新 4.7 版本资源 [`#112`](https://github.com/BTMuli/TeyvatGuide/issues/112)
## [0.4.7](https://github.com/BTMuli/TeyvatGuide/releases/v0.4.7) (2024-05-10)
- 🐛 修复 mention 类型渲染异常
- ⚡️ 对签到链接跳转进行处理,优化部分网页活动打开
- ♻️ 重构用户祈愿数据库相关处理
- 🔥 隐藏网页登录模块 [`#108`](https://github.com/BTMuli/TeyvatGuide/issues/108)
- ✨ 采用 ajv 验证 UIGF [`#109`](https://github.com/BTMuli/TeyvatGuide/issues/109)
- 💄 完善公告`table`&`p`部分的渲染
- 💄 调整帖子文本部分的样式
- ♻️ UIAF重构支持祈愿备份/恢复
- ⚡️ 完善公告正则
- ♻️ 名片组件抽离wiki添加名片信息
## [0.4.6](https://github.com/BTMuli/TeyvatGuide/releases/v0.4.6) (2024-04-24)
### Feat
- 帖子:重构文本解析,现在更加贴近应用内的渲染效果
- 应用:侧边栏点击咨讯时的参数也支持记忆了
- 应用:更新 4.6 版本的游戏资源
### Fix
- 留影叙佳期:修复侧边栏点击时初始画片数异常
- 首页:修复特定情况下日历组件左侧切换日期按钮点击无效
- 帖子:修复 `align:right` 不生效的问题
- 首页:修复特定情况下首页卡池渲染异常
### Change
- 应用:数据库更新后弹出更新日志
## [0.4.5](https://github.com/BTMuli/TeyvatGuide/releases/v0.4.5) (2024-04-05)
### Feat
- 添加收藏页面,支持导入用户收藏 [`#100`](https://github.com/BTMuli/TeyvatGuide/issues/100)
- 设置页添加更新日志跳转
- 首页:组件样式迭代
- 帖子:替换默认封面
- 帖子:支持关键词搜索 [`#103`](https://github.com/BTMuli/TeyvatGuide/issues/103)
### Fix
- 成就:修正部分成就版本信息 [`3501590f`](https://github.com/BTMuli/TeyvatGuide/commit/3501590f)
- 帖子:链接卡片添加间距
- 图鉴:修复埃洛伊背景渲染异常
- 留影叙佳期:补充遗漏数据,支持特定日期查看
### Change
- 帖子:微调解析逻辑
- 重构:通用帖子卡片抽离作为组件,添加 `select` 状态
- 帖子:抽奖详情改为 `overlay`
- 应用:调整默认颜色
## [0.4.4](https://github.com/BTMuli/TeyvatGuide/releases/v0.4.4) (2024-03-13)
### Feat
- 咨讯:优化咨讯版块切换&页面跳转体验
- 公告:在可能的情况下根据公告内容获取对应时间 [`#94`](https://github.com/BTMuli/TeyvatGuide/issues/94)
- 帖子:添加转载声明,支持图片浮窗
- 祈愿:添加 `祈愿历史` 页面,支持查看历史祈愿记录及对应信息
- 祈愿:添加集录祈愿支持,更新 UIGF 版本至 3.0 [`#96`](https://github.com/BTMuli/TeyvatGuide/issues/96)
- 应用:版本更新弹窗确认后将直接更新数据库
- 应用:添加 `留影叙佳期` 页面,汇聚三年间的相关信息
- 应用:再次支持米游社扫码登录,网页登录自动检测数据并刷新 [`#99`](https://github.com/BTMuli/TeyvatGuide/issues/99)
- 应用:添加 4.5 版本的游戏资源 [`#95`](https://github.com/BTMuli/TeyvatGuide/issues/95)
- 祈愿:支持增量更新祈愿数据
### Fix
- 图鉴:修复武器图鉴左侧列表高度异常
- 日志:修正过期日志判断逻辑
- 帖子:修复动图渲染异常
- 深渊当期不存在9层及以上的数据时进行提示
- JSBridge: 修复特定页面的分享图渲染错误
- 帖子:完善文本样式解析,修复特定情况下文本异常渲染为图片
- 应用:修复侧边栏及设置页面用户信息响应式异常
### Change
- 公告:调整样式,顶部添加相关信息
- 设置:数据路径采用系统命令而非文件选择器打开
- 日志:调试模式下不将日志写入文件
- 帖子:移除大别野组件
- 首页:素材日历移除留影叙佳期入口及生日提醒
- 深渊:胡桃数据库角色持有页面重构
- 首页:卡池组件样式重构
## [0.4.3](https://github.com/BTMuli/TeyvatGuide/releases/v0.4.3) (2024-02-09)
### Feat
- 公告:支持服务器&语言的切换 [`#81`](https://github.com/BTMuli/TeyvatGuide/issues/81)
- 战绩:世界探索部分数据结构调整,合并同类数据(如沉玉谷)[`#91`](https://github.com/BTMuli/TeyvatGuide/issues/91)
- 图鉴:角色/武器图鉴支持条件筛选(武器类型、星级等) [`#87`](https://github.com/BTMuli/TeyvatGuide/issues/87)
### Fix
- 应用:完善 UID 与服务器的对应关系 [`#90`](https://github.com/BTMuli/TeyvatGuide/issues/81)
- 应用:修正扫码登录,增加网页登录 [`#89`](https://github.com/BTMuli/TeyvatGuide/issues/89)
- 祈愿:修复 `authkey` 获取失败后无限加载的问题
- 图鉴:修复部分文本错误,增加遗漏数据
- 应用:修复外部唤起失效,**不保证*应用商店版本*的唤起可用性**
- 日志:修复过时日志检测逻辑错误
### Change
- 设置:平台图标跟随平台变更,调整图标样式 [`#88`](https://github.com/BTMuli/TeyvatGuide/issues/88)
- 深渊:调整深渊样式,完善上传错误处理
- 应用:后端代码重构,修复窗口创建时的 `error` 问题
## [0.4.2](https://github.com/BTMuli/TeyvatGuide/releases/v0.4.2) (2024-01-30)
### Feat
- 应用:支持日志记录 [`#83`](https://github.com/BTMuli/TeyvatGuide/issues/83)
- 图鉴:角色/武器图鉴支持查看材料详细信息
- 资源:游戏资源更新至 4.4 版本 [`#86`](https://github.com/BTMuli/TeyvatGuide/issues/86)
- 成就:支持查看成就详细信息,数据来源于 [`amos-data`](https://github.com/yuehaiteam/amos-data)
- 首页:祈愿卡池 Up 角色支持直接跳转到对应图鉴界面
### Fix
- 应用:完善用户账号数据返回,修复部分用户数据获取失败
- 应用:修复深渊页面分享图生成错误,完善分享&上传判断
- 首页:采用 `emit` 替代 `interval`,提升性能
- JSBridge修复特定情况下的 `panic`
### Change
- 应用:设置页面重构
- 应用:首页组件配置移至首页
- 帖子:调整别野卡片、合集 overlay 样式
## [0.4.1](https://github.com/BTMuli/TeyvatGuide/releases/v0.4.1) (2024-01-19)
### Feat
- 组件:首页素材日历添加 wiki 页面跳转
- 应用:完善 fp 获取,添加强制更新入口
- 图鉴:名片图鉴搜索支持搜索来源
- 应用:支持修改数据目录 [`#78`](https://github.com/BTMuli/TeyvatGuide/issues/78)
### Fix
- 应用:修复首页启动卡数据加载 [`#79`](https://github.com/BTMuli/TeyvatGuide/issues/79)
- 应用:修复 macOS 启动崩溃 [`#82`](https://github.com/BTMuli/TeyvatGuide/issues/82)
- 图鉴:完善切换时的底部 hint
### Change
- 图鉴:卡牌图鉴样式重构
- 组件:统一底部弹窗样式
- 应用:调整部分点击跳转逻辑
## [0.4.0](https://github.com/BTMuli/TeyvatGuide/releases/v0.4.0) (2024-01-15)
### Feat
- 应用:实装角色、武器、名片图鉴
- JSBridge添加窗口旋转子菜单
- 应用:更新米社 salt 版本
- 帖子:完成 `video` 类型数据的渲染 [`#77`](https://github.com/BTMuli/TeyvatGuide/issues/77)
### Fix
- wiki修复流浪者数据错误
- 应用:添加夏沃蕾、究极霸王超级魔剑的数据
- 帖子:修复部分格式图片渲染失败
- 公告:优化公告详情在深色模式下的表现
- JSBridge修复登录态错误
- 应用:类型重构 [`#51`](https://github.com/BTMuli/TeyvatGuide/issues/51)
- 应用:完善数据库完整性检测
### Change
- 应用:调整 `snackbar` 样式
- 应用:取消 `transparent` 配置

View File

@@ -13,6 +13,7 @@ export default [
"pnpm-lock.yaml",
"src/data/**/*.json",
"src-tauri/tauri.conf.json",
"src-tauri/gen/*.json",
"qodana.yaml",
".github",
".vscode",

View File

@@ -1,10 +1,11 @@
<!doctype html>
<html lang="en">
<html lang="en" style="background: #1e1e1e">
<head>
<meta charset="UTF-8" />
<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>

View File

@@ -1,6 +1,6 @@
{
"name": "TeyvatGuide",
"version": "0.4.8",
"version": "0.5.2",
"description": "Game Tool for Genshin Impact player",
"private": true,
"type": "module",
@@ -20,7 +20,7 @@
"tauri:icon": "tauri icon ./public/icon.png",
"vite:dev": "vite dev",
"vite:build": "vite build",
"prepare": "husky install"
"prepare": "husky"
},
"lint-staged": {
"*.ts": [
@@ -66,68 +66,77 @@
},
"dependencies": {
"@mdi/font": "7.4.47",
"@tauri-apps/api": "^1.5.5",
"ajv": "^8.13.0",
"artplayer": "^5.1.1",
"@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.0",
"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",
"tauri-plugin-log-api": "github:tauri-apps/tauri-plugin-log#v1",
"tauri-plugin-sql-api": "github:tauri-apps/tauri-plugin-sql#v1",
"uuid": "^9.0.1",
"vue": "^3.4.27",
"vue-echarts": "^6.7.2",
"uuid": "^10.0.0",
"vue": "^3.4.35",
"vue-echarts": "^6.7.3",
"vue-json-viewer": "^3.0.4",
"vue-router": "^4.3.2",
"vuetify": "^3.6.6",
"vue-router": "^4.4.2",
"vuetify": "^3.6.14",
"wcag-color": "^1.1.1",
"xml-js": "^1.6.11"
},
"devDependencies": {
"@eslint/eslintrc": "^3.0.2",
"@eslint/js": "^9.2.0",
"@tauri-apps/cli": "^1.5.14",
"@eslint/eslintrc": "^3.1.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.12.12",
"@types/uuid": "^9.0.8",
"@typescript-eslint/parser": "^7.9.0",
"@vitejs/plugin-vue": "^5.0.4",
"@types/node": "^22.1.0",
"@types/uuid": "^10.0.0",
"@typescript-eslint/parser": "^8.0.0",
"@vitejs/plugin-vue": "^5.1.2",
"concurrently": "^8.2.2",
"eslint": "^9.2.0",
"eslint-config-love": "^47.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.15.1",
"eslint-plugin-n": "^17.7.0",
"eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-promise": "^6.1.1",
"eslint-plugin-vue": "^9.26.0",
"eslint-plugin-jsonc": "^2.16.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.2.0",
"husky": "^9.0.11",
"globals": "^15.9.0",
"husky": "^9.1.4",
"jsonc-eslint-parser": "^2.4.0",
"lint-staged": "^15.2.2",
"oxlint": "^0.3.4",
"prettier": "3.2.5",
"stylelint": "^16.5.0",
"lint-staged": "^15.2.7",
"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.4.5",
"typescript-eslint": "^7.9.0",
"vite": "^5.2.11",
"vite-plugin-vue-devtools": "^7.1.3",
"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.2",
"yaml-eslint-parser": "^1.2.2"
"vue-eslint-parser": "^9.4.3",
"yaml-eslint-parser": "^1.2.3"
}
}

2763
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

3194
src-tauri/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
[package]
name = "TeyvatGuide"
version = "0.4.8"
version = "0.5.2"
description = "Game Tool for Genshin Impact player"
authors = ["BTMuli <bt-muli@outlook.com>"]
license = "MIT"
@@ -10,39 +10,69 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[build-dependencies]
tauri-build = { version = "1.4", features = [] }
tauri-build = { version = "2.0.0-beta.19", features = [] }
[dependencies]
chrono = "0.4.38"
log = "0.4.21"
serde = { version = "1.0.201", features = ["derive"] }
serde_json = "1.0.117"
tauri = { version = "1.6.7", features = [ "shell-execute", "shell-open", "window-set-always-on-top", "window-set-fullscreen", "dialog-message", "process-exit", "fs-read-dir", "window-hide", "os-all", "clipboard-all", "dialog-open", "dialog-save", "fs-create-dir", "fs-remove-dir", "fs-write-file", "fs-remove-file", "fs-read-file", "path-all", "fs-exists", "window-close", "window-set-title", "window-unminimize", "window-show", "window-set-focus", "http-request"] }
tauri-utils = "1.5.4"
url = "2.5.0"
log = "0.4.22"
serde = { version = "1.0.204", features = ["derive"] }
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"
# sqlite 插件
[dependencies.tauri-plugin-sql]
git = "https://github.com/tauri-apps/plugins-workspace"
branch = "v1"
features = ["sqlite"]
# deep link 插件
[dependencies.tauri-plugin-deep-link]
version = "0.1.2"
git = "https://github.com/tauri-apps/plugins-workspace"
branch = "v2"
# dialog 插件
[dependencies.tauri-plugin-dialog]
git = "https://github.com/tauri-apps/plugins-workspace"
branch = "v2"
# fs 插件
[dependencies.tauri-plugin-fs]
git = "https://github.com/tauri-apps/plugins-workspace"
branch = "v2"
# http 插件
[dependencies.tauri-plugin-http]
git = "https://github.com/tauri-apps/plugins-workspace"
branch = "v2"
features = ["unsafe-headers"]
# log 插件
[dependencies.tauri-plugin-log]
git = "https://github.com/tauri-apps/plugins-workspace"
branch = "v1"
branch = "v2"
# 用于打包
[profile.release]
codegen-units = 1
lto = true
opt-level = "s"
panic = "abort"
# os 插件
[dependencies.tauri-plugin-os]
git = "https://github.com/tauri-apps/plugins-workspace"
branch = "v2"
# process 插件
[dependencies.tauri-plugin-process]
git = "https://github.com/tauri-apps/plugins-workspace"
branch = "v2"
# shell 插件
[dependencies.tauri-plugin-shell]
git = "https://github.com/tauri-apps/plugins-workspace"
branch = "v2"
# single-instance 插件
[dependencies.tauri-plugin-single-instance]
git = "https://github.com/tauri-apps/plugins-workspace"
branch = "v2"
# sqlite 插件
[dependencies.tauri-plugin-sql]
git = "https://github.com/tauri-apps/plugins-workspace"
branch = "v2"
features = ["sqlite"]
[features]
# this feature is used for production builds or when `devPath` points to the filesystem

View File

@@ -0,0 +1,42 @@
{
"$schema": "./schemas/desktop-schema.json",
"identifier": "DevJson",
"description": "Capability for the dev json window",
"windows": ["Dev_JSON"],
"permissions": [
"core:app:allow-version",
"core:app:default",
"core:event:allow-listen",
"core:event:default",
"http:allow-fetch",
"log:allow-log",
"log:default",
"core:path:allow-resolve-directory",
"core:path:default",
"sql:allow-load",
"sql:allow-execute",
"sql:default",
"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/*"
}
]
}
],
"platforms": ["windows", "macOS"]
}

View File

@@ -0,0 +1,89 @@
{
"$schema": "./schemas/desktop-schema.json",
"identifier": "Mys",
"description": "Capability for the mys client window",
"windows": ["mhy_client"],
"permissions": [
"dialog:allow-message",
"dialog: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": "http:default",
"allow": [
{
"url": "https://*.miyoushe.com/*"
},
{
"url": "https://*.mihoyo.com/*"
}
]
}
],
"remote": {
"urls": ["https://*.mihoyo.com/*", "https://*.miyoushe.com/*"]
},
"platforms": ["windows", "macOS"]
}

View File

@@ -0,0 +1,75 @@
{
"$schema": "./schemas/desktop-schema.json",
"identifier": "SubWindow",
"description": "Capability for the sub window",
"windows": ["Sub_window"],
"permissions": [
"core:app:allow-version",
"core:app:default",
"dialog:allow-save",
"dialog:default",
"core:event:allow-listen",
"core:event:default",
"fs:default",
"http:allow-fetch",
"log:allow-log",
"log:default",
"core:path:allow-resolve-directory",
"core:path:default",
"shell:allow-open",
"shell:default",
"sql:allow-load",
"sql:allow-execute",
"sql:default",
"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": "**" }]
},
{
"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": "http:default",
"allow": [
{
"url": "https://*.miyoushe.com/*"
},
{
"url": "https://*.mihoyo.com/*"
}
]
}
],
"platforms": ["windows", "macOS"]
}

View File

@@ -0,0 +1,99 @@
{
"$schema": "./schemas/desktop-schema.json",
"identifier": "TeyvatGuide",
"description": "Capability for the main window",
"windows": ["TeyvatGuide"],
"permissions": [
"core:app:allow-version",
"core:app:default",
"dialog:allow-save",
"dialog:default",
"core:event:allow-listen",
"core:event:default",
"fs:default",
"http:allow-fetch",
"log:allow-log",
"log:default",
"core:path:allow-resolve-directory",
"core:path:default",
"process:allow-exit",
"process:default",
"shell:allow-execute",
"shell:allow-open",
"shell:default",
"sql:allow-load",
"sql:allow-execute",
"sql:default",
"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": "**" }]
},
{
"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": "http:default",
"allow": [
{
"url": "https://*.miyoushe.com/*"
},
{
"url": "https://*.mihoyo.com/*"
},
{
"url": "https://homa.snapgenshin.com/*"
}
]
},
{
"identifier": "shell:allow-execute",
"allow": [
{
"name": "win_open",
"cmd": "explorer",
"args": true
},
{
"name": "mac_open",
"cmd": "open",
"args": true
}
]
}
],
"platforms": ["windows", "macOS"]
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,179 @@
{
"DevJson": {
"identifier": "DevJson",
"description": "Capability for the dev json window",
"local": true,
"windows": ["Dev_JSON"],
"permissions": [
"core:app:allow-version",
"core:app:default",
"core:event:allow-listen",
"core:event:default",
"http:allow-fetch",
"log:allow-log",
"log:default",
"core:path:allow-resolve-directory",
"core:path:default",
"sql:allow-load",
"sql:allow-execute",
"sql:default",
"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/*" }]
}
],
"platforms": ["windows", "macOS"]
},
"Mys": {
"identifier": "Mys",
"description": "Capability for the mys client window",
"remote": { "urls": ["https://*.mihoyo.com/*", "https://*.miyoushe.com/*"] },
"local": true,
"windows": ["mhy_client"],
"permissions": [
"dialog:allow-message",
"dialog: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": "http:default",
"allow": [{ "url": "https://*.miyoushe.com/*" }, { "url": "https://*.mihoyo.com/*" }]
}
],
"platforms": ["windows", "macOS"]
},
"SubWindow": {
"identifier": "SubWindow",
"description": "Capability for the sub window",
"local": true,
"windows": ["Sub_window"],
"permissions": [
"core:app:allow-version",
"core:app:default",
"dialog:allow-save",
"dialog:default",
"core:event:allow-listen",
"core:event:default",
"fs:default",
"http:allow-fetch",
"log:allow-log",
"log:default",
"core:path:allow-resolve-directory",
"core:path:default",
"shell:allow-open",
"shell:default",
"sql:allow-load",
"sql:allow-execute",
"sql:default",
"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": "**" }] },
{ "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": "http:default",
"allow": [{ "url": "https://*.miyoushe.com/*" }, { "url": "https://*.mihoyo.com/*" }]
}
],
"platforms": ["windows", "macOS"]
},
"TeyvatGuide": {
"identifier": "TeyvatGuide",
"description": "Capability for the main window",
"local": true,
"windows": ["TeyvatGuide"],
"permissions": [
"core:app:allow-version",
"core:app:default",
"dialog:allow-save",
"dialog:default",
"core:event:allow-listen",
"core:event:default",
"fs:default",
"http:allow-fetch",
"log:allow-log",
"log:default",
"core:path:allow-resolve-directory",
"core:path:default",
"process:allow-exit",
"process:default",
"shell:allow-execute",
"shell:allow-open",
"shell:default",
"sql:allow-load",
"sql:allow-execute",
"sql:default",
"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": "**" }] },
{ "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": "http:default",
"allow": [
{ "url": "https://*.miyoushe.com/*" },
{ "url": "https://*.mihoyo.com/*" },
{ "url": "https://homa.snapgenshin.com/*" }
]
},
{
"identifier": "shell:allow-execute",
"allow": [
{ "args": true, "cmd": "explorer", "name": "win_open" },
{ "args": true, "cmd": "open", "name": "mac_open" }
]
}
],
"platforms": ["windows", "macOS"]
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,11 +1,14 @@
//! @file src/client/menu.rs
//! @desc 客户端菜单模块,负责操作米游社客户端菜单
//! @since Beta v0.4.5
//! @since Beta v0.5.2
use tauri::{AppHandle, CustomMenuItem, LogicalSize, Manager, Menu, Size, Submenu, WindowUrl};
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;
use url::Url;
pub fn get_mhy_client_url(func: String) -> WindowUrl {
pub fn get_mhy_client_url(func: String) -> WebviewUrl {
let mut url_res: Url = "https://bbs.mihoyo.com/ys/".parse().unwrap();
if func == "sign_in" {
url_res = "https://webstatic.mihoyo.com/bbs/event/signin-ys/index.html?act_id=e202009291139501"
@@ -13,105 +16,81 @@ pub fn get_mhy_client_url(func: String) -> WindowUrl {
.unwrap();
} else if func == "game_record" {
url_res =
"https://webstatic.mihoyo.com/app/community-game-records/index.html?bbs_presentation_style=fullscreen".parse().unwrap();
"https://webstatic.mihoyo.com/app/community-game-records/index.html?bbs_presentation_style=fullscreen".parse().unwrap();
} else if func == "birthday" {
url_res = "https://webstatic.mihoyo.com/ys/event/e20220303-birthday/index.html?activity_id=20220301153521"
.parse()
.unwrap();
.parse()
.unwrap();
}
return WindowUrl::External(url_res);
WebviewUrl::External(url_res)
}
// 创建子菜单-工具
fn create_utils_menu() -> Menu {
let retry_bridge = CustomMenuItem::new("retry".to_string(), "重试桥接");
let mock_touch = CustomMenuItem::new("mock_touch".to_string(), "模拟触摸");
let remove_overlay = CustomMenuItem::new("remove_overlay".to_string(), "移除遮罩");
let rotate_window = CustomMenuItem::new("rotate_window".to_string(), "旋转窗口");
let switch_ua = CustomMenuItem::new("switch_ua".to_string(), "切换 UA");
return Menu::new()
.add_item(retry_bridge)
.add_item(mock_touch)
.add_item(remove_overlay)
.add_item(rotate_window);
fn create_utils_menu(app: AppHandle) -> Submenu<Wry> {
let retry_bridge_submenu = MenuItemBuilder::with_id("retry", "重试桥接").build(&app).unwrap();
let mock_touch_submenu = MenuItemBuilder::with_id("mock_touch", "模拟触摸").build(&app).unwrap();
let remove_overlay_submenu =
MenuItemBuilder::with_id("remove_overlay", "移除遮罩").build(&app).unwrap();
let rotate_window_submenu =
MenuItemBuilder::with_id("rotate_window", "旋转窗口").build(&app).unwrap();
let utils_menu = SubmenuBuilder::new(&app, "工具")
.item(&retry_bridge_submenu)
.item(&mock_touch_submenu)
.item(&remove_overlay_submenu)
.item(&rotate_window_submenu)
.build()
.expect("failed to create utils_menu");
utils_menu
}
// 创建米游社客户端菜单
pub fn create_mhy_menu(func: String) -> Menu {
let top = CustomMenuItem::new("top".to_string(), "置顶");
let cancel_top = CustomMenuItem::new("cancel_top".to_string(), "取消置顶");
let sign_in = CustomMenuItem::new("sign_in".to_string(), "用户登录");
let open_post = CustomMenuItem::new("open_post".to_string(), "打开帖子");
let utils_menu = Submenu::new("工具".to_string(), create_utils_menu());
// 如果是登录
if func == "config_sign_in" {
return Menu::new().add_item(sign_in);
}
return Menu::new()
.add_item(top)
.add_item(cancel_top)
.add_item(open_post)
.add_submenu(utils_menu);
pub fn create_mhy_menu(app: AppHandle) -> Menu<Wry> {
let top_menu = MenuItemBuilder::with_id("top", "置顶").build(&app).unwrap();
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());
MenuBuilder::new(&app)
.item(&top_menu)
.item(&cancel_top_menu)
.item(&open_post_menu)
.item(&utils_menu)
.build()
.expect("failed to create mhy_menu")
}
// 菜单栏事件处理
pub fn handle_menu_event(app_handle: AppHandle, event: tauri::MenuEvent) {
match event.menu_item_id() {
"top" => handle_menu_top(app_handle),
"cancel_top" => handle_menu_cancel_top(app_handle),
"sign_in" => handle_menu_sign_in(app_handle),
"open_post" => handle_menu_open_post(app_handle),
"retry" => handle_menu_retry(app_handle),
"mock_touch" => handle_menu_mock_touch(app_handle),
"remove_overlay" => handle_menu_remove_overlay(app_handle),
"rotate_window" => handle_menu_rotate_window(app_handle),
"switch_ua" => handle_menu_switch_ua(app_handle),
pub fn handle_menu_event(window: &Window, event: MenuEvent) {
match event.id.as_ref() {
"top" => handle_menu_top(window),
"cancel_top" => handle_menu_cancel_top(window),
"open_post" => handle_menu_open_post(window),
"retry" => handle_menu_retry(window),
"mock_touch" => handle_menu_mock_touch(window),
"remove_overlay" => handle_menu_remove_overlay(window),
"rotate_window" => handle_menu_rotate_window(window),
_ => {}
}
}
// 处理置顶菜单
fn handle_menu_top(app_handle: AppHandle) {
let window = app_handle.get_window("mhy_client");
fn handle_menu_top(app_handle: &Window) {
let window = app_handle.get_webview_window("mhy_client");
if window.is_some() {
window.unwrap().set_always_on_top(true).unwrap();
}
}
// 处理取消置顶菜单
fn handle_menu_cancel_top(app_handle: AppHandle) {
let window = app_handle.get_window("mhy_client");
fn handle_menu_cancel_top(app_handle: &Window) {
let window = app_handle.get_webview_window("mhy_client");
if window.is_some() {
window.unwrap().set_always_on_top(false).unwrap();
}
}
// 处理用户登录菜单
fn handle_menu_sign_in(app_handle: AppHandle) {
let window = app_handle.get_window("mhy_client");
let execute_js = r#"
javascript:(async function(){
// 首先检测是不是 user.mihoyo.com
const url = new URL(window.location.href);
if(url.hostname !== "user.mihoyo.com"){
alert("当前页面不是米游社登录页面");
return;
}
const ck = document.cookie;
const arg = {
method: 'teyvat_sign_in',
payload: ck,
}
await window.__TAURI__.event.emit('post_mhy_client',JSON.stringify(arg));
})()"#;
if window.is_some() {
window.unwrap().eval(&execute_js).ok().unwrap();
}
}
// 处理打开帖子菜单
fn handle_menu_open_post(app_handle: AppHandle) {
let window = app_handle.get_window("mhy_client");
fn handle_menu_open_post(app_handle: &Window) {
let window = app_handle.get_webview_window("mhy_client");
let execute_js = r#"
javascript:(async function(){
let url = new URL(window.location.href);
@@ -150,8 +129,8 @@ fn handle_menu_open_post(app_handle: AppHandle) {
}
// 处理重试桥接菜单
fn handle_menu_retry(app_handle: AppHandle) {
let window = app_handle.get_window("mhy_client");
fn handle_menu_retry(app_handle: &Window) {
let window = app_handle.get_webview_window("mhy_client");
let execute_js = r#"
javascript:(async function(){
const arg = {
@@ -165,8 +144,8 @@ fn handle_menu_retry(app_handle: AppHandle) {
}
// 处理模拟触摸菜单
fn handle_menu_mock_touch(app_handle: AppHandle) {
let window = app_handle.get_window("mhy_client");
fn handle_menu_mock_touch(app_handle: &Window) {
let window = app_handle.get_webview_window("mhy_client");
let execute_js = r#"
javascript:(async function(){
const arg = {
@@ -180,8 +159,8 @@ fn handle_menu_mock_touch(app_handle: AppHandle) {
}
// 处理移除遮罩菜单
fn handle_menu_remove_overlay(app_handle: AppHandle) {
let window = app_handle.get_window("mhy_client");
fn handle_menu_remove_overlay(app_handle: &Window) {
let window = app_handle.get_webview_window("mhy_client");
let execute_js = r#"
javascript:(async function(){
const arg = {
@@ -195,39 +174,26 @@ fn handle_menu_remove_overlay(app_handle: AppHandle) {
}
// 处理旋转窗口菜单
fn handle_menu_rotate_window(app_handle: AppHandle) {
let window_get = app_handle.get_window("mhy_client");
fn handle_menu_rotate_window(app_handle: &Window) {
let window_get = app_handle.get_webview_window("mhy_client");
if window_get == None {
return;
}
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();
}
// 处理切换 UA 菜单
fn handle_menu_switch_ua(app_handle: AppHandle) {
let window = app_handle.get_window("mhy_client");
let execute_js = r#"
javascript:(async function(){
const isPc = navigator.userAgent.includes("Windows NT");
const arg = {
method: 'teyvat_switch_ua',
payload: {
url: window.location.href,
isPc,
}
}
await window.__TAURI__.event.emit('post_mhy_client',JSON.stringify(arg));
})()"#;
if window.is_some() {
window.unwrap().eval(&execute_js).ok().unwrap();
}
}

View File

@@ -1,47 +1,44 @@
//! @file src/client/mod.rs
//! @desc 客户端模块,负责操作米游社客户端
//! @since Beta v0.4.5
//! @since Beta v0.5.2
mod menu;
use tauri::async_runtime::block_on;
use tauri::{AppHandle, Manager, WindowBuilder, WindowEvent, WindowUrl};
mod utils;
use tauri::{AppHandle, Manager, WebviewWindowBuilder};
use tauri_utils::config::WebviewUrl;
#[tauri::command]
pub async fn create_mhy_client(handle: AppHandle, func: String, url: String) {
let mut mhy_window_config = handle.config().tauri.windows.get(1).unwrap().clone();
mhy_window_config.visible = true;
let mut win_width = 400.0;
let mut win_height = 800.0;
let win_ua = "Mozilla/5.0 (Linux; Android 12) Mobile miHoYoBBS/2.72.2";
let url_parse;
if url != "" {
mhy_window_config.url = WindowUrl::External(url.parse().unwrap());
url_parse = WebviewUrl::External(url.parse().unwrap());
} else {
mhy_window_config.url = menu::get_mhy_client_url(func.clone());
url_parse = menu::get_mhy_client_url(func.clone());
}
// if pc {
// let user_agent = mhy_window_config.user_agent.as_ref().unwrap();
// let version = user_agent.split("/").last().unwrap();
// mhy_window_config.user_agent = Some(format!("Mozilla/5.0 (Windows NT 10.0; Win64; x64) miHoYoBBS/{}", version));
// }
if func == "birthday"
|| func == "web_act"
|| url.starts_with("https://webstatic.mihoyo.com/ys/event/e20220303-birthday/index.html")
{
mhy_window_config.width = 1280.0;
mhy_window_config.height = 720.0;
win_width = 1280.0;
win_height = 720.0;
}
let window_get = handle.get_window("mhy_client");
if window_get.is_some() {
let mhy_client = window_get.unwrap();
mhy_client.on_window_event(move |e| {
if let WindowEvent::Destroyed = e {
let future = create_mhy_client(handle.clone(), func.clone(), url.clone());
block_on(future);
}
});
mhy_client.close().unwrap();
let window_find = handle.get_webview_window("mhy_client");
if window_find.is_some() {
window_find.unwrap().destroy().unwrap();
return;
}
WindowBuilder::from_config(&handle, mhy_window_config)
.menu(menu::create_mhy_menu(func))
let trans_size = utils::get_window_size(handle.clone(), win_width, win_height);
WebviewWindowBuilder::new(&handle, "mhy_client", url_parse)
.inner_size(trans_size.0, trans_size.1)
.title("米游社")
.center()
.user_agent(win_ua)
.menu(menu::create_mhy_menu(handle.clone()))
.on_menu_event(move |app, event| menu::handle_menu_event(app, event))
.build()
.expect("failed to create mhy_client")
.on_menu_event(move |event| menu::handle_menu_event(handle.clone(), event));
.unwrap();
}

View 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)
}

View File

@@ -1,74 +1,57 @@
//! @file src/commands.rs
//! @desc 命令模块,负责处理命令
//! @since Beta v0.4.3
//! @since Beta v0.5.1
use tauri::{Manager, WindowBuilder};
use tauri_utils::config::WindowConfig;
use tauri::{AppHandle, Emitter, Manager, WebviewWindowBuilder};
use tauri_utils::config::{WebviewUrl, WindowConfig};
// 放一个常数,用来判断应用是否初始化
static mut APP_INITIALIZED: bool = false;
static mut DEEP_LINK_REGISTERED: bool = false;
// 初始化应用
#[tauri::command]
pub async fn init_app(app_handle: tauri::AppHandle) {
pub async fn init_app(app_handle: AppHandle) {
unsafe {
if APP_INITIALIZED == true && DEEP_LINK_REGISTERED == true {
if APP_INITIALIZED == true {
return;
}
}
dbg!("init_app");
let _mhy = app_handle.get_window("mhy_client");
if _mhy.is_some() {
std::thread::sleep(std::time::Duration::from_millis(1000));
_mhy.unwrap().close().unwrap();
}
app_handle.emit_all("initApp", ()).unwrap();
app_handle.emit("initApp", ()).unwrap();
unsafe {
APP_INITIALIZED = true;
}
}
// 注册deep link
#[tauri::command]
pub async fn register_deep_link(app_handle: tauri::AppHandle) {
unsafe {
if DEEP_LINK_REGISTERED == true {
return;
}
}
dbg!("register_deep_link");
tauri_plugin_deep_link::register("teyvatguide", move |request| {
dbg!(&request);
app_handle.emit_all("active_deep_link", request).unwrap();
})
.unwrap();
unsafe {
DEEP_LINK_REGISTERED = true;
}
}
// 创建窗口
#[tauri::command]
pub async fn create_window(app_handle: tauri::AppHandle, label: String, mut option: WindowConfig) {
let window_old = app_handle.get_window(&label);
option.label = label.clone();
if window_old.is_some() {
dbg!("window exists");
window_old.unwrap().close().unwrap();
pub async fn create_window(
app_handle: AppHandle,
label: String,
url: String,
option: WindowConfig,
) {
let window_find = app_handle.get_webview_window(&label);
if window_find.is_some() {
window_find.unwrap().destroy().unwrap();
return;
}
let window_new =
Some(WindowBuilder::from_config(&app_handle, option).build().expect("failed to create window"));
window_new.unwrap();
let url_parse = WebviewUrl::App(url.parse().unwrap());
WebviewWindowBuilder::new(&app_handle, &label, url_parse)
.inner_size(option.width, option.height)
.resizable(option.resizable)
.visible(option.visible)
.title(option.title)
.center()
.build()
.unwrap();
}
// 执行 js
#[tauri::command]
pub async fn execute_js(app_handle: tauri::AppHandle, label: String, js: String) {
let window = app_handle.get_window(&label);
pub async fn execute_js(app_handle: AppHandle, label: String, js: String) {
let window = app_handle.get_webview_window(&label);
if window.is_some() {
window.unwrap().eval(&js).ok().unwrap();
window.unwrap().eval(&js).unwrap();
}
}

View File

@@ -1,46 +1,54 @@
//! @file src/main.rs
//! @desc 主模块,用于启动应用
//! @since Beta v0.4.3
//! @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;
mod utils;
// 窗口事件处理
fn window_event_handler(event: tauri::GlobalWindowEvent) -> () {
match event.event() {
fn window_event_handler(app: &tauri::Window, event: &tauri::WindowEvent) {
match event {
tauri::WindowEvent::CloseRequested { api, .. } => {
api.prevent_close();
let window = event.window().clone();
if window.label() == "TeyvatGuide" {
if app.label() == "TeyvatGuide" {
// 子窗口 label 的数组
const SUB_WINDOW_LABELS: [&str; 3] = ["Sub_window", "Dev_JSON", "mhy_client"];
for label in SUB_WINDOW_LABELS.iter() {
let sub = window.get_window(label);
let sub = app.get_webview_window(label);
if sub.is_some() {
sub.unwrap().close().unwrap();
sub.unwrap().destroy().unwrap();
}
}
}
window.close().unwrap();
app.destroy().unwrap();
}
_ => {}
}
}
fn main() {
tauri_plugin_deep_link::prepare("teyvatguide");
tauri::Builder::default()
.on_window_event(|event| window_event_handler(event))
.plugin(plugins::build_sql_plugin())
.on_window_event(move |app, event| window_event_handler(app, event))
.plugin(tauri_plugin_single_instance::init(|app, argv, _cwd| {
app.emit("active_deep_link", argv).unwrap();
}))
.plugin(tauri_plugin_deep_link::init())
.plugin(tauri_plugin_dialog::init())
.plugin(tauri_plugin_fs::init())
.plugin(tauri_plugin_http::init())
.plugin(tauri_plugin_os::init())
.plugin(tauri_plugin_process::init())
.plugin(tauri_plugin_shell::init())
.plugin(tauri_plugin_sql::Builder::default().build())
.plugin(plugins::build_log_plugin())
.setup(|_app| {
let _window = _app.get_window("TeyvatGuide");
let _window = _app.get_webview_window("TeyvatGuide");
#[cfg(debug_assertions)]
if _window.is_some() {
_window.unwrap().open_devtools();
@@ -49,7 +57,6 @@ fn main() {
})
.invoke_handler(tauri::generate_handler![
commands::init_app,
commands::register_deep_link,
commands::create_window,
commands::execute_js,
commands::get_dir_size,

View File

@@ -1,32 +1,28 @@
//! @file src/plugins.rs
//! @desc 插件模块,用于注册插件
//! @since Beta v0.4.4
//! @since Beta v0.5.2
use super::utils;
use log::LevelFilter;
use tauri::plugin::TauriPlugin;
use tauri::Runtime;
use tauri_plugin_log::{LogTarget, TimezoneStrategy};
use tauri_plugin_sql::PluginConfig;
// sqlite 插件
pub fn build_sql_plugin<R: Runtime>() -> TauriPlugin<R, Option<PluginConfig>> {
tauri_plugin_sql::Builder::default().build()
}
use tauri_plugin_log::{Target, TargetKind, TimezoneStrategy};
// 日志插件
pub fn build_log_plugin<R: Runtime>() -> TauriPlugin<R> {
if cfg!(debug_assertions) {
return tauri_plugin_log::Builder::default()
.targets([LogTarget::Stdout])
.targets([Target::new(TargetKind::Webview)])
.timezone_strategy(TimezoneStrategy::UseLocal)
.level(LevelFilter::Debug)
.build();
}
return tauri_plugin_log::Builder::default()
.targets([LogTarget::LogDir, LogTarget::Stdout])
.targets([
Target::new(TargetKind::Webview),
Target::new(TargetKind::LogDir { file_name: utils::get_current_date().into() }),
])
.timezone_strategy(TimezoneStrategy::UseLocal)
.level(LevelFilter::Info)
.log_name(utils::get_current_date())
.build();
}

View File

@@ -1,159 +1,42 @@
{
"productName": "TeyvatGuide",
"identifier": "TeyvatGuide",
"version": "0.5.2",
"build": {
"beforeDevCommand": "pnpm vite:dev",
"beforeBuildCommand": "pnpm vite:build",
"devPath": "http://localhost:4000",
"distDir": "../dist",
"withGlobalTauri": true
"devUrl": "http://localhost:4000",
"frontendDist": "../dist"
},
"package": {
"productName": "TeyvatGuide",
"version": "0.4.8"
},
"tauri": {
"allowlist": {
"all": false,
"fs": {
"all": false,
"exists": true,
"readFile": true,
"readDir": true,
"writeFile": true,
"createDir": true,
"removeDir": true,
"removeFile": true,
"scope": ["**", "**/*"]
},
"http": {
"all": false,
"request": true,
"scope": ["http://**", "https://**"]
},
"dialog": {
"all": false,
"open": true,
"save": true,
"message": true
},
"clipboard": {
"all": true
},
"path": {
"all": true
},
"window": {
"all": false,
"close": true,
"hide": true,
"setAlwaysOnTop": true,
"setFocus": true,
"setFullscreen": true,
"setTitle": true,
"show": true,
"unminimize": true
},
"os": {
"all": true
},
"process": {
"all": false,
"exit": true
},
"shell": {
"all": false,
"open": true,
"execute": true,
"scope": [
{
"name": "win_open",
"cmd": "explorer",
"args": true
},
{
"name": "mac_open",
"cmd": "open",
"args": true
}
]
"bundle": {
"active": true,
"icon": [
"icons/128x128.png",
"icons/128x128@2x.png",
"icons/32x32.png",
"icons/Square107x107Logo.png",
"icons/Square142x142Logo.png",
"icons/Square150x150Logo.png",
"icons/Square284x284Logo.png",
"icons/Square30x30Logo.png",
"icons/Square310x310Logo.png",
"icons/Square44x44Logo.png",
"icons/Square71x71Logo.png",
"icons/Square89x89Logo.png",
"icons/StoreLogo.png",
"icons/icon.ico",
"icons/icon.png"
],
"targets": ["msi", "app", "dmg"],
"windows": {
"wix": {
"language": "zh-CN"
}
},
"bundle": {
"active": true,
"icon": [
"icons/128x128.png",
"icons/128x128@2x.png",
"icons/32x32.png",
"icons/Square107x107Logo.png",
"icons/Square142x142Logo.png",
"icons/Square150x150Logo.png",
"icons/Square284x284Logo.png",
"icons/Square30x30Logo.png",
"icons/Square310x310Logo.png",
"icons/Square44x44Logo.png",
"icons/Square71x71Logo.png",
"icons/Square89x89Logo.png",
"icons/StoreLogo.png",
"icons/icon.ico",
"icons/icon.png"
],
"identifier": "TeyvatGuide",
"targets": ["msi", "app", "dmg"],
"windows": {
"wix": {
"language": "zh-CN"
}
},
"macOS": {}
},
"security": {
"dangerousRemoteDomainIpcAccess": [
{
"domain": "act.mihoyo.com",
"windows": ["mhy_client"],
"enableTauriAPI": true
},
{
"domain": "m.miyoushe.com",
"windows": ["mhy_client"],
"enableTauriAPI": true
},
{
"domain": "qaa.miyoushe.com",
"windows": ["mhy_client"],
"enableTauriAPI": true
},
{
"domain": "user.mihoyo.com",
"windows": ["mhy_client"],
"enableTauriAPI": true
},
{
"domain": "webstatic.mihoyo.com",
"windows": ["mhy_client"],
"enableTauriAPI": true
},
{
"domain": "bbs.mihoyo.com",
"windows": ["mhy_client"],
"enableTauriAPI": true
},
{
"domain": "api-takumi-record.mihoyo.com",
"windows": ["mhy_client"],
"enableTauriAPI": true
}
],
"csp": null
},
"updater": {
"active": false,
"dialog": true,
"endpoints": [
"https://raw.fastgit.org/BTMuli/TeyvatGuide/master/latest.json",
"https://raw.kgithub.com/BTMuli/TeyvatGuide/master/latest.json"
],
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDg2MkVGRjMxQzJDQzFBNTkKUldSWkdzekNNZjh1aHJGRXBEOGtwbUxLaU1wdWNVeUJaeGhoV2ZlZ3VlYmQ0b2tYZWQwODdnTHkK"
},
"macOS": {}
},
"app": {
"withGlobalTauri": true,
"windows": [
{
"fullscreen": false,
@@ -162,22 +45,20 @@
"label": "TeyvatGuide",
"width": 1600,
"height": 900,
"center": true
},
{
"fullscreen": false,
"resizable": false,
"title": "米游社",
"label": "mhy_client",
"url": "",
"userAgent": "Mozilla/5.0 (Linux; Android 12) Mobile miHoYoBBS/2.70.1",
"visible": false,
"width": 400,
"height": 800,
"center": true,
"decorations": true,
"closable": true
"visible": false
}
]
],
"security": {
"capabilities": ["TeyvatGuide", "Mys", "SubWindow", "DevJson"]
}
},
"plugins": {
"deep-link": {
"desktop": {
"schemes": ["teyvatguide"]
},
"mobile": []
}
}
}

View File

@@ -11,22 +11,24 @@
</template>
<script lang="ts" setup>
import { app, event, fs, tauri, window as TauriWindow } from "@tauri-apps/api";
import { UnlistenFn } from "@tauri-apps/api/helpers/event";
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";
import { computed, onBeforeMount, onMounted, onUnmounted, ref } from "vue";
import { useRouter } from "vue-router";
import TBackTop from "./components/app/t-backTop.vue";
import TSidebar from "./components/app/t-sidebar.vue";
import showConfirm from "./components/func/confirm";
import showSnackbar from "./components/func/snackbar";
import TGSqlite from "./plugins/Sqlite";
import { useAppStore } from "./store/modules/app";
import { useUserStore } from "./store/modules/user";
import { getBuildTime } from "./utils/TGBuild";
import TGLogger from "./utils/TGLogger";
import TGRequest from "./web/request/TGRequest";
import showConfirm from "./components/func/confirm.js";
import showSnackbar from "./components/func/snackbar.js";
import TGSqlite from "./plugins/Sqlite/index.js";
import { useAppStore } from "./store/modules/app.js";
import { useUserStore } from "./store/modules/user.js";
import { getBuildTime } from "./utils/TGBuild.js";
import TGLogger from "./utils/TGLogger.js";
import TGRequest from "./web/request/TGRequest.js";
const appStore = useAppStore();
const userStore = storeToRefs(useUserStore());
@@ -41,21 +43,55 @@ let themeListener: UnlistenFn;
let urlListener: UnlistenFn;
onBeforeMount(async () => {
const win = TauriWindow.getCurrent();
const win = webviewWindow.getCurrentWebviewWindow();
isMain.value = win.label === "TeyvatGuide";
if (isMain.value) {
const title = "Teyvat Guide v" + (await app.getVersion()) + " Beta";
await win.setTitle(title);
await listenOnInit();
await tauri.invoke("init_app");
listenOnInit();
await core.invoke("init_app");
urlListener = await getDeepLink();
}
await checkResize();
await win.show();
});
onMounted(async () => {
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 = await event.listen("readTheme", async (e) => {
const themeGet = <string>e.payload;
themeListener = event.listen("readTheme", async (e: Event<string>) => {
const themeGet = e.payload;
if (theme.value !== themeGet) {
theme.value = themeGet;
document.documentElement.className = theme.value;
@@ -64,9 +100,9 @@ onMounted(async () => {
});
// 启动后只执行一次的监听
async function listenOnInit(): Promise<void> {
await event.listen("initApp", async () => {
await tauri.invoke("register_deep_link");
function listenOnInit(): void {
console.info("[App][listenOnInit] 监听初始化事件!");
event.listen("initApp", async () => {
await checkAppLoad();
await checkDeviceFp();
try {
@@ -78,7 +114,6 @@ async function listenOnInit(): Promise<void> {
}
await checkUpdate();
});
return;
}
async function checkAppLoad(): Promise<void> {
@@ -108,15 +143,18 @@ async function resetDB(): Promise<void> {
// 检测 deviceFp
async function checkDeviceFp(): Promise<void> {
const appData = await TGSqlite.getAppData();
const deviceInfo = appData.find((item) => item.key === "deviceInfo")?.value;
const deviceLocal = appStore.deviceInfo;
if (deviceInfo === undefined) {
if (deviceLocal.device_fp === "0000000000000")
const deviceFind = appData.find((item) => item.key === "deviceInfo");
if (typeof deviceFind === "undefined") {
if (deviceLocal.device_fp === "0000000000000") {
appStore.deviceInfo = await TGRequest.Device.getFp(appStore.deviceInfo);
}
await TGSqlite.saveAppData("deviceInfo", JSON.stringify(deviceLocal));
return;
}
if (JSON.parse(deviceInfo) !== deviceLocal) appStore.deviceInfo = JSON.parse(deviceInfo);
if (JSON.parse(deviceFind.value) !== deviceLocal) {
appStore.deviceInfo = JSON.parse(deviceFind.value);
}
}
// 检测 ck,info 数据
@@ -138,9 +176,9 @@ async function checkUserLoad(): Promise<void> {
const infoLocal = userStore.briefInfo.value;
const appData = await TGSqlite.getAppData();
const infoDB = appData.find((item) => item.key === "userInfo")?.value;
if (infoDB === undefined && JSON.stringify(infoLocal) !== "{}") {
if (typeof infoDB === "undefined" && JSON.stringify(infoLocal) !== "{}") {
await TGSqlite.saveAppData("userInfo", JSON.stringify(infoLocal));
} else if (infoDB !== undefined && infoLocal !== JSON.parse(infoDB)) {
} else if (typeof infoDB !== "undefined" && infoLocal !== JSON.parse(infoDB)) {
userStore.briefInfo.value = JSON.parse(infoDB);
console.info("briefInfo 数据已更新!");
}
@@ -158,64 +196,77 @@ async function checkUserLoad(): Promise<void> {
}
if (accountDB !== accountLocal) userStore.account.value = accountDB;
const userDir = appData.find((item) => item.key === "userDir")?.value;
if (userDir === undefined) {
if (typeof userDir === "undefined") {
await TGSqlite.saveAppData("userDir", appStore.userDir);
return;
}
if (userDir !== appStore.userDir) appStore.userDir = userDir;
await fs.createDir(appStore.userDir, { recursive: true });
await mkdir(appStore.userDir, { recursive: true });
}
async function getDeepLink(): Promise<UnlistenFn> {
return await event.listen("active_deep_link", async (e) => {
const windowGet = new TauriWindow.WebviewWindow("TeyvatGuide");
return await event.listen("active_deep_link", async (e: Event<string>) => {
const windowGet = new webviewWindow.WebviewWindow("TeyvatGuide");
if (await windowGet.isMinimized()) {
await windowGet.unminimize();
}
await windowGet.setFocus();
if (typeof e.payload !== "string") {
const payload = parseDeepLink(e.payload);
if (payload === false) {
showSnackbar({
text: "无效的 deep link",
color: "error",
timeout: 3000,
});
await TGLogger.Error(`[App][getDeepLink] 无效的 deep link ${JSON.stringify(e)}`);
await TGLogger.Error(`[App][getDeepLink] 无效的 deep link ${JSON.stringify(e.payload)}`);
return;
}
await TGLogger.Info(`[App][getDeepLink] ${e.payload}`);
if (e.payload === "") return;
if (
e.payload.startsWith("teyvatguide://import_uigf") ||
e.payload.startsWith("teyvatguide://import_uiaf")
) {
await toUIAF(e.payload);
return;
}
if (e.payload.startsWith("router?path=")) {
const routerPath = e.payload.replace("router?path=", "");
if (router.currentRoute.value.path === routerPath) {
showSnackbar({
text: "已在当前页面!",
color: "warn",
timeout: 3000,
});
return;
}
await router.push(routerPath);
return;
}
showSnackbar({
text: "无效的 deep link",
color: "error",
timeout: 3000,
});
await handleDeepLink(payload);
});
}
function parseDeepLink(payload: string | string[]): string | false {
try {
if (typeof payload === "string") return payload;
if (payload.length < 2) return "teyvatguide://";
return payload[1];
} catch (e) {
if (e instanceof Error) {
TGLogger.Error(`[App][parseDeepLink] ${e.name}: ${e.message}`);
} else console.error(e);
return false;
}
}
async function handleDeepLink(payload: string): Promise<void> {
if (payload === "" || payload === "teyvatguide://") return;
if (
payload.startsWith("teyvatguide://import_uigf") ||
payload.startsWith("teyvatguide://import_uiaf")
) {
await toUIAF(payload);
return;
}
if (payload.startsWith("router?path=")) {
const routerPath = payload.replace("router?path=", "");
if (router.currentRoute.value.path === routerPath) {
showSnackbar({
text: "已在当前页面!",
color: "warn",
timeout: 3000,
});
return;
}
await router.push(routerPath);
return;
}
}
async function toUIAF(link: string) {
const url = new URL(link);
const app = url.searchParams.get("app");
if (app === null || app === "") {
if (app == null || app === "") {
await router.push("/achievements");
} else {
await router.push("/achievements/?app=" + app);

View File

@@ -1,7 +1,7 @@
/*
* @file assets/css/post-parser.css
* @description 游戏公告解析 css
* @since Beta v0.4.5
* @since Beta v0.5.0
*/
.anno-body {
@@ -37,15 +37,12 @@
line-height: 2;
}
.anno-link-icon {
width: 18px;
height: 18px;
color: #00c3ff;
}
.anno-content :deep(a) {
color: #00c3ff;
text-decoration: none;
cursor: pointer;
text-decoration: underline solid #00c3ff;
text-underline-position: under;
transform: translateY(2px);
}
.anno-content :deep(p) {

View File

@@ -6,8 +6,7 @@
</transition>
</template>
<script lang="ts" setup>
// vue
import { ref, onMounted } from "vue";
import { ref, onMounted, onUnmounted } from "vue";
const scrollTop = ref(0); // 滚动条距离顶部的距离
const canTop = ref(false); // 默认不显示
@@ -45,6 +44,11 @@ function handleScrollTop(): void {
onMounted(() => {
window.addEventListener("scroll", handleScroll);
});
// 销毁监听
onUnmounted(() => {
window.removeEventListener("scroll", handleScroll);
});
</script>
<style scoped>

View File

@@ -160,7 +160,6 @@
value="wiki-material"
:link="true"
href="/wiki/material"
v-if="isDevEnv"
>
<template #prepend>
<img src="/source/UI/wikiGCG.webp" alt="gcgIcon" class="side-icon-menu" />
@@ -242,16 +241,16 @@
</template>
<script lang="ts" setup>
import { event, window as TauriWindow } from "@tauri-apps/api";
import { UnlistenFn } from "@tauri-apps/api/helpers/event";
import { event, webviewWindow } from "@tauri-apps/api";
import { UnlistenFn, Event } from "@tauri-apps/api/event";
import { storeToRefs } from "pinia";
import { computed, onMounted, onUnmounted, ref, watch } from "vue";
import { useAppStore } from "../../store/modules/app";
import { useUserStore } from "../../store/modules/user";
import mhyClient from "../../utils/TGClient";
import TGLogger from "../../utils/TGLogger";
import showSnackbar from "../func/snackbar";
import { useAppStore } from "../../store/modules/app.js";
import { useUserStore } from "../../store/modules/user.js";
import mhyClient from "../../utils/TGClient.js";
import TGLogger from "../../utils/TGLogger.js";
import showSnackbar from "../func/snackbar.js";
const appStore = useAppStore();
const userStore = storeToRefs(useUserStore());
@@ -298,11 +297,11 @@ function collapse(): void {
let themeListener: UnlistenFn;
onMounted(async () => {
themeListener = await event.listen("readTheme", (e) => {
const theme = <string>e.payload;
themeListener = await event.listen("readTheme", (e: Event<string>) => {
const theme = e.payload;
themeGet.value = theme === "default" ? "default" : "dark";
});
if (TauriWindow.getCurrent().label === "TeyvatGuide") {
if (webviewWindow.getCurrentWebviewWindow().label === "TeyvatGuide") {
await mhyClient.run();
}
if (userStore.briefInfo.value && userStore.briefInfo.value.nickname) {
@@ -329,9 +328,7 @@ function login(): void {
});
}
onUnmounted(() => {
themeListener();
});
onUnmounted(() => themeListener());
</script>
<style lang="css" scoped>

View File

@@ -9,10 +9,10 @@
</template>
<script lang="ts" setup>
import { event } from "@tauri-apps/api";
import { UnlistenFn } from "@tauri-apps/api/helpers/event";
import { UnlistenFn, Event } from "@tauri-apps/api/event";
import { computed, onMounted, onUnmounted } from "vue";
import { useAppStore } from "../../store/modules/app";
import { useAppStore } from "../../store/modules/app.js";
const appStore = useAppStore();
const themeGet = computed({
@@ -25,8 +25,8 @@ const themeGet = computed({
});
let themeListener: UnlistenFn;
onMounted(async () => {
themeListener = await listenOnTheme();
onMounted(() => {
themeListener = listenOnTheme();
});
async function switchTheme(): Promise<void> {
@@ -34,18 +34,14 @@ async function switchTheme(): Promise<void> {
await event.emit("readTheme", themeGet.value);
}
async function listenOnTheme(): Promise<UnlistenFn> {
return await event.listen<string>("readTheme", (e) => {
function listenOnTheme(): UnlistenFn {
return event.listen("readTheme", (e: Event<string>) => {
const theme = e.payload;
themeGet.value = theme === "default" ? "default" : "dark";
});
}
onUnmounted(() => {
if (themeListener) {
themeListener();
}
});
onUnmounted(() => themeListener());
</script>
<style lang="css" scoped>
.switch-box {

View File

@@ -25,7 +25,7 @@
import { app } from "@tauri-apps/api";
import { computed, onMounted, ref } from "vue";
import { useAppStore } from "../../store/modules/app";
import { useAppStore } from "../../store/modules/app.js";
const appStore = useAppStore();
const versionApp = ref<string>();
@@ -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>

View File

@@ -2,63 +2,103 @@
<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>
&emsp;
<v-icon @click="openPath('user')" style="cursor: pointer" title="打开用户数据目录"
>mdi-folder-open
</v-icon>
&emsp;
<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>
&emsp;
<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>
&emsp;
<v-icon @click="openPath('log')" style="cursor: pointer" title="打开日志目录"
>mdi-folder-open
</v-icon>
&emsp;
<v-icon @click="copyPath('log')" style="cursor: pointer" title="复制日志目录路径"
>mdi-content-copy
</v-icon>
</template>
</v-list-item>
</v-list>
</template>
<script lang="ts" setup>
import { dialog, fs, path } from "@tauri-apps/api";
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";
import { useAppStore } from "../../store/modules/app";
import { backUpUserData } from "../../utils/dataBS";
import TGShell from "../../utils/TGShell";
import showConfirm from "../func/confirm";
import showSnackbar from "../func/snackbar";
import TGSqlite from "../../plugins/Sqlite/index.js";
import { useAppStore } from "../../store/modules/app.js";
import { backUpUserData } from "../../utils/dataBS.js";
import TGShell from "../../utils/TGShell.js";
import showConfirm from "../func/confirm.js";
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",
@@ -66,7 +106,7 @@ async function confirmCUD(): Promise<void> {
});
return;
}
const dir = await dialog.open({
const dir: string | null = await open({
directory: true,
defaultPath: oriDir,
multiple: false,
@@ -78,13 +118,6 @@ async function confirmCUD(): Promise<void> {
});
return;
}
if (typeof dir !== "string") {
showSnackbar({
color: "error",
text: "路径错误!",
});
return;
}
if (dir === oriDir) {
showSnackbar({
color: "warn",
@@ -95,11 +128,21 @@ async function confirmCUD(): Promise<void> {
appStore.userDir = dir;
await TGSqlite.saveAppData("userDir", dir);
await backUpUserData(dir);
await fs.removeDir(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);
@@ -126,11 +169,11 @@ async function confirmCLD(): Promise<void> {
return;
}
const logDir = appStore.logDir;
const files = await fs.readDir(logDir);
const files = await readDir(logDir);
const delFiles = files.filter((file) => {
// yyyy-mm-dd.log
const reg = /(\d{4}-\d{2}-\d{2}\.log)/;
const match = file.path.match(reg);
const match = file.name.match(reg);
if (!Array.isArray(match) || match.length < 1) return false;
const date = match[1].replace(".log", "");
return isOverWeek(date);
@@ -143,7 +186,8 @@ async function confirmCLD(): Promise<void> {
return;
}
for (const file of delFiles) {
await fs.removeFile(file.path);
const filePath = `${logDir}/${file.name}`;
await remove(filePath);
}
showSnackbar({
text: `已清理 ${delFiles.length} 个日志文件!`,

View File

@@ -2,7 +2,7 @@
<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 title="Tauri 版本" @click="toOuter('https://next--tauri.netlify.app/')">
<v-list-item title="Tauri 版本" @click="toOuter('https://v2.tauri.app/')">
<template #prepend>
<v-img class="config-icon" src="/platforms/tauri.webp" alt="Tauri" />
</template>
@@ -73,12 +73,13 @@
</v-list>
</template>
<script lang="ts" setup>
import { app, os } from "@tauri-apps/api";
import { app } from "@tauri-apps/api";
import { platform, version } from "@tauri-apps/plugin-os";
import { onMounted, ref } from "vue";
import TGSqlite from "../../plugins/Sqlite";
import { useAchievementsStore } from "../../store/modules/achievements";
import showSnackbar from "../func/snackbar";
import TGSqlite from "../../plugins/Sqlite/index.js";
import { useAchievementsStore } from "../../store/modules/achievements.js";
import showSnackbar from "../func/snackbar.js";
const achievementsStore = useAchievementsStore();
@@ -92,25 +93,25 @@ const dbInfo = ref<Array<TGApp.Sqlite.AppData.Item>>([]);
onMounted(async () => {
versionApp.value = await app.getVersion();
versionTauri.value = await app.getTauriVersion();
osPlatform.value = `${await os.platform()}`;
osPlatform.value = platform();
switch (osPlatform.value) {
case "linux":
iconPlatform.value = "mdi-linux";
break;
case "darwin":
case "macos":
iconPlatform.value = "mdi-apple";
break;
case "ios":
iconPlatform.value = "mdi-apple-ios";
break;
case "win32":
case "windows":
iconPlatform.value = "mdi-microsoft-windows";
break;
default:
iconPlatform.value = "mdi-desktop-classic";
break;
}
osVersion.value = await os.version();
osVersion.value = version();
try {
dbInfo.value = await TGSqlite.getAppData();
} catch (e) {

View File

@@ -1,5 +1,4 @@
<template>
<ToGameLogin v-model="scan" @success="refreshUser" />
<v-card class="tcu-box">
<template #prepend>
<v-avatar :image="userInfo.avatar" />
@@ -9,28 +8,38 @@
<template #text>{{ userInfo.desc }}</template>
<template #actions>
<v-spacer />
<v-btn variant="outlined" @click="scan = true" icon="mdi-qrcode-scan" />
<v-btn v-if="false" variant="outlined" @click="toWebLogin" icon="mdi-web" />
<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 { event, window as windowTauri } from "@tauri-apps/api";
import type { UnlistenFn } from "@tauri-apps/api/helpers/event";
import { UnlistenFn } from "@tauri-apps/api/event";
import { storeToRefs } from "pinia";
import { onMounted, onUnmounted, ref, watch } from "vue";
import TGSqlite from "../../plugins/Sqlite";
import { useAppStore } from "../../store/modules/app";
import { useUserStore } from "../../store/modules/user";
import TGClient from "../../utils/TGClient";
import TGLogger from "../../utils/TGLogger";
import { getDeviceFp } from "../../web/request/getDeviceFp";
import TGRequest from "../../web/request/TGRequest";
import showConfirm from "../func/confirm";
import showSnackbar from "../func/snackbar";
import ToGameLogin from "../overlay/to-gameLogin.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";
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";
interface TcUserBadgeEmits {
(e: "loadOuter", v: TGApp.Component.Loading.EmitParams): void;
@@ -42,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",
@@ -64,175 +72,63 @@ watch(userStore.briefInfo, (v) => {
}
});
async function toWebLogin(): Promise<void> {
const confirm = await showConfirm({
title: "请在子窗口中完成登录操作",
text: "请操作完成后点击子窗口的“用户登录”菜单",
async function tryCaptchaLogin(): Promise<void> {
const phone = await showConfirm({
mode: "input",
title: "请输入手机号",
text: "+86",
});
if (!confirm) {
if (!phone) {
showSnackbar({
color: "cancel",
text: "已取消登录",
text: "已取消验证码登录",
});
return;
}
await TGClient.open("config_sign_in", "https://user.mihoyo.com");
signListener = await event.listen("config_user_sign", async (e) => {
if (typeof e.payload !== "string") {
showSnackbar({
color: "error",
text: "登录失败!",
});
return;
}
await getTokenWeb(e.payload);
});
}
async function getTokenWeb(cookie: string): Promise<void> {
await windowTauri.appWindow.setFocus();
emits("loadOuter", { show: true, title: "正在解析 cookie" });
const ck = cookie.split(";").reduce(
(prev, curr) => {
const [key, value] = curr.split("=");
prev[key.trim()] = value.trim();
return prev;
},
<Record<string, string>>{},
);
if (!("login_ticket" in ck) || !("login_uid" in ck)) {
emits("loadOuter", { show: false });
const phoneReg = /^1[3-9]\d{9}$/;
if (!phoneReg.test(phone)) {
showSnackbar({
text: "未检测到 login_ticket, 请确认是否登录成功!",
color: "error",
text: "请输入正确的手机号",
});
try {
setTimeout(() => {
windowTauri.WebviewWindow.getByLabel("mhy_client")?.setFocus();
}, 2000);
} catch (e) {
await TGLogger.Error("[tc-userBadge][getTokenWeb] 无法获取子窗口");
await TGClient.open("config_sign_in", "https://user.mihoyo.com/");
}
return;
}
emits("loadOuter", { show: true, title: "正在获取 token" });
let cookieUser: TGApp.User.Account.Cookie = {
account_id: "",
ltuid: "",
stuid: "",
mid: "",
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: "",
stoken: loginResp.token.token,
ltoken: "",
};
cookieUser.account_id = ck["login_uid"];
cookieUser.ltuid = ck["login_uid"];
cookieUser.stuid = ck["login_uid"];
const tokenRes = await TGRequest.User.byLoginTicket.getTokens(
ck["login_ticket"],
ck["login_uid"],
);
if ("retcode" in tokenRes) {
emits("loadOuter", { show: false });
showSnackbar({
text: "获取 token 失败!",
color: "error",
});
await TGLogger.Error("[tc-userBadge][getTokenWeb] 获取 token 失败");
await TGLogger.Error(`[tc-userBadge][getTokenWeb] ${tokenRes.retcode}: ${tokenRes.message}`);
return;
}
tokenRes.map((i) => {
if (i.name === "ltoken") return (cookieUser.ltoken = i.token);
if (i.name === "stoken") return (cookieUser.stoken = i.token);
showSnackbar({
text: "登录成功,即将刷新用户信息",
color: "success",
});
if (cookieUser.ltoken === "" || cookieUser.stoken === "") {
emits("loadOuter", { show: false });
showSnackbar({
text: "获取 ltoken 或者 stoken 失败!",
color: "error",
});
await TGLogger.Error("[tc-userBadge][getTokenWeb] 获取 ltoken 或者 stoken 失败");
return;
}
const ltokenRes = await TGRequest.User.byLToken.verify(cookieUser.ltoken, cookieUser.ltuid);
if (typeof ltokenRes !== "string") {
showSnackbar({
text: "ltoken 验证失败!",
color: "error",
});
} else {
cookieUser.mid = ltokenRes;
}
if (!cookieUser.stoken.startsWith("v2")) {
const stokenRes = await TGRequest.User.bySToken.update(cookieUser.stoken, cookieUser.stuid);
if ("retcode" in stokenRes) {
showSnackbar({
text: "stoken 更新失败!",
color: "error",
});
await TGLogger.Error("[tc-userBadge][getTokenWeb] stoken 更新失败");
await TGLogger.Error(
`[tc-userBadge][getTokenWeb] ${stokenRes.retcode}: ${stokenRes.message}`,
);
} else {
cookieUser.stoken = stokenRes.token.token;
if (cookieUser.mid === "" && stokenRes.user_info.mid !== "")
cookieUser.mid = stokenRes.user_info.mid;
}
}
const cookieTokenRes = await TGRequest.User.bySToken.getCookieToken(
cookieUser.mid,
cookieUser.stoken,
);
if (typeof cookieTokenRes !== "string") {
showSnackbar({
text: "cookie_token 获取失败!",
color: "error",
});
await TGLogger.Error("[tc-userBadge][getTokenWeb] cookie_token 获取失败");
await TGLogger.Error(
`[tc-userBadge][getTokenWeb] ${cookieTokenRes.retcode}: ${cookieTokenRes.message}`,
);
} else {
cookieUser.cookie_token = cookieTokenRes;
}
userStore.cookie.value = cookieUser;
try {
await windowTauri.WebviewWindow.getByLabel("mhy_client")?.close();
} catch (e) {
await TGLogger.Error("[tc-userBadge][getTokenWeb] 无法获取子窗口");
showSnackbar({
text: "请手动关闭子窗口!",
color: "error",
});
}
signListener();
if (Object.values(cookieUser).some((i) => i === "")) {
showSnackbar({
text: "获取 cookie 失败!部分项为空!",
color: "error",
});
await TGLogger.Error("[tc-userBadge][getTokenWeb] 获取 cookie 失败");
return;
}
await TGSqlite.saveAppData("cookie", JSON.stringify(cookieUser));
const failCount = await refreshUserInfo();
if (failCount > 0) {
showSnackbar({
color: "error",
text: "获取用户信息失败!",
});
} else {
showSnackbar({
text: "登录成功!",
color: "success",
});
appStore.isLogin = true;
}
loading.value = false;
emits("loadOuter", { show: false });
setTimeout(() => {
refreshUser();
}, 1000);
}
async function refreshUser() {
@@ -241,7 +137,7 @@ async function refreshUser() {
await TGLogger.Error("[tc-userBadge][refreshUser] cookie 不存在");
showSnackbar({
color: "error",
text: "扫码登录后才能刷新用户信息!",
text: "登录后才能刷新用户信息!",
});
appStore.isLogin = false;
return;
@@ -321,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({
@@ -392,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();
});

View File

@@ -16,7 +16,7 @@
<script lang="ts" setup>
import { onMounted, onUpdated, ref } from "vue";
import { saveImgLocal } from "../../utils/TGShare";
import { saveImgLocal } from "../../utils/TGShare.js";
interface DucDetailOlbProps {
modelValue: TGApp.Sqlite.Character.RoleConstellation[];

View File

@@ -10,7 +10,7 @@
<script lang="ts" setup>
import { onMounted, onUpdated, ref } from "vue";
import { saveImgLocal } from "../../utils/TGShare";
import { saveImgLocal } from "../../utils/TGShare.js";
interface DucDetailOrtProps {
modelValue: TGApp.Sqlite.Character.RoleTalent[];

View File

@@ -13,17 +13,18 @@
<div class="duc-doc-lt">
<DucDetailOlt :data="props.dataVal" mode="avatar" />
<DucDetailOlt :data="JSON.parse(props.dataVal.weapon)" mode="weapon" />
<v-btn
class="duc-doc-btn"
@click="share"
variant="outlined"
:loading="loading"
data-html2canvas-ignore
>
<v-icon>mdi-share-variant</v-icon>
<span>分享</span>
</v-btn>
<DucDetailRelics :data="props.dataVal.reliquary" />
</div>
<v-btn
class="duc-doc-btn"
@click="share"
variant="outlined"
:loading="loading"
data-html2canvas-ignore
>
<v-icon>mdi-share-variant</v-icon>
<span>分享</span>
</v-btn>
<!-- 右侧天赋 -->
<div class="duc-doc-rt">
<DucDetailOrt :model-value="JSON.parse(props.dataVal.talent)" />
@@ -32,6 +33,11 @@
<div class="duc-doc-lb">
<DucDetailOlb :model-value="JSON.parse(props.dataVal.constellation)" />
</div>
<!-- 底部水印信息 -->
<div class="duc-doc-bt">
UID: {{ props.dataVal.uid }} Updated: {{ props.dataVal.updated }} | Rendered by
TeyvatGuide v{{ version }}
</div>
</div>
<!-- 右侧箭头 -->
<div class="duc-arrow-right" @click="handleClick('right')">
@@ -41,15 +47,17 @@
</TOverlay>
</template>
<script lang="ts" setup>
import { app } from "@tauri-apps/api";
import { computed, onMounted, onUpdated, ref } from "vue";
import TGSqlite from "../../plugins/Sqlite";
import { generateShareImg } from "../../utils/TGShare";
import TGSqlite from "../../plugins/Sqlite/index.js";
import { generateShareImg } from "../../utils/TGShare.js";
import TOverlay from "../main/t-overlay.vue";
import DucDetailOlb from "./duc-detail-olb.vue";
import DucDetailOlt from "./duc-detail-olt.vue";
import DucDetailOrt from "./duc-detail-ort.vue";
import DucDetailRelics from "./duc-detail-relics.vue";
interface DucDetailOverlayProps {
modelValue: boolean;
@@ -64,6 +72,7 @@ type DucDetailOverlayEmits = {
const props = defineProps<DucDetailOverlayProps>();
const emits = defineEmits<DucDetailOverlayEmits>();
const version = await app.getVersion();
const visible = computed({
get: () => props.modelValue,
set: (value) => {
@@ -90,6 +99,8 @@ onMounted(async () => {
});
onUpdated(async () => {
await loadData();
console.log("updated, loadData");
console.log(props.dataVal);
});
async function loadData(): Promise<void> {
@@ -183,6 +194,9 @@ async function share(): Promise<void> {
}
.duc-doc-btn {
position: absolute;
bottom: 90px;
left: 370px;
color: var(--tgc-white-1);
}
@@ -199,4 +213,13 @@ async function share(): Promise<void> {
left: 10px;
padding: 5px;
}
.duc-doc-bt {
position: absolute;
right: 10px;
bottom: -1px;
color: var(--tgc-white-1);
font-size: 12px;
text-shadow: 0 0 2px var(--tgc-dark-2);
}
</style>

View File

@@ -0,0 +1,82 @@
<template>
<div class="duc-dr-box">
<div class="duc-dr-bg">
<img :src="`/icon/relic/${props.pos}.webp`" alt="relic" />
</div>
<div v-if="props.modelValue" class="duc-dr-bg">
<img :src="`/icon/bg/${props.modelValue.star}-Star.webp`" alt="bg" />
</div>
<div v-if="props.modelValue" class="duc-dr-icon">
<img :src="props.modelValue.icon" alt="relic" />
</div>
<div v-if="props.modelValue !== false" class="duc-dr-level">
{{ props.modelValue.level }}
</div>
</div>
</template>
<script lang="ts" setup>
interface ducDetailRelicProps {
modelValue: TGApp.Sqlite.Character.RoleReliquary | false;
pos: number;
}
const props = defineProps<ducDetailRelicProps>();
</script>
<style lang="css" scoped>
.duc-dr-box {
position: relative;
width: 60px;
height: 60px;
border-radius: 50%;
background: rgb(50 56 68/50%);
}
.duc-dr-bg {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.duc-dr-bg:nth-child(1) {
padding: 5px;
}
.duc-dr-bg img {
width: 100%;
height: 100%;
border-radius: 50%;
}
.duc-dr-icon {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.duc-dr-icon img {
width: 100%;
height: 100%;
border-radius: 5px;
}
.duc-dr-level {
position: absolute;
right: -4px;
bottom: -4px;
display: flex;
width: 24px;
height: 24px;
align-items: center;
justify-content: center;
border-radius: 50%;
background: var(--tgc-yellow-3);
color: var(--tgc-white-1);
font-family: var(--font-title);
font-size: 12px;
line-height: 1;
}
</style>

View File

@@ -0,0 +1,45 @@
<template>
<div class="ddr-box">
<DucDetailRelic
v-for="(relic, index) in transData"
:key="index"
:model-value="relic"
:pos="index + 1"
/>
</div>
</template>
<script lang="ts" setup>
import { computed } from "vue";
import DucDetailRelic from "./duc-detail-relic.vue";
interface DucDetailRelicsProps {
data: string;
}
const props = defineProps<DucDetailRelicsProps>();
const transData = computed<Array<TGApp.Sqlite.Character.RoleReliquary | false>>(() => {
if (!props.data || props.data === "") return [false, false, false, false, false];
try {
const parsedData: TGApp.Sqlite.Character.RoleReliquary[] = JSON.parse(props.data);
let relics: Array<TGApp.Sqlite.Character.RoleReliquary | false> = [];
for (let i = 0; i < 5; i++) {
const relic = parsedData.find((relic) => relic.pos === i + 1);
if (relic) relics.push(relic);
else relics.push(false);
}
return relics;
} catch (e) {
console.error(e);
return [false, false, false, false, false];
}
});
</script>
<style lang="css" scoped>
.ddr-box {
display: flex;
flex-wrap: wrap;
justify-content: space-around;
gap: 10px;
}
</style>

View 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;

View 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>

View File

@@ -123,17 +123,17 @@ function loadData(): void {
star4List.value.push({
...item,
gachaCount: reset4count.value,
icon: getIcon(item.itemId, item.type),
icon: getIcon(item.itemId),
});
reset4count.value = 0;
reset4count.value = 1;
} else if (item.rank === "5") {
reset4count.value++;
star5List.value.push({
...item,
gachaCount: reset5count.value,
icon: getIcon(item.itemId, item.type),
icon: getIcon(item.itemId),
});
reset5count.value = 0;
reset5count.value = 1;
}
});
star5avg.value = getStar5Avg();

View File

@@ -2,6 +2,7 @@
<v-chart :option="getPoolData()" autoresize />
</template>
<script lang="ts" setup>
// todo 解决引用问题
import type { EChartsOption } from "echarts";
import { PieChart } from "echarts/charts";
import {
@@ -16,7 +17,7 @@ import { CanvasRenderer } from "echarts/renderers";
import { onMounted, provide } from "vue";
import VChart, { THEME_KEY } from "vue-echarts";
import showSnackbar from "../func/snackbar";
import showSnackbar from "../func/snackbar.js";
// echarts
use([

View File

@@ -54,11 +54,11 @@
import { onMounted, ref } from "vue";
import { useRouter } from "vue-router";
import { AppGachaData, AppCharacterData, AppWeaponData } from "../../data";
import { createPost } from "../../utils/TGWindow";
import { timestampToDate } from "../../utils/toolFunc";
import showConfirm from "../func/confirm";
import showSnackbar from "../func/snackbar";
import { AppGachaData, AppCharacterData, AppWeaponData } from "../../data/index.js";
import { createPost } from "../../utils/TGWindow.js";
import { timestampToDate } from "../../utils/toolFunc.js";
import showConfirm from "../func/confirm.js";
import showSnackbar from "../func/snackbar.js";
import TItembox, { TItemBoxData } from "../main/t-itembox.vue";
interface GroHistoryMap {

View File

@@ -31,7 +31,7 @@
import { onBeforeMount, ref } from "vue";
import { useRouter } from "vue-router";
import TSAvatarBirth from "../../plugins/Sqlite/modules/avatarBirth";
import TSAvatarBirth from "../../plugins/Sqlite/modules/avatarBirth.js";
const isBirthday = ref<boolean>(false);
const router = useRouter();

View File

@@ -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 }}
@@ -45,7 +55,7 @@
<script lang="ts" setup>
import { onMounted, ref, watch } from "vue";
import { AppCalendarData } from "../../data";
import { AppCalendarData } from "../../data/index.js";
import TibCalendarItem from "../itembox/tib-calendar-item.vue";
import ToCalendar from "../overlay/to-calendar.vue";

View File

@@ -56,11 +56,11 @@
import { ref, onMounted, onUnmounted } from "vue";
import { useRouter } from "vue-router";
import Mys from "../../plugins/Mys";
import { useHomeStore } from "../../store/modules/home";
import { createPost, createTGWindow } from "../../utils/TGWindow";
import { stamp2LastTime } from "../../utils/toolFunc";
import showSnackbar from "../func/snackbar";
import Mys from "../../plugins/Mys/index.js";
import { useHomeStore } from "../../store/modules/home.js";
import { createPost, createTGWindow } from "../../utils/TGWindow.js";
import { stamp2LastTime } from "../../utils/toolFunc.js";
import showSnackbar from "../func/snackbar.js";
import TItembox, { TItemBoxData } from "../main/t-itembox.vue";
import THomeCard from "./t-homecard.vue";
@@ -193,7 +193,7 @@ async function toOuter(
});
return;
}
createTGWindow(url, "Sub_window", `Pool_${title}`, 1200, 800, true, true);
await createTGWindow(url, "Sub_window", `Pool_${title}`, 1200, 800, true, true);
}
function getCBox(info: TGApp.App.Character.WikiBriefInfo): TItemBoxData {

View File

@@ -40,9 +40,9 @@
<script lang="ts" setup>
import { ref, onMounted, onUnmounted } from "vue";
import Mys from "../../plugins/Mys";
import { createPost } from "../../utils/TGWindow";
import { stamp2LastTime } from "../../utils/toolFunc";
import Mys from "../../plugins/Mys/index.js";
import { createPost } from "../../utils/TGWindow.js";
import { stamp2LastTime } from "../../utils/toolFunc.js";
import THomeCard from "./t-homecard.vue";

View File

@@ -4,7 +4,7 @@
<script lang="ts" setup>
import { onMounted, ref } from "vue";
import TGSqlite from "../../plugins/Sqlite";
import TGSqlite from "../../plugins/Sqlite/index.js";
import TItemBox from "../main/t-itembox.vue";
import type { TItemBoxData } from "../main/t-itembox.vue";

View File

@@ -4,7 +4,7 @@
<script lang="ts" setup>
import { onMounted, ref } from "vue";
import TGSqlite from "../../plugins/Sqlite";
import TGSqlite from "../../plugins/Sqlite/index.js";
import TItemBox from "../main/t-itembox.vue";
import type { TItemBoxData } from "../main/t-itembox.vue";

View File

@@ -4,11 +4,11 @@
<script setup lang="ts">
import { ref, onMounted, computed } from "vue";
import TGSqlite from "../../plugins/Sqlite";
import TGSqlite from "../../plugins/Sqlite/index.js";
import TItemBox, { type TItemBoxData } from "../main/t-itembox.vue";
interface TibWikiAbyssProps {
modelValue: string;
modelValue: string | number;
}
const props = defineProps<TibWikiAbyssProps>();

View File

@@ -4,7 +4,7 @@
<script setup lang="ts">
import { ref, onMounted, computed } from "vue";
import TGSqlite from "../../plugins/Sqlite";
import TGSqlite from "../../plugins/Sqlite/index.js";
import TItemBox, { type TItemBoxData } from "../main/t-itembox.vue";
interface TibWikiAbyssProps {

Some files were not shown because too many files have changed in this diff Show More