Compare commits
38 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0f546e8a57 | ||
|
|
26070919c9 | ||
|
|
bfd73c3d73 | ||
|
|
415927cd0f | ||
|
|
aa6c75e59f | ||
|
|
c31c86bd56 | ||
|
|
1ee3c35216 | ||
|
|
6676357296 | ||
|
|
3ba72969d9 | ||
|
|
c9ea10f0ef | ||
|
|
17f1b39414 | ||
|
|
70216734a3 | ||
|
|
c25bde1b7a | ||
|
|
beb457a884 | ||
|
|
1b1abb9b88 | ||
|
|
112bd3b938 | ||
|
|
55adf31613 | ||
|
|
cdddbae520 | ||
|
|
c4bd07069c | ||
|
|
71b45584e8 | ||
|
|
e343d37a01 | ||
|
|
7f5ffab2a7 | ||
|
|
d2e6d112d5 | ||
|
|
5b390d3ad1 | ||
|
|
798c4bd7d5 | ||
|
|
7e133176e5 | ||
|
|
87a345ffa7 | ||
|
|
2bafb6d491 | ||
|
|
0f278ad25e | ||
|
|
1da157abbd | ||
|
|
2fa9f88da2 | ||
|
|
9e2f91b4d4 | ||
|
|
2b15e1a351 | ||
|
|
25f95d9f90 | ||
|
|
03e33872c2 | ||
|
|
5b5f96c2d3 | ||
|
|
0005e4eb74 | ||
|
|
89d1b2c6a7 |
168
CHANGELOG.md
@@ -9,160 +9,16 @@ Update: 2025-02-11
|
||||
>
|
||||
> 更新于 `2025-02-11 10:57:49`
|
||||
|
||||
## [0.6.9](https://github.com/BTMuli/TeyvatGuide/releases/v0.6.9) (2025-02-11)
|
||||
## [0.7.0](https://github.com/BTMuli/TeyvatGuide/releases/v0.7.0) (025-02-28)
|
||||
|
||||
- 🍱 更新5.4资源 [`#141`](https://github.com/BTMuli/TeyvatGuide/issues/141)
|
||||
- 🐛 修复米游社子窗口路径解析异常
|
||||
- 🐛 修复特定条件下真境剧诗角色元素图标渲染异常
|
||||
- 🐛 修复名片图鉴浮窗渲染异常
|
||||
- 🚸 调整兑换码入口显示判断逻辑
|
||||
- 🚸 成就导入不允许点击外部取消,调整刷新逻辑
|
||||
- 🚸 下载封面图时显示封面链接
|
||||
- 💄 调整部分UI
|
||||
|
||||
## [0.6.8](https://github.com/BTMuli/TeyvatGuide/releases/v0.6.8) (2025-01-22)
|
||||
|
||||
- ✨ 扫码登录
|
||||
- ✨ 调整祈愿记录图表样式,新增祈愿日历&祈愿堆叠柱状图
|
||||
- ✨ 支持配置帖子详情图像质量,默认80%
|
||||
- ✨ 支持帖子详情图像查看原图,当质量配置为100%时,该按钮不显示
|
||||
- ✨ 深渊上传支持胡桃账户设置
|
||||
- 🚸 降低祈愿全量刷新耗时
|
||||
- 🚸 加快帖子加载速度,降低内存占用
|
||||
- 💄 调整角色卡片样式
|
||||
- 💄 调整角色名片样式,增加描述清晰度
|
||||
- 💄 调整深渊Wiki队伍搭配窗口高度
|
||||
- 💄 mac下不显示分享设置
|
||||
- 💄 调整战绩页新洞天渲染样式
|
||||
- 🔥 深渊Wiki移除第10层数据
|
||||
- 🐛 修复深渊数据恢复异常
|
||||
- 🐛 修复 loading 组件 empty 状态设置异常
|
||||
- ♻️ 优化帖子加载逻辑,当刷新内容不足20条时,下次刷新数量为20-当前数量,如刷新数量为19条,则下次刷新数量为1条
|
||||
- ♻️ 动态获取分区列表&版块列表
|
||||
- ♻️ 重构部分路由处理,当话题/帖子切换分区/版块时,页面刷新不重置当前分区/版块
|
||||
|
||||
## [0.6.7](https://github.com/BTMuli/TeyvatGuide/releases/v0.6.7) (2024-12-31)
|
||||
|
||||
- 🍱 更新5.3版本游戏资源 [`#139`](https://github.com/BTMuli/TeyvatGuide/issues/139)
|
||||
- ✨ 支持嵌入B站视频的分享图渲染
|
||||
- 🐛 修复版块跳转异常
|
||||
- 🐛 修复清理日志异常
|
||||
- 🐛 修复特定帖子`link_card_ids`数据解析异常
|
||||
- 🐛 修复帖子文本居中异常
|
||||
- 🐛 修复侧边栏跳转角色/武器图鉴异常
|
||||
- ✏️ 调整分享图大小计算方式,采用1024进制而非原有的1000进制
|
||||
- 💄 调整用户等级UI,浅色深色下统一为白色文字
|
||||
- 💄 调整回复弹窗位置,上移一段距离以避免底部提示遮挡
|
||||
- 💄 首页素材日历组件只显示日期,移除具体时间
|
||||
- 💄 调整链接卡片提示文字
|
||||
- 💄 调整剧诗角色列表显示UI
|
||||
- 🚸 版块/咨讯页数据获取/刷新显示成功提示
|
||||
- 🚸 首页近期活动卡片Icon补充缺失的点击逻辑
|
||||
- 🚸 调整合集组件改版后的滚动逻辑,更加流畅
|
||||
- 👽️ 由于API变更,调整版块数据获取逻辑
|
||||
- 👽️ 由于返回数据格式变更,调整视频时长的计算逻辑
|
||||
- 👽️ 由于返回数据格式变更,处理帖子内的转义字符
|
||||
- ♻️ loading组件重构,部分页面显示更精准的进度
|
||||
- ♻️ 应用元数据格式重构,剔除冗余数据
|
||||
|
||||
## [0.6.6](https://github.com/BTMuli/TeyvatGuide/releases/v0.6.6) (2024-12-13)
|
||||
|
||||
- 🐛 修复主题切换响应异常
|
||||
- 🐛 修复增量刷新逻辑异常
|
||||
- ⚡️ 显著降低运行内存占用
|
||||
|
||||
## [0.6.5](https://github.com/BTMuli/TeyvatGuide/releases/v0.6.5) (2024-12-11)
|
||||
|
||||
- 🍱 添加下半卡池数据&部分资源
|
||||
- ✨ 帖子内容中涉及的话题链接支持应用内跳转
|
||||
- ♻️ 首页组件加载逻辑重构
|
||||
- ✨ UIGF4导入/导出浮窗,支持自选UID
|
||||
- 💄 调整剧诗部分数据缺失时的显示
|
||||
- 🐛 调整部分UI,修复切换账户后角色详情刷新异常
|
||||
|
||||
## [0.6.4](https://github.com/BTMuli/TeyvatGuide/releases/v0.6.4) (2024-12-03)
|
||||
|
||||
- 🐛 修复子回复渲染异常
|
||||
- ✏️ 祈愿记录将验证非空ID
|
||||
- 🐛 修复战绩分享图渲染异常
|
||||
- 🐛 修复`dialog`组件`input`默认值无效
|
||||
- 🎨 调整帖子查找overlay逻辑
|
||||
- ✨ 分享图生成阈值自定义
|
||||
- 👽️ 全量刷新时清理旧数据,修复由于米哈游数据异常导致的重复数据
|
||||
|
||||
## [0.6.3](https://github.com/BTMuli/TeyvatGuide/releases/v0.6.3) (2024-11-19)
|
||||
|
||||
- 🐛 修复用户战绩角色数据`undefined`
|
||||
- 🐛 修复咨讯页加载更多异常
|
||||
- 🐛 修复验证码登录提示`-100`,数据刷新后若为已登录UID则不会再提示切换
|
||||
- 🐛 修复部分公告渲染异常
|
||||
- 🐛 修复成就页面在存在搜索内容时点击左侧成就系列无响应
|
||||
- ✨ 帖子新增 UID 卡片解析&渲染
|
||||
- ✨ 帖子新增自定义表情解析&渲染
|
||||
- ✨ 真境剧诗适配,新增真境剧诗页面,支持获取&分享&上传(胡桃数据库),可通过深渊页面进入
|
||||
- ✨ 新增话题页面,可通过帖子卡片标签点击或帖子详情顶部标签点击进入
|
||||
- ✨ 更完善的`loading`显示,调整了组件UI
|
||||
- 🍱 更新5.2版本资源 [`#133`](https://github.com/BTMuli/TeyvatGuide/issues/133)
|
||||
- 💄 调整祈愿记录UP四星颜色
|
||||
- 💄 修复帖子页兑换码弹窗高度异常
|
||||
- 💄 调整帖子卡片UI,增加显示帖子话题(如存在),话题&版块支持点击跳转
|
||||
- 💄 调整帖子详情页UI,顶部话题&版块支持点击跳转
|
||||
- 💄 调整帖子显示数量,支持加载更多,默认排序改为`最新回复`,移除`默认排序`,增加`热门`排序
|
||||
- 💄 咨讯、帖子等页面刷新时自动滚动到顶部
|
||||
- 🔥 深渊数据库显示移除第9层统计数据
|
||||
- 👽️ 米游社子窗口增加`genshinnet`域名支持
|
||||
- 🎨 优化帖子详情数据加载的错误处理
|
||||
- ♻️ `snackbar`、`confirm`、`loading`组件重构
|
||||
- ♻️ 请求模块重构
|
||||
|
||||
## [0.6.2](https://github.com/BTMuli/TeyvatGuide/releases/v0.6.2) (2024-10-31)
|
||||
|
||||
- 🐛 修复用户登录状态异常 [`#132`](https://github.com/BTMuli/TeyvatGudie/issues/132)
|
||||
- 💄 帖子子回复取消保持,点击其他隐藏
|
||||
- 💄 调整未登录时的部分内容渲染
|
||||
- 💄 调整保存时图片的hint
|
||||
- 💄 `mac`:修复回顶组件宽度异常
|
||||
- 💄 `mac`:修复视频封面位置异常
|
||||
- 💄 调整角色卡片UI,维持名片比例
|
||||
- ♻️ 深渊数据库重构,概览显示差距
|
||||
- 🍱 更新下半卡池
|
||||
- 👽️ 修正咨讯Api
|
||||
|
||||
## [0.6.1](https://github.com/BTMuli/TeyvatGuide/releases/v0.6.1) (2024-10-22)
|
||||
|
||||
- 🐛 新用户数据库初始化异常 [`#131`](https://github.com/BTMuli/TeyavtGuide/issues/131)
|
||||
- 🐛 修复角色数据未即时刷新
|
||||
- 🐛 修复`openSystemBrowser`回调执行异常
|
||||
- ♻️ 公告卡片组件抽离,支持分享
|
||||
- 🎨 成就页面&名片图鉴页面采用虚拟列表优化性能
|
||||
- 🎨 调整卡片封面加载逻辑
|
||||
- 💄 处理特定情况下的内容溢出
|
||||
- 💄 适配深渊新字段,显示跳过楼层
|
||||
- 💄深渊分享显示应用信息,圣遗物详情推荐属性高亮
|
||||
- 💄调整帖子子窗口副标题样式
|
||||
- 💄调整留影叙佳期选项样式
|
||||
|
||||
## [0.6.0](https://github.com/BTMuli/TeyvatGuide/releases/v0.6.0) (2024-10-09)
|
||||
|
||||
- ✨ 应用支持多账号 [`#126`](https://github.com/BTMuli/TeyvatGuide/issues/126)
|
||||
- ✨ 支持手动输入CK&用户删除
|
||||
- ✨ 帖子卡片支持分享
|
||||
- ✨ 支持官服用户直接启动原神 [`#80`](https://github.com/BTMuli/TeyvatGuide/issues/80)
|
||||
- ♻️ 重构成就表格,支持多存档
|
||||
- ♻️ 重构深渊数据加载逻辑,适配多存档
|
||||
- ♻️ 重构用户登录逻辑及切换
|
||||
- ♻️ 重构祈愿、深渊、角色页面逻辑,支持游戏账号切换
|
||||
- ♻️ 战绩页面适配多账户
|
||||
- 💄 帖子/公告子窗口添加窗口置顶按钮
|
||||
- 💄 调整视频分享截图
|
||||
- 💄 回复分享图忽略导出图标
|
||||
- 💄 显示用户等级
|
||||
- 💄 处理特定情况下的回复内容溢出
|
||||
- 💄 兑换码支持分享,调整了兑换码浮窗UI
|
||||
- 💄 公告对列表进行缩进
|
||||
- 💄 材料Wiki样式优化,支持分类筛选&查询
|
||||
- 💄 材料详情浮窗支持分享
|
||||
- ✏️ JSBridge新增`openSystemBrowser`回调处理
|
||||
- ✏️ 修正公告正则
|
||||
- 👽️ 更新国际服公告Api
|
||||
- 📖 添加 macOS 平台门禁属性导致无法打开应用的修复指引 [`#130`](https://github.com/BTMuli/TeyvatGuide/issues/130)
|
||||
- ✨ 新增无痕浏览配置,默认开启
|
||||
- ✨ 登录状态且关闭无痕浏览时,可对帖子进行点赞操作
|
||||
- ✨ 新增实用脚本页面,支持一键完成米游币每日任务 [`#144`](https://github.com/BTMuli/TeyvatGuide/issues/144)
|
||||
- 🐛 修复公告解析异常
|
||||
- 🐛 修复角色卡片视图(详细)浮窗切换时背景图更新异常
|
||||
- 🐛 修复路由跳转不生效
|
||||
- ♻️ 重构首页素材日历组件生日计算,修复生日计算异常
|
||||
- 🚸 设置页登录二维码支持生成分享图,点击底部图标触发
|
||||
- 💄 调整首页素材日历组件可视页码
|
||||
- 💄 调整部分页面UI
|
||||
- 🍱 更新下半卡池数据
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
Author: 目棃
|
||||
Description: 说明文档
|
||||
Date: 2023-03-05
|
||||
Update: 2025-02-11
|
||||
Update: 2025-02-28
|
||||
---
|
||||
|
||||
> 本文档 [`Frontmatter`](https://github.com/BTMuli/MuCli#Frontmatter) 由 [MuCli](https://github.com/BTMuli/Mucli) 自动生成于 `2023-03-05 14:41:55`
|
||||
>
|
||||
> 更新于 `2025-02-11 10:55:00`
|
||||
> 更新于 `2025-02-28 09:40:24`
|
||||
|
||||
 
|
||||
|
||||
@@ -66,6 +66,7 @@ Game Tool for Genshin Impact player, supports Windows and macOS.
|
||||
- [x] 真境剧诗数据获取
|
||||
- [x] 祈愿数据获取(近一年)
|
||||
- [x] 用户收藏帖子获取
|
||||
- [x] 一键完成米游币每日任务 **需要验证码登录**
|
||||
|
||||
- Wiki 功能:
|
||||
|
||||
@@ -97,8 +98,8 @@ Game Tool for Genshin Impact player, supports Windows and macOS.
|
||||
|
||||
- Changelog: [CHANGELOG](CHANGELOG.md)
|
||||
- 资源来源:[项目资源说明](docs/项目资源说明.md)
|
||||
- UIAF:[UIAF v1.1](docs/UIAF.md)
|
||||
- UIGF:[UIGF v3.0](docs/UIGF3.md),[UIGF v4.0](docs/UIGF.md)
|
||||
- UIAF:[UIAF v1.1](docs/standards/UIAF.md)
|
||||
- UIGF:[UIGF v3.0](docs/standards/UIGF3.md),[UIGF v4.0](docs/standards/UIGF.md)
|
||||
- [macOS 平台门禁属性导致应用无法打开应用的修复指引](docs/macos-gatekeeper/README.md)
|
||||
|
||||
## 特定项目 / Special Project
|
||||
|
||||
168
docs/changelogs/CHANGELOG-v0.6.x.md
Normal file
@@ -0,0 +1,168 @@
|
||||
---
|
||||
Author: 目棃
|
||||
Description: CHANGELOG
|
||||
Date: 2024-10-09
|
||||
Update: 2025-02-11
|
||||
---
|
||||
|
||||
> 本文档 [`Frontmatter`](https://github.com/BTMuli/MuCli#Frontmatter) 由 [MuCli](https://github.com/BTMuli/Mucli) 自动生成于 `2024-10-09 15:51:43`
|
||||
>
|
||||
> 更新于 `2025-02-11 10:57:49`
|
||||
|
||||
## [0.6.9](https://github.com/BTMuli/TeyvatGuide/releases/v0.6.9) (2025-02-11)
|
||||
|
||||
- 🍱 更新5.4资源 [`#141`](https://github.com/BTMuli/TeyvatGuide/issues/141)
|
||||
- 🐛 修复米游社子窗口路径解析异常
|
||||
- 🐛 修复特定条件下真境剧诗角色元素图标渲染异常
|
||||
- 🐛 修复名片图鉴浮窗渲染异常
|
||||
- 🚸 调整兑换码入口显示判断逻辑
|
||||
- 🚸 成就导入不允许点击外部取消,调整刷新逻辑
|
||||
- 🚸 下载封面图时显示封面链接
|
||||
- 💄 调整部分UI
|
||||
|
||||
## [0.6.8](https://github.com/BTMuli/TeyvatGuide/releases/v0.6.8) (2025-01-22)
|
||||
|
||||
- ✨ 扫码登录
|
||||
- ✨ 调整祈愿记录图表样式,新增祈愿日历&祈愿堆叠柱状图
|
||||
- ✨ 支持配置帖子详情图像质量,默认80%
|
||||
- ✨ 支持帖子详情图像查看原图,当质量配置为100%时,该按钮不显示
|
||||
- ✨ 深渊上传支持胡桃账户设置
|
||||
- 🚸 降低祈愿全量刷新耗时
|
||||
- 🚸 加快帖子加载速度,降低内存占用
|
||||
- 💄 调整角色卡片样式
|
||||
- 💄 调整角色名片样式,增加描述清晰度
|
||||
- 💄 调整深渊Wiki队伍搭配窗口高度
|
||||
- 💄 mac下不显示分享设置
|
||||
- 💄 调整战绩页新洞天渲染样式
|
||||
- 🔥 深渊Wiki移除第10层数据
|
||||
- 🐛 修复深渊数据恢复异常
|
||||
- 🐛 修复 loading 组件 empty 状态设置异常
|
||||
- ♻️ 优化帖子加载逻辑,当刷新内容不足20条时,下次刷新数量为20-当前数量,如刷新数量为19条,则下次刷新数量为1条
|
||||
- ♻️ 动态获取分区列表&版块列表
|
||||
- ♻️ 重构部分路由处理,当话题/帖子切换分区/版块时,页面刷新不重置当前分区/版块
|
||||
|
||||
## [0.6.7](https://github.com/BTMuli/TeyvatGuide/releases/v0.6.7) (2024-12-31)
|
||||
|
||||
- 🍱 更新5.3版本游戏资源 [`#139`](https://github.com/BTMuli/TeyvatGuide/issues/139)
|
||||
- ✨ 支持嵌入B站视频的分享图渲染
|
||||
- 🐛 修复版块跳转异常
|
||||
- 🐛 修复清理日志异常
|
||||
- 🐛 修复特定帖子`link_card_ids`数据解析异常
|
||||
- 🐛 修复帖子文本居中异常
|
||||
- 🐛 修复侧边栏跳转角色/武器图鉴异常
|
||||
- ✏️ 调整分享图大小计算方式,采用1024进制而非原有的1000进制
|
||||
- 💄 调整用户等级UI,浅色深色下统一为白色文字
|
||||
- 💄 调整回复弹窗位置,上移一段距离以避免底部提示遮挡
|
||||
- 💄 首页素材日历组件只显示日期,移除具体时间
|
||||
- 💄 调整链接卡片提示文字
|
||||
- 💄 调整剧诗角色列表显示UI
|
||||
- 🚸 版块/咨讯页数据获取/刷新显示成功提示
|
||||
- 🚸 首页近期活动卡片Icon补充缺失的点击逻辑
|
||||
- 🚸 调整合集组件改版后的滚动逻辑,更加流畅
|
||||
- 👽️ 由于API变更,调整版块数据获取逻辑
|
||||
- 👽️ 由于返回数据格式变更,调整视频时长的计算逻辑
|
||||
- 👽️ 由于返回数据格式变更,处理帖子内的转义字符
|
||||
- ♻️ loading组件重构,部分页面显示更精准的进度
|
||||
- ♻️ 应用元数据格式重构,剔除冗余数据
|
||||
|
||||
## [0.6.6](https://github.com/BTMuli/TeyvatGuide/releases/v0.6.6) (2024-12-13)
|
||||
|
||||
- 🐛 修复主题切换响应异常
|
||||
- 🐛 修复增量刷新逻辑异常
|
||||
- ⚡️ 显著降低运行内存占用
|
||||
|
||||
## [0.6.5](https://github.com/BTMuli/TeyvatGuide/releases/v0.6.5) (2024-12-11)
|
||||
|
||||
- 🍱 添加下半卡池数据&部分资源
|
||||
- ✨ 帖子内容中涉及的话题链接支持应用内跳转
|
||||
- ♻️ 首页组件加载逻辑重构
|
||||
- ✨ UIGF4导入/导出浮窗,支持自选UID
|
||||
- 💄 调整剧诗部分数据缺失时的显示
|
||||
- 🐛 调整部分UI,修复切换账户后角色详情刷新异常
|
||||
|
||||
## [0.6.4](https://github.com/BTMuli/TeyvatGuide/releases/v0.6.4) (2024-12-03)
|
||||
|
||||
- 🐛 修复子回复渲染异常
|
||||
- ✏️ 祈愿记录将验证非空ID
|
||||
- 🐛 修复战绩分享图渲染异常
|
||||
- 🐛 修复`dialog`组件`input`默认值无效
|
||||
- 🎨 调整帖子查找overlay逻辑
|
||||
- ✨ 分享图生成阈值自定义
|
||||
- 👽️ 全量刷新时清理旧数据,修复由于米哈游数据异常导致的重复数据
|
||||
|
||||
## [0.6.3](https://github.com/BTMuli/TeyvatGuide/releases/v0.6.3) (2024-11-19)
|
||||
|
||||
- 🐛 修复用户战绩角色数据`undefined`
|
||||
- 🐛 修复咨讯页加载更多异常
|
||||
- 🐛 修复验证码登录提示`-100`,数据刷新后若为已登录UID则不会再提示切换
|
||||
- 🐛 修复部分公告渲染异常
|
||||
- 🐛 修复成就页面在存在搜索内容时点击左侧成就系列无响应
|
||||
- ✨ 帖子新增 UID 卡片解析&渲染
|
||||
- ✨ 帖子新增自定义表情解析&渲染
|
||||
- ✨ 真境剧诗适配,新增真境剧诗页面,支持获取&分享&上传(胡桃数据库),可通过深渊页面进入
|
||||
- ✨ 新增话题页面,可通过帖子卡片标签点击或帖子详情顶部标签点击进入
|
||||
- ✨ 更完善的`loading`显示,调整了组件UI
|
||||
- 🍱 更新5.2版本资源 [`#133`](https://github.com/BTMuli/TeyvatGuide/issues/133)
|
||||
- 💄 调整祈愿记录UP四星颜色
|
||||
- 💄 修复帖子页兑换码弹窗高度异常
|
||||
- 💄 调整帖子卡片UI,增加显示帖子话题(如存在),话题&版块支持点击跳转
|
||||
- 💄 调整帖子详情页UI,顶部话题&版块支持点击跳转
|
||||
- 💄 调整帖子显示数量,支持加载更多,默认排序改为`最新回复`,移除`默认排序`,增加`热门`排序
|
||||
- 💄 咨讯、帖子等页面刷新时自动滚动到顶部
|
||||
- 🔥 深渊数据库显示移除第9层统计数据
|
||||
- 👽️ 米游社子窗口增加`genshinnet`域名支持
|
||||
- 🎨 优化帖子详情数据加载的错误处理
|
||||
- ♻️ `snackbar`、`confirm`、`loading`组件重构
|
||||
- ♻️ 请求模块重构
|
||||
|
||||
## [0.6.2](https://github.com/BTMuli/TeyvatGuide/releases/v0.6.2) (2024-10-31)
|
||||
|
||||
- 🐛 修复用户登录状态异常 [`#132`](https://github.com/BTMuli/TeyvatGudie/issues/132)
|
||||
- 💄 帖子子回复取消保持,点击其他隐藏
|
||||
- 💄 调整未登录时的部分内容渲染
|
||||
- 💄 调整保存时图片的hint
|
||||
- 💄 `mac`:修复回顶组件宽度异常
|
||||
- 💄 `mac`:修复视频封面位置异常
|
||||
- 💄 调整角色卡片UI,维持名片比例
|
||||
- ♻️ 深渊数据库重构,概览显示差距
|
||||
- 🍱 更新下半卡池
|
||||
- 👽️ 修正咨讯Api
|
||||
|
||||
## [0.6.1](https://github.com/BTMuli/TeyvatGuide/releases/v0.6.1) (2024-10-22)
|
||||
|
||||
- 🐛 新用户数据库初始化异常 [`#131`](https://github.com/BTMuli/TeyavtGuide/issues/131)
|
||||
- 🐛 修复角色数据未即时刷新
|
||||
- 🐛 修复`openSystemBrowser`回调执行异常
|
||||
- ♻️ 公告卡片组件抽离,支持分享
|
||||
- 🎨 成就页面&名片图鉴页面采用虚拟列表优化性能
|
||||
- 🎨 调整卡片封面加载逻辑
|
||||
- 💄 处理特定情况下的内容溢出
|
||||
- 💄 适配深渊新字段,显示跳过楼层
|
||||
- 💄深渊分享显示应用信息,圣遗物详情推荐属性高亮
|
||||
- 💄调整帖子子窗口副标题样式
|
||||
- 💄调整留影叙佳期选项样式
|
||||
|
||||
## [0.6.0](https://github.com/BTMuli/TeyvatGuide/releases/v0.6.0) (2024-10-09)
|
||||
|
||||
- ✨ 应用支持多账号 [`#126`](https://github.com/BTMuli/TeyvatGuide/issues/126)
|
||||
- ✨ 支持手动输入CK&用户删除
|
||||
- ✨ 帖子卡片支持分享
|
||||
- ✨ 支持官服用户直接启动原神 [`#80`](https://github.com/BTMuli/TeyvatGuide/issues/80)
|
||||
- ♻️ 重构成就表格,支持多存档
|
||||
- ♻️ 重构深渊数据加载逻辑,适配多存档
|
||||
- ♻️ 重构用户登录逻辑及切换
|
||||
- ♻️ 重构祈愿、深渊、角色页面逻辑,支持游戏账号切换
|
||||
- ♻️ 战绩页面适配多账户
|
||||
- 💄 帖子/公告子窗口添加窗口置顶按钮
|
||||
- 💄 调整视频分享截图
|
||||
- 💄 回复分享图忽略导出图标
|
||||
- 💄 显示用户等级
|
||||
- 💄 处理特定情况下的回复内容溢出
|
||||
- 💄 兑换码支持分享,调整了兑换码浮窗UI
|
||||
- 💄 公告对列表进行缩进
|
||||
- 💄 材料Wiki样式优化,支持分类筛选&查询
|
||||
- 💄 材料详情浮窗支持分享
|
||||
- ✏️ JSBridge新增`openSystemBrowser`回调处理
|
||||
- ✏️ 修正公告正则
|
||||
- 👽️ 更新国际服公告Api
|
||||
- 📖 添加 macOS 平台门禁属性导致无法打开应用的修复指引 [`#130`](https://github.com/BTMuli/TeyvatGuide/issues/130)
|
||||
@@ -2,12 +2,12 @@
|
||||
Author: 目棃
|
||||
Description: 项目资源说明
|
||||
Date: 2023-03-10
|
||||
Update: 2024-09-19
|
||||
Update: 2025-02-28
|
||||
---
|
||||
|
||||
> 本文档 [`Frontmatter`](https://github.com/BTMuli/MuCli#Frontmatter) 由 [MuCli](https://github.com/BTMuli/Mucli) 自动生成于 `2023-03-10 22:05:44`
|
||||
>
|
||||
> 更新于 `2024-09-19 15:28:16`
|
||||
> 更新于 `2025-02-28 09:40:33`
|
||||
|
||||
## 说明
|
||||
|
||||
@@ -28,6 +28,7 @@ Update: 2024-09-19
|
||||
- 我的角色:Hoyolab
|
||||
- 深渊记录:Hoyolab
|
||||
- 祈愿记录:Hoyolab
|
||||
- 实用脚本:Hoyolab
|
||||
- 图鉴:Hoyolab,其子目录图标同样来源于 Hoyolab
|
||||
- 模式切换:`mdi-weather-night` `mdi-weather-sunny`
|
||||
- 设置:个人绘制 SVG
|
||||
|
||||
58
package.json
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"name": "teyvatguide",
|
||||
"version": "0.6.9",
|
||||
"version": "0.7.0",
|
||||
"description": "Game Tool for GenshinImpact player",
|
||||
"private": true,
|
||||
"packageManager": "pnpm@10.2.0",
|
||||
"packageManager": "pnpm@10.5.2",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "tauri build",
|
||||
@@ -12,6 +12,7 @@
|
||||
"eslint:pre": "pnpx @eslint/config-inspector@latest",
|
||||
"lint": "concurrently \"pnpm:lint:*(!fix)\"",
|
||||
"lint:fix": "concurrently \"pnpm:lint:*:fix\"",
|
||||
"lint:vue": "vue-tsc --noEmit",
|
||||
"lint:code": "eslint .",
|
||||
"lint:code:fix": "eslint . --fix",
|
||||
"lint:style": "stylelint \"src/**/*.{vue,css,scss}\" -f verbose",
|
||||
@@ -68,12 +69,12 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@mdi/font": "7.4.47",
|
||||
"@tauri-apps/api": "^2.2.0",
|
||||
"@tauri-apps/api": "^2.3.0",
|
||||
"@tauri-apps/plugin-deep-link": "^2.2.0",
|
||||
"@tauri-apps/plugin-dialog": "^2.2.0",
|
||||
"@tauri-apps/plugin-fs": "^2.2.0",
|
||||
"@tauri-apps/plugin-http": "^2.3.0",
|
||||
"@tauri-apps/plugin-log": "^2.2.1",
|
||||
"@tauri-apps/plugin-log": "^2.2.2",
|
||||
"@tauri-apps/plugin-os": "^2.2.0",
|
||||
"@tauri-apps/plugin-process": "^2.2.0",
|
||||
"@tauri-apps/plugin-shell": "^2.2.0",
|
||||
@@ -81,65 +82,66 @@
|
||||
"ajv": "^8.17.1",
|
||||
"artplayer": "^5.2.2",
|
||||
"clipboard": "^2.0.11",
|
||||
"color-convert": "^2.0.1",
|
||||
"color-convert": "^3.0.1",
|
||||
"echarts": "^5.6.0",
|
||||
"html2canvas": "^1.4.1",
|
||||
"js-md5": "^0.8.3",
|
||||
"jsencrypt": "^3.3.2",
|
||||
"pinia": "^2.3.1",
|
||||
"pinia": "^3.0.1",
|
||||
"pinia-plugin-persistedstate": "^4.2.0",
|
||||
"qrcode.vue": "^3.6.0",
|
||||
"sass": "^1.83.4",
|
||||
"sass-loader": "^16.0.4",
|
||||
"uuid": "^11.0.5",
|
||||
"sass": "^1.85.1",
|
||||
"sass-loader": "^16.0.5",
|
||||
"uuid": "^11.1.0",
|
||||
"vue": "^3.5.13",
|
||||
"vue-echarts": "^7.0.3",
|
||||
"vue-json-viewer": "^3.0.4",
|
||||
"vue-json-pretty": "^2.4.0",
|
||||
"vue-router": "^4.5.0",
|
||||
"vuetify": "^3.7.9",
|
||||
"vuetify": "^3.7.14",
|
||||
"wcag-color": "^1.1.1",
|
||||
"xml-js": "^1.6.11"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/eslintrc": "^3.2.0",
|
||||
"@eslint/js": "^9.19.0",
|
||||
"@tauri-apps/cli": "2.2.7",
|
||||
"@eslint/eslintrc": "^3.3.0",
|
||||
"@eslint/js": "^9.21.0",
|
||||
"@tauri-apps/cli": "2.3.0",
|
||||
"@types/color-convert": "^2.0.4",
|
||||
"@types/fs-extra": "^11.0.4",
|
||||
"@types/js-md5": "^0.7.2",
|
||||
"@types/node": "^22.13.0",
|
||||
"@types/node": "^22.13.5",
|
||||
"@types/uuid": "^10.0.0",
|
||||
"@typescript-eslint/parser": "^8.23.0",
|
||||
"@typescript-eslint/parser": "^8.25.0",
|
||||
"@vitejs/plugin-vue": "^5.2.1",
|
||||
"concurrently": "^9.1.2",
|
||||
"eslint": "^9.19.0",
|
||||
"eslint": "^9.21.0",
|
||||
"eslint-plugin-import": "^2.31.0",
|
||||
"eslint-plugin-jsonc": "^2.19.1",
|
||||
"eslint-plugin-prettier": "^5.2.3",
|
||||
"eslint-plugin-vue": "^9.32.0",
|
||||
"eslint-plugin-yml": "^1.16.0",
|
||||
"eslint-plugin-yml": "^1.17.0",
|
||||
"fs-extra": "^11.3.0",
|
||||
"globals": "^15.14.0",
|
||||
"globals": "^16.0.0",
|
||||
"husky": "^9.1.7",
|
||||
"jsonc-eslint-parser": "^2.4.0",
|
||||
"lint-staged": "^15.4.3",
|
||||
"oxlint": "^0.15.9",
|
||||
"prettier": "3.4.2",
|
||||
"oxlint": "^0.15.12",
|
||||
"prettier": "3.5.2",
|
||||
"stylelint": "^16.14.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-high-performance-animation": "^1.11.0",
|
||||
"stylelint-order": "^6.0.4",
|
||||
"stylelint-prettier": "^5.0.3",
|
||||
"stylelint-scss": "^6.11.0",
|
||||
"tsx": "^4.19.2",
|
||||
"stylelint-scss": "^6.11.1",
|
||||
"tsx": "^4.19.3",
|
||||
"typescript": "^5.7.3",
|
||||
"typescript-eslint": "^8.23.0",
|
||||
"vite": "^6.0.11",
|
||||
"vite-plugin-vue-devtools": "^7.7.1",
|
||||
"vite-plugin-vuetify": "^2.0.4",
|
||||
"typescript-eslint": "^8.25.0",
|
||||
"vite": "^6.2.0",
|
||||
"vite-plugin-vue-devtools": "^7.7.2",
|
||||
"vite-plugin-vuetify": "^2.1.0",
|
||||
"vue-eslint-parser": "^9.4.3",
|
||||
"vue-tsc": "^2.2.4",
|
||||
"yaml-eslint-parser": "^1.2.3"
|
||||
}
|
||||
}
|
||||
|
||||
2140
pnpm-lock.yaml
generated
BIN
public/platforms/mhy/launcher.webp
Normal file
|
After Width: | Height: | Size: 8.5 KiB |
|
Before Width: | Height: | Size: 30 KiB |
BIN
public/source/UI/act_birthday.webp
Normal file
|
After Width: | Height: | Size: 6.2 KiB |
|
Before Width: | Height: | Size: 2.0 KiB |
BIN
public/source/UI/posts.webp
Normal file
|
After Width: | Height: | Size: 776 B |
BIN
public/source/UI/toolbox.webp
Normal file
|
After Width: | Height: | Size: 5.0 KiB |
|
Before Width: | Height: | Size: 2.9 KiB |
BIN
public/source/UI/video_play_bili.webp
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
920
src-tauri/Cargo.lock
generated
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "TeyvatGuide"
|
||||
version = "0.6.9"
|
||||
version = "0.7.0"
|
||||
description = "Game Tool for Genshin Impact player"
|
||||
authors = ["BTMuli <bt-muli@outlook.com>"]
|
||||
license = "MIT"
|
||||
@@ -10,15 +10,15 @@ edition = "2021"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[build-dependencies]
|
||||
tauri-build = { version = "2.0.5", features = [] }
|
||||
tauri-build = { version = "2.0.6", features = [] }
|
||||
|
||||
[dependencies]
|
||||
chrono = "0.4.39"
|
||||
log = "0.4.25"
|
||||
serde = { version = "1.0.217", features = ["derive"] }
|
||||
serde_json = "1.0.138"
|
||||
tauri = { version = "2.2.5", features = [] }
|
||||
tauri-utils = "2.1.1"
|
||||
chrono = "0.4.40"
|
||||
log = "0.4.26"
|
||||
serde = { version = "1.0.218", features = ["derive"] }
|
||||
serde_json = "1.0.139"
|
||||
tauri = { version = "2.3.0", features = [] }
|
||||
tauri-utils = "2.2.0"
|
||||
url = "2.5.4"
|
||||
walkdir = "2.5.0"
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! @file src/client/mod.rs
|
||||
//! @desc 客户端模块,负责操作米游社客户端
|
||||
//! @since Beta v0.6.8
|
||||
//! @since Beta v0.7.0
|
||||
|
||||
mod menu;
|
||||
mod utils;
|
||||
@@ -8,7 +8,7 @@ mod utils;
|
||||
use tauri::{AppHandle, Manager, WebviewWindowBuilder};
|
||||
use tauri_utils::config::WebviewUrl;
|
||||
|
||||
static BBS_VERSION: &'static str = "2.80.1";
|
||||
static BBS_VERSION: &'static str = "2.82.0";
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn create_mhy_client(handle: AppHandle, func: String, url: String) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"productName": "TeyvatGuide",
|
||||
"identifier": "TeyvatGuide",
|
||||
"version": "0.6.9",
|
||||
"version": "0.7.0",
|
||||
"build": {
|
||||
"beforeDevCommand": "pnpm vite:dev",
|
||||
"beforeBuildCommand": "pnpm vite:build",
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
<v-list-item :title.attr="'帖子'" :link="true" href="/posts/forum">
|
||||
<template #title>帖子</template>
|
||||
<template #prepend>
|
||||
<img src="/source/UI/posts.png" alt="posts" class="side-icon" />
|
||||
<img src="/source/UI/posts.webp" alt="posts" class="side-icon" />
|
||||
</template>
|
||||
</v-list-item>
|
||||
<v-list-item :title.attr="'成就'" :link="true" href="/achievements">
|
||||
@@ -62,6 +62,12 @@
|
||||
<img src="/source/UI/userGacha.webp" alt="gacha" class="side-icon" />
|
||||
</template>
|
||||
</v-list-item>
|
||||
<v-list-item :title.attr="'实用脚本'" :link="true" href="/user/scripts">
|
||||
<template #title>实用脚本</template>
|
||||
<template #prepend>
|
||||
<img src="/source/UI/toolbox.webp" alt="scripts" class="side-icon" />
|
||||
</template>
|
||||
</v-list-item>
|
||||
<v-divider />
|
||||
<v-list-item
|
||||
v-show="isDevEnv"
|
||||
@@ -114,7 +120,7 @@
|
||||
<v-list-item :title.attr="'留影叙佳期'" :link="true" href="/archive/birthday">
|
||||
<template #title>留影叙佳期</template>
|
||||
<template #prepend>
|
||||
<img src="/source/UI/act_birthday.png" alt="archive_birthday_icon" class="side-icon" />
|
||||
<img src="/source/UI/act_birthday.webp" alt="archive_birthday_icon" class="side-icon" />
|
||||
</template>
|
||||
</v-list-item>
|
||||
<div class="bottom-menu">
|
||||
@@ -145,7 +151,7 @@
|
||||
</v-list-item>
|
||||
<v-list-item class="side-item-menu" title="收藏" :link="true" href="/collection">
|
||||
<template #prepend>
|
||||
<img src="/source/UI/posts.png" alt="collect" class="side-icon-menu" />
|
||||
<img src="/source/UI/posts.webp" alt="collect" class="side-icon-menu" />
|
||||
</template>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<ToGameLogin v-model="showLoginQr" @success="tryGetTokens" />
|
||||
<ToGameLogin v-model="showLoginQr" @success="tryGetTokens" v-model:launcher="isLauncherQr" />
|
||||
<v-card class="tcu-box">
|
||||
<template #prepend>
|
||||
<v-avatar :image="userInfo.avatar" />
|
||||
@@ -100,8 +100,15 @@
|
||||
<v-list-item-title>验证码登录</v-list-item-title>
|
||||
<v-list-item-subtitle>使用手机号登录</v-list-item-subtitle>
|
||||
</v-list-item>
|
||||
<v-list-item @click="showLoginQr = true" append-icon="mdi-qrcode-scan">
|
||||
<v-list-item-title>扫码登录</v-list-item-title>
|
||||
<v-list-item @click="tryCodeLogin(true)">
|
||||
<v-list-item-title>扫码登录(启动器)</v-list-item-title>
|
||||
<v-list-item-subtitle>使用米游社扫码登录</v-list-item-subtitle>
|
||||
<template #append>
|
||||
<img src="/platforms/mhy/launcher.webp" alt="launcher" class="menu-icon" />
|
||||
</template>
|
||||
</v-list-item>
|
||||
<v-list-item @click="tryCodeLogin(false)" append-icon="mdi-qrcode-scan" v-if="false">
|
||||
<v-list-item-title>扫码登录(游戏)</v-list-item-title>
|
||||
<v-list-item-subtitle>使用米游社扫码登录</v-list-item-subtitle>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
@@ -131,6 +138,7 @@ const { isLogin } = storeToRefs(useAppStore());
|
||||
const { uid, briefInfo, cookie, account } = storeToRefs(useUserStore());
|
||||
|
||||
const showLoginQr = ref<boolean>(false);
|
||||
const isLauncherQr = ref<boolean>(true);
|
||||
const accounts = shallowRef<Array<TGApp.App.Account.User>>([]);
|
||||
const gameAccounts = shallowRef<Array<TGApp.Sqlite.Account.Game>>([]);
|
||||
const userInfo = computed<TGApp.App.Account.BriefInfo>(() => {
|
||||
@@ -247,6 +255,11 @@ async function tryCaptchaLogin(): Promise<void> {
|
||||
await tryGetTokens(ck);
|
||||
}
|
||||
|
||||
async function tryCodeLogin(isLauncher: boolean): Promise<void> {
|
||||
isLauncherQr.value = isLauncher;
|
||||
showLoginQr.value = true;
|
||||
}
|
||||
|
||||
async function refreshUser(uid: string) {
|
||||
let account = await TSUserAccount.account.getAccount(uid);
|
||||
if (!account) {
|
||||
@@ -554,4 +567,9 @@ async function clearUser(user: TGApp.App.Account.User): Promise<void> {
|
||||
.tcu-btn {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.menu-icon {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -6,24 +6,32 @@
|
||||
</div>
|
||||
<div class="tog-mid">
|
||||
<qrcode-vue
|
||||
v-if="codeData"
|
||||
v-if="codeUrl"
|
||||
class="tog-qr"
|
||||
:value="codeData.url"
|
||||
:value="codeUrl"
|
||||
render-as="svg"
|
||||
:background="'var(--box-bg-1)'"
|
||||
foreground="var(--box-text-1)"
|
||||
/>
|
||||
</div>
|
||||
<div class="tog-bottom" @click="share()">
|
||||
<img src="/platforms/mhy/launcher.webp" alt="icon" v-if="isLauncherCode" />
|
||||
<img src="/platforms/mhy/mys.webp" alt="icon" v-else />
|
||||
</div>
|
||||
</div>
|
||||
</TOverlay>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import TOverlay from "@comp/app/t-overlay.vue";
|
||||
import showLoading from "@comp/func/loading.js";
|
||||
import showSnackbar from "@comp/func/snackbar.js";
|
||||
import QrcodeVue from "qrcode.vue";
|
||||
import { onUnmounted, shallowRef, watch } from "vue";
|
||||
import { computed, onUnmounted, ref, watch } from "vue";
|
||||
|
||||
import { generateShareImg } from "@/utils/TGShare.js";
|
||||
import hk4eReq from "@/web/request/hk4eReq.js";
|
||||
import PassportReq from "@/web/request/passportReq.js";
|
||||
import takumiReq from "@/web/request/takumiReq.js";
|
||||
|
||||
type ToGameLoginEmits = (e: "success", data: TGApp.App.Account.Cookie) => void;
|
||||
|
||||
@@ -31,8 +39,14 @@ type ToGameLoginEmits = (e: "success", data: TGApp.App.Account.Cookie) => void;
|
||||
let cycleTimer: NodeJS.Timeout | null = null;
|
||||
|
||||
const model = defineModel<boolean>({ default: false });
|
||||
const isLauncherCode = defineModel<boolean>("launcher", { default: false });
|
||||
const emits = defineEmits<ToGameLoginEmits>();
|
||||
const codeData = shallowRef<TGApp.BBS.GameLogin.GetLoginQrData>();
|
||||
const codeUrl = ref<string>();
|
||||
const codeTicket = computed<string>(() => {
|
||||
if (!codeUrl.value) return "";
|
||||
const url = new URL(codeUrl.value);
|
||||
return url.searchParams.get("ticket") || "";
|
||||
});
|
||||
|
||||
watch(model, async (value) => {
|
||||
if (value) {
|
||||
@@ -41,25 +55,43 @@ watch(model, async (value) => {
|
||||
}
|
||||
});
|
||||
|
||||
async function share(): Promise<void> {
|
||||
const shareDom = document.querySelector<HTMLDivElement>(".tog-box");
|
||||
if (shareDom === null) {
|
||||
showSnackbar.error("分享失败");
|
||||
return;
|
||||
}
|
||||
await generateShareImg(`tco-gameLogin`, shareDom);
|
||||
}
|
||||
|
||||
async function freshQr(): Promise<void> {
|
||||
const res = await PassportReq.qrLogin.create();
|
||||
let res;
|
||||
if (isLauncherCode.value) res = await PassportReq.qrLogin.create();
|
||||
else res = await hk4eReq.loginQr.create();
|
||||
console.log(res);
|
||||
if ("retcode" in res) {
|
||||
showSnackbar.error(`[${res.retcode}] ${res.message}`);
|
||||
return;
|
||||
}
|
||||
codeData.value = res;
|
||||
codeUrl.value = res.url;
|
||||
}
|
||||
|
||||
async function cycleGetData() {
|
||||
if (cycleTimer === null || !codeData.value) return;
|
||||
const res = await PassportReq.qrLogin.query(codeData.value.ticket);
|
||||
if (cycleTimer === null || codeTicket.value === "") return;
|
||||
if (isLauncherCode.value) await cycleGetDataLauncher(cycleTimer);
|
||||
else await cycleGetDataGame(cycleTimer);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
async function cycleGetDataLauncher(timer: NodeJS.Timeout): Promise<void> {
|
||||
const res = await PassportReq.qrLogin.query(codeTicket.value);
|
||||
console.log(res);
|
||||
if ("retcode" in res) {
|
||||
showSnackbar.error(`[${res.retcode}] ${res.message}`);
|
||||
if (res.retcode === -106) {
|
||||
await freshQr();
|
||||
} else {
|
||||
clearInterval(cycleTimer);
|
||||
clearInterval(timer);
|
||||
cycleTimer = null;
|
||||
model.value = false;
|
||||
}
|
||||
@@ -67,7 +99,7 @@ async function cycleGetData() {
|
||||
}
|
||||
if (res.status === "Created" || res.status === "Scanned") return;
|
||||
if (res.status === "Confirmed") {
|
||||
clearInterval(cycleTimer);
|
||||
clearInterval(timer);
|
||||
cycleTimer = null;
|
||||
const ck: TGApp.App.Account.Cookie = {
|
||||
account_id: res.user_info.aid,
|
||||
@@ -83,6 +115,54 @@ async function cycleGetData() {
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
async function cycleGetDataGame(timer: NodeJS.Timeout): Promise<void> {
|
||||
const res = await hk4eReq.loginQr.state(codeTicket.value);
|
||||
console.log(res);
|
||||
if ("retcode" in res) {
|
||||
showSnackbar.error(`[${res.retcode}] ${res.message}`);
|
||||
if (res.retcode === -106) {
|
||||
await freshQr();
|
||||
} else {
|
||||
clearInterval(timer);
|
||||
cycleTimer = null;
|
||||
model.value = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (res.stat === "Init" || res.stat === "Scanned") return;
|
||||
if (res.stat === "Confirmed") {
|
||||
clearInterval(timer);
|
||||
cycleTimer = null;
|
||||
if (res.payload.proto === "Raw") {
|
||||
showSnackbar.error(`返回数据异常:${res.payload}`);
|
||||
model.value = false;
|
||||
return;
|
||||
}
|
||||
const statusRaw: TGApp.Game.Login.StatusPayloadRaw = JSON.parse(res.payload.raw);
|
||||
await showLoading.start("正在获取SToken");
|
||||
const stResp = await takumiReq.game.stoken(statusRaw);
|
||||
console.log(stResp);
|
||||
await showLoading.end();
|
||||
if ("retcode" in stResp) {
|
||||
showSnackbar.error(`[${stResp.retcode}] ${stResp.message}`);
|
||||
model.value = false;
|
||||
return;
|
||||
}
|
||||
// const ck: TGApp.App.Account.Cookie = {
|
||||
// account_id: statusRaw.uid,
|
||||
// ltuid: statusRaw.uid,
|
||||
// stuid: statusRaw.uid,
|
||||
// mid: res.user_info.mid,
|
||||
// cookie_token: "",
|
||||
// stoken: res.tokens[0].token,
|
||||
// ltoken: "",
|
||||
// };
|
||||
// emits("success", ck);
|
||||
model.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
onUnmounted(() => {
|
||||
if (cycleTimer !== null) clearInterval(cycleTimer);
|
||||
cycleTimer = null;
|
||||
@@ -126,4 +206,15 @@ onUnmounted(() => {
|
||||
width: 256px;
|
||||
height: 256px;
|
||||
}
|
||||
|
||||
.tog-bottom {
|
||||
margin: 0 auto;
|
||||
cursor: pointer;
|
||||
|
||||
img {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<img @click="toBirth(true)" :src="i.head_icon" alt="empty" class="tcb-cur" v-else />
|
||||
</div>
|
||||
<span>的生日哦~</span>
|
||||
<img @click="toBirth(true)" src="/source/UI/act_birthday.png" alt="empty" class="active" />
|
||||
<img @click="toBirth(true)" src="/source/UI/act_birthday.webp" alt="empty" class="active" />
|
||||
</div>
|
||||
<div>即将到来:{{ next[0].role_birthday }}</div>
|
||||
<div v-for="i in next" :key="i.role_id" class="tcb-item">
|
||||
@@ -143,6 +143,7 @@ function parseDesc(intro: string): string {
|
||||
|
||||
.tcb-item img {
|
||||
height: 100px;
|
||||
border-radius: 50%;
|
||||
aspect-ratio: 1;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
{{ text.text }}
|
||||
</v-btn>
|
||||
</div>
|
||||
<v-pagination class="tc-page" v-model="page" :total-visible="visible" :length="length" />
|
||||
<v-pagination class="tc-page" v-model="page" :total-visible="9" :length="length" />
|
||||
</div>
|
||||
<div class="tc-content">
|
||||
<TCalendarBirth />
|
||||
|
||||
@@ -78,7 +78,7 @@
|
||||
import TMiImg from "@comp/app/t-mi-img.vue";
|
||||
import showSnackbar from "@comp/func/snackbar.js";
|
||||
import TSUserAvatar from "@Sqlite/modules/userAvatar.js";
|
||||
import { computed, onMounted, ref } from "vue";
|
||||
import { computed, ref } from "vue";
|
||||
|
||||
import TuaDcConstellations from "./tua-dc-constellations.vue";
|
||||
import TuaDcProp from "./tua-dc-prop.vue";
|
||||
@@ -109,15 +109,11 @@ const propMain = computed<Array<TGApp.Game.Avatar.PropMapItem | false>>(() =>
|
||||
props.modelValue.propSelected.map((item) => userStore.getProp(item.property_type)),
|
||||
);
|
||||
|
||||
const bg = ref<string>("/WIKI/nameCard/profile/原神·印象.webp");
|
||||
const loading = ref<boolean>(false);
|
||||
|
||||
onMounted(async () => await loadData());
|
||||
|
||||
async function loadData(): Promise<void> {
|
||||
const bg = computed<string>(() => {
|
||||
const card = TSUserAvatar.getAvatarCard(props.modelValue.cid);
|
||||
bg.value = `url("/WIKI/nameCard/profile/${card}.webp")`;
|
||||
}
|
||||
return `url("/WIKI/nameCard/profile/${card}.webp")`;
|
||||
});
|
||||
const loading = ref<boolean>(false);
|
||||
|
||||
async function share(): Promise<void> {
|
||||
const shareBox = document.querySelector<HTMLElement>(".tua-dc-container");
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
<script lang="ts" setup>
|
||||
// about import err,see:https://github.com/apache/echarts/issues/19992
|
||||
import showLoading from "@comp/func/loading.js";
|
||||
// @ts-expect-error no-exported-member
|
||||
import { BarChart, HeatmapChart, PieChart } from "echarts/charts.js";
|
||||
import {
|
||||
CalendarComponent,
|
||||
@@ -38,11 +37,8 @@ import {
|
||||
TooltipComponent,
|
||||
VisualMapComponent,
|
||||
} from "echarts/components.js";
|
||||
// @ts-expect-error no-exported-member
|
||||
import { use } from "echarts/core.js";
|
||||
// @ts-expect-error no-exported-member
|
||||
import { LabelLayout } from "echarts/features.js";
|
||||
// @ts-expect-error no-exported-member
|
||||
import { CanvasRenderer } from "echarts/renderers.js";
|
||||
import type { EChartsOption } from "echarts/types/dist/shared.js";
|
||||
import { storeToRefs } from "pinia";
|
||||
|
||||
@@ -1,24 +1,22 @@
|
||||
<!-- todo 优化,增加筛选功能 -->
|
||||
<template>
|
||||
<div class="ua-gt-box">
|
||||
<v-data-table
|
||||
:headers="headers"
|
||||
:items="props.modelValue"
|
||||
height="500px"
|
||||
fixed-header
|
||||
fixed-footer
|
||||
>
|
||||
<template v-slot:item="{ item }">
|
||||
<tr class="ua-gt-tr">
|
||||
<td>{{ item.time }}</td>
|
||||
<td>{{ getPool(item.uigfType) }}</td>
|
||||
<td>{{ item.type }}</td>
|
||||
<td>{{ item.name }}</td>
|
||||
<td>{{ item.rank }}</td>
|
||||
</tr>
|
||||
</template>
|
||||
</v-data-table>
|
||||
</div>
|
||||
<v-data-table
|
||||
:headers="headers"
|
||||
:items="props.modelValue"
|
||||
fixed-header
|
||||
fixed-footer
|
||||
class="ua-gt-box"
|
||||
>
|
||||
<template v-slot:item="{ item }">
|
||||
<tr class="ua-gt-tr">
|
||||
<td>{{ item.time }}</td>
|
||||
<td>{{ getPool(item.uigfType) }}</td>
|
||||
<td>{{ item.type }}</td>
|
||||
<td>{{ item.name }}</td>
|
||||
<td>{{ item.rank }}</td>
|
||||
</tr>
|
||||
</template>
|
||||
</v-data-table>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
type GroTableProps = { modelValue: Array<TGApp.Sqlite.GachaRecords.SingleTable> };
|
||||
@@ -52,8 +50,7 @@ function getPool(type: string) {
|
||||
</script>
|
||||
<style lang="css" scoped>
|
||||
.ua-gt-box {
|
||||
height: 100%;
|
||||
max-height: calc(100vh - 120px);
|
||||
height: calc(100vh - 200px);
|
||||
padding-right: 5px;
|
||||
border-radius: 5px;
|
||||
overflow-y: auto;
|
||||
|
||||
356
src/components/userScripts/tus-mission.vue
Normal file
@@ -0,0 +1,356 @@
|
||||
<template>
|
||||
<div class="tusm-box">
|
||||
<div class="tusm-top">
|
||||
<div class="tusm-title">米游币任务({{ todayPoints }}/{{ totalPoints }})</div>
|
||||
<div class="tusm-acts">
|
||||
<v-btn @click="tryRefresh()" class="tusm-btn" :loading="loadState">刷新</v-btn>
|
||||
<v-btn @click="tryAuto()" class="tusm-btn" :loading="loadMission">执行</v-btn>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tusm-content">
|
||||
<div v-for="mission in parseMissions" :key="mission.id" class="mission-item">
|
||||
<div class="left">
|
||||
<v-icon v-if="!mission.status" color="var(--tgc-od-grey)">mdi-circle</v-icon>
|
||||
<v-icon v-else color="var(--tgc-od-green)">mdi-check-circle</v-icon>
|
||||
<span>{{ mission.name }} - {{ mission.reward }}米游币</span>
|
||||
</div>
|
||||
<div class="right">
|
||||
<span>
|
||||
<v-progress-linear
|
||||
rounded
|
||||
:model-value="(mission.process / mission.total) * 100"
|
||||
height="8"
|
||||
color="var(--tgc-od-blue)"
|
||||
/>
|
||||
</span>
|
||||
<span>{{ mission.process }}/{{ mission.total }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import showSnackbar from "@comp/func/snackbar.js";
|
||||
import { getRecentForumPostList } from "@Mys/request/painterReq.js";
|
||||
import { getPostFull } from "@Mys/request/postReq.js";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { ref, shallowRef } from "vue";
|
||||
|
||||
import { useUserStore } from "@/store/modules/user.js";
|
||||
import TGLogger from "@/utils/TGLogger.js";
|
||||
import apiHubReq from "@/web/request/apiHubReq.js";
|
||||
|
||||
type ParseMission = {
|
||||
id: number;
|
||||
key: string;
|
||||
name: string;
|
||||
process: number;
|
||||
total: number;
|
||||
status: boolean;
|
||||
reward: number;
|
||||
};
|
||||
|
||||
const { cookie } = storeToRefs(useUserStore());
|
||||
const parseMissions = shallowRef<Array<ParseMission>>([]);
|
||||
const missionList = shallowRef<Array<TGApp.BBS.Mission.MissionItem>>([]);
|
||||
const todayPoints = ref<number>(0);
|
||||
const totalPoints = ref<number>(0);
|
||||
const loadScript = defineModel<boolean>();
|
||||
const loadState = ref<boolean>(false);
|
||||
const loadMission = ref<boolean>(false);
|
||||
|
||||
function mergeMission(
|
||||
list: Array<TGApp.BBS.Mission.MissionItem>,
|
||||
state: Array<TGApp.BBS.Mission.StateItem>,
|
||||
): void {
|
||||
const res: Array<ParseMission> = [];
|
||||
for (const item of list) {
|
||||
const stateFind = state.find((i) => i.mission_id === item.id);
|
||||
if (!stateFind) {
|
||||
res.push({
|
||||
id: item.id,
|
||||
key: item.mission_key,
|
||||
name: item.name,
|
||||
process: 0,
|
||||
total: item.threshold,
|
||||
status: false,
|
||||
reward: item.points,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
res.push({
|
||||
id: item.id,
|
||||
key: item.mission_key,
|
||||
name: item.name,
|
||||
total: item.threshold,
|
||||
process: stateFind.happened_times,
|
||||
status: stateFind.process === 1,
|
||||
reward: item.points,
|
||||
});
|
||||
}
|
||||
res.sort((a, b) => a.id - b.id);
|
||||
parseMissions.value = res;
|
||||
}
|
||||
|
||||
async function tryRefresh(): Promise<void> {
|
||||
if (loadScript.value) {
|
||||
showSnackbar.warn("任务正在执行中,请稍后再试");
|
||||
return;
|
||||
}
|
||||
loadScript.value = true;
|
||||
loadState.value = true;
|
||||
await TGLogger.ScriptSep("米游币任务");
|
||||
await TGLogger.Script("[米游币任务]刷新任务状态");
|
||||
if (!cookie.value) {
|
||||
await TGLogger.Script("[米游币任务]未检测到Cookie");
|
||||
showSnackbar.warn("当前账号未登录,请先登录");
|
||||
await TGLogger.ScriptSep("米游币任务", false);
|
||||
return;
|
||||
}
|
||||
const ck = {
|
||||
stoken: cookie.value.stoken,
|
||||
stuid: cookie.value.stuid,
|
||||
mid: cookie.value.mid,
|
||||
};
|
||||
await refreshState(ck);
|
||||
await TGLogger.ScriptSep("米游币任务", false);
|
||||
loadScript.value = false;
|
||||
loadState.value = false;
|
||||
}
|
||||
|
||||
async function tryAuto(): Promise<void> {
|
||||
if (loadScript.value) {
|
||||
showSnackbar.warn("任务正在执行中,请稍后再试");
|
||||
return;
|
||||
}
|
||||
loadScript.value = true;
|
||||
loadMission.value = true;
|
||||
await TGLogger.ScriptSep("米游币任务");
|
||||
await TGLogger.Script("[米游币任务]开始执行任务");
|
||||
if (!cookie.value) {
|
||||
await TGLogger.Script("[米游币任务]未检测到Cookie");
|
||||
showSnackbar.warn("当前账号未登录,请先登录");
|
||||
await TGLogger.ScriptSep("米游币任务", false);
|
||||
return;
|
||||
}
|
||||
const ck = {
|
||||
stoken: cookie.value.stoken,
|
||||
stuid: cookie.value.stuid,
|
||||
mid: cookie.value.mid,
|
||||
};
|
||||
const ckPost = { ltoken: cookie.value.ltoken, ltuid: cookie.value.ltuid };
|
||||
await refreshState(ck);
|
||||
const signFind = parseMissions.value.find((i) => i.key === "continuous_sign");
|
||||
if (signFind && !signFind.status) {
|
||||
await autoSign(ck);
|
||||
} else {
|
||||
await TGLogger.Script("[米游币任务]未找到打卡任务或今日已打卡");
|
||||
}
|
||||
const postFilter = parseMissions.value.filter((i) => i.key !== "continuous_sign");
|
||||
if (postFilter.every((i) => i.status)) {
|
||||
await TGLogger.Script("[米游币任务]所有任务已完成");
|
||||
await TGLogger.ScriptSep("米游币任务", false);
|
||||
loadScript.value = false;
|
||||
loadMission.value = false;
|
||||
return;
|
||||
}
|
||||
let isShare = false;
|
||||
let likeCnt = 0;
|
||||
let viewCnt = 0;
|
||||
const shareFind = postFilter.find((i) => i.key === "share_post_0");
|
||||
if (shareFind) isShare = shareFind.status;
|
||||
const likeFind = postFilter.find((i) => i.key === "post_up_0");
|
||||
if (likeFind) likeCnt = likeFind.process;
|
||||
const viewFind = postFilter.find((i) => i.key === "view_post_0");
|
||||
if (viewFind) viewCnt = viewFind.process;
|
||||
await TGLogger.Script("[米游币任务]获取帖子列表");
|
||||
const listResp = await getRecentForumPostList(26, 2, 2, undefined, 20);
|
||||
for (const post of listResp.list) {
|
||||
if (!isShare) {
|
||||
await TGLogger.Script(`[米游币任务]正在分享帖子${post.post.post_id}`);
|
||||
const shareResp = await apiHubReq.post.share(post.post.post_id, ck);
|
||||
if (shareResp.retcode === 0) {
|
||||
await TGLogger.Script("[米游币任务]分享成功");
|
||||
isShare = true;
|
||||
} else {
|
||||
await TGLogger.Script(`[米游币任务]分享失败:${shareResp.retcode} ${shareResp.message}`);
|
||||
}
|
||||
}
|
||||
if (likeCnt < 5 || viewCnt < 3) {
|
||||
await TGLogger.Script(`[米游币任务]正在浏览帖子${post.post.post_id}`);
|
||||
const detailResp = await getPostFull(Number(post.post.post_id), ckPost);
|
||||
if ("retcode" in detailResp) {
|
||||
await TGLogger.Script(
|
||||
`[米游币任务]获取帖子${post.post.post_id}失败:${detailResp.retcode} ${detailResp.message}`,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
viewCnt++;
|
||||
if (likeCnt < 5) {
|
||||
const isLike = detailResp.self_operation.upvote_type === 1;
|
||||
if (isLike) {
|
||||
await TGLogger.Script(`[米游币任务]帖子${post.post.post_id}已点赞,跳过`);
|
||||
continue;
|
||||
}
|
||||
await TGLogger.Script(`[米游币任务]正在点赞帖子${post.post.post_id}`);
|
||||
const likeResp = await apiHubReq.post.like(post.post.post_id, ckPost);
|
||||
if (likeResp.retcode === 0) {
|
||||
await TGLogger.Script("[米游币任务]点赞成功");
|
||||
likeCnt++;
|
||||
} else {
|
||||
await TGLogger.Script(`[米游币任务]点赞失败:${likeResp.retcode} ${likeResp.message}`);
|
||||
continue;
|
||||
}
|
||||
await TGLogger.Script(`[米游币任务]正在取消点赞帖子${post.post.post_id}`);
|
||||
await new Promise<void>((resolve) => setTimeout(resolve, 1000));
|
||||
const unlikeResp = await apiHubReq.post.like(post.post.post_id, ckPost, true);
|
||||
if (unlikeResp.retcode === 0) {
|
||||
await TGLogger.Script("[米游币任务]取消点赞成功");
|
||||
} else {
|
||||
await TGLogger.Script(
|
||||
`[米游币任务]取消点赞失败:${unlikeResp.retcode} ${unlikeResp.message}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isShare && likeCnt >= 5 && viewCnt >= 3) {
|
||||
await TGLogger.Script("[米游币任务]所有任务已完成");
|
||||
break;
|
||||
}
|
||||
}
|
||||
await TGLogger.Script("[米游币任务]任务执行完毕,即将刷新任务状态");
|
||||
await refreshState(ck);
|
||||
await TGLogger.ScriptSep("米游币任务", false);
|
||||
loadScript.value = false;
|
||||
loadMission.value = false;
|
||||
}
|
||||
|
||||
async function refreshState(ck: Record<string, string>): Promise<void> {
|
||||
await TGLogger.Script("[米游币任务]刷新任务状态");
|
||||
if (missionList.value.length === 0) {
|
||||
await TGLogger.Script("[米游币任务]未检测到任务列表,正在获取");
|
||||
const listResp = await apiHubReq.mission.list(ck);
|
||||
if (listResp.retcode !== 0) {
|
||||
await TGLogger.Script(
|
||||
`[米游币任务]获取任务列表失败:${listResp.retcode} ${listResp.message}`,
|
||||
);
|
||||
showSnackbar.error(`[${listResp.retcode}] ${listResp.message}`);
|
||||
await TGLogger.ScriptSep("米游币任务", false);
|
||||
return;
|
||||
}
|
||||
missionList.value = listResp.data.missions;
|
||||
await TGLogger.Script("[米游币任务]获取任务列表成功");
|
||||
}
|
||||
await TGLogger.Script("[米游币任务]正在获取任务状态");
|
||||
const stateResp = await apiHubReq.mission.state(ck);
|
||||
if (stateResp.retcode !== 0) {
|
||||
await TGLogger.Script(
|
||||
`[米游币任务]获取任务状态失败:${stateResp.retcode} ${stateResp.message}`,
|
||||
);
|
||||
showSnackbar.error(`[${stateResp.retcode}] ${stateResp.message}`);
|
||||
await TGLogger.ScriptSep("米游币任务", false);
|
||||
return;
|
||||
}
|
||||
await TGLogger.Script("[米游币任务]获取任务状态成功");
|
||||
todayPoints.value = stateResp.data.already_received_points;
|
||||
totalPoints.value = stateResp.data.today_total_points;
|
||||
await TGLogger.Script("[米游币任务]合并任务数据");
|
||||
mergeMission(missionList.value, stateResp.data.states);
|
||||
await TGLogger.Script("[米游币任务]任务数据合并完成");
|
||||
}
|
||||
|
||||
async function autoSign(ck: Record<string, string>): Promise<void> {
|
||||
const signFind = parseMissions.value.find((i) => i.key === "continuous_sign");
|
||||
if (!signFind) {
|
||||
await TGLogger.Script("[米游币任务]未找到打卡任务");
|
||||
return;
|
||||
}
|
||||
if (signFind.status) {
|
||||
await TGLogger.Script("[米游币任务]今日已打卡");
|
||||
return;
|
||||
}
|
||||
await TGLogger.Script("[米游币任务]正在执行打卡");
|
||||
const resp = await apiHubReq.sign(ck);
|
||||
if (resp.retcode !== 0) {
|
||||
await TGLogger.Script(`[米游币任务]打卡失败:${resp.retcode} ${resp.message}`);
|
||||
showSnackbar.error(`[${resp.retcode}] ${resp.message}`);
|
||||
return;
|
||||
}
|
||||
await TGLogger.Script("[米游币任务]打卡成功");
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.tusm-box {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 12px;
|
||||
background: var(--box-bg-1);
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--common-shadow-2);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
color: var(--box-text-1);
|
||||
}
|
||||
|
||||
.tusm-top {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.tusm-title {
|
||||
font-family: var(--font-title);
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tusm-acts {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.tusm-btn {
|
||||
background: var(--tgc-btn-1);
|
||||
color: var(--btn-text);
|
||||
}
|
||||
|
||||
.tusm-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.mission-item {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 10px;
|
||||
background: var(--box-bg-2);
|
||||
border-radius: 4px;
|
||||
color: var(--box-text-2);
|
||||
|
||||
.left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.right {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 4px;
|
||||
|
||||
:first-child {
|
||||
width: 100px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
101
src/components/userScripts/tus-output.vue
Normal file
@@ -0,0 +1,101 @@
|
||||
<template>
|
||||
<div class="tuso-box">
|
||||
<div class="tuso-top">
|
||||
<div class="tuso-title">输出日志</div>
|
||||
<div class="tuso-top-acts">
|
||||
<v-btn class="tuso-btn" @click="clearLog()">清空</v-btn>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tuso-mid" ref="logRef">
|
||||
<div class="tuso-log" v-for="log in logs" :key="log">{{ log }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { event } from "@tauri-apps/api";
|
||||
import type { Event, UnlistenFn } from "@tauri-apps/api/event";
|
||||
import { nextTick, onMounted, onUnmounted, ref, useTemplateRef } from "vue";
|
||||
|
||||
let logListener: UnlistenFn | null = null;
|
||||
const logs = ref<Array<string>>([]);
|
||||
const logEl = useTemplateRef<HTMLDivElement>("logRef");
|
||||
|
||||
onMounted(async () => {
|
||||
logListener = await event.listen<string>("userScriptLog", async (e: Event<string>) => {
|
||||
logs.value.push(e.payload);
|
||||
await nextTick();
|
||||
logEl.value?.scrollTo({ top: logEl.value.scrollHeight, behavior: "smooth" });
|
||||
});
|
||||
});
|
||||
|
||||
function clearLog(): void {
|
||||
logs.value = [];
|
||||
}
|
||||
|
||||
onUnmounted(() => {
|
||||
if (logListener !== null) {
|
||||
logListener();
|
||||
logListener = null;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.tuso-box {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
margin-left: auto;
|
||||
width: 800px;
|
||||
min-width: 800px;
|
||||
padding: 12px;
|
||||
background: var(--box-bg-1);
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--common-shadow-2);
|
||||
}
|
||||
|
||||
.tuso-top {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.tuso-title {
|
||||
color: var(--common-text-title);
|
||||
font-family: var(--font-title);
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.tuso-top-acts {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.tuso-btn {
|
||||
background: var(--tgc-btn-1);
|
||||
color: var(--btn-text);
|
||||
}
|
||||
|
||||
.tuso-mid {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
background: var(--box-bg-2);
|
||||
border-radius: 4px;
|
||||
padding: 8px;
|
||||
box-sizing: border-box;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.tuso-log {
|
||||
position: relative;
|
||||
height: 24px;
|
||||
min-height: 24px;
|
||||
white-space: pre;
|
||||
}
|
||||
</style>
|
||||
@@ -133,15 +133,14 @@ const levelColor = computed<string>(() => {
|
||||
.tpa-level-right {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 50%;
|
||||
background: v-bind(levelColor);
|
||||
color: var(--tgc-white-1);
|
||||
font-size: 10px;
|
||||
line-height: 18px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tpa-level-right {
|
||||
|
||||
@@ -45,7 +45,9 @@ function getParsedData(data: SctPostDataArr): SctPostDataArr {
|
||||
res.push(cur);
|
||||
continue;
|
||||
}
|
||||
tp.insert = tp.insert.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
||||
if (typeof tp.insert === "string") {
|
||||
tp.insert = tp.insert.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
||||
}
|
||||
if (tp.insert === "\n") {
|
||||
child.push(tp);
|
||||
cur = { insert: "", attributes: tp.attributes, children: child };
|
||||
@@ -53,7 +55,7 @@ function getParsedData(data: SctPostDataArr): SctPostDataArr {
|
||||
child = [];
|
||||
continue;
|
||||
}
|
||||
const parsedText = getParsedText(tp);
|
||||
const parsedText = getParsedText(<TpTextType>tp);
|
||||
let check = 0;
|
||||
for (let i = 0; i < parsedText.length; i++) {
|
||||
const text = parsedText[i];
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
'tp-texts-header6': props.data.attributes && props.data.attributes.header === 6,
|
||||
}"
|
||||
:title="getTitle()"
|
||||
:style="{ textAlign: props.data.attributes?.align }"
|
||||
>
|
||||
<component
|
||||
:is="getComp(text)"
|
||||
@@ -27,6 +26,7 @@ import type { Component } from "vue";
|
||||
import TpImage from "./tp-image.vue";
|
||||
import TpMention, { type TpMention as TpMentionType } from "./tp-mention.vue";
|
||||
import TpText, { type TpText as TpTextType } from "./tp-text.vue";
|
||||
import TpUnknown from "./tp-unknown.vue";
|
||||
|
||||
type TpTexts = { children: Array<TpTextType | TpMentionType> } & TpTextType;
|
||||
type TpTextsProps = { data: TpTexts };
|
||||
@@ -36,7 +36,8 @@ const props = defineProps<TpTextsProps>();
|
||||
function getComp(text: TpTextType | TpMentionType): Component {
|
||||
if (typeof text.insert === "string") return TpText;
|
||||
if ("image" in text.insert) return TpImage;
|
||||
return TpMention;
|
||||
if ("mention" in text.insert) return TpMention;
|
||||
return TpUnknown;
|
||||
}
|
||||
|
||||
function getTitle(): string {
|
||||
@@ -51,6 +52,7 @@ function getTitle(): string {
|
||||
line-break: anywhere;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
text-align: v-bind("props.data.attributes?.align");
|
||||
|
||||
&.tp-inline {
|
||||
display: inline;
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
</div>
|
||||
<div class="tpu-main">UID {{ props.data.insert.game_user_info.game_uid }}</div>
|
||||
<div class="tpu-sub">
|
||||
<span>{{ props.data.insert.game_user_info.nickname }}</span>
|
||||
<span>{{ nickname }}</span>
|
||||
<span>|</span>
|
||||
<span>{{ props.data.insert.game_user_info.region_name }}</span>
|
||||
<span>|</span>
|
||||
@@ -19,6 +19,7 @@
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import showSnackbar from "@comp/func/snackbar.js";
|
||||
import { computed } from "vue";
|
||||
|
||||
type TpUid = {
|
||||
insert: {
|
||||
@@ -35,6 +36,9 @@ type TpUid = {
|
||||
type TpUidProps = { data: TpUid };
|
||||
|
||||
const props = defineProps<TpUidProps>();
|
||||
const nickname = computed<string>(() =>
|
||||
decodeURIComponent(props.data.insert.game_user_info.nickname),
|
||||
);
|
||||
console.log("tpUid", props.data.insert.game_user_info);
|
||||
|
||||
function copyUid(): void {
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
/>
|
||||
<div class="tp-video-share">
|
||||
<img alt="cover" :src="videoCover" class="tp-video-cover" />
|
||||
<img alt="icon" src="/source/UI/video_play_bili.png" class="tp-video-icon" />
|
||||
<img alt="icon" src="/source/UI/video_play_bili.webp" class="tp-video-icon" />
|
||||
<div class="tp-video-info">
|
||||
<span>{{ videoData.bvid }}|{{ timestampToDate(videoData.ctime * 1000) }}</span>
|
||||
<span>{{ videoData.title }}</span>
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
<div class="tp-vote-list">
|
||||
<div v-for="(item, index) in votes?.data" :key="index" class="tp-vote-item">
|
||||
<div class="tp-vote-item-title">
|
||||
<span>{{ item.title }}</span>
|
||||
<span>
|
||||
<span class="title">{{ item.title }}</span>
|
||||
<span class="val">
|
||||
<span>{{ item.count }}票</span>
|
||||
<span>{{ item.percent.toFixed(2) }}%</span>
|
||||
</span>
|
||||
@@ -90,25 +90,26 @@ function getWidth(item: TpVoteData): string {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.tp-vote-item-title :first-child {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.tp-vote-item-title :last-child {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
}
|
||||
.val {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
white-space: nowrap;
|
||||
|
||||
.tp-vote-item-title :last-child :first-child {
|
||||
font-size: 12px;
|
||||
}
|
||||
:first-child {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tp-vote-item-title :last-child :last-child {
|
||||
font-size: 10px;
|
||||
:last-child {
|
||||
font-size: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tp-vote-progress {
|
||||
|
||||
@@ -769,7 +769,7 @@
|
||||
},
|
||||
{
|
||||
"id": 10000109,
|
||||
"contentId": 0,
|
||||
"contentId": 504440,
|
||||
"dropDays": [1, 4, 7],
|
||||
"name": "梦见月瑞希",
|
||||
"itemType": "character",
|
||||
@@ -2129,7 +2129,7 @@
|
||||
},
|
||||
{
|
||||
"id": 14518,
|
||||
"contentId": 0,
|
||||
"contentId": 504503,
|
||||
"dropDays": [2, 5, 7],
|
||||
"name": "寝正月初晴",
|
||||
"itemType": "weapon",
|
||||
@@ -4129,7 +4129,7 @@
|
||||
},
|
||||
{
|
||||
"id": 13432,
|
||||
"contentId": 0,
|
||||
"contentId": 504502,
|
||||
"dropDays": [3, 6, 7],
|
||||
"name": "且住亭御咄",
|
||||
"itemType": "weapon",
|
||||
|
||||
@@ -2520,5 +2520,41 @@
|
||||
"postId": "61893154",
|
||||
"up5List": [14518, 15513],
|
||||
"up4List": [12416, 13416, 15416, 11402, 14409]
|
||||
},
|
||||
{
|
||||
"name": "众水的颂诗",
|
||||
"version": "5.4",
|
||||
"order": 2,
|
||||
"banner": "https://sdk.hoyoverse.com/upload/ann/2025/02/17/16502c6ed976ad895e0e36db251af49b_5339316421173554000.jpg",
|
||||
"from": "2025-03-04T18:00:00+08:00",
|
||||
"to": "2025-03-25T14:59:00+08:00",
|
||||
"type": 301,
|
||||
"postId": "62298070",
|
||||
"up5List": [10000089],
|
||||
"up4List": [10000088, 10000036, 10000080]
|
||||
},
|
||||
{
|
||||
"name": "劫中泛滥",
|
||||
"version": "5.4",
|
||||
"order": 2,
|
||||
"banner": "https://sdk.hoyoverse.com/upload/ann/2025/02/17/1b6cf538066209d3aa510291d351f7cb_20450832996386688.jpg",
|
||||
"from": "2025-03-04T18:00:00+08:00",
|
||||
"to": "2025-03-25T14:59:00+08:00",
|
||||
"type": 400,
|
||||
"postId": "62298071",
|
||||
"up5List": [10000086],
|
||||
"up4List": [10000088, 10000036, 10000080]
|
||||
},
|
||||
{
|
||||
"name": "神铸赋形",
|
||||
"version": "5.4",
|
||||
"order": 2,
|
||||
"banner": "https://sdk.hoyoverse.com/upload/ann/2025/02/17/f517f95eb862d8196bedcb9e44db3b5b_2023560355910093809.jpg",
|
||||
"from": "2025-03-04T18:00:00+08:00",
|
||||
"to": "2025-03-25T14:59:00+08:00",
|
||||
"type": 302,
|
||||
"postId": "62298072",
|
||||
"up5List": [11513, 14513],
|
||||
"up4List": [11401, 12403, 13401, 14403, 15405]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
{ "id": 15503, "contentId": 1682, "name": "终末嗟叹之诗", "star": 5, "weapon": "弓" },
|
||||
{ "id": 15502, "contentId": 219, "name": "阿莫斯之弓", "star": 5, "weapon": "弓" },
|
||||
{ "id": 15501, "contentId": 323, "name": "天空之翼", "star": 5, "weapon": "弓" },
|
||||
{ "id": 14518, "contentId": 0, "name": "寝正月初晴", "star": 5, "weapon": "法器" },
|
||||
{ "id": 14518, "contentId": 504503, "name": "寝正月初晴", "star": 5, "weapon": "法器" },
|
||||
{ "id": 14517, "contentId": 503948, "name": "祭星者之望", "star": 5, "weapon": "法器" },
|
||||
{ "id": 14516, "contentId": 501962, "name": "冲浪时光", "star": 5, "weapon": "法器" },
|
||||
{ "id": 14515, "contentId": 500674, "name": "鹤鸣余音", "star": 5, "weapon": "法器" },
|
||||
@@ -101,7 +101,7 @@
|
||||
{ "id": 14403, "contentId": 197, "name": "祭礼残章", "star": 4, "weapon": "法器" },
|
||||
{ "id": 14402, "contentId": 192, "name": "流浪乐章", "star": 4, "weapon": "法器" },
|
||||
{ "id": 14401, "contentId": 185, "name": "西风秘典", "star": 4, "weapon": "法器" },
|
||||
{ "id": 13432, "contentId": 0, "name": "且住亭御咄", "star": 4, "weapon": "长柄武器" },
|
||||
{ "id": 13432, "contentId": 504502, "name": "且住亭御咄", "star": 4, "weapon": "长柄武器" },
|
||||
{ "id": 13431, "contentId": 501966, "name": "虹的行迹", "star": 4, "weapon": "长柄武器" },
|
||||
{ "id": 13430, "contentId": 502897, "name": "镇山之钉", "star": 4, "weapon": "长柄武器" },
|
||||
{ "id": 13427, "contentId": 7407, "name": "勘探钻机", "star": 4, "weapon": "长柄武器" },
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="ab-container">
|
||||
<div class="ab-draw-top">
|
||||
<div @click="toAct" class="ab-draw-act" title="前往网页活动">
|
||||
<img src="/source/UI/act_birthday.png" alt="archive_birthday_icon" class="side-icon" />
|
||||
<img src="/source/UI/act_birthday.webp" alt="archive_birthday_icon" class="side-icon" />
|
||||
</div>
|
||||
<v-switch class="ab-draw-switch" v-model="isAether" />
|
||||
<span>{{ isAether ? "空" : "荧" }}</span>
|
||||
@@ -20,7 +20,9 @@
|
||||
<div class="ab-draw-grid">
|
||||
<div v-for="item in selectedItem" :key="item.op_id" class="ab-draw">
|
||||
<div class="ab-draw-cover" @click="showImg(item)">
|
||||
<TMiImg :src="item.unread_picture[Number(isAether)]" :alt="item.word_text" />
|
||||
<div class="ab-draw-img">
|
||||
<TMiImg :src="item.unread_picture[Number(isAether)]" :alt="item.word_text" />
|
||||
</div>
|
||||
<div class="ab-draw-hide" />
|
||||
<v-icon class="ab-draw-icon">mdi-magnify</v-icon>
|
||||
</div>
|
||||
@@ -158,6 +160,15 @@ function getItemProps(item: TGApp.Archive.Birth.RoleItem) {
|
||||
aspect-ratio: 125 / 54;
|
||||
}
|
||||
|
||||
.ab-draw-img {
|
||||
position: relative;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.ab-draw-hide {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
|
||||
@@ -305,6 +305,7 @@ async function uploadAbyss(): Promise<void> {
|
||||
}
|
||||
showSnackbar.success(res.message ?? "上传深渊数据成功");
|
||||
await TGLogger.Info("[UserAbyss][uploadAbyss] 上传深渊数据成功");
|
||||
await TGLogger.Info(`[${res.retcode}] ${res.message}`);
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
showSnackbar.error(e.message);
|
||||
|
||||
264
src/pages/User/Scripts.vue
Normal file
@@ -0,0 +1,264 @@
|
||||
<template>
|
||||
<v-app-bar>
|
||||
<template #prepend>
|
||||
<div class="us-top-title">
|
||||
<img alt="icon" src="/source/UI/toolbox.webp" />
|
||||
<span>实用脚本</span>
|
||||
<v-select
|
||||
class="us-top-select"
|
||||
variant="outlined"
|
||||
v-model="curAccount"
|
||||
:items="accounts"
|
||||
item-title="uid"
|
||||
:hide-details="true"
|
||||
title="账号UID"
|
||||
>
|
||||
<template #selection="{ item }">
|
||||
<div class="select-main">
|
||||
<img alt="icon" :src="item.raw.brief.avatar" />
|
||||
<div class="content">
|
||||
<span>{{ item.raw.brief.nickname }}</span>
|
||||
<span>UID:{{ item.raw.uid }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #item="{ props, item }">
|
||||
<div class="select-item" v-bind="props">
|
||||
<img alt="icon" :src="item.raw.brief.avatar" />
|
||||
<div class="content">
|
||||
<span>{{ item.raw.brief.nickname }}</span>
|
||||
<span>UID:{{ item.raw.uid }}</span>
|
||||
</div>
|
||||
<div class="append">
|
||||
<v-icon v-if="item.raw.uid === uid" color="green" title="当前登录账号">
|
||||
mdi-account-check
|
||||
</v-icon>
|
||||
<v-icon
|
||||
v-else
|
||||
size="small"
|
||||
icon="mdi-account-convert"
|
||||
title="切换用户"
|
||||
@click="loadAccount(item.raw.uid)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</v-select>
|
||||
</div>
|
||||
</template>
|
||||
<template #append>
|
||||
<span class="top-hint" @click="tryCkVerify()" title="点击验证">
|
||||
需要验证码登录所需cookie!!!
|
||||
</span>
|
||||
</template>
|
||||
</v-app-bar>
|
||||
<div class="us-page-container">
|
||||
<!-- 左侧脚本列表 -->
|
||||
<div class="us-scripts">
|
||||
<div class="us-title">脚本列表</div>
|
||||
<TusMission v-model="runScript" />
|
||||
</div>
|
||||
<!-- 右侧脚本输出 -->
|
||||
<TusOutput />
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import showDialog from "@comp/func/dialog.js";
|
||||
import showLoading from "@comp/func/loading.js";
|
||||
import showSnackbar from "@comp/func/snackbar.js";
|
||||
import TusMission from "@comp/userScripts/tus-mission.vue";
|
||||
import TusOutput from "@comp/userScripts/tus-output.vue";
|
||||
import TSUserAccount from "@Sqlite/modules/userAccount.js";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { onMounted, ref, shallowRef } from "vue";
|
||||
|
||||
import { useUserStore } from "@/store/modules/user.js";
|
||||
import apiHubReq from "@/web/request/apiHubReq.js";
|
||||
|
||||
const { uid, briefInfo, cookie, account } = storeToRefs(useUserStore());
|
||||
const accounts = shallowRef<Array<TGApp.App.Account.User>>([]);
|
||||
const curAccount = shallowRef<TGApp.App.Account.User>();
|
||||
const runScript = ref<boolean>(false);
|
||||
|
||||
onMounted(async () => {
|
||||
accounts.value = await TSUserAccount.account.getAllAccount();
|
||||
curAccount.value = accounts.value.find((i) => i.uid === uid.value);
|
||||
});
|
||||
|
||||
async function loadAccount(ac: string): Promise<void> {
|
||||
if (uid.value && ac === uid.value) {
|
||||
showSnackbar.warn("该账户已经登录,无需切换");
|
||||
return;
|
||||
}
|
||||
const accountGet = await TSUserAccount.account.getAccount(ac);
|
||||
if (!accountGet) {
|
||||
showSnackbar.warn(`未找到${uid}的账号信息,请重新登录`);
|
||||
return;
|
||||
}
|
||||
uid.value = ac;
|
||||
briefInfo.value = accountGet.brief;
|
||||
cookie.value = accountGet.cookie;
|
||||
const gameAccount = await TSUserAccount.game.getCurAccount(ac);
|
||||
if (!gameAccount) {
|
||||
showSnackbar.warn(`未找到${uid}的游戏账号信息,请尝试刷新`);
|
||||
return;
|
||||
}
|
||||
account.value = gameAccount;
|
||||
showSnackbar.success(`成功切换到用户${uid}`);
|
||||
}
|
||||
|
||||
async function tryCkVerify(): Promise<void> {
|
||||
if (!cookie.value) {
|
||||
showSnackbar.warn("当前账号未登录,请先登录");
|
||||
return;
|
||||
}
|
||||
const check = await showDialog.check("确定验证?", "将通过执行米社社区打卡以验证ck有效性");
|
||||
if (!check) {
|
||||
showSnackbar.cancel("已取消验证");
|
||||
return;
|
||||
}
|
||||
await showLoading.start("正在验证CK有效性");
|
||||
const ck = {
|
||||
stoken: cookie.value.stoken,
|
||||
stuid: cookie.value.stuid,
|
||||
mid: cookie.value.mid,
|
||||
};
|
||||
const resp = await apiHubReq.sign(ck);
|
||||
await showLoading.update(`[${resp.retcode}] ${resp.message}`);
|
||||
if (resp.retcode === -100) {
|
||||
showSnackbar.error("CK验证失败,请通过验证码登录重新获取CK");
|
||||
await showLoading.end();
|
||||
return;
|
||||
}
|
||||
await showLoading.end();
|
||||
showSnackbar.success("CK验证成功");
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.us-top-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 10px;
|
||||
gap: 10px;
|
||||
|
||||
img {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
span {
|
||||
color: var(--common-text-title);
|
||||
font-family: var(--font-title);
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.us-top-select {
|
||||
width: 250px;
|
||||
max-width: 250px;
|
||||
}
|
||||
|
||||
.select-main {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
column-gap: 4px;
|
||||
height: 24px;
|
||||
|
||||
img {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.content {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
:first-child {
|
||||
font-family: var(--font-title);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
:last-child {
|
||||
font-size: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.select-item {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
column-gap: 4px;
|
||||
padding: 8px;
|
||||
box-sizing: border-box;
|
||||
|
||||
img {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.content {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
:first-child {
|
||||
font-family: var(--font-title);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
:last-child {
|
||||
font-size: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.append {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.top-hint {
|
||||
position: relative;
|
||||
padding: 8px;
|
||||
font-size: 20px;
|
||||
color: var(--tgc-pink-1);
|
||||
font-family: var(--font-title);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.us-page-container {
|
||||
display: flex;
|
||||
height: calc(100vh - 100px);
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
column-gap: 12px;
|
||||
}
|
||||
|
||||
.us-scripts {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
overflow-y: auto;
|
||||
row-gap: 4px;
|
||||
}
|
||||
|
||||
.us-title {
|
||||
position: relative;
|
||||
margin-right: auto;
|
||||
color: var(--common-text-title);
|
||||
font-family: var(--font-title);
|
||||
font-size: 24px;
|
||||
}
|
||||
</style>
|
||||
@@ -95,11 +95,11 @@ watch(
|
||||
);
|
||||
|
||||
onMounted(async () => {
|
||||
await showLoading.start("正在获取深渊数据", "正在获取深渊概览");
|
||||
overview.value = {
|
||||
cur: await Hutao.Abyss.overview(),
|
||||
last: await Hutao.Abyss.overview(true),
|
||||
};
|
||||
await showLoading.start("正在获取深渊数据", "正在获取上期深渊概览");
|
||||
const lastData = await Hutao.Abyss.overview(true);
|
||||
await showLoading.update("正在获取本期深渊概览");
|
||||
const curData = await Hutao.Abyss.overview();
|
||||
overview.value = { scur: curData, last: lastData };
|
||||
await showLoading.update("正在获取角色使用率数据");
|
||||
abyssData.use = <AbyssDataItem<TGApp.Plugins.Hutao.Abyss.AvatarUse[]>>await getData("use");
|
||||
await showLoading.end();
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
<div class="wc-box">
|
||||
<div class="wc-left">
|
||||
<div class="wc-select">
|
||||
<v-btn @click="showSelect = true">筛选角色</v-btn>
|
||||
<v-btn @click="resetSelect = true">重置筛选</v-btn>
|
||||
<v-btn @click="showSelect = true" class="wc-btn">筛选角色</v-btn>
|
||||
<v-btn @click="resetSelect = true" class="wc-btn">重置筛选</v-btn>
|
||||
</div>
|
||||
<div class="wc-list">
|
||||
<TwcListItem
|
||||
@@ -129,6 +129,16 @@ async function toOuter(item?: TGApp.App.Character.WikiBriefInfo): Promise<void>
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.wc-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
background: var(--tgc-btn-1);
|
||||
color: var(--btn-text);
|
||||
}
|
||||
|
||||
.wc-list {
|
||||
position: relative;
|
||||
display: grid;
|
||||
|
||||
@@ -228,7 +228,7 @@ function searchMaterial(): void {
|
||||
.twm-item-right {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
max-width: 100%;
|
||||
max-width: calc(100% - 50px);
|
||||
color: var(--box-text-2);
|
||||
font-size: 14px;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
@@ -2,10 +2,8 @@
|
||||
<div class="ww-box">
|
||||
<div class="ww-left">
|
||||
<div class="ww-select">
|
||||
<v-btn @click="showSelect = true">
|
||||
<span>筛选武器</span>
|
||||
</v-btn>
|
||||
<v-btn @click="resetSelect = true">重置筛选</v-btn>
|
||||
<v-btn @click="showSelect = true" class="ww-btn">筛选武器</v-btn>
|
||||
<v-btn @click="resetSelect = true" class="ww-btn">重置筛选</v-btn>
|
||||
</div>
|
||||
<div class="ww-list">
|
||||
<TwcListItem
|
||||
@@ -116,6 +114,16 @@ async function toOuter(item?: TGApp.App.Weapon.WikiBriefInfo): Promise<void> {
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.ww-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
background: var(--tgc-btn-1);
|
||||
color: var(--btn-text);
|
||||
}
|
||||
|
||||
.ww-list {
|
||||
position: relative;
|
||||
display: grid;
|
||||
|
||||
@@ -76,6 +76,7 @@
|
||||
:label="devMode ? '开启' : '关闭'"
|
||||
:inset="true"
|
||||
color="#FAC51E"
|
||||
class="config-switch"
|
||||
@click="submitDevMode"
|
||||
/>
|
||||
</template>
|
||||
@@ -92,10 +93,28 @@
|
||||
:label="isNeedResize ? '开启' : '关闭'"
|
||||
:inset="true"
|
||||
color="#FAC51E"
|
||||
class="config-switch"
|
||||
@click="submitResize"
|
||||
/>
|
||||
</template>
|
||||
</v-list-item>
|
||||
<v-list-item title="无痕浏览" subtitle="关闭后将记录帖子浏览记录">
|
||||
<template #prepend>
|
||||
<div class="config-icon">
|
||||
<v-icon>mdi-incognito</v-icon>
|
||||
</div>
|
||||
</template>
|
||||
<template #append>
|
||||
<v-switch
|
||||
v-model="appStore.incognito"
|
||||
:label="appStore.incognito ? '开启' : '关闭'"
|
||||
:inset="true"
|
||||
class="config-switch"
|
||||
color="#FAC51E"
|
||||
@click="switchIncognito"
|
||||
/>
|
||||
</template>
|
||||
</v-list-item>
|
||||
<v-list-item title="分享设置" v-if="platform() === 'windows'">
|
||||
<template #subtitle>默认保存到剪贴板,超过{{ shareDefaultFile }}MB时保存到文件</template>
|
||||
<template #prepend>
|
||||
@@ -138,7 +157,7 @@ import TcGameBadge from "@comp/pageConfig/tc-gameBadge.vue";
|
||||
import TcInfo from "@comp/pageConfig/tc-info.vue";
|
||||
import TcUserBadge from "@comp/pageConfig/tc-userBadge.vue";
|
||||
import TGSqlite from "@Sqlite/index.js";
|
||||
import { core } from "@tauri-apps/api";
|
||||
import { core, event } from "@tauri-apps/api";
|
||||
import { open } from "@tauri-apps/plugin-dialog";
|
||||
import { remove } from "@tauri-apps/plugin-fs";
|
||||
import { platform } from "@tauri-apps/plugin-os";
|
||||
@@ -491,6 +510,16 @@ function submitResize(): void {
|
||||
}
|
||||
showSnackbar.success("已开启窗口回正!");
|
||||
}
|
||||
|
||||
// 开启无痕浏览
|
||||
async function switchIncognito(): Promise<void> {
|
||||
await event.emitTo("Sub_window", "switchIncognito");
|
||||
if (appStore.incognito) {
|
||||
showSnackbar.success("已关闭无痕浏览!");
|
||||
return;
|
||||
}
|
||||
showSnackbar.success("已开启无痕浏览!");
|
||||
}
|
||||
</script>
|
||||
<style lang="css" scoped>
|
||||
.config-box {
|
||||
@@ -529,6 +558,10 @@ function submitResize(): void {
|
||||
color: var(--box-text-2);
|
||||
}
|
||||
|
||||
.config-switch {
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.config-right {
|
||||
position: fixed;
|
||||
top: 16px;
|
||||
|
||||
@@ -13,9 +13,27 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="btn-list">
|
||||
<v-btn @click="test()" class="test-btn">测试</v-btn>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup></script>
|
||||
<script lang="ts" setup>
|
||||
import showSnackbar from "@comp/func/snackbar.js";
|
||||
|
||||
import takumiReq from "@/web/request/takumiReq.js";
|
||||
|
||||
const gameToken = "LCLQ2pYLnEDh7p03ogJVxL9dZqbeLtUE";
|
||||
const uid = "249066520";
|
||||
|
||||
async function test(): Promise<void> {
|
||||
const resp = await takumiReq.game.stoken({ uid: uid, token: gameToken });
|
||||
if (resp.retcode !== 0) {
|
||||
showSnackbar.error(`[${resp.retcode}] ${resp.message}`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="css" scoped>
|
||||
.test-box {
|
||||
display: flex;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<v-app-bar>
|
||||
<div class="pc-top">
|
||||
<div class="pc-title">
|
||||
<img src="/source/UI/posts.png" alt="posts" />
|
||||
<img src="/source/UI/posts.webp" alt="posts" />
|
||||
<span>收藏</span>
|
||||
</div>
|
||||
<v-select
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<v-app-bar>
|
||||
<template #prepend>
|
||||
<div class="posts-top">
|
||||
<img src="/source/UI/posts.png" alt="posts" />
|
||||
<img src="/source/UI/posts.webp" alt="posts" />
|
||||
<span>帖子</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
/**
|
||||
* @file plugins/Mys/request/postReq.ts
|
||||
* @description 帖子相关的获取
|
||||
* @since Beta v0.6.7
|
||||
* @since Beta v0.7.0
|
||||
*/
|
||||
|
||||
import TGHttp from "@/utils/TGHttp.js";
|
||||
import { getRequestHeader } from "@/web/utils/getRequestHeader.js";
|
||||
|
||||
// MysPostApiBaseUrl => Mpabu
|
||||
const Mpabu: Readonly<string> = "https://bbs-api.mihoyo.com/post/wapi/";
|
||||
@@ -14,17 +15,27 @@ const Referer: Readonly<string> = "https://bbs.mihoyo.com/";
|
||||
|
||||
/**
|
||||
* @description 获取单个帖子信息
|
||||
* @since Beta v0.6.3
|
||||
* @since Beta v0.7.0
|
||||
* @param {number} postId 帖子 ID
|
||||
* @param {Record<string, string>} cookie Cookie
|
||||
* @return {Promise<TGApp.Plugins.Mys.Post.FullData|TGApp.BBS.Response.Base>}
|
||||
*/
|
||||
export async function getPostFull(
|
||||
postId: number,
|
||||
cookie?: Record<string, string>,
|
||||
): Promise<TGApp.Plugins.Mys.Post.FullData | TGApp.BBS.Response.Base> {
|
||||
const param = { post_id: postId, read: 1 };
|
||||
let header;
|
||||
if (cookie) {
|
||||
header = {
|
||||
...getRequestHeader(cookie, "GET", param, "K2", true),
|
||||
"x-rpc-client_type": "2",
|
||||
};
|
||||
} else header = { referer: Referer };
|
||||
const resp = await TGHttp<TGApp.Plugins.Mys.Post.Response>(`${Mpabu}getPostFull`, {
|
||||
method: "GET",
|
||||
headers: { referer: Referer },
|
||||
query: { post_id: postId },
|
||||
headers: header,
|
||||
query: param,
|
||||
});
|
||||
if (resp.retcode !== 0) return <TGApp.BBS.Response.Base>resp;
|
||||
return resp.data.post;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
* @file plugins/Sqlite/modules/avatarBirth.ts
|
||||
* @description 角色生日模块
|
||||
* @since Beta v0.4.6
|
||||
* @since Beta v0.7.0
|
||||
*/
|
||||
|
||||
import { AppCharacterData, ArcBirCalendar, ArcBirRole } from "@/data/index.js";
|
||||
import { AppCharacterData, ArcBirCalendar, ArcBirRole, WikiCharacterData } from "@/data/index.js";
|
||||
|
||||
/**
|
||||
* @description 判断今天是不是角色生日
|
||||
@@ -32,44 +32,77 @@ function isAvatarBirth(): TGApp.Archive.Birth.CalendarItem[] {
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取角色生日
|
||||
* @description 校验是否是闰年
|
||||
* @since Beta v0.4.6
|
||||
* @param {string} roleBirthday - 角色生日
|
||||
* @return {[number,number]} 角色生日
|
||||
* @param {number} year - 年份
|
||||
* @return {boolean} 是否是闰年
|
||||
*/
|
||||
function getRoleBirth(roleBirthday: string): [number, number] {
|
||||
const arr: string[] = roleBirthday.split("/");
|
||||
return [Number(arr[0]), Number(arr[1])];
|
||||
function isLeapYear(year: number): boolean {
|
||||
return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取下一个角色生日
|
||||
* @since Beta v0.4.5
|
||||
* @since Beta v0.7.0
|
||||
* @param {[number,number]} date - 日期
|
||||
* @return {TGApp.Archive.Birth.RoleItem[]} 下一个角色生日
|
||||
*/
|
||||
function getNextAvatarBirth(date?: [number, number]): TGApp.Archive.Birth.RoleItem[] {
|
||||
const year = new Date().getFullYear();
|
||||
let month, day;
|
||||
if (date) {
|
||||
month = date[0];
|
||||
day = date[1];
|
||||
} else {
|
||||
const dateNow = new Date();
|
||||
month = dateNow.getMonth() + 1;
|
||||
day = dateNow.getDate();
|
||||
month = new Date().getMonth() + 1;
|
||||
day = new Date().getDate();
|
||||
}
|
||||
const birthDateList: Date[] = [];
|
||||
for (const item of ArcBirRole) {
|
||||
const roleBirth = getRoleBirth(item.role_birthday);
|
||||
if (roleBirth[0] < month || (roleBirth[0] === month && roleBirth[1] <= day)) {
|
||||
birthDateList.push(new Date(year + 1, roleBirth[0] - 1, roleBirth[1]));
|
||||
} else birthDateList.push(new Date(year, roleBirth[0] - 1, roleBirth[1]));
|
||||
const sortList = AppCharacterData.sort((a, b) => {
|
||||
if (a.birthday[0] === b.birthday[0]) return a.birthday[1] - b.birthday[1];
|
||||
return a.birthday[0] - b.birthday[0];
|
||||
});
|
||||
let filterList = sortList.filter((i) => {
|
||||
if (i.birthday[0] > month) return true;
|
||||
return i.birthday[0] === month && i.birthday[1] > day;
|
||||
});
|
||||
if (filterList.length === 0) filterList = sortList;
|
||||
let birthGet = filterList[0];
|
||||
if (birthGet.id === 10000032 && !isLeapYear(new Date().getFullYear())) {
|
||||
birthGet = filterList[1];
|
||||
}
|
||||
birthDateList.sort((a, b) => a.getTime() - b.getTime());
|
||||
const nextDateGet = birthDateList[0];
|
||||
const nextDate = [nextDateGet.getMonth() + 1, nextDateGet.getDate()];
|
||||
return ArcBirRole.filter((i) => i.role_birthday === `${nextDate[0]}/${nextDate[1]}`);
|
||||
const dataGet = AppCharacterData.filter(
|
||||
(i) => i.birthday.toString() === birthGet.birthday.toString(),
|
||||
);
|
||||
const res: TGApp.Archive.Birth.RoleItem[] = [];
|
||||
for (const i of dataGet) {
|
||||
const find = ArcBirRole.find((j) => j.role_id === i.id);
|
||||
if (find) {
|
||||
res.push(find);
|
||||
continue;
|
||||
}
|
||||
const find2 = WikiCharacterData.find((j) => j.id === i.id);
|
||||
if (!find2) continue;
|
||||
// 只写了用到的字段
|
||||
res.push({
|
||||
belong: find2.brief.camp,
|
||||
current_compensate_num: 0,
|
||||
divine_type: "",
|
||||
element: find2.element,
|
||||
head_image: `/WIKI/character/${i.id}.webp`,
|
||||
introduce: find2.description,
|
||||
is_compensate_num: false,
|
||||
is_finish_task: false,
|
||||
is_god: false,
|
||||
seat_life: "",
|
||||
text: "",
|
||||
year_compensate_num: 0,
|
||||
role_id: i.id,
|
||||
name: i.name,
|
||||
role_birthday: `${i.birthday[0]}/${i.birthday[1]}`,
|
||||
head_icon: `/WIKI/character/${i.id}.webp`,
|
||||
is_subscribe: false,
|
||||
});
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
const TSAvatarBirth = { isAvatarBirth, getNextAvatarBirth };
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* @file router index.ts
|
||||
* @description 路由入口
|
||||
* @since Beta v0.6.8
|
||||
* @since Beta v0.7.0
|
||||
*/
|
||||
|
||||
import { createRouter, createWebHistory } from "vue-router";
|
||||
@@ -9,11 +9,13 @@ import { createRouter, createWebHistory } from "vue-router";
|
||||
import routes from "./routes.js";
|
||||
|
||||
const router = createRouter({ history: createWebHistory(), routes: routes });
|
||||
// 只有在特定页面忽略参数变化
|
||||
const ignoreRoutes: ReadonlyArray<string> = ["酒馆", "话题"];
|
||||
|
||||
// 解决路由重复问题
|
||||
router.afterEach((to, from) => {
|
||||
if (from.name === to.name) {
|
||||
if (from.query !== to.query) return;
|
||||
if (from.query !== to.query && ignoreRoutes.includes(from.name?.toString() ?? "")) return;
|
||||
window.location.reload();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* @file router modules user.ts
|
||||
* @description user 路由模块
|
||||
* @since Beta v0.6.3
|
||||
* @since Beta v0.7.0
|
||||
*/
|
||||
import type { RouteRecordRaw } from "vue-router";
|
||||
|
||||
@@ -31,6 +31,11 @@ const userRoutes = (<const>[
|
||||
name: "原神战绩",
|
||||
component: async () => await import("@/pages/User/Record.vue"),
|
||||
},
|
||||
{
|
||||
path: "/user/scripts",
|
||||
name: "实用脚本",
|
||||
component: async () => await import("@/pages/User/Scripts.vue"),
|
||||
},
|
||||
]) satisfies Array<RouteRecordRaw>;
|
||||
|
||||
export default userRoutes;
|
||||
|
||||
@@ -61,6 +61,8 @@ export const useAppStore = defineStore(
|
||||
const shareDefaultFile = ref<number>(10);
|
||||
// 图像压缩质量
|
||||
const imageQualityPercent = ref<number>(80);
|
||||
// 无痕浏览
|
||||
const incognito = ref<boolean>(true);
|
||||
|
||||
// 初始化
|
||||
function init(): void {
|
||||
@@ -75,6 +77,7 @@ export const useAppStore = defineStore(
|
||||
gameDir.value = "未设置";
|
||||
shareDefaultFile.value = 10;
|
||||
imageQualityPercent.value = 10;
|
||||
incognito.value = true;
|
||||
initDevice();
|
||||
}
|
||||
|
||||
@@ -109,6 +112,7 @@ export const useAppStore = defineStore(
|
||||
gameDir,
|
||||
shareDefaultFile,
|
||||
imageQualityPercent,
|
||||
incognito,
|
||||
init,
|
||||
changeTheme,
|
||||
getImageUrl,
|
||||
@@ -132,6 +136,7 @@ export const useAppStore = defineStore(
|
||||
"needResize",
|
||||
"shareDefaultFile",
|
||||
"imageQualityPercent",
|
||||
"incognito",
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
112
src/types/BBS/Mission.d.ts
vendored
Normal file
@@ -0,0 +1,112 @@
|
||||
/**
|
||||
* @file types/BBS/Mission.d.ts
|
||||
* @description BBS 任务相关类型定义文件
|
||||
* @since Beta v0.7.0
|
||||
*/
|
||||
|
||||
declare namespace TGApp.BBS.Mission {
|
||||
/**
|
||||
* @description 任务信息返回
|
||||
* @interface InfoResp
|
||||
* @extends TGApp.BBS.Response.BaseWithData
|
||||
* @since Beta v0.7.0
|
||||
* @property {TGApp.BBS.Mission.InfoRes} data 任务信息
|
||||
* @return InfoResp
|
||||
*/
|
||||
type InfoResp = TGApp.BBS.Response.BaseWithData<InfoRes>;
|
||||
|
||||
/**
|
||||
* @description 任务信息
|
||||
* @interface InfoRes
|
||||
* @since Beta v0.7.0
|
||||
* @property {Array<MissionItem>} missions 任务列表
|
||||
* @property {Array<MissionItem>} more_missions 更多任务列表
|
||||
* @return InfoRes
|
||||
*/
|
||||
type InfoRes = { missions: Array<MissionItem>; more_missions: Array<MissionItem> };
|
||||
|
||||
/**
|
||||
* @description 任务项
|
||||
* @interface MissionItem
|
||||
* @since Beta v0.7.0
|
||||
* @property {number} id 任务 ID
|
||||
* @property {string} name 任务名称
|
||||
* @property {string} desc 任务描述
|
||||
* @property {number} threshold 任务完成阈值
|
||||
* @property {number} limit 任务限制
|
||||
* @property {number} exp 任务经验
|
||||
* @property {number} points 米游币
|
||||
* @property {number} active_time 任务激活时间,秒级时间戳
|
||||
* @property {number} end_time 任务结束时间
|
||||
* @property {boolean} is_auto_send_award 是否自动发放奖励
|
||||
* @property {number} continuous_cycle_times 连续周期次数
|
||||
* @property {number} next_points 下一次奖励米游币
|
||||
* @property {string} mission_key 任务 key
|
||||
* @return MissionItem
|
||||
*/
|
||||
type MissionItem = {
|
||||
id: number;
|
||||
name: string;
|
||||
desc: string;
|
||||
threshold: number;
|
||||
limit: number;
|
||||
exp: number;
|
||||
points: number;
|
||||
active_time: number;
|
||||
end_time: number;
|
||||
is_auto_send_award: boolean;
|
||||
continuous_cycle_times: number;
|
||||
next_points: number;
|
||||
mission_key: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description 任务状态返回
|
||||
* @interface StateResp
|
||||
* @extends TGApp.BBS.Response.BaseWithData
|
||||
* @since Beta v0.7.0
|
||||
* @property {TGApp.BBS.Mission.StateRes} data 任务状态
|
||||
* @return StateResp
|
||||
*/
|
||||
type StateResp = TGApp.BBS.Response.BaseWithData<StateRes>;
|
||||
|
||||
/**
|
||||
* @description 任务状态
|
||||
* @interface StateRes
|
||||
* @since Beta v0.7.0
|
||||
* @property {Array<StateItem>} states 任务状态列表
|
||||
* @property {number} already_received_points 已领取的米游币
|
||||
* @property {number} total_points 总米游币
|
||||
* @property {number} today_total_points 今日总米游币
|
||||
* @property {boolean} is_unclaimed 是否有未领取的奖励
|
||||
* @property {number} can_get_points 可领取的米游币
|
||||
* @return StateRes
|
||||
*/
|
||||
type StateRes = {
|
||||
states: Array<StateItem>;
|
||||
already_received_points: number;
|
||||
total_points: number;
|
||||
today_total_points: number;
|
||||
is_unclaimed: boolean;
|
||||
can_get_points: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description 任务状态项
|
||||
* @interface StateItem
|
||||
* @since Beta v0.7.0
|
||||
* @property {number} mission_id 任务 ID
|
||||
* @property {number} process 任务进度 0:未完成,1:已完成
|
||||
* @property {number} happened_times 发生次数
|
||||
* @property {boolean} is_get_award 是否领取奖励
|
||||
* @property {string} mission_key 任务 key
|
||||
* @return StateItem
|
||||
*/
|
||||
type StateItem = {
|
||||
mission_id: number;
|
||||
process: number;
|
||||
happened_times: number;
|
||||
is_get_award: boolean;
|
||||
mission_key: string;
|
||||
};
|
||||
}
|
||||
75
src/types/Game/Login.d.ts
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
* @file types/Game/Login.d.ts
|
||||
* @description 扫码登录模块类型定义文件
|
||||
* @since Beta v0.7.0
|
||||
*/
|
||||
|
||||
declare namespace TGApp.Game.Login {
|
||||
/**
|
||||
* @description 获取登录二维码返回数据
|
||||
* @since Beta v0.7.0
|
||||
* @interface QrResp
|
||||
* @extends TGApp.BBS.Response.BaseWithData
|
||||
* @property {QrRes} data 数据
|
||||
* @return QrResp
|
||||
*/
|
||||
type QrResp = TGApp.BBS.Response.BaseWithData & { data: QrRes };
|
||||
|
||||
/**
|
||||
* @description 获取登录二维码返回数据
|
||||
* @since Beta v0.7.0
|
||||
* @interface QrRes
|
||||
* @property {string} url 二维码链接
|
||||
* @return QrRes
|
||||
*/
|
||||
type QrRes = { url: string };
|
||||
|
||||
/**
|
||||
* @description 获取登录状态返回数据
|
||||
* @since Beta v0.7.0
|
||||
* @interface StatusResp
|
||||
* @extends TGApp.BBS.Response.BaseWithData
|
||||
* @property {StatusRes} data 数据
|
||||
* @return StatusResp
|
||||
*/
|
||||
type StatusResp = TGApp.BBS.Response.BaseWithData & { data: StatusRes };
|
||||
|
||||
/**
|
||||
* @description 二维码状态
|
||||
* @since Beta v0.7.0
|
||||
* @interface QrStatus
|
||||
* @return QrStatus
|
||||
*/
|
||||
type QrStatus = "Init" | "Scanned" | "Confirmed";
|
||||
|
||||
/**
|
||||
* @description 获取登录状态返回数据
|
||||
* @since Beta v0.7.0
|
||||
* @interface StatusRes
|
||||
* @property {string} stat 状态 // Init: 未扫码,Scanned: 已扫码,Confirmed: 已确认
|
||||
* @property {string} payload 状态数据
|
||||
* @return StatusRes
|
||||
*/
|
||||
type StatusRes = { stat: QrStatus; payload: StatusPayload };
|
||||
|
||||
/**
|
||||
* @description 获取登录状态返回数据
|
||||
* @since Beta v0.7.0
|
||||
* @interface StatusPayload
|
||||
* @property {string} ext 未知
|
||||
* @property {string} proto 未知
|
||||
* @property {string} raw 序列化数据,反序列化后是 StatusPayloadRaw
|
||||
* @return StatusPayload
|
||||
*/
|
||||
type StatusPayload = { ext: string; proto: string; raw: string };
|
||||
|
||||
/**
|
||||
* @description 反序列化后的登录状态数据
|
||||
* @since Beta v0.7.0
|
||||
* @interface StatusPayloadRaw
|
||||
* @property {string} uid 用户 UID
|
||||
* @property {string} token 用户 token
|
||||
* @return StatusPayloadRaw
|
||||
*/
|
||||
type StatusPayloadRaw = { uid: string; token: string };
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
* @file utils/TGBbs.ts
|
||||
* @description 关于 BBS 的工具函数
|
||||
* @since Beta v0.6.8
|
||||
* @since Beta v0.7.0
|
||||
*/
|
||||
|
||||
const BBS_VERSION: Readonly<string> = "2.80.1";
|
||||
const BBS_VERSION: Readonly<string> = "2.82.0";
|
||||
const BBS_UA_MOBILE: Readonly<string> = `Mozilla/5.0 (Linux; Android 12) Mobile miHoYoBBS/${BBS_VERSION}`;
|
||||
|
||||
/**
|
||||
@@ -33,17 +33,6 @@ const CHANNEL_LIST: Readonly<Array<ChannelItem>> = [
|
||||
{ title: "大别野", gid: 5, mini: "dby" },
|
||||
];
|
||||
|
||||
/**
|
||||
* @description 获取游戏id
|
||||
* @since Beta v0.6.8
|
||||
* @param {string} mini
|
||||
* @returns {string}
|
||||
*/
|
||||
export function getGameId(mini: string): number {
|
||||
const game = CHANNEL_LIST.find((item) => item.mini === mini);
|
||||
return game ? game.gid : 0;
|
||||
}
|
||||
|
||||
const TGBbs = { version: BBS_VERSION, ua: BBS_UA_MOBILE, channels: CHANNEL_LIST };
|
||||
|
||||
export default TGBbs;
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
/**
|
||||
* @file utils/TGLogger.ts
|
||||
* @description 日志工具
|
||||
* @since Beta v0.6.8
|
||||
* @since Beta v0.7.0
|
||||
*/
|
||||
|
||||
import { attachConsole, error, info, warn, debug } from "@tauri-apps/plugin-log";
|
||||
import { event } from "@tauri-apps/api";
|
||||
import { attachConsole, debug, error, info, warn } from "@tauri-apps/plugin-log";
|
||||
|
||||
import { timestampToDate } from "@/utils/toolFunc.js";
|
||||
|
||||
/**
|
||||
* @description 日志工具
|
||||
@@ -71,6 +74,34 @@ class Logger {
|
||||
if (write) await error(message);
|
||||
console.error(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 输出日志-脚本
|
||||
* @since Beta v0.7.0
|
||||
* @param {string} message 日志信息
|
||||
* @returns {Promise<void>} 无返回值
|
||||
*/
|
||||
async Script(message: string): Promise<void> {
|
||||
const timeNow = timestampToDate(new Date().getTime());
|
||||
const msg = `[${timeNow}]${message}`;
|
||||
await event.emitTo("TeyvatGuide", "userScriptLog", msg);
|
||||
await info(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 输出日志-脚本分隔符
|
||||
* @since Beta v0.7.0
|
||||
* @param {string} label 标签
|
||||
* @param {boolean} [start] 是否为开始,默认为 true
|
||||
* @returns {Promise<void>} 无返回值
|
||||
*/
|
||||
async ScriptSep(label: string, start: boolean = true): Promise<void> {
|
||||
const midStr = `${label} ${start ? "START" : "END--"}`;
|
||||
const msg = `//--------------------${midStr}--------------------//`;
|
||||
await event.emitTo("TeyvatGuide", "userScriptLog", msg);
|
||||
if (!start) await event.emitTo("TeyvatGuide", "userScriptLog", "");
|
||||
await info(msg);
|
||||
}
|
||||
}
|
||||
|
||||
const TGLogger = Logger.getInstance();
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
*/
|
||||
|
||||
import TSUserGacha from "@Sqlite/modules/userGacha.js";
|
||||
// @ts-expect-error no-export-member
|
||||
import type { BarSeriesOption } from "echarts/charts.js";
|
||||
import type { EChartsOption, XAXisOption } from "echarts/types/dist/shared.js";
|
||||
|
||||
|
||||
@@ -1,28 +1,96 @@
|
||||
<template>
|
||||
<TSwitchTheme />
|
||||
<div class="anno-json">
|
||||
<div class="anno-title">活动列表 JSON</div>
|
||||
<JsonViewer :value="jsonList" copyable boxed />
|
||||
<div class="anno-title">活动内容 JSON</div>
|
||||
<JsonViewer :value="jsonContent" copyable boxed />
|
||||
<div class="taj-page">
|
||||
<v-expansion-panels>
|
||||
<v-expansion-panel>
|
||||
<template #title>
|
||||
<div class="taj-title">活动列表 JSON</div>
|
||||
</template>
|
||||
<template #text>
|
||||
<div class="taj-box">
|
||||
<vue-json-pretty
|
||||
:data="JSON.parse(JSON.stringify(jsonList))"
|
||||
:show-icon="true"
|
||||
:show-length="true"
|
||||
:show-line="true"
|
||||
:show-line-number="true"
|
||||
:show-double-quotes="true"
|
||||
:show-key-value-space="true"
|
||||
:collapsed-on-click-brackets="true"
|
||||
:deep="2"
|
||||
:theme="jsonTheme"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</v-expansion-panel>
|
||||
<v-expansion-panel>
|
||||
<template #title>
|
||||
<div class="taj-title">活动内容 JSON</div>
|
||||
</template>
|
||||
<template #text>
|
||||
<div class="taj-box">
|
||||
<vue-json-pretty
|
||||
:data="JSON.parse(JSON.stringify(jsonContent))"
|
||||
:show-icon="true"
|
||||
:show-length="true"
|
||||
:show-line="true"
|
||||
:show-line-number="true"
|
||||
:show-double-quotes="true"
|
||||
:show-key-value-space="true"
|
||||
:collapsed-on-click-brackets="true"
|
||||
:deep="2"
|
||||
:theme="jsonTheme"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</v-expansion-panel>
|
||||
<v-expansion-panel>
|
||||
<template #title>
|
||||
<div class="taj-title">解析 JSON</div>
|
||||
</template>
|
||||
<template #text>
|
||||
<div class="taj-box">
|
||||
<vue-json-pretty
|
||||
:data="JSON.parse(JSON.stringify(parsedJson))"
|
||||
:show-icon="true"
|
||||
:show-length="true"
|
||||
:show-line="true"
|
||||
:show-line-number="true"
|
||||
:show-double-quotes="true"
|
||||
:show-key-value-space="true"
|
||||
:collapsed-on-click-brackets="true"
|
||||
:deep="2"
|
||||
:theme="jsonTheme"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</v-expansion-panel>
|
||||
</v-expansion-panels>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import TSwitchTheme from "@comp/app/t-switchTheme.vue";
|
||||
import showLoading from "@comp/func/loading.js";
|
||||
import { onMounted, shallowRef } from "vue";
|
||||
import JsonViewer from "vue-json-viewer";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { computed, onMounted, shallowRef } from "vue";
|
||||
import VueJsonPretty from "vue-json-pretty";
|
||||
import "vue-json-pretty/lib/styles.css";
|
||||
import { useRoute } from "vue-router";
|
||||
|
||||
import { useAppStore } from "@/store/modules/app.js";
|
||||
import Hk4eApi, { type AnnoLang, AnnoServer } from "@/web/request/hk4eReq.js";
|
||||
import parseAnnoContent from "@/web/utils/annoParser.js";
|
||||
|
||||
// 数据
|
||||
const route = useRoute();
|
||||
const annoId = Number(route.params.anno_id);
|
||||
const region = <AnnoServer>route.params.region;
|
||||
const lang = <AnnoLang>route.params.lang;
|
||||
const { theme } = storeToRefs(useAppStore());
|
||||
const jsonList = shallowRef<TGApp.BBS.Announcement.AnnoSingle>();
|
||||
const jsonContent = shallowRef<TGApp.BBS.Announcement.ContentItem>();
|
||||
const parsedJson = shallowRef<Array<TGApp.Plugins.Mys.SctPost.Base>>();
|
||||
const jsonTheme = computed<"dark" | "light">(() => (theme.value === "dark" ? "dark" : "light"));
|
||||
|
||||
onMounted(async () => {
|
||||
await showLoading.start("正在获取公告数据");
|
||||
@@ -41,32 +109,30 @@ onMounted(async () => {
|
||||
}
|
||||
}
|
||||
jsonContent.value = await Hk4eApi.anno.content(annoId, region, lang);
|
||||
parsedJson.value = parseAnnoContent(jsonContent.value);
|
||||
await showLoading.end();
|
||||
});
|
||||
</script>
|
||||
<style lang="css" scoped>
|
||||
.anno-json {
|
||||
padding: 20px;
|
||||
border-radius: 20px;
|
||||
<style lang="scss" scoped>
|
||||
.taj-page {
|
||||
width: 800px;
|
||||
margin: 0 auto;
|
||||
font-family: var(--font-text);
|
||||
}
|
||||
|
||||
.anno-title {
|
||||
width: 100%;
|
||||
margin: 10px 0;
|
||||
color: #546d8b;
|
||||
.taj-title {
|
||||
color: var(--common-text-title);
|
||||
font-family: var(--font-title);
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.jv-container {
|
||||
background: var(--box-bg-2) !important;
|
||||
}
|
||||
|
||||
.jv-key,
|
||||
.jv-array {
|
||||
color: var(--box-text-4) !important;
|
||||
.taj-box {
|
||||
border-radius: 4px;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
box-sizing: border-box;
|
||||
max-width: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
<TPinWin />
|
||||
<TShareBtn selector=".anno-body" :title="`Anno_${route.params.anno_id}`" />
|
||||
<div class="anno-body" v-if="annoData">
|
||||
<div class="anno-info">AnnoID: {{ annoId }} | Render by TeyvatGuide v{{ appVersion }}</div>
|
||||
<div class="anno-info" @click="createAnnoJson">
|
||||
AnnoID: {{ annoId }} | Render by TeyvatGuide v{{ appVersion }}
|
||||
</div>
|
||||
<div class="anno-title">{{ annoData.title }}</div>
|
||||
<div class="anno-subtitle">{{ parseText(annoData.subtitle) }}</div>
|
||||
<div class="anno-content">
|
||||
@@ -57,7 +59,7 @@ onMounted(async () => {
|
||||
return;
|
||||
}
|
||||
const isDev = useAppStore().devMode ?? false;
|
||||
if (isDev) await createAnnoJson(annoId, region, lang);
|
||||
if (isDev) await createAnnoJson();
|
||||
await showLoading.end();
|
||||
});
|
||||
|
||||
@@ -67,7 +69,7 @@ function parseText(title: string): string {
|
||||
return div.innerText;
|
||||
}
|
||||
|
||||
async function createAnnoJson(annoId: number, region: AnnoServer, lang: AnnoLang): Promise<void> {
|
||||
async function createAnnoJson(): Promise<void> {
|
||||
const jsonPath = `/anno_detail_json/${region}/${annoId}/${lang}`;
|
||||
const jsonTitle = `Anno_${region}_${annoId}_${lang}_JSON`;
|
||||
await createTGWindow(jsonPath, "Dev_JSON", jsonTitle, 960, 720, false, false);
|
||||
|
||||
@@ -1,10 +1,50 @@
|
||||
<template>
|
||||
<TSwitchTheme />
|
||||
<div class="post-json">
|
||||
<div class="post-title">帖子返回内容 JSON</div>
|
||||
<JsonViewer :value="jsonData" copyable boxed />
|
||||
<div class="post-title" v-show="!isEmpty">结构化内容解析</div>
|
||||
<JsonViewer v-if="!isEmpty" :value="parseData" copyable boxed />
|
||||
<div class="tpj-page">
|
||||
<v-expansion-panels>
|
||||
<v-expansion-panel>
|
||||
<template #title>
|
||||
<div class="tpj-title">帖子返回内容 JSON</div>
|
||||
</template>
|
||||
<template #text>
|
||||
<div class="tpj-box">
|
||||
<vue-json-pretty
|
||||
:data="JSON.parse(JSON.stringify(jsonData))"
|
||||
:show-icon="true"
|
||||
:show-length="true"
|
||||
:show-line="true"
|
||||
:show-line-number="true"
|
||||
:show-double-quotes="true"
|
||||
:show-key-value-space="true"
|
||||
:collapsed-on-click-brackets="true"
|
||||
:deep="2"
|
||||
:theme="jsonTheme"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</v-expansion-panel>
|
||||
<v-expansion-panel>
|
||||
<template #title>
|
||||
<div class="tpj-title">帖子解析内容 JSON</div>
|
||||
</template>
|
||||
<template #text>
|
||||
<div class="tpj-box">
|
||||
<vue-json-pretty
|
||||
:data="parseData"
|
||||
:show-icon="true"
|
||||
:show-length="true"
|
||||
:show-line="true"
|
||||
:show-line-number="true"
|
||||
:show-double-quotes="true"
|
||||
:show-key-value-space="true"
|
||||
:collapsed-on-click-brackets="true"
|
||||
:deep="2"
|
||||
:theme="jsonTheme"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</v-expansion-panel>
|
||||
</v-expansion-panels>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
@@ -12,16 +52,23 @@ import TSwitchTheme from "@comp/app/t-switchTheme.vue";
|
||||
import showLoading from "@comp/func/loading.js";
|
||||
import showSnackbar from "@comp/func/snackbar.js";
|
||||
import Mys from "@Mys/index.js";
|
||||
import { onMounted, ref, shallowRef } from "vue";
|
||||
import JsonViewer from "vue-json-viewer";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { computed, onMounted, ref, shallowRef } from "vue";
|
||||
import VueJsonPretty from "vue-json-pretty";
|
||||
import "vue-json-pretty/lib/styles.css";
|
||||
import { useRoute } from "vue-router";
|
||||
|
||||
import { useAppStore } from "@/store/modules/app.js";
|
||||
import { useUserStore } from "@/store/modules/user.js";
|
||||
import TGLogger from "@/utils/TGLogger.js";
|
||||
|
||||
const { theme } = storeToRefs(useAppStore());
|
||||
const { cookie } = storeToRefs(useUserStore());
|
||||
const postId = Number(useRoute().params.post_id);
|
||||
const isEmpty = ref<boolean>(false);
|
||||
const jsonData = shallowRef<TGApp.Plugins.Mys.Post.FullData>();
|
||||
const parseData = shallowRef<Array<TGApp.Plugins.Mys.SctPost.Base>>();
|
||||
const jsonTheme = computed<"dark" | "light">(() => (theme.value === "dark" ? "dark" : "light"));
|
||||
|
||||
onMounted(async () => {
|
||||
await showLoading.start(`正在获取帖子数据`);
|
||||
@@ -29,7 +76,9 @@ onMounted(async () => {
|
||||
await showLoading.empty("未获取到PostID");
|
||||
return;
|
||||
}
|
||||
const resp = await Mys.Post.getPostFull(postId);
|
||||
let ck: Record<string, string> | undefined = undefined;
|
||||
if (cookie.value) ck = { ltoken: cookie.value.ltoken, ltuid: cookie.value.ltuid };
|
||||
const resp = await Mys.Post.getPostFull(postId, ck);
|
||||
if ("retcode" in resp) {
|
||||
await showLoading.empty("获取数据失败", `[${resp.retcode}]${resp.message}`);
|
||||
showSnackbar.error(`[${resp.retcode}]${resp.message}`);
|
||||
@@ -52,29 +101,27 @@ onMounted(async () => {
|
||||
await showLoading.end();
|
||||
});
|
||||
</script>
|
||||
<style lang="css" scoped>
|
||||
.post-json {
|
||||
padding: 20px;
|
||||
border-radius: 20px;
|
||||
<style lang="scss" scoped>
|
||||
.tpj-page {
|
||||
width: 800px;
|
||||
margin: 0 auto;
|
||||
font-family: var(--font-text);
|
||||
}
|
||||
|
||||
.post-title {
|
||||
width: 100%;
|
||||
margin: 10px 0;
|
||||
color: #546d8b;
|
||||
.tpj-title {
|
||||
color: var(--common-text-title);
|
||||
font-family: var(--font-title);
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.jv-container {
|
||||
background: var(--box-bg-2) !important;
|
||||
}
|
||||
|
||||
.jv-key,
|
||||
.jv-array {
|
||||
color: var(--box-text-4) !important;
|
||||
.tpj-box {
|
||||
border-radius: 4px;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
box-sizing: border-box;
|
||||
max-width: 100%;
|
||||
background: var(--box-bg-1);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -27,7 +27,12 @@
|
||||
<v-icon>mdi-comment</v-icon>
|
||||
<span>{{ postData?.stat?.reply_num }}</span>
|
||||
</div>
|
||||
<div class="mpm-item" :title="`点赞数:${postData?.stat?.like_num}`">
|
||||
<div
|
||||
class="mpm-item"
|
||||
:title="`点赞数:${postData?.stat?.like_num}`"
|
||||
@click="tryLike()"
|
||||
:class="{ like: isLike }"
|
||||
>
|
||||
<v-icon>mdi-thumb-up</v-icon>
|
||||
<span>{{ postData?.stat?.like_num }}</span>
|
||||
</div>
|
||||
@@ -96,24 +101,32 @@ import VpBtnReply from "@comp/viewPost/vp-btn-reply.vue";
|
||||
import VpOverlayCollection from "@comp/viewPost/vp-overlay-collection.vue";
|
||||
import Mys from "@Mys/index.js";
|
||||
import { app, webviewWindow } from "@tauri-apps/api";
|
||||
import { emit } from "@tauri-apps/api/event";
|
||||
import { onMounted, onUnmounted, ref, shallowRef } from "vue";
|
||||
import { emit, listen, UnlistenFn } from "@tauri-apps/api/event";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { onBeforeMount, onMounted, onUnmounted, ref, shallowRef } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
|
||||
import { useAppStore } from "@/store/modules/app.js";
|
||||
import { useUserStore } from "@/store/modules/user.js";
|
||||
import TGBbs from "@/utils/TGBbs.js";
|
||||
import TGClient from "@/utils/TGClient.js";
|
||||
import TGLogger from "@/utils/TGLogger.js";
|
||||
import { createTGWindow } from "@/utils/TGWindow.js";
|
||||
import apiHubReq from "@/web/request/apiHubReq.js";
|
||||
|
||||
const { cookie } = storeToRefs(useUserStore());
|
||||
const { incognito } = storeToRefs(useAppStore());
|
||||
const appVersion = ref<string>();
|
||||
const postId = Number(useRoute().params.post_id);
|
||||
const showCollection = ref<boolean>(false);
|
||||
const isLike = ref<boolean>(false);
|
||||
const shareTime = ref<number>(Math.floor(Date.now() / 1000));
|
||||
const renderPost = shallowRef<Array<TGApp.Plugins.Mys.SctPost.Base>>([]);
|
||||
const postData = shallowRef<TGApp.Plugins.Mys.Post.FullData>();
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
let shareTimer: NodeJS.Timeout | null = null;
|
||||
let incognitoListener: UnlistenFn | null = null;
|
||||
|
||||
function getGameIcon(gameId: number): string {
|
||||
const find = TGBbs.channels.find((item) => item.gid === gameId);
|
||||
@@ -121,6 +134,10 @@ function getGameIcon(gameId: number): string {
|
||||
return "/platforms/mhy/mys.webp";
|
||||
}
|
||||
|
||||
onBeforeMount(async () => {
|
||||
incognitoListener = await listen<void>("switchIncognito", () => window.location.reload());
|
||||
});
|
||||
|
||||
onMounted(async () => {
|
||||
await showLoading.start(`正在加载帖子数据`);
|
||||
appVersion.value = await app.getVersion();
|
||||
@@ -131,7 +148,11 @@ onMounted(async () => {
|
||||
return;
|
||||
}
|
||||
await showLoading.update(`帖子ID: ${postId}`);
|
||||
const resp = await Mys.Post.getPostFull(postId);
|
||||
let ck: undefined | Record<string, string> = undefined;
|
||||
if (cookie.value && incognito.value === false) {
|
||||
ck = { ltoken: cookie.value.ltoken, ltuid: cookie.value.ltuid };
|
||||
}
|
||||
const resp = await Mys.Post.getPostFull(postId, ck);
|
||||
if ("retcode" in resp) {
|
||||
await showLoading.empty("数据加载失败", `[${resp.retcode}]${resp.message}`);
|
||||
showSnackbar.error(`[${resp.retcode}] ${resp.message}`);
|
||||
@@ -140,6 +161,7 @@ onMounted(async () => {
|
||||
return;
|
||||
}
|
||||
postData.value = resp;
|
||||
isLike.value = postData.value.self_operation.upvote_type !== 0;
|
||||
await showLoading.update("正在渲染数据");
|
||||
renderPost.value = await getRenderPost(postData.value);
|
||||
await webviewWindow
|
||||
@@ -242,13 +264,36 @@ async function createPostJson(postId: number): Promise<void> {
|
||||
await createTGWindow(jsonPath, "Dev_JSON", jsonTitle, 960, 720, false, false);
|
||||
}
|
||||
|
||||
function toPost(): void {
|
||||
const channel = TGBbs.channels.find((item) => item.gid === postData.value?.post.game_id);
|
||||
if (channel) {
|
||||
window.open(`https://miyoushe.com/${channel.mini}/#/article/${postId}`);
|
||||
} else {
|
||||
window.open(`https://miyoushe.com/ys/#/article/${postId}`);
|
||||
async function tryLike(): Promise<void> {
|
||||
if (!cookie.value) {
|
||||
showSnackbar.error("请先登录");
|
||||
return;
|
||||
}
|
||||
if (!postData.value) {
|
||||
showSnackbar.error("数据未加载");
|
||||
return;
|
||||
}
|
||||
if (incognito.value) {
|
||||
showSnackbar.error("无法在无痕浏览模式下操作");
|
||||
return;
|
||||
}
|
||||
const ck = { ltoken: cookie.value.ltoken, ltuid: cookie.value.ltuid };
|
||||
const resp = await apiHubReq.post.like(postData.value.post.post_id, ck, isLike.value);
|
||||
if (resp.retcode !== 0) {
|
||||
showSnackbar.error(`[${resp.retcode}] ${resp.message}`);
|
||||
return;
|
||||
}
|
||||
isLike.value = !isLike.value;
|
||||
postData.value.stat!.like_num += isLike.value ? 1 : -1;
|
||||
showSnackbar.success(isLike.value ? "点赞成功" : "取消点赞成功");
|
||||
}
|
||||
|
||||
async function toPost(): Promise<void> {
|
||||
const channel = TGBbs.channels.find((item) => item.gid === postData.value?.post.game_id);
|
||||
const link = channel
|
||||
? `https://m.miyoushe.com/${channel.mini}/#/article/${postId}`
|
||||
: `https://m.miyoushe.com/ys/#/article/${postId}`;
|
||||
await TGClient.open("web_thin", link);
|
||||
}
|
||||
|
||||
async function toTopic(topic: TGApp.Plugins.Mys.Topic.Info): Promise<void> {
|
||||
@@ -265,6 +310,10 @@ onUnmounted(() => {
|
||||
clearInterval(shareTimer);
|
||||
shareTimer = null;
|
||||
}
|
||||
if (incognitoListener !== null) {
|
||||
incognitoListener();
|
||||
incognitoListener = null;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<style lang="css" scoped>
|
||||
@@ -380,6 +429,10 @@ onUnmounted(() => {
|
||||
margin-left: 10px;
|
||||
column-gap: 2px;
|
||||
opacity: 0.8;
|
||||
|
||||
&.like {
|
||||
color: var(--tgc-pink-1);
|
||||
}
|
||||
}
|
||||
|
||||
/* extra */
|
||||
|
||||
15
src/vite-env.d.ts
vendored
@@ -13,21 +13,6 @@ declare module "*.vue" {
|
||||
export default component;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description vue-json-viewer
|
||||
* @package vue-json-viewer
|
||||
* @version 3.0.4
|
||||
*/
|
||||
declare module "vue-json-viewer" {
|
||||
import type { DefineComponent } from "vue";
|
||||
const component: DefineComponent<{
|
||||
value: any;
|
||||
copyable: boolean;
|
||||
boxed: boolean;
|
||||
}>;
|
||||
export default component;
|
||||
}
|
||||
|
||||
declare type ImportMeta = { readonly env: { MODE: string } };
|
||||
|
||||
declare interface TauriProcessEnv extends NodeJS.ProcessEnv {
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
/**
|
||||
* @file web/request/apiHubReq.ts
|
||||
* @description apiHub下的请求
|
||||
* @since Beta v0.6.8
|
||||
* @since Beta v0.7.0
|
||||
*/
|
||||
|
||||
import TGHttp from "@/utils/TGHttp.js";
|
||||
import { getRequestHeader } from "@/web/utils/getRequestHeader.js";
|
||||
|
||||
// MysApiHubApiBaseUrl => Mahabu
|
||||
const Mahabu: Readonly<string> = "https://bbs-api.miyoushe.com/apihub/api/";
|
||||
// MysApiHubWapiBaseUrl => Mahwbu
|
||||
const Mahwbu: Readonly<string> = "https://bbs-api.miyoushe.com/apihub/wapi/";
|
||||
// MysApiHubBaseUrl => Mahbu
|
||||
const Mahbu: Readonly<string> = "https://bbs-api.miyoushe.com/apihub/";
|
||||
const Referer: Readonly<string> = "https://bbs.mihoyo.com/";
|
||||
|
||||
/**
|
||||
@@ -19,7 +18,7 @@ const Referer: Readonly<string> = "https://bbs.mihoyo.com/";
|
||||
*/
|
||||
async function getAllGamesForums(): Promise<Array<TGApp.BBS.Forum.GameForum>> {
|
||||
return (
|
||||
await TGHttp<TGApp.BBS.Forum.GameForumResp>(`${Mahwbu}getAllGamesForums`, {
|
||||
await TGHttp<TGApp.BBS.Forum.GameForumResp>(`${Mahbu}wapi/getAllGamesForums`, {
|
||||
method: "GET",
|
||||
headers: { "Content-Type": "application/json", referer: Referer },
|
||||
})
|
||||
@@ -33,13 +32,68 @@ async function getAllGamesForums(): Promise<Array<TGApp.BBS.Forum.GameForum>> {
|
||||
*/
|
||||
async function getGameList(): Promise<Array<TGApp.BBS.Game.Item>> {
|
||||
return (
|
||||
await TGHttp<TGApp.BBS.Game.ListResp>(`${Mahwbu}getGameList`, {
|
||||
await TGHttp<TGApp.BBS.Game.ListResp>(`${Mahbu}wapi/getGameList`, {
|
||||
method: "GET",
|
||||
headers: { "Content-Type": "application/json", referer: Referer },
|
||||
})
|
||||
).data.list;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取用户米游币任务完成情况
|
||||
* @since Beta v0.7.0
|
||||
* @param {Record<string,string>} cookie 用户 Cookie
|
||||
* @return {Promise<TGApp.BBS.Mission.InfoRes>}
|
||||
*/
|
||||
async function getMissions(cookie: Record<string, string>): Promise<TGApp.BBS.Mission.InfoResp> {
|
||||
const header = getRequestHeader(cookie, "GET", {});
|
||||
return await TGHttp<TGApp.BBS.Mission.InfoResp>(`${Mahbu}api/getMissions`, {
|
||||
method: "GET",
|
||||
headers: header,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取分享配置
|
||||
* @since Beta v0.7.0
|
||||
* @description **需要验证码登录返回的 Cookie**
|
||||
* @param {string} postId 帖子 ID
|
||||
* @param {Record<string,string>} cookie 用户 Cookie
|
||||
* @return {Promise<TGApp.BBS.Response.Base>}
|
||||
*/
|
||||
async function getShareConf(
|
||||
postId: string,
|
||||
cookie: Record<string, string>,
|
||||
): Promise<TGApp.BBS.Response.Base> {
|
||||
const params = { entity_id: postId, entity_type: 1 };
|
||||
const header = {
|
||||
...getRequestHeader(cookie, "GET", params, "K2", true),
|
||||
"x-rpc-client_type": "2",
|
||||
};
|
||||
return await TGHttp<TGApp.BBS.Response.Base>(`${Mahbu}api/getShareConf`, {
|
||||
method: "GET",
|
||||
headers: header,
|
||||
query: params,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取任务完成情况
|
||||
* @since Beta v0.7.0
|
||||
* @description **需要验证码登录的 Cookie**
|
||||
* @param {Record<string,string>} cookie 用户 Cookie
|
||||
* @return {Promise<TGApp.BBS.Mission.StateResp>}
|
||||
*/
|
||||
async function getUserMissionsState(
|
||||
cookie: Record<string, string>,
|
||||
): Promise<TGApp.BBS.Mission.StateResp> {
|
||||
const header = getRequestHeader(cookie, "GET", {});
|
||||
return await TGHttp<TGApp.BBS.Mission.StateResp>(`${Mahbu}sapi/getUserMissionsState`, {
|
||||
method: "GET",
|
||||
headers: header,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取投票信息
|
||||
* @since Beta v0.6.2
|
||||
@@ -49,7 +103,7 @@ async function getGameList(): Promise<Array<TGApp.BBS.Game.Item>> {
|
||||
*/
|
||||
async function getVotes(id: string, uid: string): Promise<TGApp.BBS.Vote.Info> {
|
||||
return (
|
||||
await TGHttp<TGApp.BBS.Vote.InfoResp>(`${Mahabu}getVotes`, {
|
||||
await TGHttp<TGApp.BBS.Vote.InfoResp>(`${Mahbu}api/getVotes`, {
|
||||
method: "GET",
|
||||
headers: { "Content-Type": "application/json", referer: Referer },
|
||||
query: { owner_uid: uid, vote_ids: id },
|
||||
@@ -66,7 +120,7 @@ async function getVotes(id: string, uid: string): Promise<TGApp.BBS.Vote.Info> {
|
||||
*/
|
||||
async function getVoteResult(id: string, uid: string): Promise<TGApp.BBS.Vote.Result> {
|
||||
return (
|
||||
await TGHttp<TGApp.BBS.Vote.ResultResp>(`${Mahabu}getVotesResult`, {
|
||||
await TGHttp<TGApp.BBS.Vote.ResultResp>(`${Mahbu}api/getVotesResult`, {
|
||||
method: "GET",
|
||||
headers: { "Content-Type": "application/json", referer: Referer },
|
||||
query: { owner_uid: uid, vote_ids: id },
|
||||
@@ -82,7 +136,7 @@ async function getVoteResult(id: string, uid: string): Promise<TGApp.BBS.Vote.Re
|
||||
*/
|
||||
async function homeNew(gid: number = 2): Promise<TGApp.BBS.Navigator.Navigator[]> {
|
||||
return (
|
||||
await TGHttp<TGApp.BBS.Navigator.HomeResponse>(`${Mahabu}home/new`, {
|
||||
await TGHttp<TGApp.BBS.Navigator.HomeResponse>(`${Mahbu}api/home/new`, {
|
||||
method: "GET",
|
||||
headers: { "x-rpc-client_type": "2" },
|
||||
query: { gids: gid },
|
||||
@@ -90,11 +144,63 @@ async function homeNew(gid: number = 2): Promise<TGApp.BBS.Navigator.Navigator[]
|
||||
).data.navigator;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 签到
|
||||
* @since Beta v0.7.0
|
||||
* @description **需要验证码登录获取的 Cookie**
|
||||
* @param {Record<string,string>} cookie 用户 Cookie
|
||||
* @param {string} gid
|
||||
* @return {Promise<TGApp.BBS.Response.Base>}
|
||||
*/
|
||||
async function signIn(
|
||||
cookie: Record<string, string>,
|
||||
gid: number = 2,
|
||||
): Promise<TGApp.BBS.Response.Base> {
|
||||
const data = { gids: gid };
|
||||
const header = {
|
||||
...getRequestHeader(cookie, "POST", JSON.stringify(data), "X6"),
|
||||
"x-rpc-client_type": "2",
|
||||
};
|
||||
return await TGHttp<TGApp.BBS.Response.Base>(`${Mahbu}app/api/signIn`, {
|
||||
method: "POST",
|
||||
headers: header,
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 点赞
|
||||
* @since Beta v0.7.0
|
||||
* @param {string} id 帖子 ID
|
||||
* @param {Record<string,string>} cookie 用户 Cookie
|
||||
* @param {boolean} cancel 是否取消点赞
|
||||
* @return {Promise<TGApp.BBS.Response.Base>}
|
||||
*/
|
||||
async function upVotePost(
|
||||
id: string,
|
||||
cookie: Record<string, string>,
|
||||
cancel: boolean = false,
|
||||
): Promise<TGApp.BBS.Response.Base> {
|
||||
const data = { is_cancel: cancel, post_id: id };
|
||||
const header = {
|
||||
...getRequestHeader(cookie, "POST", data, "K2", true),
|
||||
"x-rpc-client_type": "2",
|
||||
};
|
||||
return await TGHttp<TGApp.BBS.Response.Base>(`${Mahbu}api/upvotePost`, {
|
||||
method: "POST",
|
||||
headers: header,
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
}
|
||||
|
||||
const apiHubReq = {
|
||||
vote: { info: getVotes, result: getVoteResult },
|
||||
home: homeNew,
|
||||
forum: getAllGamesForums,
|
||||
game: getGameList,
|
||||
mission: { list: getMissions, state: getUserMissionsState },
|
||||
sign: signIn,
|
||||
post: { like: upVotePost, share: getShareConf },
|
||||
};
|
||||
|
||||
export default apiHubReq;
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
/**
|
||||
* @file web/request/hk4eReq.ts
|
||||
* @description Hk4eApi 请求模块
|
||||
* @since Beta v0.6.3
|
||||
* @since Beta v0.7.0
|
||||
*/
|
||||
|
||||
import TGHttp from "@/utils/TGHttp.js";
|
||||
import { getDeviceInfo } from "@/utils/toolFunc.js";
|
||||
|
||||
export enum AnnoServer {
|
||||
CN_ISLAND = "cn_gf01",
|
||||
@@ -20,6 +21,7 @@ export type AnnoLang = "zh-cn" | "zh-tw" | "en" | "ja";
|
||||
const AnnoApi: Readonly<string> = "https://hk4e-ann-api.mihoyo.com/common/hk4e_cn/announcement/api";
|
||||
const AnnoApiGlobal: Readonly<string> =
|
||||
"https://sg-hk4e-api.hoyoverse.com/common/hk4e_global/announcement/api";
|
||||
const SdkApi: Readonly<string> = "https://hk4e-sdk.mihoyo.com/hk4e_cn/";
|
||||
|
||||
/**
|
||||
* @description 判断是否为国内服务器
|
||||
@@ -137,9 +139,48 @@ async function getGachaLog(
|
||||
return resp.data.list;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取登录二维码
|
||||
* @since Beta v0.7.0
|
||||
* @param {string} appId 应用 ID // 目前只有2/7能用
|
||||
* @returns {Promise<TGApp.Game.Login.QrRes|TGApp.BBS.Response.Base>}
|
||||
*/
|
||||
async function fetchPandaQr(
|
||||
appId: string = "2",
|
||||
): Promise<TGApp.Game.Login.QrRes | TGApp.BBS.Response.Base> {
|
||||
const data = { app_id: appId, device: getDeviceInfo("device_id") };
|
||||
const resp = await TGHttp<TGApp.Game.Login.QrResp>(`${SdkApi}combo/panda/qrcode/fetch`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
if (resp.retcode !== 0) return <TGApp.BBS.Response.Base>resp;
|
||||
return resp.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取登录状态
|
||||
* @since Beta v0.7.0
|
||||
* @param {string} ticket 二维码 ticket
|
||||
* @param {string} appId 应用 ID
|
||||
* @returns {Promise<TGApp.BBS.Response.Base|TGApp.Game.Login.StatusRes>}
|
||||
*/
|
||||
async function queryPandaQr(
|
||||
ticket: string,
|
||||
appId: string = "2",
|
||||
): Promise<TGApp.BBS.Response.Base | TGApp.Game.Login.StatusRes> {
|
||||
const data = { app_id: appId, ticket, device: getDeviceInfo("device_id") };
|
||||
const resp = await TGHttp<TGApp.Game.Login.StatusResp>(`${SdkApi}combo/panda/qrcode/query`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
if (resp.retcode !== 0) return <TGApp.BBS.Response.Base>resp;
|
||||
return resp.data;
|
||||
}
|
||||
|
||||
const Hk4eApi = {
|
||||
anno: { list: getAnnoList, content: getAnnoContent },
|
||||
gacha: getGachaLog,
|
||||
loginQr: { create: fetchPandaQr, state: queryPandaQr },
|
||||
};
|
||||
|
||||
export default Hk4eApi;
|
||||
|
||||
@@ -1,15 +1,59 @@
|
||||
/**
|
||||
* @file web/request/takumiReq.ts
|
||||
* @description Takumi 相关请求函数
|
||||
* @since Beta v0.6.3
|
||||
* @since Beta v0.7.0
|
||||
*/
|
||||
import TGBbs from "@/utils/TGBbs.js";
|
||||
import TGHttp from "@/utils/TGHttp.js";
|
||||
import { getRequestHeader } from "@/web/utils/getRequestHeader.js";
|
||||
import { getDeviceInfo } from "@/utils/toolFunc.js";
|
||||
import { getDS, getRequestHeader } from "@/web/utils/getRequestHeader.js";
|
||||
|
||||
// TakumiAuthApiBaseUrl => taAbu
|
||||
const taAbu: Readonly<string> = "https://api-takumi.mihoyo.com/auth/api/";
|
||||
// TakumiBingApiBaseUrl => tbAbu
|
||||
const tbAbu: Readonly<string> = "https://api-takumi.mihoyo.com/binding/api/";
|
||||
// TakumiApiBaseUrl => taBu
|
||||
const taBu: Readonly<string> = "https://api-takumi.mihoyo.com/";
|
||||
|
||||
/**
|
||||
* @description 根据gameToken获取stoken
|
||||
* @todo -100
|
||||
* @param {TGApp.Game.Login.StatusPayloadRaw} raw 状态数据
|
||||
* @returns {Promise<TGApp.BBS.Response.Base|string>}
|
||||
*/
|
||||
async function getSTokenByGameToken(
|
||||
raw: TGApp.Game.Login.StatusPayloadRaw,
|
||||
): Promise<TGApp.BBS.Response.Base> {
|
||||
const data = { account_id: raw.uid, game_token: raw.token };
|
||||
// const header = {
|
||||
// ...getRequestHeader(ck, "POST", JSON.stringify(data), "X6"),
|
||||
// "x-rpc-client_type": "4",
|
||||
// "x-rpc-app_id": "bll8iq97cem8",
|
||||
// "x-rpc-game_biz": "bbs_cn",
|
||||
// };
|
||||
const header = {
|
||||
"x-rpc-app_version": TGBbs.version,
|
||||
"x-rpc-aigis": "",
|
||||
"Content-Type": "application/json",
|
||||
"x-rpc-game_biz": "bbs_cn",
|
||||
"x-rpc-sys_version": "12",
|
||||
"x-rpc-device_id": getDeviceInfo("device_id"),
|
||||
"x-rpc-device_name": getDeviceInfo("device_name"),
|
||||
"x-rpc-device_model": getDeviceInfo("product"),
|
||||
"x-rpc-app_id": "bll8iq97cem8",
|
||||
"x-rpc-client_type": "4",
|
||||
"User-Agent": "okhttp/4.9.3",
|
||||
ds: getDS("POST", JSON.stringify(data), "X6", false),
|
||||
cookie: `account_id=${raw.uid};ltuid=${raw.uid};stuid=${raw.uid};game_token=${raw.token};`,
|
||||
};
|
||||
const resp = await TGHttp<TGApp.BBS.Response.Base>(
|
||||
`${taBu}account/ma-cn-session/app/getTokenByGameToken`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: header,
|
||||
body: JSON.stringify(data),
|
||||
},
|
||||
);
|
||||
if (resp.retcode !== 0) return <TGApp.BBS.Response.Base>resp;
|
||||
console.log(resp);
|
||||
return resp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 根据stoken获取action_ticket
|
||||
@@ -26,7 +70,7 @@ async function getActionTicketBySToken(
|
||||
): Promise<ActionTicketByStokenResp> {
|
||||
const ck = { stoken: cookie.stoken, mid: cookie.mid };
|
||||
const params = { action_type: actionType, stoken: cookie.stoken, uid: user.gameUid };
|
||||
return await TGHttp<ActionTicketByStokenResp>(`${taAbu}getActionTicketBySToken`, {
|
||||
return await TGHttp<ActionTicketByStokenResp>(`${taBu}auth/api/getActionTicketBySToken`, {
|
||||
method: "GET",
|
||||
headers: getRequestHeader(ck, "GET", params, "K2"),
|
||||
query: params,
|
||||
@@ -52,7 +96,7 @@ async function genAuthKey(
|
||||
region: account.region,
|
||||
};
|
||||
const resp = await TGHttp<TGApp.Game.Gacha.AuthkeyResponse | TGApp.BBS.Response.Base>(
|
||||
`${tbAbu}genAuthKey`,
|
||||
`${taBu}binding/api/genAuthKey`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: getRequestHeader(ck, "POST", JSON.stringify(data), "LK2", true),
|
||||
@@ -74,7 +118,7 @@ async function genAuthKey2(
|
||||
cookie: Record<string, string>,
|
||||
payload: Record<string, string>,
|
||||
): Promise<TGApp.BBS.Response.Base> {
|
||||
return await TGHttp<TGApp.BBS.Response.Base>(`${tbAbu}genAuthKey`, {
|
||||
return await TGHttp<TGApp.BBS.Response.Base>(`${taBu}binding/api/genAuthKey`, {
|
||||
method: "POST",
|
||||
headers: getRequestHeader(cookie, "POST", JSON.stringify(payload), "LK2", true),
|
||||
body: JSON.stringify(payload),
|
||||
@@ -92,7 +136,7 @@ async function getUserGameRolesByCookie(
|
||||
): Promise<TGApp.BBS.Account.GameAccount[] | TGApp.BBS.Response.Base> {
|
||||
const ck = { account_id: cookie.account_id, cookie_token: cookie.cookie_token };
|
||||
const params = { game_biz: "hk4e_cn" };
|
||||
const resp = await TGHttp<GameAccountsResp>(`${tbAbu}getUserGameRolesByCookie`, {
|
||||
const resp = await TGHttp<GameAccountsResp>(`${taBu}binding/api/getUserGameRolesByCookie`, {
|
||||
method: "GET",
|
||||
headers: getRequestHeader(ck, "GET", params),
|
||||
query: params,
|
||||
@@ -104,6 +148,7 @@ async function getUserGameRolesByCookie(
|
||||
const TakumiApi = {
|
||||
auth: { actionTicket: getActionTicketBySToken },
|
||||
bind: { authKey: genAuthKey, authKey2: genAuthKey2, gameRoles: getUserGameRolesByCookie },
|
||||
game: { stoken: getSTokenByGameToken },
|
||||
};
|
||||
|
||||
export default TakumiApi;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* @file web/utils/annoParser.ts
|
||||
* @description 解析游戏内公告数据
|
||||
* @since Beta v0.6.8
|
||||
* @since Beta v0.7.0
|
||||
*/
|
||||
|
||||
import TpText from "@comp/viewPost/tp-text.vue";
|
||||
@@ -9,41 +9,6 @@ import { h, render } from "vue";
|
||||
|
||||
import { decodeRegExp } from "@/utils/toolFunc.js";
|
||||
|
||||
/**
|
||||
* @description 预处理p
|
||||
* @since Beta v0.5.2
|
||||
* @param {HTMLParagraphElement} p p 元素
|
||||
* @returns {HTMLParagraphElement} 解析后的 p 元素
|
||||
*/
|
||||
function handleAnnoP(p: HTMLParagraphElement): HTMLParagraphElement {
|
||||
if (p.children.length === 0) p.innerHTML = decodeRegExp(p.innerHTML);
|
||||
else {
|
||||
p.querySelectorAll("*").forEach((child) => {
|
||||
child.innerHTML = decodeRegExp(child.innerHTML);
|
||||
child.querySelectorAll("span").forEach(handleAnnoSpan);
|
||||
});
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 预处理 span
|
||||
* @since Beta v0.4.4
|
||||
* @param {HTMLSpanElement} span span 元素
|
||||
* @returns {HTMLSpanElement} 解析后的 span 元素
|
||||
*/
|
||||
function handleAnnoSpan(span: HTMLSpanElement): HTMLSpanElement {
|
||||
if (span.style.fontSize) span.style.fontSize = "";
|
||||
if (span.children.length === 0) span.innerHTML = decodeRegExp(span.innerHTML);
|
||||
else {
|
||||
span.querySelectorAll("*").forEach((child) => {
|
||||
if (child.children.length === 0) child.innerHTML = decodeRegExp(child.innerHTML);
|
||||
if (child.tagName === "T") child.outerHTML = child.innerHTML;
|
||||
});
|
||||
}
|
||||
return span;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 预处理table
|
||||
* @since Beta v0.4.7
|
||||
@@ -62,14 +27,12 @@ function handleAnnoTable(table: HTMLTableElement): HTMLTableElement {
|
||||
|
||||
/**
|
||||
* @description 预处理公告内容
|
||||
* @since Beta v0.4.4
|
||||
* @since Beta v0.7.0
|
||||
* @param {string} data 游戏内公告数据
|
||||
* @returns {string} 解析后的数据
|
||||
*/
|
||||
function handleAnnoContent(data: string): string {
|
||||
const htmlBase = new DOMParser().parseFromString(data, "text/html");
|
||||
htmlBase.querySelectorAll("p").forEach(handleAnnoP);
|
||||
htmlBase.querySelectorAll("span").forEach(handleAnnoSpan);
|
||||
const htmlBase = new DOMParser().parseFromString(decodeRegExp(data), "text/html");
|
||||
htmlBase.querySelectorAll("table").forEach(handleAnnoTable);
|
||||
return htmlBase.body.innerHTML;
|
||||
}
|
||||
@@ -94,7 +57,7 @@ function parseAnnoContent(
|
||||
|
||||
/**
|
||||
* @description 解析公告节点
|
||||
* @since Beta v0.6.8
|
||||
* @since Beta v0.7.0
|
||||
* @param {Node} node - 节点
|
||||
* @param {Record<string, string>} attr - 属性
|
||||
* @returns {TGApp.Plugins.Mys.SctPost.Base} 结构化数据
|
||||
@@ -104,7 +67,12 @@ function parseAnnoNode(
|
||||
attr?: Record<string, string>,
|
||||
): Array<TGApp.Plugins.Mys.SctPost.Base> {
|
||||
let defaultRes: TGApp.Plugins.Mys.SctPost.Base = {
|
||||
insert: { tag: node.nodeName, text: node.textContent, type: node.nodeType },
|
||||
insert: {
|
||||
tag: node.nodeName,
|
||||
text: node.textContent,
|
||||
type: node.nodeType,
|
||||
func: "parseAnnoNode",
|
||||
},
|
||||
};
|
||||
if (node.nodeType !== Node.ELEMENT_NODE && node.nodeType !== Node.TEXT_NODE) return [defaultRes];
|
||||
if (node.nodeType === Node.TEXT_NODE) {
|
||||
@@ -118,8 +86,10 @@ function parseAnnoNode(
|
||||
style: element.style.cssText,
|
||||
html: element.innerHTML,
|
||||
outerHtml: element.outerHTML,
|
||||
func: "parseAnnoNode",
|
||||
},
|
||||
};
|
||||
console.log(defaultRes);
|
||||
if (element.tagName === "P") {
|
||||
element.innerHTML = decodeRegExp(element.innerHTML);
|
||||
return [parseAnnoParagraph(element, attr)];
|
||||
@@ -172,7 +142,7 @@ function parseAnnoNode(
|
||||
|
||||
/**
|
||||
* @description 解析公告段落
|
||||
* @since Beta v0.6.8
|
||||
* @since Beta v0.7.0
|
||||
* @param {HTMLElement} p - 段落元素
|
||||
* @param {Record<string, string>} attr - 属性
|
||||
* @returns {TGApp.Plugins.Mys.SctPost.Base} 结构化数据
|
||||
@@ -189,8 +159,10 @@ function parseAnnoParagraph(
|
||||
html: p.innerHTML,
|
||||
outerHtml: p.outerHTML,
|
||||
childrenLength: p.childNodes.length,
|
||||
func: "parseAnnoParagraph",
|
||||
},
|
||||
};
|
||||
console.log(defaultRes);
|
||||
if (p.childNodes.length === 0) {
|
||||
return { insert: p.innerHTML === "" ? "\n" : (p.textContent ?? "") };
|
||||
}
|
||||
@@ -236,6 +208,7 @@ function parseAnnoParagraph(
|
||||
if (resS.length > 1) childRes = { insert: element.outerHTML };
|
||||
else childRes = resS[0];
|
||||
} else if (element.tagName === "T") {
|
||||
console.log(element.outerHTML);
|
||||
element.innerHTML = element.outerHTML;
|
||||
const resE = parseAnnoNode(element);
|
||||
if (resE.length > 1) childRes = { insert: element.outerHTML };
|
||||
@@ -251,7 +224,7 @@ function parseAnnoParagraph(
|
||||
|
||||
/**
|
||||
* @description 解析公告 span
|
||||
* @since Beta v0.6.8
|
||||
* @since Beta v0.7.0
|
||||
* @param {HTMLElement} span - span 元素
|
||||
* @param {Record<string, string>} attr - 属性
|
||||
* @returns {TGApp.Plugins.Mys.SctPost.Base} 结构化数据
|
||||
@@ -264,12 +237,16 @@ function parseAnnoSpan(
|
||||
insert: {
|
||||
tag: span.tagName,
|
||||
text: span.textContent,
|
||||
style: span.style.cssText,
|
||||
style: span.style?.cssText,
|
||||
html: span.innerHTML,
|
||||
outerHtml: span.outerHTML,
|
||||
childrenLength: span.childNodes.length,
|
||||
func: "parseAnnoSpan",
|
||||
},
|
||||
};
|
||||
let spanAttrs: Record<string, string> | undefined = attr;
|
||||
if (!spanAttrs) spanAttrs = {};
|
||||
if (span.style.color !== "") spanAttrs.color = span.style.color;
|
||||
if (span.childNodes.length === 0) {
|
||||
console.error(span.innerHTML);
|
||||
return {
|
||||
@@ -289,9 +266,6 @@ function parseAnnoSpan(
|
||||
if (res.length > 1) return defaultRes;
|
||||
return res[0];
|
||||
}
|
||||
let spanAttrs: Record<string, string> | undefined = attr;
|
||||
if (!spanAttrs) spanAttrs = {};
|
||||
if (span.style.color !== "") spanAttrs.color = span.style.color;
|
||||
const parse = decodeRegExp(span.innerHTML);
|
||||
if (parse.includes("</t>")) {
|
||||
const dom = new DOMParser().parseFromString(parse, "text/html");
|
||||
@@ -299,12 +273,13 @@ function parseAnnoSpan(
|
||||
}
|
||||
return { insert: parse, attributes: spanAttrs };
|
||||
}
|
||||
return defaultRes;
|
||||
// todo 优化处理
|
||||
return { insert: span.textContent ?? "", attributes: spanAttrs };
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 解析公告图片
|
||||
* @since Beta v0.5.3
|
||||
* @since Beta v0.7.0
|
||||
* @param {HTMLElement} img - 图片元素
|
||||
* @returns {TGApp.Plugins.Mys.SctPost.Base} 结构化数据
|
||||
*/
|
||||
@@ -317,6 +292,7 @@ function parseAnnoImage(img: HTMLElement): TGApp.Plugins.Mys.SctPost.Base {
|
||||
style: img.style.cssText,
|
||||
html: img.innerHTML,
|
||||
outerHtml: img.outerHTML,
|
||||
func: "parseAnnoImage",
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -326,7 +302,7 @@ function parseAnnoImage(img: HTMLElement): TGApp.Plugins.Mys.SctPost.Base {
|
||||
|
||||
/**
|
||||
* @description 解析公告锚点
|
||||
* @since Beta v0.6.8
|
||||
* @since Beta v0.7.0
|
||||
* @param {HTMLElement} a - 锚点元素
|
||||
* @returns {TGApp.Plugins.Mys.SctPost.Base} 结构化数据
|
||||
*/
|
||||
@@ -339,6 +315,7 @@ function parseAnnoAnchor(a: HTMLElement): TGApp.Plugins.Mys.SctPost.Base {
|
||||
style: a.style.cssText,
|
||||
html: a.innerHTML,
|
||||
outerHtml: a.outerHTML,
|
||||
func: "parseAnnoAnchor",
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -354,7 +331,7 @@ function parseAnnoAnchor(a: HTMLElement): TGApp.Plugins.Mys.SctPost.Base {
|
||||
|
||||
/**
|
||||
* @description 解析公告详情
|
||||
* @since Beta v0.5.3
|
||||
* @since Beta v0.7.0
|
||||
* @param {HTMLElement} details - 详情元素
|
||||
* @returns {TGApp.Plugins.Mys.SctPost.Base} 结构化数据
|
||||
*/
|
||||
@@ -366,6 +343,7 @@ function parseAnnoDetails(details: HTMLElement): TGApp.Plugins.Mys.SctPost.Base
|
||||
style: details.style.cssText,
|
||||
html: details.innerHTML,
|
||||
outerHtml: details.outerHTML,
|
||||
func: "parseAnnoDetails",
|
||||
},
|
||||
};
|
||||
if (details.tagName !== "DETAILS") return defaultRes;
|
||||
@@ -388,7 +366,7 @@ function parseAnnoDetails(details: HTMLElement): TGApp.Plugins.Mys.SctPost.Base
|
||||
|
||||
/**
|
||||
* @description 解析公告表格
|
||||
* @since Beta v0.6.8
|
||||
* @since Beta v0.7.0
|
||||
* @param {HTMLElement} table - 表格元素
|
||||
* @returns {TGApp.Plugins.Mys.SctPost.Base} 结构化数据
|
||||
*/
|
||||
@@ -400,6 +378,7 @@ function parseAnnoTable(table: HTMLElement): TGApp.Plugins.Mys.SctPost.Base {
|
||||
style: table.style.cssText,
|
||||
html: table.innerHTML,
|
||||
outerHtml: table.outerHTML,
|
||||
func: "parseAnnoTable",
|
||||
},
|
||||
};
|
||||
if (table.tagName !== "TABLE") return defaultRes;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* @file web/utils/getRequestHeader.ts
|
||||
* @description 获取请求头
|
||||
* @since Beta v0.6.8
|
||||
* @since Beta v0.7.0
|
||||
*/
|
||||
|
||||
import Md5 from "js-md5";
|
||||
@@ -24,12 +24,12 @@ const enum SaltType {
|
||||
|
||||
/**
|
||||
* @description salt 值
|
||||
* @version 2.80.1
|
||||
* @since Beta v0.6.7
|
||||
* @version 2.82.0
|
||||
* @since Beta v0.7.0
|
||||
*/
|
||||
const Salt: Readonly<Record<keyof typeof SaltType, string>> = {
|
||||
K2: "G1rXOpMLQS77VPWEGycOSxekCTZe2Q8M",
|
||||
LK2: "sd1e1gQJuvqBfZxas1oeAACXzbim5cge",
|
||||
K2: "RGcLwIWYOQwTQPJ8Qw41kioel738ch3Z",
|
||||
LK2: "1M69A7AaPUhTFCdH0D2iMatZ0MTiLmPf",
|
||||
X4: "xV8v4Qu54lUKrEYFZkJhB8cuOh9Asafs",
|
||||
X6: "t0qEgfub6cvueAPgR5m9aQWWVciEer7v",
|
||||
PROD: "t0qEgfub6cvueAPgR5m9aQWWVciEer7v",
|
||||
@@ -57,7 +57,7 @@ function getRandomNumber(min: number, max: number): number {
|
||||
* @param {boolean} isSign 是否为签名
|
||||
* @returns {string} ds
|
||||
*/
|
||||
function getDS(
|
||||
export function getDS(
|
||||
method: string,
|
||||
data: string,
|
||||
saltType: keyof typeof SaltType,
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
"esModuleInterop": true,
|
||||
"lib": ["DOM", "ESNext"],
|
||||
"skipLibCheck": true,
|
||||
"types": ["vite/client", "node", "color-convert", "js-md5", "uuid", "src/vite-env"],
|
||||
"types": ["vite/client", "node", "color-convert", "js-md5", "uuid"],
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"composite": true,
|
||||
"baseUrl": ".",
|
||||
@@ -30,6 +30,7 @@
|
||||
"*.yml",
|
||||
"*.yaml",
|
||||
"package.json",
|
||||
"src/vite-env.d.ts",
|
||||
"src/**/*.d.ts",
|
||||
"src/**/*.ts",
|
||||
"src/**/*.vue",
|
||||
|
||||