Compare commits

..

126 Commits

Author SHA1 Message Date
目棃
8c9885441b 🚀 v0.7.1 2025-03-06 10:02:05 +08:00
目棃
6bbef1332b 💄 grid行高自适应 2025-03-06 09:35:24 +08:00
目棃
cd5e151926 🚸 防抖 2025-03-06 09:27:04 +08:00
目棃
41921a2a6a 🍱 部分缓存 2025-03-06 09:14:52 +08:00
目棃
3cead7eda6 🍱 缓存 2025-03-05 20:25:58 +08:00
目棃
26f670fae3 🧑‍💻 recommend vue-official instead of volar 2025-03-05 16:35:41 +08:00
目棃
bc9d848633 🏷️ add await 2025-03-04 17:05:47 +08:00
目棃
8f2ea6deea 🥅 捕获分享图生成异常 2025-03-02 14:16:20 +08:00
目棃
92603e8599 🐛 修复获取话题帖子异常 2025-03-02 12:22:54 +08:00
目棃
530f45e75c 🐛 尝试修复签到1034
close #145
2025-03-02 10:47:51 +08:00
目棃
d0936a0a60 ♻️ 重构部分请求 2025-03-01 14:27:43 +08:00
目棃
7d0e0f187c 🚨 typo 2025-03-01 12:37:18 +08:00
目棃
cad8de6cd7 ⬆️ 更新依赖 2025-03-01 12:35:10 +08:00
目棃
d29ade356a 💄 调整帖子卡片样式 2025-02-28 17:32:50 +08:00
目棃
b14ad0e687 💄 调整公告卡片样式 2025-02-28 17:30:35 +08:00
目棃
aeca190f2c 💄 调整搜索浮窗样式&帖子卡片样式 2025-02-28 16:50:57 +08:00
目棃
3ac68c1fd9 🐛 修复视频时长计算异常 2025-02-28 15:53:06 +08:00
目棃
8a09d8e442 📝 更新参考项目列表 2025-02-28 10:54:31 +08:00
目棃
0f546e8a57 🚀 v0.7.0 2025-02-28 10:05:04 +08:00
目棃
26070919c9 🚸 昵称转码 2025-02-28 09:53:09 +08:00
目棃
bfd73c3d73 💄 调整UI 2025-02-28 09:43:57 +08:00
目棃
415927cd0f 📝 更新文档 2025-02-28 09:43:32 +08:00
目棃
aa6c75e59f 🐛 修复路由跳转不生效 2025-02-27 23:08:04 +08:00
目棃
c31c86bd56 🍱 更新下半卡池 2025-02-27 21:49:14 +08:00
目棃
1ee3c35216 💄 调整等级样式 2025-02-27 17:21:28 +08:00
目棃
6676357296 🧑‍💻 更换json渲染组件,渲染公告解析json 2025-02-27 14:06:08 +08:00
目棃
3ba72969d9 ⬆️ 更新依赖 2025-02-27 14:01:51 +08:00
目棃
c9ea10f0ef 🔊 打印到文件 2025-02-26 17:33:04 +08:00
目棃
17f1b39414 🚸 二维码支持生成分享图 2025-02-26 17:27:15 +08:00
目棃
70216734a3 🌱 comboToken登录 2025-02-26 11:31:15 +08:00
目棃
c25bde1b7a 💡 v0.6.10 → v0.7.0 2025-02-26 10:10:36 +08:00
目棃
beb457a884 🚸 确保同一时间只能执行一个脚本
close #144
2025-02-26 09:05:39 +08:00
目棃
1b1abb9b88 🏷️ 修正调用异常 2025-02-25 17:56:33 +08:00
目棃
112bd3b938 🚸 调整逻辑 2025-02-25 16:17:51 +08:00
目棃
55adf31613 完成米游社任务
#144
2025-02-25 15:49:08 +08:00
目棃
cdddbae520 🌱 获取任务完成情况 2025-02-25 13:43:47 +08:00
目棃
c4bd07069c 🎨 移除测试点击 2025-02-25 12:00:14 +08:00
目棃
71b45584e8 🎨 修正逻辑 2025-02-25 10:42:22 +08:00
目棃
e343d37a01 💡 特定请求需要验证码登录返回ck 2025-02-25 10:39:59 +08:00
目棃
7f5ffab2a7 🎨 调整条件 2025-02-25 10:39:40 +08:00
目棃
d2e6d112d5 🔧 无痕浏览,默认开启 2025-02-24 19:09:49 +08:00
目棃
5b390d3ad1 🌱 新页面 2025-02-24 17:56:49 +08:00
目棃
798c4bd7d5 🍱 png2webp 2025-02-24 15:18:07 +08:00
目棃
7e133176e5 🌱 分享 2025-02-24 15:12:41 +08:00
目棃
87a345ffa7 🐛 修正浏览参数 2025-02-24 15:02:09 +08:00
目棃
2bafb6d491 🌱 点赞 2025-02-24 14:43:09 +08:00
目棃
0f278ad25e 🌱 浏览帖子 2025-02-24 13:44:41 +08:00
目棃
1da157abbd 🌱 获取任务完成情况 2025-02-24 12:01:50 +08:00
目棃
2fa9f88da2 💄 调整可视页码 2025-02-23 21:31:06 +08:00
目棃
9e2f91b4d4 ♻️ 重构生日计算 2025-02-22 11:30:14 +08:00
目棃
2b15e1a351 💄 微调UI 2025-02-20 17:00:59 +08:00
目棃
25f95d9f90 🐛 修复背景图片更新异常 2025-02-13 17:20:02 +08:00
目棃
03e33872c2 ♻️ 处理特殊情况x2 2025-02-12 15:34:57 +08:00
目棃
5b5f96c2d3 ♻️ 处理特殊情况 2025-02-12 15:15:18 +08:00
目棃
0005e4eb74 🐛 修复解析异常 2025-02-12 13:47:14 +08:00
目棃
89d1b2c6a7 ⬆️ 更新依赖 2025-02-12 13:33:42 +08:00
目棃
8eb1d19512 🚀 v0.6.9 2025-02-11 10:58:41 +08:00
目棃
8e0bde749f 成就分享截图 2025-02-11 10:51:53 +08:00
目棃
93cc9d916e 💄 修复名片图鉴浮窗渲染异常 2025-02-11 10:15:53 +08:00
目棃
18bb76a2a3 🍱 更新5.4资源
close #141
2025-02-11 09:49:04 +08:00
目棃
06a4821888 💄 修复元素为None时的渲染异常 2025-02-09 19:28:23 +08:00
目棃
0157ae413b 💄 更换图标 2025-02-09 13:34:59 +08:00
目棃
cb97cddb89 🚸 调整hint 2025-02-06 23:22:41 +08:00
目棃
90699a547e 🚸 调整指针样式 2025-02-06 19:12:44 +08:00
目棃
54fa04ff65 🚸 显示封面链接 2025-02-06 12:03:54 +08:00
目棃
dfa9251ae9 💄 居中 2025-02-04 12:11:19 +08:00
目棃
25fdbd8444 🚸 部分资源释放 2025-02-01 19:44:49 +08:00
目棃
ade05d8dab 🚸 部分资源缓存 2025-02-01 19:03:06 +08:00
目棃
4cbd5c516a 🐛 修复路径解析异常 2025-02-01 11:26:59 +08:00
目棃
dab6faafde 🚸 成就导入不允许点击外部取消,调整刷新逻辑 2025-01-28 17:32:14 +08:00
目棃
87ff3a1148 🚸 调整兑换码判断 2025-01-25 21:11:49 +08:00
目棃
44d181b26a 💄 调整剧诗icon 2025-01-25 21:03:32 +08:00
目棃
7a112f4d17 💄 调整战绩页面UI 2025-01-22 23:47:11 +08:00
目棃
c1cf8b4aa4 🚀 v0.6.8 2025-01-22 10:59:43 +08:00
目棃
02bb7b9d6b ⬆️ 更新依赖 2025-01-22 10:21:15 +08:00
目棃
b3a7d92887 🐛 fix empty err 2025-01-20 11:22:04 +08:00
目棃
a46f037075 💄 调整UI 2025-01-20 11:12:47 +08:00
目棃
d0669d4a32 💄 fix render err 2025-01-20 09:00:30 +08:00
目棃
ae88c4b5ab 💄 fix special render err 2025-01-17 17:45:57 +08:00
目棃
e332dcf384 💄 fix overflow 2025-01-17 17:37:55 +08:00
目棃
054a0839c6 💄 fix cover render err 2025-01-17 17:30:11 +08:00
目棃
18c96102b3 🚸 调整hint 2025-01-17 16:13:31 +08:00
目棃
d78b794dc6 🏷️ fix typo 2025-01-17 16:09:25 +08:00
目棃
c9f74537b0 ♻️ 部分分区动态获取,重构部分调用 2025-01-17 16:06:36 +08:00
目棃
da78d27239 ♻️ 重构部分路由处理 2025-01-17 14:48:38 +08:00
目棃
a3c78ce134 🚸 fix hint err 2025-01-17 11:14:08 +08:00
目棃
59b601a620 💄 fix incorrect scroll 2025-01-17 09:57:57 +08:00
目棃
c8db77f32d ⬆️ 更新依赖 2025-01-17 09:46:42 +08:00
目棃
a542174f54 深渊上传支持胡桃账户设置 2025-01-17 09:40:37 +08:00
目棃
adaa7b1080 🍱 recreate icon 2025-01-16 18:03:29 +08:00
目棃
4713cd4119 🍱 fix process icon err 2025-01-16 17:48:22 +08:00
目棃
fbd3d0703d 🚨 fix qodana warn 2025-01-16 17:08:11 +08:00
目棃
0d4ce338f3 🚸 复制/下载时自动切换原图 2025-01-16 17:06:30 +08:00
目棃
54b98c7efe 🚸 调整loading 2025-01-16 16:53:23 +08:00
目棃
8e68ca36bb 💄 调整UI 2025-01-16 16:24:22 +08:00
目棃
8ecaed05ea ♻️ 动态获取版块 2025-01-16 15:51:59 +08:00
目棃
e8a79fb409 尝试修复mac复制异常 2025-01-16 11:49:07 +08:00
目棃
b6ea50a597 🔥 移除第10层数据 2025-01-16 10:19:54 +08:00
目棃
547cb2cce7 抽卡日历&堆叠柱状图 2025-01-13 17:11:20 +08:00
目棃
5f6f6e0ea6 🐛 修复祈愿图标渲染异常 2025-01-13 16:31:33 +08:00
目棃
dd7e8e2261 🚸 响应主题 2025-01-13 15:07:36 +08:00
目棃
9d995994ca ♻️ 基础组件封装 2025-01-13 14:29:00 +08:00
目棃
a7ca51dcc1 🎨 微调 2025-01-13 11:18:55 +08:00
目棃
0fd9e9a934 🚸 100%时返回原图 2025-01-13 09:07:05 +08:00
目棃
0464c0fff1 🚸 查看原图 2025-01-12 11:26:20 +08:00
目棃
0e6a15bd8f 🚸 优化加载 2025-01-11 14:35:57 +08:00
目棃
63b7f685c1 🚸 支持配置图像质量 2025-01-10 17:53:13 +08:00
目棃
aba87dfac0 🔨 抬升版本号脚本 2025-01-09 14:28:53 +08:00
目棃
08785514ee 🍱 重新生成图标 2025-01-09 14:13:04 +08:00
目棃
940ad44052 ♻️ 优化逻辑 2025-01-09 11:29:54 +08:00
目棃
2cdd15b726 💄 调整UI 2025-01-09 11:25:24 +08:00
目棃
3c0b02d26b 💄 mac下不显示分享设置 2025-01-09 11:19:37 +08:00
目棃
72a9408d38 ♻️ 组件抽离,优化逻辑 2025-01-09 11:15:35 +08:00
目棃
b867c008ae 💄 调整高度 2025-01-09 10:21:30 +08:00
目棃
274eb75754 🏷️ 修正类型 2025-01-09 10:21:07 +08:00
目棃
e7bd815b08 💄 增加清晰度 2025-01-09 09:55:38 +08:00
目棃
b7f9d083e7 🚸 处理图片以加快加载速度 2025-01-09 09:41:11 +08:00
目棃
6744c875b6 ♻️ 优化加载逻辑 2025-01-09 09:20:58 +08:00
目棃
73f7247b59 💄 修复渲染异常,调整share可用性 2025-01-08 20:31:17 +08:00
目棃
8b5aa106ce 🐛 修复深渊数据恢复异常 2025-01-08 17:53:23 +08:00
目棃
248130312c 🐛 修复数据缺失 2025-01-08 17:42:18 +08:00
目棃
4f718288c3 💄 调整卡片样式 2025-01-08 16:17:28 +08:00
目棃
c7b13983bc 🚸 全量刷新时提升加载速度 2025-01-08 15:31:55 +08:00
目棃
98dea9d912 🏷️ 微调 2025-01-08 15:23:24 +08:00
目棃
d5f40a5775 扫码登录 2025-01-08 14:58:00 +08:00
目棃
f801363440 ♻️ defineModel 2024-12-31 16:41:02 +08:00
234 changed files with 9698 additions and 5840 deletions

View File

@@ -55,7 +55,7 @@ jobs:
- name: setup pnpm
uses: pnpm/action-setup@v2
with:
version: 9.15.0
version: 10.2.0
- name: Install frontend dependencies
run: pnpm install

View File

@@ -5,6 +5,6 @@
"stylelint.vscode-stylelint",
"tauri-apps.tauri-vscode",
"Vue.vscode-typescript-vue-plugin",
"Vue.volar"
"Vue.vue-official"
]
}

View File

@@ -2,135 +2,32 @@
Author: 目棃
Description: CHANGELOG
Date: 2024-10-09
Update: 2024-12-31
Update: 2025-03-06
---
> 本文档 [`Frontmatter`](https://github.com/BTMuli/MuCli#Frontmatter) 由 [MuCli](https://github.com/BTMuli/Mucli) 自动生成于 `2024-10-09 15:51:43`
>
> 更新于 `2024-12-31 13:41:03`
> 更新于 `2025-03-06 10:01:22`
## [0.6.7](https://github.com/BTMuli/TeyvatGuide/releases/v0.6.7) (2024-12-31)
## [0.7.1](https://github.com/BTMuli/TeyvatGuide/releases/v0.7.1) (2025-03-06)
- 🍱 更新5.3版本游戏资源 [`#139`](https://github.com/BTMuli/TeyvatGuide/issues/139)
- ✨ 支持嵌入B站视频的分享图渲染
- 🐛 修复版块跳转异常
- 🐛 修复清理日志异常
- 🐛 修复特定帖子`link_card_ids`数据解析异常
- 🐛 修复帖子文本居中异常
- 🐛 修复侧边栏跳转角色/武器图鉴异常
- ✏️ 调整分享图大小计算方式采用1024进制而非原有的1000进制
- 💄 调整用户等级UI浅色深色下统一为白色文字
- 💄 调整回复弹窗位置,上移一段距离以避免底部提示遮挡
- 💄 首页素材日历组件只显示日期,移除具体时间
- 💄 调整链接卡片提示文字
- 💄 调整剧诗角色列表显示UI
- 🚸 版块/咨讯页数据获取/刷新显示成功提示
- 🚸 首页近期活动卡片Icon补充缺失的点击逻辑
- 🚸 调整合集组件改版后的滚动逻辑,更加流畅
- 👽️ 由于API变更调整版块数据获取逻辑
- 👽️ 由于返回数据格式变更,调整视频时长的计算逻辑
- 👽️ 由于返回数据格式变更,处理帖子内的转义字符
- ♻️ loading组件重构部分页面显示更精准的进度
- ♻️ 应用元数据格式重构,剔除冗余数据
- 🐛 修复B站视频时长计算异常
- 🐛 处理脚本签到1034 [`#145`](https://github.com/BTMuli/TeyvatGuide/issues/145)
- 💄 调整搜索浮窗样式&帖子/公告卡片样式
- 🥅 捕获分享图生成异常
- 🚸 部分页面添加防抖处理
- 🍱 调整缓存图片范围
## [0.6.6](https://github.com/BTMuli/TeyvatGuide/releases/v0.6.6) (2024-12-13)
## [0.7.0](https://github.com/BTMuli/TeyvatGuide/releases/v0.7.0) (2025-02-28)
- 🐛 修复主题切换响应异常
- 🐛 修复增量刷新逻辑异常
- ⚡️ 显著降低运行内存占用
## [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
- 🍱 更新下半卡池数据

View File

@@ -2,12 +2,12 @@
Author: 目棃
Description: 说明文档
Date: 2023-03-05
Update: 2024-12-03
Update: 2025-02-28
---
> 本文档 [`Frontmatter`](https://github.com/BTMuli/MuCli#Frontmatter) 由 [MuCli](https://github.com/BTMuli/Mucli) 自动生成于 `2023-03-05 14:41:55`
>
> 更新于 `2024-12-03 16:14:14`
> 更新于 `2025-02-28 10:54:28`
![](https://img.shields.io/github/last-commit/BTMuli/TeyvatGuide?style=for-the-badge) ![](https://img.shields.io/github/commits-since/BTMuli/TeyvatGuide/latest?include_prereleases&style=for-the-badge)
@@ -58,12 +58,15 @@ Game Tool for Genshin Impact player, supports Windows and macOS.
- 登陆功能:
> 支持 扫码/验证码/Cookie 登陆,支持多账号管理
- [x] 原神战绩数据获取
- [x] 角色详情数据获取
- [x] 螺旋深渊数据获取
- [x] 真境剧诗数据获取
- [x] 祈愿数据获取(近一年)
- [x] 用户收藏帖子获取
- [x] 一键完成米游币每日任务 **需要验证码登录**
- Wiki 功能:
@@ -95,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
@@ -139,6 +142,8 @@ Game Tool for Genshin Impact player, supports Windows and macOS.
- [paimon-moe](https://github.com/MadeBaruna/paimon-moe)
- [Adachi-BOT](https://github.com/Arondight/Adachi-BOT)
- [amos-data](https://github.com/yuehaiteam/amos-data)
- [MihoyoBBSTools](https://github.com/Womsxd/MihoyoBBSTools)
- [nonebot-plugin-mystool](https://github.com/Ljzd-PRO/nonebot-plugin-mystool)
感谢 JetBrains 提供的开源许可证。

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

View File

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

View File

@@ -1,9 +1,9 @@
{
"name": "teyvatguide",
"version": "0.6.7",
"version": "0.7.1",
"description": "Game Tool for GenshinImpact player",
"private": true,
"packageManager": "pnpm@9.15.0",
"packageManager": "pnpm@10.5.2",
"type": "module",
"scripts": {
"build": "tauri build",
@@ -12,12 +12,14 @@
"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",
"lint:style:fix": "pnpm lint:style --fix",
"lint:rust:fix": "cd src-tauri && cargo fmt",
"prettier": "prettier . --write",
"upv": "tsx scripts/update-version.ts",
"tauri": "tauri",
"vite:dev": "vite dev",
"vite:build": "vite build",
@@ -67,74 +69,79 @@
},
"dependencies": {
"@mdi/font": "7.4.47",
"@tauri-apps/api": "^2.1.1",
"@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.2.0",
"@tauri-apps/plugin-log": "^2.2.0",
"@tauri-apps/plugin-http": "^2.3.0",
"@tauri-apps/plugin-log": "^2.2.3",
"@tauri-apps/plugin-os": "^2.2.0",
"@tauri-apps/plugin-process": "^2.2.0",
"@tauri-apps/plugin-shell": "^2.2.0",
"@tauri-apps/plugin-sql": "^2.2.0",
"ajv": "^8.17.1",
"artplayer": "^5.2.1",
"artplayer": "^5.2.2",
"clipboard": "^2.0.11",
"color-convert": "^2.0.1",
"echarts": "^5.5.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.0",
"pinia-plugin-persistedstate": "^4.1.3",
"sass": "^1.83.0",
"sass-loader": "^16.0.4",
"uuid": "^11.0.3",
"pinia": "^3.0.1",
"pinia-plugin-persistedstate": "^4.2.0",
"qrcode.vue": "^3.6.0",
"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.5",
"vuetify": "^3.7.14",
"wcag-color": "^1.1.1",
"xml-js": "^1.6.11"
},
"devDependencies": {
"@eslint/eslintrc": "^3.2.0",
"@eslint/js": "^9.17.0",
"@tauri-apps/cli": "2.1.0",
"@eslint/eslintrc": "^3.3.0",
"@eslint/js": "^9.21.0",
"@tauri-apps/cli": "2.3.1",
"@types/color-convert": "^2.0.4",
"@types/fs-extra": "^11.0.4",
"@types/js-md5": "^0.7.2",
"@types/node": "^22.10.2",
"@types/node": "^22.13.7",
"@types/uuid": "^10.0.0",
"@typescript-eslint/parser": "^8.18.0",
"@typescript-eslint/parser": "^8.25.0",
"@vitejs/plugin-vue": "^5.2.1",
"concurrently": "^9.1.0",
"eslint": "^9.17.0",
"concurrently": "^9.1.2",
"eslint": "^9.21.0",
"eslint-plugin-import": "^2.31.0",
"eslint-plugin-jsonc": "^2.18.2",
"eslint-plugin-prettier": "^5.2.1",
"eslint-plugin-jsonc": "^2.19.1",
"eslint-plugin-prettier": "^5.2.3",
"eslint-plugin-vue": "^9.32.0",
"eslint-plugin-yml": "^1.16.0",
"globals": "^15.13.0",
"eslint-plugin-yml": "^1.17.0",
"fs-extra": "^11.3.0",
"globals": "^16.0.0",
"husky": "^9.1.7",
"jsonc-eslint-parser": "^2.4.0",
"lint-staged": "^15.2.11",
"oxlint": "^0.15.2",
"prettier": "3.4.2",
"stylelint": "^16.11.0",
"lint-staged": "^15.4.3",
"oxlint": "^0.15.12",
"prettier": "3.5.2",
"stylelint": "^16.15.0",
"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.2",
"stylelint-scss": "^6.10.0",
"typescript": "^5.7.2",
"typescript-eslint": "^8.18.0",
"vite": "^6.0.3",
"vite-plugin-vue-devtools": "^7.6.8",
"vite-plugin-vuetify": "^2.0.4",
"stylelint-prettier": "^5.0.3",
"stylelint-scss": "^6.11.1",
"tsx": "^4.19.3",
"typescript": "^5.8.2",
"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",
"yaml-eslint-parser": "^1.2.3"
"vue-tsc": "^2.2.4",
"yaml-eslint-parser": "^1.3.0"
}
}

3889
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
public/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

BIN
public/source/UI/posts.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 776 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

32
scripts/update-version.ts Normal file
View File

@@ -0,0 +1,32 @@
/**
* @file scripts/update-version.ts
* @description 抬升版本号
* @since Beta v0.6.8
*/
import fs from "fs-extra";
import { resolve } from "path";
import { fileURLToPath } from "node:url";
import { execSync } from "node:child_process";
// 获取参数
const args = process.argv;
const version = args[2];
if (!version) {
console.error("请输入版本号");
process.exit(1);
}
const __dirname = resolve(fileURLToPath(import.meta.url), "../");
// 更新package.json
const pkgJson = fs.readJsonSync(resolve(__dirname, "../package.json"));
pkgJson.version = version;
fs.writeJsonSync(resolve(__dirname, "../package.json"), pkgJson);
// 更新src-tauri
const tauriDir = resolve(__dirname, "../src-tauri");
const tauriToml = fs.readFileSync(resolve(tauriDir, "Cargo.toml"), "utf-8").split("\n");
tauriToml[2] = `version = "${version}"`;
fs.writeFileSync(resolve(tauriDir, "Cargo.toml"), tauriToml.join("\n"));
const tauriJson = fs.readJsonSync(resolve(tauriDir, "tauri.conf.json"));
tauriJson.version = version;
fs.writeJsonSync(resolve(tauriDir, "tauri.conf.json"), tauriJson);
execSync("pnpm prettier", { cwd: resolve(__dirname, "..") });

1668
src-tauri/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
[package]
name = "TeyvatGuide"
version = "0.6.7"
version = "0.7.1"
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.3", features = [] }
tauri-build = { version = "2.0.6", features = [] }
[dependencies]
chrono = "0.4.39"
log = "0.4.22"
serde = { version = "1.0.216", features = ["derive"] }
serde_json = "1.0.133"
tauri = { version = "2.1.1", features = [] }
tauri-utils = "2.1.0"
chrono = "0.4.40"
log = "0.4.26"
serde = { version = "1.0.218", features = ["derive"] }
serde_json = "1.0.139"
tauri = { version = "2.3.1", features = [] }
tauri-utils = "2.2.0"
url = "2.5.4"
walkdir = "2.5.0"

View File

@@ -29,15 +29,9 @@
{
"identifier": "http:default",
"allow": [
{
"url": "https://*.miyoushe.com/*"
},
{
"url": "https://*.mihoyo.com/*"
},
{
"url": "https://*.hoyoverse.com/*"
}
{ "url": "https://*.miyoushe.com/*" },
{ "url": "https://*.mihoyo.com/*" },
{ "url": "https://*.hoyoverse.com/*" }
]
}
],

View File

@@ -14,74 +14,19 @@
"core:window:default",
"core:path:allow-resolve-directory",
"core:path:default",
{
"identifier": "fs:allow-exists",
"allow": [
{
"path": "**"
}
]
},
{
"identifier": "fs:allow-mkdir",
"allow": [
{
"path": "**"
}
]
},
{
"identifier": "fs:allow-read-dir",
"allow": [
{
"path": "**"
}
]
},
{
"identifier": "fs:allow-read-text-file",
"allow": [
{
"path": "**"
}
]
},
{
"identifier": "fs:allow-remove",
"allow": [
{
"path": "**"
}
]
},
{
"identifier": "fs:allow-write-file",
"allow": [
{
"path": "**"
}
]
},
{
"identifier": "fs:allow-write-text-file",
"allow": [
{
"path": "**"
}
]
},
{ "identifier": "fs:allow-exists", "allow": [{ "path": "**" }] },
{ "identifier": "fs:allow-mkdir", "allow": [{ "path": "**" }] },
{ "identifier": "fs:allow-read-dir", "allow": [{ "path": "**" }] },
{ "identifier": "fs:allow-read-text-file", "allow": [{ "path": "**" }] },
{ "identifier": "fs:allow-remove", "allow": [{ "path": "**" }] },
{ "identifier": "fs:allow-write-file", "allow": [{ "path": "**" }] },
{ "identifier": "fs:allow-write-text-file", "allow": [{ "path": "**" }] },
{
"identifier": "http:default",
"allow": [
{
"url": "https://*.miyoushe.com/*"
},
{
"url": "https://*.mihoyo.com/*"
},
{
"url": "https://*.genshinnet.com/*"
}
{ "url": "https://*.miyoushe.com/*" },
{ "url": "https://*.mihoyo.com/*" },
{ "url": "https://*.genshinnet.com/*" }
]
}
],

View File

@@ -33,86 +33,23 @@
"core:window:allow-show",
"core:window:allow-set-always-on-top",
"core:window:default",
{
"identifier": "fs:allow-exists",
"allow": [
{
"path": "**"
}
]
},
{
"identifier": "fs:allow-mkdir",
"allow": [
{
"path": "**"
}
]
},
{
"identifier": "fs:allow-read-dir",
"allow": [
{
"path": "**"
}
]
},
{
"identifier": "fs:allow-read-text-file",
"allow": [
{
"path": "**"
}
]
},
{
"identifier": "fs:allow-remove",
"allow": [
{
"path": "**"
}
]
},
{
"identifier": "fs:allow-write-file",
"allow": [
{
"path": "**"
}
]
},
{
"identifier": "fs:allow-write-text-file",
"allow": [
{
"path": "**"
}
]
},
{ "identifier": "fs:allow-exists", "allow": [{ "path": "**" }] },
{ "identifier": "fs:allow-mkdir", "allow": [{ "path": "**" }] },
{ "identifier": "fs:allow-read-dir", "allow": [{ "path": "**" }] },
{ "identifier": "fs:allow-read-text-file", "allow": [{ "path": "**" }] },
{ "identifier": "fs:allow-remove", "allow": [{ "path": "**" }] },
{ "identifier": "fs:allow-write-file", "allow": [{ "path": "**" }] },
{ "identifier": "fs:allow-write-text-file", "allow": [{ "path": "**" }] },
{
"identifier": "http:default",
"allow": [
{
"url": "https://*.miyoushe.com/*"
},
{
"url": "https://*.mihoyo.com/*"
},
{
"url": "https://*.mihoyogift.com/*"
},
{
"url": "https://*.bilibili.com/*"
},
{
"url": "http://*.hdslb.com/*"
},
{
"url": "https://*.hoyoverse.com/*"
},
{
"url": "https://*.genshinnet.com/*"
}
{ "url": "https://*.miyoushe.com/*" },
{ "url": "https://*.mihoyo.com/*" },
{ "url": "https://*.mihoyogift.com/*" },
{ "url": "https://*.bilibili.com/*" },
{ "url": "http://*.hdslb.com/*" },
{ "url": "https://*.hoyoverse.com/*" },
{ "url": "https://*.genshinnet.com/*" }
]
}
],

View File

@@ -37,97 +37,28 @@
"core:window:allow-set-focus",
"core:window:allow-show",
"core:window:allow-unminimize",
{
"identifier": "fs:allow-exists",
"allow": [
{
"path": "**"
}
]
},
{
"identifier": "fs:allow-mkdir",
"allow": [
{
"path": "**"
}
]
},
{
"identifier": "fs:allow-read-dir",
"allow": [
{
"path": "**"
}
]
},
{
"identifier": "fs:allow-read-text-file",
"allow": [
{
"path": "**"
}
]
},
{
"identifier": "fs:allow-remove",
"allow": [
{
"path": "**"
}
]
},
{
"identifier": "fs:allow-write-file",
"allow": [
{
"path": "**"
}
]
},
{
"identifier": "fs:allow-write-text-file",
"allow": [
{
"path": "**"
}
]
},
{ "identifier": "fs:allow-exists", "allow": [{ "path": "**" }] },
{ "identifier": "fs:allow-mkdir", "allow": [{ "path": "**" }] },
{ "identifier": "fs:allow-read-dir", "allow": [{ "path": "**" }] },
{ "identifier": "fs:allow-read-text-file", "allow": [{ "path": "**" }] },
{ "identifier": "fs:allow-remove", "allow": [{ "path": "**" }] },
{ "identifier": "fs:allow-write-file", "allow": [{ "path": "**" }] },
{ "identifier": "fs:allow-write-text-file", "allow": [{ "path": "**" }] },
{
"identifier": "http:default",
"allow": [
{
"url": "https://*.miyoushe.com/*"
},
{
"url": "https://*.mihoyo.com/*"
},
{
"url": "https://homa.snapgenshin.com/*"
},
{
"url": "https://*.hoyoverse.com/*"
}
{ "url": "https://*.miyoushe.com/*" },
{ "url": "https://*.mihoyo.com/*" },
{ "url": "https://homa.snapgenshin.com/*" },
{ "url": "https://*.hoyoverse.com/*" }
]
},
{
"identifier": "shell:allow-execute",
"allow": [
{
"name": "win_open",
"cmd": "explorer",
"args": true
},
{
"name": "mac_open",
"cmd": "open",
"args": true
},
{
"name": "exec-sh",
"cmd": "powershell",
"args": true
}
{ "name": "win_open", "cmd": "explorer", "args": true },
{ "name": "mac_open", "cmd": "open", "args": true },
{ "name": "exec-sh", "cmd": "powershell", "args": true }
]
}
],

View File

@@ -773,6 +773,11 @@
"description": "Enables the reparent command without any pre-configured scope.",
"commands": { "allow": ["reparent"], "deny": [] }
},
"allow-set-webview-background-color": {
"identifier": "allow-set-webview-background-color",
"description": "Enables the set_webview_background_color command without any pre-configured scope.",
"commands": { "allow": ["set_webview_background_color"], "deny": [] }
},
"allow-set-webview-focus": {
"identifier": "allow-set-webview-focus",
"description": "Enables the set_webview_focus command without any pre-configured scope.",
@@ -853,6 +858,11 @@
"description": "Denies the reparent command without any pre-configured scope.",
"commands": { "allow": [], "deny": ["reparent"] }
},
"deny-set-webview-background-color": {
"identifier": "deny-set-webview-background-color",
"description": "Denies the set_webview_background_color command without any pre-configured scope.",
"commands": { "allow": [], "deny": ["set_webview_background_color"] }
},
"deny-set-webview-focus": {
"identifier": "deny-set-webview-focus",
"description": "Denies the set_webview_focus command without any pre-configured scope.",
@@ -1100,6 +1110,21 @@
"description": "Enables the set_always_on_top command without any pre-configured scope.",
"commands": { "allow": ["set_always_on_top"], "deny": [] }
},
"allow-set-background-color": {
"identifier": "allow-set-background-color",
"description": "Enables the set_background_color command without any pre-configured scope.",
"commands": { "allow": ["set_background_color"], "deny": [] }
},
"allow-set-badge-count": {
"identifier": "allow-set-badge-count",
"description": "Enables the set_badge_count command without any pre-configured scope.",
"commands": { "allow": ["set_badge_count"], "deny": [] }
},
"allow-set-badge-label": {
"identifier": "allow-set-badge-label",
"description": "Enables the set_badge_label command without any pre-configured scope.",
"commands": { "allow": ["set_badge_label"], "deny": [] }
},
"allow-set-closable": {
"identifier": "allow-set-closable",
"description": "Enables the set_closable command without any pre-configured scope.",
@@ -1185,6 +1210,11 @@
"description": "Enables the set_minimizable command without any pre-configured scope.",
"commands": { "allow": ["set_minimizable"], "deny": [] }
},
"allow-set-overlay-icon": {
"identifier": "allow-set-overlay-icon",
"description": "Enables the set_overlay_icon command without any pre-configured scope.",
"commands": { "allow": ["set_overlay_icon"], "deny": [] }
},
"allow-set-position": {
"identifier": "allow-set-position",
"description": "Enables the set_position command without any pre-configured scope.",
@@ -1445,6 +1475,21 @@
"description": "Denies the set_always_on_top command without any pre-configured scope.",
"commands": { "allow": [], "deny": ["set_always_on_top"] }
},
"deny-set-background-color": {
"identifier": "deny-set-background-color",
"description": "Denies the set_background_color command without any pre-configured scope.",
"commands": { "allow": [], "deny": ["set_background_color"] }
},
"deny-set-badge-count": {
"identifier": "deny-set-badge-count",
"description": "Denies the set_badge_count command without any pre-configured scope.",
"commands": { "allow": [], "deny": ["set_badge_count"] }
},
"deny-set-badge-label": {
"identifier": "deny-set-badge-label",
"description": "Denies the set_badge_label command without any pre-configured scope.",
"commands": { "allow": [], "deny": ["set_badge_label"] }
},
"deny-set-closable": {
"identifier": "deny-set-closable",
"description": "Denies the set_closable command without any pre-configured scope.",
@@ -1530,6 +1575,11 @@
"description": "Denies the set_minimizable command without any pre-configured scope.",
"commands": { "allow": [], "deny": ["set_minimizable"] }
},
"deny-set-overlay-icon": {
"identifier": "deny-set-overlay-icon",
"description": "Denies the set_overlay_icon command without any pre-configured scope.",
"commands": { "allow": [], "deny": ["set_overlay_icon"] }
},
"deny-set-position": {
"identifier": "deny-set-position",
"description": "Denies the set_position command without any pre-configured scope.",
@@ -3594,7 +3644,7 @@
"process": {
"default_permission": {
"identifier": "default",
"description": "This permission set configures which\nprocess feeatures are by default exposed.\n\n#### Granted Permissions\n\nThis enables to quit via `allow-exit` and restart via `allow-restart`\nthe application.\n",
"description": "This permission set configures which\nprocess features are by default exposed.\n\n#### Granted Permissions\n\nThis enables to quit via `allow-exit` and restart via `allow-restart`\nthe application.\n",
"permissions": ["allow-exit", "allow-restart"]
},
"permissions": {

View File

@@ -2626,6 +2626,11 @@
"type": "string",
"const": "core:webview:allow-reparent"
},
{
"description": "Enables the set_webview_background_color command without any pre-configured scope.",
"type": "string",
"const": "core:webview:allow-set-webview-background-color"
},
{
"description": "Enables the set_webview_focus command without any pre-configured scope.",
"type": "string",
@@ -2706,6 +2711,11 @@
"type": "string",
"const": "core:webview:deny-reparent"
},
{
"description": "Denies the set_webview_background_color command without any pre-configured scope.",
"type": "string",
"const": "core:webview:deny-set-webview-background-color"
},
{
"description": "Denies the set_webview_focus command without any pre-configured scope.",
"type": "string",
@@ -2921,6 +2931,21 @@
"type": "string",
"const": "core:window:allow-set-always-on-top"
},
{
"description": "Enables the set_background_color command without any pre-configured scope.",
"type": "string",
"const": "core:window:allow-set-background-color"
},
{
"description": "Enables the set_badge_count command without any pre-configured scope.",
"type": "string",
"const": "core:window:allow-set-badge-count"
},
{
"description": "Enables the set_badge_label command without any pre-configured scope.",
"type": "string",
"const": "core:window:allow-set-badge-label"
},
{
"description": "Enables the set_closable command without any pre-configured scope.",
"type": "string",
@@ -3006,6 +3031,11 @@
"type": "string",
"const": "core:window:allow-set-minimizable"
},
{
"description": "Enables the set_overlay_icon command without any pre-configured scope.",
"type": "string",
"const": "core:window:allow-set-overlay-icon"
},
{
"description": "Enables the set_position command without any pre-configured scope.",
"type": "string",
@@ -3266,6 +3296,21 @@
"type": "string",
"const": "core:window:deny-set-always-on-top"
},
{
"description": "Denies the set_background_color command without any pre-configured scope.",
"type": "string",
"const": "core:window:deny-set-background-color"
},
{
"description": "Denies the set_badge_count command without any pre-configured scope.",
"type": "string",
"const": "core:window:deny-set-badge-count"
},
{
"description": "Denies the set_badge_label command without any pre-configured scope.",
"type": "string",
"const": "core:window:deny-set-badge-label"
},
{
"description": "Denies the set_closable command without any pre-configured scope.",
"type": "string",
@@ -3351,6 +3396,11 @@
"type": "string",
"const": "core:window:deny-set-minimizable"
},
{
"description": "Denies the set_overlay_icon command without any pre-configured scope.",
"type": "string",
"const": "core:window:deny-set-overlay-icon"
},
{
"description": "Denies the set_position command without any pre-configured scope.",
"type": "string",
@@ -5132,7 +5182,7 @@
"const": "os:deny-version"
},
{
"description": "This permission set configures which\nprocess feeatures are by default exposed.\n\n#### Granted Permissions\n\nThis enables to quit via `allow-exit` and restart via `allow-restart`\nthe application.\n",
"description": "This permission set configures which\nprocess features are by default exposed.\n\n#### Granted Permissions\n\nThis enables to quit via `allow-exit` and restart via `allow-restart`\nthe application.\n",
"type": "string",
"const": "process:default"
},

View File

@@ -2626,6 +2626,11 @@
"type": "string",
"const": "core:webview:allow-reparent"
},
{
"description": "Enables the set_webview_background_color command without any pre-configured scope.",
"type": "string",
"const": "core:webview:allow-set-webview-background-color"
},
{
"description": "Enables the set_webview_focus command without any pre-configured scope.",
"type": "string",
@@ -2706,6 +2711,11 @@
"type": "string",
"const": "core:webview:deny-reparent"
},
{
"description": "Denies the set_webview_background_color command without any pre-configured scope.",
"type": "string",
"const": "core:webview:deny-set-webview-background-color"
},
{
"description": "Denies the set_webview_focus command without any pre-configured scope.",
"type": "string",
@@ -2921,6 +2931,21 @@
"type": "string",
"const": "core:window:allow-set-always-on-top"
},
{
"description": "Enables the set_background_color command without any pre-configured scope.",
"type": "string",
"const": "core:window:allow-set-background-color"
},
{
"description": "Enables the set_badge_count command without any pre-configured scope.",
"type": "string",
"const": "core:window:allow-set-badge-count"
},
{
"description": "Enables the set_badge_label command without any pre-configured scope.",
"type": "string",
"const": "core:window:allow-set-badge-label"
},
{
"description": "Enables the set_closable command without any pre-configured scope.",
"type": "string",
@@ -3006,6 +3031,11 @@
"type": "string",
"const": "core:window:allow-set-minimizable"
},
{
"description": "Enables the set_overlay_icon command without any pre-configured scope.",
"type": "string",
"const": "core:window:allow-set-overlay-icon"
},
{
"description": "Enables the set_position command without any pre-configured scope.",
"type": "string",
@@ -3266,6 +3296,21 @@
"type": "string",
"const": "core:window:deny-set-always-on-top"
},
{
"description": "Denies the set_background_color command without any pre-configured scope.",
"type": "string",
"const": "core:window:deny-set-background-color"
},
{
"description": "Denies the set_badge_count command without any pre-configured scope.",
"type": "string",
"const": "core:window:deny-set-badge-count"
},
{
"description": "Denies the set_badge_label command without any pre-configured scope.",
"type": "string",
"const": "core:window:deny-set-badge-label"
},
{
"description": "Denies the set_closable command without any pre-configured scope.",
"type": "string",
@@ -3351,6 +3396,11 @@
"type": "string",
"const": "core:window:deny-set-minimizable"
},
{
"description": "Denies the set_overlay_icon command without any pre-configured scope.",
"type": "string",
"const": "core:window:deny-set-overlay-icon"
},
{
"description": "Denies the set_position command without any pre-configured scope.",
"type": "string",
@@ -5132,7 +5182,7 @@
"const": "os:deny-version"
},
{
"description": "This permission set configures which\nprocess feeatures are by default exposed.\n\n#### Granted Permissions\n\nThis enables to quit via `allow-exit` and restart via `allow-restart`\nthe application.\n",
"description": "This permission set configures which\nprocess features are by default exposed.\n\n#### Granted Permissions\n\nThis enables to quit via `allow-exit` and restart via `allow-restart`\nthe application.\n",
"type": "string",
"const": "process:default"
},

BIN
src-tauri/icons/icon.icns Normal file

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 65 KiB

View File

@@ -1,6 +1,6 @@
//! @file src/client/mod.rs
//! @desc 客户端模块,负责操作米游社客户端
//! @since Beta v0.6.7
//! @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.79.1";
static BBS_VERSION: &'static str = "2.82.0";
#[tauri::command]
pub async fn create_mhy_client(handle: AppHandle, func: String, url: String) {

View File

@@ -1,7 +1,7 @@
{
"productName": "TeyvatGuide",
"identifier": "TeyvatGuide",
"version": "0.6.7",
"version": "0.7.1",
"build": {
"beforeDevCommand": "pnpm vite:dev",
"beforeBuildCommand": "pnpm vite:build",
@@ -11,28 +11,24 @@
"bundle": {
"active": true,
"icon": [
"icons/32x32.png",
"icons/128x128.png",
"icons/128x128@2x.png",
"icons/32x32.png",
"icons/icon.icns",
"icons/icon.ico",
"icons/icon.png",
"icons/Square30x30Logo.png",
"icons/Square44x44Logo.png",
"icons/Square71x71Logo.png",
"icons/Square89x89Logo.png",
"icons/Square107x107Logo.png",
"icons/Square142x142Logo.png",
"icons/Square150x150Logo.png",
"icons/Square284x284Logo.png",
"icons/Square30x30Logo.png",
"icons/Square310x310Logo.png",
"icons/Square44x44Logo.png",
"icons/Square71x71Logo.png",
"icons/Square89x89Logo.png",
"icons/StoreLogo.png",
"icons/icon.ico",
"icons/icon.png"
"icons/Square310x310Logo.png"
],
"targets": ["msi", "app", "dmg"],
"windows": {
"wix": {
"language": "zh-CN"
}
},
"windows": { "wix": { "language": "zh-CN" } },
"macOS": {}
},
"app": {
@@ -49,16 +45,7 @@
"visible": false
}
],
"security": {
"capabilities": ["TeyvatGuide", "Mys", "SubWindow", "DevJson"]
}
"security": { "capabilities": ["TeyvatGuide", "Mys", "SubWindow", "DevJson"] }
},
"plugins": {
"deep-link": {
"desktop": {
"schemes": ["teyvatguide"]
},
"mobile": []
}
}
"plugins": { "deep-link": { "desktop": { "schemes": ["teyvatguide"] }, "mobile": [] } }
}

View File

@@ -212,11 +212,13 @@ async function handleDeepLink(payload: string): Promise<void> {
}
if (payload.startsWith("router?path=")) {
const routerPath = payload.replace("router?path=", "");
if (router.currentRoute.value.path === routerPath) {
const curPath = router.currentRoute.value.path;
if (curPath === routerPath) {
showSnackbar.warn("已在当前页面!", 3000);
return;
}
await router.push(routerPath);
await router.push({ path: routerPath, query: {} });
window.location.pathname = routerPath;
return;
}
}

View File

@@ -1,7 +1,7 @@
<template>
<div class="tgn-container">
<div v-for="navItem in nav" :key="navItem.id" class="tgn-nav" @click="toNav(navItem)">
<img alt="navIcon" :src="navItem.icon" />
<TMiImg alt="navIcon" :src="navItem.icon" :ori="true" />
<span>{{ navItem.name }}</span>
</div>
<div v-if="props.modelValue === 2 && hasNav" class="tgn-nav">
@@ -11,9 +11,9 @@
</div>
</template>
<script lang="ts" setup>
import TMiImg from "@comp/app/t-mi-img.vue";
import showDialog from "@comp/func/dialog.js";
import showSnackbar from "@comp/func/snackbar.js";
import Mys from "@Mys/index.js";
import { emit } from "@tauri-apps/api/event";
import { storeToRefs } from "pinia";
import { computed, onMounted, ref, shallowRef, watch } from "vue";
@@ -24,6 +24,7 @@ import { useAppStore } from "@/store/modules/app.js";
import TGClient from "@/utils/TGClient.js";
import TGLogger from "@/utils/TGLogger.js";
import { createPost } from "@/utils/TGWindow.js";
import ApiHubReq from "@/web/request/apiHubReq.js";
import OtherApi from "@/web/request/otherReq.js";
type TGameNavProps = { modelValue: number };
@@ -35,9 +36,9 @@ const codeData = shallowRef<TGApp.BBS.Navigator.CodeData[]>([]);
const showOverlay = ref<boolean>(false);
const actId = ref<string>();
const hasNav = computed<boolean>(() => {
if (props.modelValue !== 2) return false;
return nav.value.find((item) => item.name === "前瞻直播") !== undefined;
const hasNav = computed<TGApp.BBS.Navigator.Navigator | undefined>(() => {
if (props.modelValue !== 2) return undefined;
return nav.value.find((item) => item.name === "前瞻直播" || item.name === "直播兑换码");
});
onMounted(async () => await loadNav());
@@ -48,14 +49,13 @@ watch(
);
async function loadNav(): Promise<void> {
nav.value = await Mys.ApiHub.homeNew(props.modelValue);
nav.value = await ApiHubReq.home(props.modelValue);
}
async function tryGetCode(): Promise<void> {
if (props.modelValue !== 2) return;
const navFind = nav.value.find((item) => item.name === "前瞻直播");
if (!navFind) return;
const actIdFind = new URL(navFind.app_path).searchParams.get("act_id");
if (!hasNav.value) return;
const actIdFind = new URL(hasNav.value.app_path).searchParams.get("act_id");
if (!actIdFind) {
showSnackbar.warn("未找到活动ID");
return;
@@ -78,7 +78,7 @@ async function toNav(item: TGApp.BBS.Navigator.Navigator): Promise<void> {
return;
}
await TGLogger.Info(`[TGameNav][toNav] 打开网页活动 ${item.name}`);
await TGLogger.Info(`[TGameNav}][toNav] ${item.app_path}`);
await TGLogger.Info(`[TGameNav][toNav] ${item.app_path}`);
const link = new URL(item.app_path);
const mysList = [
"https://act.mihoyo.com",
@@ -131,6 +131,7 @@ async function toBBS(link: URL): Promise<void> {
showSnackbar.warn(`不支持的链接:${link.href}`);
}
// todo 动态获取版块列表
function getLocalPath(forum?: string): string {
if (!forum) return "";
const forumLocalMap: Record<string, string> = {

View File

@@ -7,7 +7,9 @@
<slot name="icon"><img :src="props.modelValue.icon" alt="icon" /></slot>
</div>
<div class="tib-cover">
<div class="tib-lt"><img :src="props.modelValue.lt" alt="lt" /></div>
<div class="tib-lt" v-show="props.modelValue.lt !== ''">
<img :src="props.modelValue.lt" alt="lt" />
</div>
<div v-show="props.modelValue.rt" class="tib-rt">{{ props.modelValue.rt }}</div>
<div class="tib-inner">
<slot name="inner-icon">

View File

@@ -0,0 +1,68 @@
<template>
<img
:src="localUrl"
:alt="props.alt"
:title="props.title"
v-if="localUrl"
@click="emits('click')"
:class="props.class"
/>
<div class="progress" v-else>
<v-progress-circular indeterminate color="primary" size="25" />
</div>
</template>
<script lang="ts" setup>
import { onMounted, onUnmounted, ref, watch } from "vue";
import { useAppStore } from "@/store/modules/app.js";
import { saveImgLocal } from "@/utils/TGShare.js";
type TMiImgProps = {
src: string;
alt: string;
title?: string;
class?: string;
ori?: boolean;
};
type TMiImgEmits = (e: "click") => void;
const props = defineProps<TMiImgProps>();
const emits = defineEmits<TMiImgEmits>();
const appStore = useAppStore();
const localUrl = ref<string>();
onMounted(async () => {
if (!props.src) return;
if (!props.src.startsWith("http")) {
localUrl.value = props.src;
return;
}
const link = props.ori ? props.src : appStore.getImageUrl(props.src);
localUrl.value = await saveImgLocal(link);
});
watch(
() => props.src,
async () => {
if (!props.src) return;
if (localUrl.value) URL.revokeObjectURL(localUrl.value);
localUrl.value = undefined;
const link = props.ori ? props.src : appStore.getImageUrl(props.src);
localUrl.value = await saveImgLocal(link);
},
);
onUnmounted(() => {
if (localUrl.value) URL.revokeObjectURL(localUrl.value);
});
</script>
<style lang="scss" scoped>
.progress {
width: 25px;
height: 25px;
display: flex;
position: relative;
justify-content: center;
align-items: center;
}
</style>

View File

@@ -10,17 +10,16 @@
<script lang="ts" setup>
import { ref, watch } from "vue";
type TolProps = { modelValue: boolean; blurVal?: string };
type TolEmits = (e: "update:modelValue", v: boolean) => void;
const emit = defineEmits<TolEmits>();
const props = withDefaults(defineProps<TolProps>(), { modelValue: false, blurVal: "20px" });
type TolProps = { blurVal?: string };
withDefaults(defineProps<TolProps>(), { blurVal: "20px" });
const model = defineModel<boolean>({ default: false });
const showTolo = ref<boolean>(false);
const showToli = ref<boolean>(false);
watch(
() => props.modelValue,
() => model.value,
async () => {
if (props.modelValue) {
if (model.value) {
showTolo.value = true;
showToli.value = true;
return;
@@ -33,7 +32,7 @@ watch(
);
function toClick(): void {
emit("update:modelValue", false);
model.value = false;
}
</script>
<style lang="css" scoped>
@@ -83,10 +82,12 @@ function toClick(): void {
top: 0;
left: 0;
display: flex;
overflow: hidden;
width: 100%;
height: 100%;
align-items: center;
justify-content: center;
border-radius: 8px;
-webkit-backdrop-filter: blur(v-bind(blurVal));
backdrop-filter: blur(v-bind(blurVal));
background: rgb(0 0 0 / 50%);

View File

@@ -2,8 +2,7 @@
<div v-if="card" :id="`post-card-${card.postId}`" class="tpc-card">
<div class="tpc-top">
<div class="tpc-cover" @click="createPost(card)">
<img :src="localCover" alt="cover" v-if="localCover" />
<v-progress-circular color="primary" :indeterminate="true" v-else-if="card.cover !== ''" />
<TMiImg :src="card.cover" alt="cover" :ori="true" v-if="card.cover !== ''" />
<img src="/source/UI/defaultCover.webp" alt="cover" v-else />
<div v-if="card.status" class="tpc-act">
<div class="tpc-status">{{ card.status?.label }}</div>
@@ -67,17 +66,32 @@
</div>
</template>
<script lang="ts" setup>
import TMiImg from "@comp/app/t-mi-img.vue";
import showSnackbar from "@comp/func/snackbar.js";
import TpAvatar from "@comp/viewPost/tp-avatar.vue";
import { emit } from "@tauri-apps/api/event";
import { computed, onMounted, onUnmounted, ref, shallowRef, watch } from "vue";
import { computed, onMounted, shallowRef, watch } from "vue";
import { generateShareImg, saveImgLocal } from "@/utils/TGShare.js";
import { generateShareImg } from "@/utils/TGShare.js";
import { createPost } from "@/utils/TGWindow.js";
type TPostCardProps = { modelValue: TGApp.Plugins.Mys.Post.FullData; selectMode?: boolean };
type TPostCardEmits = (e: "onSelected", v: string) => void;
type TPostStatus = TGApp.Plugins.Mys.News.RenderStatus & { stat: ActStat };
type TPostStatus = RenderStatus & { stat: ActStat };
type RenderForum = { name: string; icon: string; id: number };
type RenderStatus = { stat: number; label: string; color: string };
type RenderData = { mark: number; forward: number; like: number; reply: number; view: number };
export type RenderCard = {
title: string;
cover: string;
postId: number;
subtitle: string;
user: TGApp.Plugins.Mys.User.Post | null;
forum: RenderForum | null;
data: RenderData | null;
status?: RenderStatus;
topics: Array<TGApp.BBS.Topic.Info>;
};
enum ActStat {
UNKNOWN,
@@ -94,40 +108,21 @@ const stats: Readonly<Array<TPostStatus>> = [
];
const props = withDefaults(defineProps<TPostCardProps>(), { selectMode: false });
const emits = defineEmits<TPostCardEmits>();
const card = shallowRef<TGApp.Plugins.Mys.News.RenderCard>();
const localCover = ref<string>();
const card = shallowRef<RenderCard>();
const cardBg = computed<string>(() => {
if (card.value && card.value.status) return card.value.status.color;
return "none";
});
onMounted(async () => await reload(props.modelValue));
onMounted(async () => (card.value = getPostCard(props.modelValue)));
watch(
() => props.modelValue,
async () => await reload(props.modelValue),
async () => (card.value = getPostCard(props.modelValue)),
);
async function reload(data: TGApp.Plugins.Mys.Post.FullData): Promise<void> {
if (localCover.value) {
URL.revokeObjectURL(localCover.value);
localCover.value = undefined;
}
card.value = getPostCard(data);
if (card.value && card.value.cover !== "") {
localCover.value = await saveImgLocal(card.value.cover);
}
}
onUnmounted(() => {
if (localCover.value) {
URL.revokeObjectURL(localCover.value);
localCover.value = undefined;
}
});
function getActivityStatus(status: number): TGApp.Plugins.Mys.News.RenderStatus {
function getActivityStatus(status: number): RenderStatus {
if (status satisfies ActStat) {
const stat: ActStat = status;
return stats[stat];
@@ -142,18 +137,12 @@ function getPostCover(item: TGApp.Plugins.Mys.Post.FullData): string {
else if (item.post.images.length > 0) cover = item.post.images[0];
if (cover === undefined) return "";
if (cover.endsWith(".gif")) return cover;
return `${cover}?x-oss-process=image/format,png`;
return `${cover}?x-oss-process=image/resize,m_fill,w_360,h_130,limit_0/format,png`;
}
/**
* @description 获取公共属性
* @since Beta v0.6.1
* @param {TGApp.Plugins.Mys.Post.FullData} item 咨讯列表项
* @returns {TGApp.Plugins.Mys.News.RenderCard} 渲染用咨讯列表项
*/
function getCommonCard(item: TGApp.Plugins.Mys.Post.FullData): TGApp.Plugins.Mys.News.RenderCard {
let forumData: TGApp.Plugins.Mys.News.RenderForum | null = null;
let statData: TGApp.Plugins.Mys.News.RenderData | null = null;
function getCommonCard(item: TGApp.Plugins.Mys.Post.FullData): RenderCard {
let forumData: RenderForum | null = null;
let statData: RenderData | null = null;
if (item.forum !== null) {
forumData = { name: item.forum.name, icon: item.forum.icon, id: item.forum.id };
}
@@ -178,7 +167,7 @@ function getCommonCard(item: TGApp.Plugins.Mys.Post.FullData): TGApp.Plugins.Mys
};
}
function getPostCard(item: TGApp.Plugins.Mys.Post.FullData): TGApp.Plugins.Mys.News.RenderCard {
function getPostCard(item: TGApp.Plugins.Mys.Post.FullData): RenderCard {
const commonCard = getCommonCard(item);
if (
item.news_meta !== undefined &&
@@ -205,12 +194,12 @@ async function shareCard(): Promise<void> {
await generateShareImg(fileName, shareDom, 2.5);
}
async function toTopic(topic: TGApp.Plugins.Mys.Topic.Info): Promise<void> {
async function toTopic(topic: TGApp.BBS.Topic.Info): Promise<void> {
const gid = props.modelValue.post.game_id;
await emit("active_deep_link", `router?path=/posts/topic/${gid}/${topic.id}`);
}
async function toForum(forum: TGApp.Plugins.Mys.News.RenderForum): Promise<void> {
async function toForum(forum: RenderForum): Promise<void> {
const gid = props.modelValue.post.game_id;
await emit("active_deep_link", `router?path=/posts/forum/${gid}/${forum.id}`);
}
@@ -292,7 +281,6 @@ async function toForum(forum: TGApp.Plugins.Mys.News.RenderForum): Promise<void>
align-items: flex-start;
justify-content: flex-start;
color: var(--box-text-5);
cursor: pointer;
font-size: 12px;
gap: 5px;
@@ -309,6 +297,7 @@ async function toForum(forum: TGApp.Plugins.Mys.News.RenderForum): Promise<void>
border: 1px solid var(--common-shadow-1);
border-radius: 5px;
background: var(--box-bg-2);
cursor: pointer;
gap: 3px;
}
@@ -320,13 +309,13 @@ async function toForum(forum: TGApp.Plugins.Mys.News.RenderForum): Promise<void>
align-items: center;
justify-content: flex-start;
padding: 5px;
-webkit-backdrop-filter: blur(20px);
backdrop-filter: blur(20px);
background: var(--common-shadow-2);
background: var(--tgc-od-white);
border-bottom-left-radius: 5px;
border-top-right-radius: 5px;
box-shadow: 0 0 10px var(--tgc-dark-1);
color: var(--tgc-white-1);
cursor: pointer;
opacity: 0.8;
text-shadow: 0 0 5px var(--tgc-dark-1);
}
@@ -440,12 +429,13 @@ async function toForum(forum: TGApp.Plugins.Mys.News.RenderForum): Promise<void>
padding: 0 5px;
-webkit-backdrop-filter: blur(20px);
backdrop-filter: blur(20px);
background: var(--common-shadow-1);
background: var(--tgc-od-orange);
border-bottom-right-radius: 5px;
border-top-left-radius: 5px;
box-shadow: 2px 2px 5px var(--tgc-dark-1);
color: var(--tgc-white-1);
font-size: 12px;
opacity: 0.8;
text-shadow: 0 0 5px var(--tgc-dark-1);
}
</style>

View File

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

View File

@@ -45,26 +45,16 @@
</template>
<script setup lang="ts">
import showSnackbar from "@comp/func/snackbar.js";
import { computed } from "vue";
import TOverlay from "./t-overlay.vue";
import { generateShareImg } from "@/utils/TGShare.js";
import { timestampToDate } from "@/utils/toolFunc.js";
type ToLiveCodeProps = {
data: TGApp.BBS.Navigator.CodeData[];
actId: string | undefined;
modelValue: boolean;
};
type ToLiveCodeEmits = (e: "update:modelValue", v: boolean) => void;
type ToLiveCodeProps = { data: Array<TGApp.BBS.Navigator.CodeData>; actId: string | undefined };
const props = withDefaults(defineProps<ToLiveCodeProps>(), { modelValue: false });
const emits = defineEmits<ToLiveCodeEmits>();
const visible = computed<boolean>({
get: () => props.modelValue,
set: (v) => emits("update:modelValue", v),
});
const props = defineProps<ToLiveCodeProps>();
const visible = defineModel<boolean>({ default: false });
function copy(code: string): void {
navigator.clipboard.writeText(code);

View File

@@ -1,6 +1,6 @@
<template>
<TOverlay v-model="visible">
<div v-if="props.data" class="ton-container">
<TOverlay v-model="visible" v-if="props.data">
<div class="ton-container">
<slot name="left"></slot>
<div class="ton-box">
<img
@@ -48,10 +48,10 @@ enum ToNameCardTypeEnum {
}
type ToNameCardTypeMap = { [key in ToNameCardTypeEnum]: string };
type ToNameCardProps = { modelValue: boolean; data?: TGApp.App.NameCard.Item };
type ToNameCardEmits = (e: "update:modelValue", v: boolean) => void;
type ToNameCardProps = { data?: TGApp.App.NameCard.Item };
const props = defineProps<ToNameCardProps>();
const emits = defineEmits<ToNameCardEmits>();
const visible = defineModel<boolean>();
const typeMap: ToNameCardTypeMap = {
0: "其他",
1: "成就",
@@ -61,10 +61,6 @@ const typeMap: ToNameCardTypeMap = {
5: "未知",
};
const loading = ref<boolean>(false);
const visible = computed<boolean>({
get: () => props.modelValue,
set: (v) => emits("update:modelValue", v),
});
const getType = computed<string>(() => {
if (!props.data) return typeMap[ToNameCardTypeEnum.unknown];
if (!(props.data.type satisfies ToNameCardTypeEnum)) return typeMap[5];
@@ -141,10 +137,11 @@ async function shareNameCard(): Promise<void> {
</script>
<style lang="css" scoped>
.ton-container {
position: relative;
display: flex;
align-items: center;
justify-content: center;
column-gap: 10px;
column-gap: 8px;
}
.ton-box {
@@ -159,6 +156,7 @@ async function shareNameCard(): Promise<void> {
position: absolute;
width: 100%;
height: 100%;
border-radius: 10px;
}
.ton-type {
@@ -176,12 +174,14 @@ async function shareNameCard(): Promise<void> {
right: 0;
left: 0;
display: flex;
overflow: hidden;
width: 100%;
height: 100%;
flex-direction: column;
align-items: flex-start;
justify-content: flex-end;
padding: 10px;
border-radius: 10px;
backdrop-filter: blur(5px);
background: rgb(0 0 0 / 25%);
color: var(--tgc-white-1);

View File

@@ -2,7 +2,7 @@
<v-list class="top-nc-box" @click="emit('selected', props.data)">
<v-list-item :title="props.data.name">
<template #subtitle>
<span :title="props.data.desc">{{ props.data.desc }}</span>
<span class="desc" :title="props.data.desc">{{ props.data.desc }}</span>
</template>
<template #prepend>
<v-img
@@ -42,4 +42,8 @@ const bgImage = computed<string>(() => {
cursor: pointer;
font-family: var(--font-title);
}
.desc {
text-shadow: 1px 1px 1px #222;
}
</style>

View File

@@ -1,11 +1,11 @@
/**
* @file component/func/geetest.ts
* @description 封装自定义 geetest 组件,通过函数调用的方式,简化 geetest 的使用
* @since Beta v0.6.6
* @since Beta v0.7.1
*/
import { h, render } from "vue";
import type { ComponentInternalInstance, VNode } from "vue";
import { h, render } from "vue";
import geetest from "./geetest.vue";
@@ -21,12 +21,12 @@ const geetestId = "tg-func-geetest";
interface GeetestInstance extends ComponentInternalInstance {
exposeProxy: {
displayBox: (
props: TGApp.Plugins.Mys.Geetest.reqResp,
) => Promise<TGApp.Plugins.Mys.Geetest.validateResp | false>;
props: TGApp.BBS.Geetest.CreateRes,
) => Promise<TGApp.BBS.Geetest.GeetestVerifyRes | false>;
};
}
function renderBox(props: TGApp.Plugins.Mys.Geetest.reqResp): VNode {
function renderBox(props: TGApp.BBS.Geetest.CreateRes): VNode {
const container = document.createElement("div");
container.id = geetestId;
const boxVNode: VNode = h(geetest, props);
@@ -39,14 +39,14 @@ let geetestInstance: VNode;
/**
* @function showGeetest
* @since Beta v0.5.1
* @since Beta v0.7.1
* @description 弹出 geetest 验证
* @param {TGApp.Plugins.Mys.Geetest.reqResp} props geetest 验证的参数
* @return {Promise<TGApp.Plugins.Mys.Geetest.validateResp>} 验证成功返回验证数据
* @param {TGApp.BBS.Geetest.CreateRes} props geetest 验证的参数
* @return {Promise<TGApp.BBS.Geetest.GeetestVerifyRes|false>} 验证成功返回验证数据
*/
async function showGeetest(
props: TGApp.Plugins.Mys.Geetest.reqResp,
): Promise<TGApp.Plugins.Mys.Geetest.validateResp | false> {
props: TGApp.BBS.Geetest.CreateRes,
): Promise<TGApp.BBS.Geetest.GeetestVerifyRes | false> {
if (geetestInstance !== undefined) {
const boxVue = <GeetestInstance>geetestInstance.component;
return boxVue.exposeProxy.displayBox(props);

View File

@@ -40,14 +40,14 @@ watch(
);
declare function initGeetest(
params: TGApp.Plugins.Mys.Geetest.InitGeetestParams,
callback: (captchaObj: TGApp.Plugins.Mys.Geetest.GeetestCaptcha) => void,
params: TGApp.BBS.Geetest.InitGeetestParams,
callback: (captchaObj: TGApp.BBS.Geetest.GeetestCaptcha) => void,
): void;
async function displayBox(
props: TGApp.Plugins.Mys.Geetest.reqResp,
): Promise<TGApp.Plugins.Mys.Geetest.validateResp | false> {
return await new Promise<TGApp.Plugins.Mys.Geetest.validateResp>((resolve) => {
props: TGApp.BBS.Geetest.CreateRes,
): Promise<TGApp.BBS.Geetest.GeetestVerifyRes | false> {
return await new Promise<TGApp.BBS.Geetest.GeetestVerifyRes>((resolve) => {
initGeetest(
{
gt: props.gt,
@@ -58,7 +58,7 @@ async function displayBox(
area: "#verify",
width: "250px",
},
(captchaObj: TGApp.Plugins.Mys.Geetest.GeetestCaptcha) => {
(captchaObj: TGApp.BBS.Geetest.GeetestCaptcha) => {
if (geetestEl.value === null) return;
geetestEl.value.innerHTML = "";
captchaObj.appendTo("#geetest");

View File

@@ -6,7 +6,7 @@
<div class="loading-box">
<div class="loading-title">
<span>{{ data.title }}</span>
<div class="loading-circle" v-show="!empty">
<div class="loading-circle" v-show="!data.empty">
<div />
<div />
</div>
@@ -15,7 +15,7 @@
{{ data.subtitle }}
</div>
<div class="loading-img">
<img v-if="!empty" src="/source/UI/loading.webp" alt="loading" />
<img v-if="!data.empty" src="/source/UI/loading.webp" alt="loading" />
<img v-else src="/source/UI/empty.webp" alt="empty" />
</div>
</div>
@@ -125,8 +125,9 @@ defineExpose({ displayBox });
.loading-container {
display: flex;
min-width: 800px;
width: 800px;
min-height: 300px;
box-sizing: border-box;
padding: 15px;
border-radius: 15px;
background: rgb(255 255 255 / 5%);
@@ -136,7 +137,7 @@ defineExpose({ displayBox });
.loading-box {
display: flex;
width: 100%;
box-sizing: content-box;
box-sizing: border-box;
flex-direction: column;
align-items: center;
justify-content: center;
@@ -159,10 +160,15 @@ defineExpose({ displayBox });
}
.loading-subtitle {
position: relative;
width: 100%;
max-width: 100%;
height: 25px;
box-sizing: border-box;
padding: 0 20px;
font-size: 1rem;
text-align: center;
word-break: break-all;
}
.loading-img {

View File

@@ -103,13 +103,16 @@ defineExpose({ displayBox });
.func-snackbar {
display: flex;
min-width: 200px;
height: 40px;
max-width: calc(100% - 40px);
min-height: 40px;
box-sizing: border-box;
align-items: center;
justify-content: center;
padding: 10px 20px;
border-radius: 5px;
background-color: v-bind(bgColor);
box-shadow: 0 0 10px rgb(0 0 0 / 20%);
word-break: break-all;
}
.func-snackbar-text {

View File

@@ -55,7 +55,7 @@
<script lang="ts" setup>
import TOverlay from "@comp/app/t-overlay.vue";
import showSnackbar from "@comp/func/snackbar.js";
import { computed, ref } from "vue";
import { ref } from "vue";
import HtaOverviewLine from "./hta-overview-line.vue";
@@ -63,19 +63,11 @@ import type { AbyssDataItem } from "@/pages/WIKI/Abyss.vue";
import { generateShareImg } from "@/utils/TGShare.js";
import { timestampToDate } from "@/utils/toolFunc.js";
type HtaOverlayOverviewProps = {
modelValue: boolean;
data: AbyssDataItem<TGApp.Plugins.Hutao.Abyss.OverviewData>;
};
type HtaOverlayOverviewEmits = (e: "update:modelValue", v: boolean) => void;
type HtaOverlayOverviewProps = { data: AbyssDataItem<TGApp.Plugins.Hutao.Abyss.OverviewData> };
const props = defineProps<HtaOverlayOverviewProps>();
const emits = defineEmits<HtaOverlayOverviewEmits>();
const visible = defineModel<boolean>();
const loadShare = ref<boolean>(false);
const visible = computed<boolean>({
get: () => props.modelValue,
set: (v) => emits("update:modelValue", v),
});
async function share(): Promise<void> {
loadShare.value = true;

View File

@@ -1,5 +1,4 @@
<template>
<!-- todo fix typo err -->
<v-data-table :headers="headers" fixed-header :items="holdData" height="calc(100vh - 160px)">
<template v-slot:item="{ item }">
<tr class="hta-th-tr">
@@ -53,7 +52,7 @@ type HtaTabHoldData = {
const props = defineProps<HtaTabHoldProps>();
const holdData = shallowRef<Array<HtaTabHoldData>>([]);
const headers = [
const headers = <const>[
{ title: "角色", align: "center", key: "AvatarId" },
{ title: "持有", align: "center", key: "HoldingRate.cur" },
{ title: "0命", align: "center", key: "Constellations[0].RateCur" },

View File

@@ -66,7 +66,6 @@ onMounted(async () => {
overflow: auto;
width: 100%;
height: 100%;
max-height: calc(100vh - 130px);
overflow-x: hidden;
}
@@ -102,7 +101,7 @@ onMounted(async () => {
.hta-tuf-item {
position: relative;
width: 100%;
max-height: calc(100vh - 200px);
max-height: calc(100vh - 160px);
border-radius: 5px;
background: var(--box-bg-1);
}

View File

@@ -1,7 +1,6 @@
<template>
<div class="hta-tu-box">
<v-tabs v-model="tab" direction="vertical" class="hta-tu-tab">
<v-tab :value="10">第10层</v-tab>
<v-tab :value="11">第11层</v-tab>
<v-tab :value="12">第12层</v-tab>
</v-tabs>
@@ -29,7 +28,7 @@ type HtaTabUpProps = { data: AbyssDataItem<Array<TGApp.Plugins.Hutao.Abyss.Avata
type HtaTabUpData = { Floor: number; Ranks: Array<AbyssDataItem<TGApp.Plugins.Hutao.Base.Rate>> };
const props = defineProps<HtaTabUpProps>();
const tab = ref<number>(10);
const tab = ref<number>(11);
const select = shallowRef<Array<HtaTabUpData>>([]);
onMounted(async () => {

View File

@@ -1,7 +1,6 @@
<template>
<div class="hta-tus-box">
<v-tabs v-model="tab" direction="vertical" class="hta-tus-tab">
<v-tab :value="10">第10层</v-tab>
<v-tab :value="11">第11层</v-tab>
<v-tab :value="12">第12层</v-tab>
</v-tabs>
@@ -29,7 +28,7 @@ type HtaTabUseProps = { data: AbyssDataItem<Array<TGApp.Plugins.Hutao.Abyss.Avat
type HtaTabUseData = { Floor: number; Ranks: Array<AbyssDataItem<{ Item: number; Rate: number }>> };
const props = defineProps<HtaTabUseProps>();
const tab = ref<number>(10);
const tab = ref<number>(11);
const select = shallowRef<Array<HtaTabUseData>>([]);
onMounted(async () => {

View File

@@ -1,13 +1,13 @@
<template>
<div :id="`anno_card_${props.modelValue.id}`" class="anno-card">
<div class="anno-cover" :title="props.modelValue.title" @click="createAnno">
<img :src="localBanner" alt="cover" v-if="localBanner" />
<v-progress-circular
color="primary"
:indeterminate="true"
v-else-if="props.modelValue.banner !== ''"
<TMiImg
:src="props.modelValue.banner"
alt="cover"
:ori="true"
v-if="props.modelValue.banner !== ''"
/>
<img src="/source/UI/defaultCover.webp" alt="cover" v-else />
<img alt="cover" src="/source/UI/defaultCover.webp" v-else />
<div class="anno-info">
<div class="anno-time">
<v-icon>mdi-clock-time-four-outline</v-icon>
@@ -19,55 +19,22 @@
{{ parseTitle(props.modelValue.subtitle) }}
</div>
<div class="anno-label" :title="`标签:${props.modelValue.tagLabel}`">
<img :src="localTag" alt="tag" v-if="localTag" />
<v-icon v-else>mdi-tag</v-icon>
<img :src="props.modelValue.tagIcon" alt="tag" />
<span>{{ props.modelValue.tagLabel }}</span>
</div>
<div class="anno-id">{{ props.modelValue.id }}</div>
</div>
</template>
<script lang="ts" setup>
import TMiImg from "@comp/app/t-mi-img.vue";
import showSnackbar from "@comp/func/snackbar.js";
import { onMounted, onUnmounted, ref, watch } from "vue";
import TGLogger from "@/utils/TGLogger.js";
import { generateShareImg, saveImgLocal } from "@/utils/TGShare.js";
import { generateShareImg } from "@/utils/TGShare.js";
import { createTGWindow } from "@/utils/TGWindow.js";
type TAnnoCardProps = { region: string; modelValue: TGApp.App.Announcement.ListCard; lang: string };
const props = defineProps<TAnnoCardProps>();
const localBanner = ref<string>();
const localTag = ref<string>();
onMounted(async () => {
if (props.modelValue.banner !== "") {
localBanner.value = await saveImgLocal(props.modelValue.banner);
}
localTag.value = await saveImgLocal(props.modelValue.tagIcon);
});
watch(
() => props.modelValue,
async () => {
if (localBanner.value && localBanner.value.startsWith("blob:")) {
URL.revokeObjectURL(localBanner.value);
localBanner.value = undefined;
}
if (props.modelValue.banner !== "") {
localBanner.value = await saveImgLocal(props.modelValue.banner);
}
if (localTag.value && localTag.value.startsWith("blob:")) {
URL.revokeObjectURL(localTag.value);
localTag.value = undefined;
}
localTag.value = await saveImgLocal(props.modelValue.tagIcon);
},
);
onUnmounted(() => {
if (localBanner.value) URL.revokeObjectURL(localBanner.value);
if (localTag.value) URL.revokeObjectURL(localTag.value);
});
function parseTitle(title: string): string {
const dom = new DOMParser().parseFromString(title, "text/html");
@@ -106,7 +73,7 @@ async function shareAnno(): Promise<void> {
display: flex;
overflow: hidden;
width: 100%;
align-items: center;
align-items: flex-start;
justify-content: center;
aspect-ratio: 36 / 13;
cursor: pointer;
@@ -162,12 +129,11 @@ async function shareAnno(): Promise<void> {
align-items: center;
justify-content: flex-start;
padding: 5px;
-webkit-backdrop-filter: blur(20px);
backdrop-filter: blur(20px);
background: var(--common-shadow-2);
background-color: var(--tgc-od-white);
border-bottom-left-radius: 5px;
box-shadow: 0 0 10px var(--tgc-dark-1);
color: var(--tgc-white-1);
opacity: 0.8;
text-shadow: 0 0 5px var(--tgc-dark-1);
}
@@ -190,14 +156,13 @@ async function shareAnno(): Promise<void> {
align-items: center;
justify-content: center;
padding: 0 5px;
-webkit-backdrop-filter: blur(20px);
backdrop-filter: blur(20px);
background: var(--common-shadow-1);
background: var(--tgc-od-orange);
border-bottom-right-radius: 5px;
border-top-left-radius: 5px;
box-shadow: 2px 2px 5px var(--tgc-dark-1);
box-shadow: 0 0 10px var(--tgc-dark-1);
color: var(--tgc-white-1);
font-size: 12px;
opacity: 0.8;
text-shadow: 0 0 5px var(--tgc-dark-1);
}
</style>

View File

@@ -2,11 +2,11 @@
<TOverlay v-model="visible" blur-val="5px">
<div class="toab-container" v-if="props.data">
<div class="toab-img">
<img :src="props.data.take_picture[Number(props.choice)]" alt="顶部图像" />
<TMiImg :ori="true" :src="props.data.take_picture[Number(props.choice)]" alt="顶部图像" />
<div class="toab-dialog" v-show="showText">
<div v-for="(item, index) in textParse" :key="index" class="toab-dialog-item">
<div class="toab-dialog-item-icon" v-if="item.icon" :title="item.name">
<img :src="item.icon" alt="对白头像" />
<TMiImg :src="item.icon" alt="对白头像" :ori="true" />
</div>
<div v-else-if="item.name !== '未知'" class="toab-dialog-item-name">
{{ item.name }}
@@ -33,35 +33,28 @@
</TOverlay>
</template>
<script setup lang="ts">
import TMiImg from "@comp/app/t-mi-img.vue";
import TOverlay from "@comp/app/t-overlay.vue";
import showSnackbar from "@comp/func/snackbar.js";
import { fetch } from "@tauri-apps/plugin-http";
import { computed, onMounted, ref, shallowRef, watch } from "vue";
import { onMounted, ref, shallowRef, watch } from "vue";
import { xml2json } from "xml-js";
import TGLogger from "@/utils/TGLogger.js";
import { copyToClipboard, getImageBuffer, saveCanvasImg } from "@/utils/TGShare.js";
import { bytesToSize } from "@/utils/toolFunc.js";
type ToArcBirthProps = {
modelValue: boolean;
data?: TGApp.Archive.Birth.DrawItem;
choice: boolean;
};
type ToArcBirthEmits = (e: "update:modelValue", v: boolean) => void;
type ToArcBirthProps = { data?: TGApp.Archive.Birth.DrawItem; choice: boolean };
type XmlKeyMap = { id: string; rel: string; group?: string; icon: string };
type XmlTextList = { chara: string; img: string; text: string };
type XmlTextParse = { name: string; icon?: string; text: string };
const props = defineProps<ToArcBirthProps>();
const emits = defineEmits<ToArcBirthEmits>();
const visible = defineModel<boolean>();
const showText = ref<boolean>(false);
const buffer = shallowRef<Uint8Array | null>(null);
const textParse = shallowRef<Array<XmlTextParse>>([]);
const visible = computed<boolean>({
get: () => props.modelValue,
set: (v) => emits("update:modelValue", v),
});
onMounted(() => clearData());
watch(() => props.data, clearData);
watch(() => props.choice, clearData);

View File

@@ -26,26 +26,23 @@ import TOverlay from "@comp/app/t-overlay.vue";
import showDialog from "@comp/func/dialog.js";
import showSnackbar from "@comp/func/snackbar.js";
import TSUserCollection from "@Sqlite/modules/userCollect.js";
import { computed, ref, shallowRef, watch } from "vue";
import { ref, shallowRef, watch } from "vue";
type ToPostCollectProps = { modelValue: boolean; post: Array<string> };
type ToPostCollectEmits = {
(e: "update:modelValue", v: boolean): void;
(e: "submit"): void;
};
type ToPostCollectProps = { post: Array<string> };
type ToPostCollectEmits = (e: "submit") => void;
const props = defineProps<ToPostCollectProps>();
const emits = defineEmits<ToPostCollectEmits>();
const visible = defineModel<boolean>();
const select = ref<string>();
const submit = ref<boolean>(false);
const collectList = shallowRef<Array<TGApp.Sqlite.UserCollection.UFCollection>>([]);
const visible = computed<boolean>({
get: () => props.modelValue,
set: (v) => emits("update:modelValue", v),
});
watch(
() => props.modelValue,
async (val) => (val ? await freshData() : null),
() => visible.value,
async () => {
if (visible.value) await freshData();
},
);
async function onSubmit(): Promise<void> {

View File

@@ -1,4 +1,5 @@
<template>
<ToGameLogin v-model="showLoginQr" @success="tryGetTokens" v-model:launcher="isLauncherQr" />
<v-card class="tcu-box">
<template #prepend>
<v-avatar :image="userInfo.avatar" />
@@ -36,12 +37,6 @@
</template>
<template #actions>
<v-spacer />
<v-btn
variant="outlined"
@click="tryCaptchaLogin()"
icon="mdi-cellphone"
title="验证码登录"
/>
<v-btn
variant="outlined"
@click="confirmRefreshUser(uid!)"
@@ -90,10 +85,32 @@
/>
</template>
</v-list-item>
</v-list>
</v-menu>
<v-menu location="start">
<template v-slot:activator="{ props }">
<v-btn variant="outlined" icon="mdi-account-plus" title="添加账户" v-bind="props" />
</template>
<v-list>
<v-list-item @click="addByCookie()" append-icon="mdi-account-plus">
<v-list-item-title>手动添加</v-list-item-title>
<v-list-item-subtitle>手动输入Cookie</v-list-item-subtitle>
</v-list-item>
<v-list-item @click="tryCaptchaLogin()" append-icon="mdi-cellphone">
<v-list-item-title>验证码登录</v-list-item-title>
<v-list-item-subtitle>使用手机号登录</v-list-item-subtitle>
</v-list-item>
<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>
</v-menu>
</template>
@@ -104,10 +121,11 @@ import showDialog from "@comp/func/dialog.js";
import showGeetest from "@comp/func/geetest.js";
import showLoading from "@comp/func/loading.js";
import showSnackbar from "@comp/func/snackbar.js";
import ToGameLogin from "@comp/pageConfig/tco-gameLogin.vue";
import Mys from "@Mys/index.js";
import TSUserAccount from "@Sqlite/modules/userAccount.js";
import { storeToRefs } from "pinia";
import { computed, shallowRef } from "vue";
import { computed, ref, shallowRef } from "vue";
import { useAppStore } from "@/store/modules/app.js";
import { useUserStore } from "@/store/modules/user.js";
@@ -119,6 +137,8 @@ import TakumiApi from "@/web/request/takumiReq.js";
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>(() => {
@@ -126,42 +146,12 @@ const userInfo = computed<TGApp.App.Account.BriefInfo>(() => {
return {
nickname: "未登录",
uid: "-1",
desc: "请使用短信验证码登录",
desc: "请登录",
avatar: "/source/UI/lumine.webp",
};
});
async function tryCaptchaLogin(): Promise<void> {
const phone = await showDialog.input("请输入手机号", "+86");
if (!phone) {
showSnackbar.cancel("已取消验证码登录");
return;
}
const phoneReg = /^1[3-9]\d{9}$/;
if (!phoneReg.test(phone)) {
showSnackbar.warn("请输入正确的手机号");
return;
}
const actionType = await tryGetCaptcha(phone);
if (!actionType) return;
showSnackbar.success(`已发送验证码到 ${phone}`);
const captcha = await showDialog.input("请输入验证码", "验证码:", undefined, false);
if (!captcha) {
showSnackbar.warn("输入验证码为空");
return;
}
const loginResp = await tryLoginByCaptcha(phone, captcha, actionType);
if (!loginResp) return;
await showLoading.start("正在尝试登录...");
const ck: TGApp.App.Account.Cookie = {
account_id: loginResp.user_info.aid,
ltuid: loginResp.user_info.aid,
stuid: loginResp.user_info.aid,
mid: loginResp.user_info.mid,
cookie_token: "",
stoken: loginResp.token.token,
ltoken: "",
};
async function tryGetTokens(ck: TGApp.App.Account.Cookie): Promise<void> {
await showLoading.update("正在获取 LToken");
const ltokenRes = await PassportApi.lToken.get(ck);
if (typeof ltokenRes !== "string") {
@@ -231,6 +221,45 @@ async function tryCaptchaLogin(): Promise<void> {
showSnackbar.success("成功登录!");
}
async function tryCaptchaLogin(): Promise<void> {
const phone = await showDialog.input("请输入手机号", "+86");
if (!phone) {
showSnackbar.cancel("已取消验证码登录");
return;
}
const phoneReg = /^1[3-9]\d{9}$/;
if (!phoneReg.test(phone)) {
showSnackbar.warn("请输入正确的手机号");
return;
}
const actionType = await tryGetCaptcha(phone);
if (!actionType) return;
showSnackbar.success(`已发送验证码到 ${phone}`);
const captcha = await showDialog.input("请输入验证码", "验证码:", undefined, false);
if (!captcha) {
showSnackbar.warn("输入验证码为空");
return;
}
const loginResp = await tryLoginByCaptcha(phone, captcha, actionType);
if (!loginResp) return;
await showLoading.start("正在尝试登录...");
const ck: TGApp.App.Account.Cookie = {
account_id: loginResp.user_info.aid,
ltuid: loginResp.user_info.aid,
stuid: loginResp.user_info.aid,
mid: loginResp.user_info.mid,
cookie_token: "",
stoken: loginResp.token.token,
ltoken: "",
};
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) {
@@ -537,4 +566,9 @@ async function clearUser(user: TGApp.App.Account.User): Promise<void> {
.tcu-btn {
margin-left: 5px;
}
.menu-icon {
width: 24px;
height: 24px;
}
</style>

View File

@@ -0,0 +1,220 @@
<template>
<TOverlay v-model="model" hide blur-val="20px">
<div class="tog-box">
<div class="tog-top">
<div class="tog-title">请使用米游社进行扫码操作</div>
</div>
<div class="tog-mid">
<qrcode-vue
v-if="codeUrl"
class="tog-qr"
: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 { 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;
// eslint-disable-next-line no-undef
let cycleTimer: NodeJS.Timeout | null = null;
const model = defineModel<boolean>({ default: false });
const isLauncherCode = defineModel<boolean>("launcher", { default: false });
const emits = defineEmits<ToGameLoginEmits>();
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) {
await freshQr();
cycleTimer = setInterval(cycleGetData, 1000);
}
});
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> {
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;
}
codeUrl.value = res.url;
}
async function cycleGetData() {
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(timer);
cycleTimer = null;
model.value = false;
}
return;
}
if (res.status === "Created" || res.status === "Scanned") return;
if (res.status === "Confirmed") {
clearInterval(timer);
cycleTimer = null;
const ck: TGApp.App.Account.Cookie = {
account_id: res.user_info.aid,
ltuid: res.user_info.aid,
stuid: res.user_info.aid,
mid: res.user_info.mid,
cookie_token: "",
stoken: res.tokens[0].token,
ltoken: "",
};
emits("success", ck);
model.value = false;
}
}
// 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;
});
</script>
<style lang="css" scoped>
.tog-box {
display: flex;
flex-direction: column;
padding: 10px;
border-radius: 5px;
background-color: var(--box-bg-1);
color: var(--app-page-content);
gap: 10px;
}
.tog-top {
border-bottom: 1px solid var(--common-shadow-4);
font-family: var(--font-title);
text-align: center;
}
.tog-title {
color: var(--common-text-title);
font-size: 20px;
}
.tog-mid {
display: flex;
width: 100%;
align-items: center;
justify-content: center;
padding: 10px;
border: 1px solid var(--common-shadow-2);
border-radius: 5px;
aspect-ratio: 1;
background: var(--box-bg-2);
}
.tog-qr {
width: 256px;
height: 256px;
}
.tog-bottom {
margin: 0 auto;
cursor: pointer;
img {
width: 32px;
height: 32px;
border-radius: 4px;
}
}
</style>

View File

@@ -6,20 +6,30 @@
</div>
<div class="tcb-top-active" v-else>
<span>今天是</span>
<img
v-for="i in cur"
:key="i.role_id"
class="tcb-cur"
:alt="i.name"
:src="i.head_icon"
:title="i.name"
/>
<div v-for="i in cur" :key="i.role_id">
<TMiImg
:src="i.head_icon"
:alt="i.name"
:title="i.name"
:ori="true"
v-if="i.head_icon.startsWith('http')"
/>
<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">
<img :src="i.head_icon" :alt="i.name" @click="toBirth(i)" :title="i.name" />
<TMiImg
v-if="i.head_icon.startsWith('http')"
:src="i.head_icon"
:alt="i.name"
@click="toBirth(i)"
:title="i.name"
:ori="true"
/>
<img v-else :src="i.head_icon" alt="empty" @click="toBirth(i)" />
<div class="tcb-item-info">
<span>{{ i.name }} 所属{{ i.belong === "" ? "未知" : i.belong }}</span>
<span>{{ parseDesc(i.introduce) }}</span>
@@ -28,6 +38,7 @@
</div>
</template>
<script lang="ts" setup>
import TMiImg from "@comp/app/t-mi-img.vue";
import TSAvatarBirth from "@Sqlite/modules/avatarBirth.js";
import { storeToRefs } from "pinia";
import { onBeforeMount, ref, shallowRef } from "vue";
@@ -46,11 +57,13 @@ onBeforeMount(async () => {
if (check.length !== 0) {
isBirthday.value = true;
cur.value = check;
console.log(cur.value);
}
next.value = TSAvatarBirth.getNextAvatarBirth();
console.log(next.value);
});
function toBirth(type: TGApp.Archive.Birth.RoleItem | true): void {
async function toBirth(type: TGApp.Archive.Birth.RoleItem | true): Promise<void> {
let dateStr;
if (type === true) {
const date = new Date();
@@ -61,15 +74,15 @@ function toBirth(type: TGApp.Archive.Birth.RoleItem | true): void {
dateStr = type.role_birthday;
}
if (type !== true) {
router.push({ name: "留影叙佳期", params: { date: dateStr } });
await router.push({ name: "留影叙佳期", params: { date: dateStr } });
return;
}
if (cur.value.length > 0 && !cur.value[0].is_subscribe) {
recentNewsType.value = "news";
router.push("/news/2/news");
await router.push("/news/2/news");
return;
}
router.push({ name: "留影叙佳期", params: { date: dateStr } });
await router.push({ name: "留影叙佳期", params: { date: dateStr } });
}
function parseDesc(intro: string): string {
@@ -130,6 +143,7 @@ function parseDesc(intro: string): string {
.tcb-item img {
height: 100px;
border-radius: 50%;
aspect-ratio: 1;
cursor: pointer;
}

View File

@@ -36,20 +36,12 @@ import { useRouter } from "vue-router";
import TibCalendarMaterial from "./ph-calendar-material.vue";
type ToCalendarProps = {
modelValue: boolean;
dataType: "weapon" | "character";
dataVal: TGApp.App.Calendar.Item;
};
type ToCalendarEmits = (e: "update:modelValue", v: boolean) => void;
type ToCalendarProps = { dataType: "weapon" | "character"; dataVal: TGApp.App.Calendar.Item };
const router = useRouter();
const props = defineProps<ToCalendarProps>();
const emits = defineEmits<ToCalendarEmits>();
const router = useRouter();
const visible = computed<boolean>({
get: () => props.modelValue,
set: (v) => emits("update:modelValue", v),
});
const visible = defineModel<boolean>();
const boxData = computed<TItemBoxData>(() => ({
bg: `/icon/bg/${props.dataVal.star}-Star.webp`,
icon: `/WIKI/${props.dataType}/${props.dataVal.id}.webp`,

View File

@@ -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 />

View File

@@ -9,7 +9,7 @@
<div class="pool-grid">
<div v-for="pool in poolSelect" :key="pool.postId" class="pool-card">
<div class="pool-cover" @click="createPost(pool.postId, pool.title)">
<img :src="pool.cover" alt="cover" />
<TMiImg :src="pool.cover" alt="cover" :ori="true" />
</div>
<div class="pool-bottom">
<div class="pool-character">
@@ -56,6 +56,7 @@
</template>
<script lang="ts" setup>
import TItembox, { type TItemBoxData } from "@comp/app/t-itemBox.vue";
import TMiImg from "@comp/app/t-mi-img.vue";
import showSnackbar from "@comp/func/snackbar.js";
import Mys from "@Mys/index.js";
import { storeToRefs } from "pinia";

View File

@@ -3,7 +3,7 @@
<div class="top">
<div class="main">
<div class="left" @click="openPosition(props.position)">
<img :src="props.position.icon" alt="icon" />
<TMiImg :ori="true" :src="props.position.icon" alt="icon" />
</div>
<div class="right">
<div class="title">{{ props.position.title }}</div>
@@ -38,6 +38,7 @@
</div>
</template>
<script lang="ts" setup>
import TMiImg from "@comp/app/t-mi-img.vue";
import showSnackbar from "@comp/func/snackbar.js";
import type { PositionItem } from "@comp/pageHome/ph-comp-position.vue";
@@ -102,6 +103,7 @@ async function openPosition(card: TGApp.Plugins.Mys.Position.RenderCard): Promis
width: 40px;
height: 40px;
border-radius: 3px;
background: var(--box-bg-2);
object-fit: contain;
img {

View File

@@ -7,10 +7,10 @@
v-for="(item, index) in channelList"
:key="index"
class="toc-list-item"
:class="{ active: props.gid === item.gid }"
:class="props.gid === item.gid.toString() ? 'active' : ''"
@click="toChannel(item)"
>
<img :src="item.icon" alt="icon" />
<TMiImg :src="item.icon" alt="icon" :ori="true" />
<span>{{ item.title }}</span>
</div>
</div>
@@ -18,30 +18,30 @@
</TOverlay>
</template>
<script lang="ts" setup>
import TMiImg from "@comp/app/t-mi-img.vue";
import TOverlay from "@comp/app/t-overlay.vue";
import showSnackbar from "@comp/func/snackbar.js";
import { storeToRefs } from "pinia";
import { computed } from "vue";
import { useRouter } from "vue-router";
import { onMounted, shallowRef } from "vue";
import { type NewsType, useAppStore } from "@/store/modules/app.js";
import type { ToChannelItem } from "@/web/constant/bbs.js";
import TGConstant from "@/web/constant/TGConstant.js";
import apiHubReq from "@/web/request/apiHubReq.js";
type ChannelItem = { icon: string; title: string; gid: number };
type ToChannelProps = { gid?: string; curType?: string };
type ToChannelProps = { gid?: string; curType?: string; modelValue: boolean };
type ToChannelEmits = (e: "update:modelValue", v: boolean) => void;
const router = useRouter();
const props = withDefaults(defineProps<ToChannelProps>(), { modelValue: false });
const emits = defineEmits<ToChannelEmits>();
const { recentNewsType } = storeToRefs(useAppStore());
const channelList = TGConstant.BBS.CHANNELS;
const visible = computed<boolean>({
get: () => props.modelValue,
set: (v) => emits("update:modelValue", v),
const channelList = shallowRef<Array<ChannelItem>>();
const props = defineProps<ToChannelProps>();
const visible = defineModel<boolean>({ default: false });
onMounted(async () => {
const allGames = await apiHubReq.game();
channelList.value = allGames.map((i) => ({ icon: i.app_icon, title: i.name, gid: i.id }));
});
async function toChannel(item: ToChannelItem): Promise<void> {
if (props.gid === item.gid) {
async function toChannel(item: ChannelItem): Promise<void> {
if (props.gid === item.gid.toString()) {
showSnackbar.warn("当前已经在该频道");
return;
}
@@ -53,7 +53,7 @@ async function toChannel(item: ToChannelItem): Promise<void> {
link = link.replace("{type}", "notice");
recentNewsType.value = "notice";
}
await router.push(link);
window.location.href = link;
}
</script>
<style lang="css" scoped>
@@ -107,24 +107,4 @@ async function toChannel(item: ToChannelItem): Promise<void> {
font-size: 16px;
}
}
.toc-close {
display: flex;
width: 100%;
height: 60px;
align-items: center;
justify-content: center;
}
.toc-close-btn {
display: flex;
width: 30px;
height: 30px;
align-items: center;
justify-content: center;
border-radius: 50%;
background: var(--box-bg-1);
color: var(--app-page-content);
cursor: pointer;
}
</style>

View File

@@ -29,7 +29,7 @@
import TOverlay from "@comp/app/t-overlay.vue";
import showSnackbar from "@comp/func/snackbar.js";
import { getVersion } from "@tauri-apps/api/app";
import { computed, onMounted, ref } from "vue";
import { onMounted, ref } from "vue";
import TwoConvert from "./two-convert.vue";
import TwoSource from "./two-source.vue";
@@ -37,15 +37,11 @@ import TwoSource from "./two-source.vue";
import { generateShareImg } from "@/utils/TGShare.js";
import { parseHtmlText } from "@/utils/toolFunc.js";
type TwoMaterialProps = { modelValue: boolean; data: TGApp.App.Material.WikiItem };
type TwoMaterialEmits = (e: "update:modelValue", v: boolean) => void;
type TwoMaterialProps = { data: TGApp.App.Material.WikiItem };
const props = defineProps<TwoMaterialProps>();
const emits = defineEmits<TwoMaterialEmits>();
const visible = defineModel<boolean>();
const version = ref<string>();
const visible = computed<boolean>({
get: () => props.modelValue,
set: (v) => emits("update:modelValue", v),
});
onMounted(async () => (version.value = await getVersion()));

View File

@@ -66,7 +66,8 @@
</template>
<script setup lang="ts">
import TOverlay from "@comp/app/t-overlay.vue";
import { computed, ref, watch } from "vue";
import showSnackbar from "@comp/func/snackbar.js";
import { ref, toRaw, watch } from "vue";
export type SelectedCValue = {
star: Array<number>;
@@ -74,14 +75,8 @@ export type SelectedCValue = {
elements: Array<string>;
area: Array<string>;
};
type TwoSelectCProps = { modelValue: boolean; reset: boolean };
type TwoSelectCEmits = {
(e: "update:modelValue", v: boolean): void;
(e: "update:reset", v: boolean): void;
(e: "select-c", v: SelectedCValue): void;
};
type TwoSelectCEmits = (e: "select-c", v: SelectedCValue) => void;
const props = defineProps<TwoSelectCProps>();
const emits = defineEmits<TwoSelectCEmits>();
const selectStarList = [4, 5];
const selectWeaponList = ["单手剑", "双手剑", "弓", "法器", "长柄武器"];
@@ -93,24 +88,29 @@ const selectedStar = ref<Array<number>>(selectStarList);
const selectedWeapon = ref<Array<string>>(selectWeaponList);
const selectedElements = ref<Array<string>>(selectElementList);
const selectedArea = ref<Array<string>>(selectAreaList);
const visible = computed<boolean>({
get: () => props.modelValue,
set: (v) => emits("update:modelValue", v),
});
const reset = computed<boolean>({
get: () => props.reset,
set: (v: boolean) => emits("update:reset", v),
});
const visible = defineModel<boolean>();
const resetModel = defineModel<boolean>("reset");
watch(
() => props.reset,
(value) => {
if (value) {
() => resetModel.value,
() => {
if (resetModel.value) {
if (
toRaw(selectedStar.value) === selectStarList &&
toRaw(selectedWeapon.value) === selectWeaponList &&
toRaw(selectedElements.value) === selectElementList &&
toRaw(selectedArea.value) === selectAreaList
) {
showSnackbar.warn("无需重置");
resetModel.value = false;
return;
}
selectedStar.value = selectStarList;
selectedWeapon.value = selectWeaponList;
selectedElements.value = selectElementList;
selectedArea.value = selectAreaList;
reset.value = false;
resetModel.value = false;
showSnackbar.success("已重置");
}
},
);

View File

@@ -36,17 +36,12 @@
</template>
<script setup lang="ts">
import TOverlay from "@comp/app/t-overlay.vue";
import { computed, ref, watch } from "vue";
import showSnackbar from "@comp/func/snackbar.js";
import { ref, toRaw, watch } from "vue";
export type SelectedWValue = { star: Array<number>; weapon: Array<string> };
type TwoSelectWProps = { modelValue: boolean; reset: boolean };
type TwoSelectWEmits = {
(e: "update:modelValue", value: boolean): void;
(e: "update:reset", value: boolean): void;
(e: "select-w", value: SelectedWValue): void;
};
export type SelectedWValue = { star: Array<number>; weapon: Array<string>; isReset: boolean };
type TwoSelectWEmits = (e: "select-w", value: SelectedWValue) => void;
const props = defineProps<TwoSelectWProps>();
const emits = defineEmits<TwoSelectWEmits>();
const selectStarList = [4, 5];
@@ -54,29 +49,31 @@ const selectWeaponList = ["单手剑", "双手剑", "弓", "法器", "长柄武
const selectedStar = ref<Array<number>>(selectStarList);
const selectedWeapon = ref<Array<string>>(selectWeaponList);
const visible = computed<boolean>({
get: () => props.modelValue,
set: (v) => emits("update:modelValue", v),
});
const reset = computed<boolean>({
get: () => props.reset,
set: (v) => emits("update:reset", v),
});
const visible = defineModel<boolean>();
const resetModel = defineModel<boolean>("reset");
watch(
() => props.reset,
(value) => {
if (value) {
() => resetModel.value,
() => {
if (resetModel.value) {
if (
toRaw(selectedStar.value) === selectStarList &&
toRaw(selectedWeapon.value) === selectWeaponList
) {
showSnackbar.warn("无需重置");
resetModel.value = false;
return;
}
selectedStar.value = selectStarList;
selectedWeapon.value = selectWeaponList;
reset.value = false;
confirmSelect();
resetModel.value = false;
emits("select-w", { star: selectedStar.value, weapon: selectedWeapon.value, isReset: true });
}
},
);
function confirmSelect(): void {
emits("select-w", { star: selectedStar.value, weapon: selectedWeapon.value });
emits("select-w", { star: selectedStar.value, weapon: selectedWeapon.value, isReset: false });
visible.value = false;
}
</script>

View File

@@ -4,10 +4,12 @@
<slot name="left"></slot>
<div class="tua-ao-box">
<div class="tua-ao-top">
<span class="tua-ao-click" @click="searchDirect(props.data.name)">
<span class="tua-ao-click" title="查询" @click="searchDirect(props.data.name)">
{{ props.data.name }}
</span>
<span>{{ props.data.description }}</span>
<span class="tua-ao-click" title="分享" @click="share()">
{{ props.data.description }}
</span>
</div>
<div class="tua-ao-mid-title">
<span>所属系列</span>
@@ -53,32 +55,38 @@
</template>
<script lang="ts" setup>
import TOverlay from "@comp/app/t-overlay.vue";
import showSnackbar from "@comp/func/snackbar.js";
import VpOverlaySearch from "@comp/viewPost/vp-overlay-search.vue";
import { computed, ref } from "vue";
import { ref } from "vue";
import { AppAchievementSeriesData } from "@/data/index.js";
import TGLogger from "@/utils/TGLogger.js";
import { generateShareImg } from "@/utils/TGShare.js";
type ToAchiInfoProps = { modelValue: boolean; data: TGApp.Sqlite.Achievement.RenderAchi };
type ToAchiInfoEmits = {
(e: "update:modelValue", v: boolean): void;
(e: "select-series", v: number): void;
};
type ToAchiInfoProps = { data: TGApp.Sqlite.Achievement.RenderAchi };
type ToAchiInfoEmits = (e: "select-series", v: number) => void;
const props = defineProps<ToAchiInfoProps>();
const emits = defineEmits<ToAchiInfoEmits>();
const visible = defineModel<boolean>();
const showSearch = ref<boolean>(false);
const search = ref<string>();
const visible = computed<boolean>({
get: () => props.modelValue,
set: (v) => emits("update:modelValue", v),
});
async function searchDirect(word: string): Promise<void> {
await TGLogger.Info(`[ToAchiInfo][${props.data.id}][Search] 查询 ${word}`);
search.value = word;
showSearch.value = true;
}
async function share(): Promise<void> {
const achiBox = document.querySelector<HTMLElement>(".tua-ao-box");
if (achiBox === null) {
showSnackbar.error("未找到成就详情");
return;
}
const fileName = `【成就详情】【${props.data.id}】-${props.data.name}`;
await generateShareImg(fileName, achiBox);
}
</script>
<style lang="css" scoped>
.tua-ao-container {

View File

@@ -38,14 +38,11 @@ import { AppAchievementSeriesData } from "@/data/index.js";
import { timestampToDate } from "@/utils/toolFunc.js";
type TuaAchiProps = { modelValue: TGApp.Sqlite.Achievement.RenderAchi };
type TuaAchiEmits = {
(e: "update:modelValue", data: TGApp.Sqlite.Achievement.RenderAchi): void;
(e: "update:update", data: boolean): void;
(e: "select-achi", data: TGApp.Sqlite.Achievement.RenderAchi): void;
};
type TuaAchiEmits = (e: "select-achi", data: TGApp.Sqlite.Achievement.RenderAchi) => void;
const props = defineProps<TuaAchiProps>();
const emits = defineEmits<TuaAchiEmits>();
const model = defineModel<TGApp.Sqlite.Achievement.RenderAchi>();
const data = ref<TGApp.Sqlite.Achievement.RenderAchi>(toRaw(props.modelValue));
watch(
@@ -69,7 +66,7 @@ async function setAchiStat(stat: boolean): Promise<void> {
if (!stat) {
data.value.isCompleted = false;
await TSUserAchi.updateAchi(data.value);
emits("update:modelValue", data.value);
model.value = data.value;
await event.emit("updateAchi", data.value.series);
showSnackbar.success(`已将成就 ${data.value.name}(${data.value.id}) 状态设为未完成`);
return;
@@ -80,7 +77,7 @@ async function setAchiStat(stat: boolean): Promise<void> {
return;
}
if (progress === undefined) progress = data.value.progress.toString();
if (isNaN(Number(progress)) || progress === "0") {
if (isNaN(Number(progress))) {
showSnackbar.warn("请输入有效数字!");
return;
}
@@ -90,7 +87,7 @@ async function setAchiStat(stat: boolean): Promise<void> {
await TSUserAchi.updateAchi(data.value);
await event.emit("updateAchi", data.value.series);
showSnackbar.success(`已将成就 ${data.value.name}(${data.value.id}) 状态设为已完成`);
emits("update:modelValue", data.value);
model.value = data.value;
}
</script>
<style lang="css" scoped>

View File

@@ -6,7 +6,7 @@
class="tua-dcc-item"
:title="`${constellation.pos}命-${constellation.name}`"
>
<img :src="constellation.icon" alt="constellation" class="tua-dcc-icon" />
<TMiImg :ori="true" :src="constellation.icon" alt="constellation" class="tua-dcc-icon" />
<div v-if="!constellation.is_actived" class="tua-dcc-lock">
<v-icon size="10px" color="var(--tgc-od-white)">mdi-lock</v-icon>
</div>
@@ -14,6 +14,8 @@
</div>
</template>
<script lang="ts" setup>
import TMiImg from "@comp/app/t-mi-img.vue";
type TuaDcConstellationsProps = { modelValue: Array<TGApp.Game.Avatar.Constellation> };
const props = defineProps<TuaDcConstellationsProps>();

View File

@@ -16,7 +16,7 @@
v-if="props.modelValue === false"
class="empty"
/>
<img :src="props.modelValue.icon" :alt="props.modelValue.name" v-else />
<TMiImg :ori="true" :src="props.modelValue.icon" :alt="props.modelValue.name" v-else />
</div>
</div>
<div class="tua-dcr-right">
@@ -35,6 +35,7 @@
:src="propMain.icon"
alt="propMain"
/>
<v-icon v-else class="icon" size="14">mdi-adjust</v-icon>
<span :style="getPropMainStyle()">
{{ propMain !== false ? propMain.name : "未知属性" }}
</span>
@@ -44,6 +45,7 @@
<div v-for="(prop, index) in propSubs" :key="index" class="tua-dcr-prop">
<span class="tua-prop-sub">
<img v-if="prop !== false && prop.icon !== ''" :src="prop.icon" alt="propSub" />
<v-icon v-else class="icon" size="14">mdi-information-outline</v-icon>
<span :style="getPropSubStyle(prop, props.recommend.sub_property_list)">
{{ prop !== false ? prop.name : "未知属性" }}
</span>
@@ -57,6 +59,7 @@
</div>
</template>
<script lang="ts" setup>
import TMiImg from "@comp/app/t-mi-img.vue";
import { computed } from "vue";
import { useUserStore } from "@/store/modules/user.js";
@@ -142,6 +145,7 @@ function getPropSubStyle(
background: rgb(0 0 0 / 20%);
color: var(--tgc-white-1);
font-size: 12px;
row-gap: 5px;
text-shadow: 0 0 5px rgb(0 0 0 / 50%);
}
@@ -185,8 +189,11 @@ function getPropSubStyle(
.tua-dcr-icon {
position: relative;
display: flex;
width: 36px;
height: 36px;
align-items: center;
justify-content: center;
img {
width: 100%;
@@ -235,6 +242,7 @@ function getPropSubStyle(
width: 100%;
align-items: center;
justify-content: space-between;
border-bottom: 1px solid rgb(255 255 255 / 20%);
column-gap: 5px;
font-family: var(--font-title);
@@ -242,6 +250,7 @@ function getPropSubStyle(
display: flex;
align-items: center;
justify-content: center;
column-gap: 5px;
}
img {
@@ -269,6 +278,10 @@ function getPropSubStyle(
width: 14px;
height: 14px;
}
.icon {
opacity: 0.4;
}
}
.tua-prop-time {

View File

@@ -6,7 +6,7 @@
:title="skill.name"
class="tua-dct-item"
>
<img :src="skill.icon" alt="talent" class="tua-dct-icon" />
<TMiImg :ori="true" :src="skill.icon" alt="talent" class="tua-dct-icon" />
<div v-if="!skill.is_unlock" class="tua-dct-lock">
<v-icon size="10px" color="var(--tgc-od-white)">mdi-lock</v-icon>
</div>
@@ -17,6 +17,8 @@
</div>
</template>
<script lang="ts" setup>
import TMiImg from "@comp/app/t-mi-img.vue";
type TuaDcTalentsProps = { modelValue: Array<TGApp.Game.Avatar.Skill> };
const props = defineProps<TuaDcTalentsProps>();

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