Compare commits

...

208 Commits

Author SHA1 Message Date
BTMuli
80e27a20a7 🚀 v0.3.6 2023-11-28 15:19:05 +08:00
BTMuli
26d1883d98 添加 isLogin,用以判断是否登录 2023-11-28 15:15:36 +08:00
BTMuli
91fd375263 保留旧版,默认新版,旧版增加左右切换
close #20
2023-11-25 20:40:59 +08:00
BTMuli
3b0ed774df 🐛 修复分享图生成错误 2023-11-25 20:27:30 +08:00
BTMuli
5f3f6640a4 💩 分享图生成错误 2023-11-25 17:15:52 +08:00
BTMuli
b1424fb582 🌱 完成左下命座显示 2023-11-25 16:34:12 +08:00
BTMuli
f64b48c356 🌱 完成右侧天赋展示 4/5
#20
2023-11-25 16:21:34 +08:00
BTMuli
90242829a9 🌱 角色详情页面初步改造 3/5
#20
2023-11-25 00:05:45 +08:00
BTMuli
d6ae3765b6 💄 微调样式 2023-11-21 16:23:14 +08:00
BTMuli
92a1775d19 🐛 修复顶部名片渲染错误 2023-11-21 15:49:00 +08:00
BTMuli
fd3822fe70 ♻️ 列表渲染改成原生,降低性能消耗 2023-11-21 15:23:51 +08:00
BTMuli
618d3a1632 🚸 应用加载时关闭隐藏的子窗口 2023-11-21 13:53:18 +08:00
BTMuli
1c3b73bde9 ️ 优化 hint 2023-11-18 22:13:02 +08:00
BTMuli
c541d67abc 隐藏已完成成就
close #19
2023-11-18 22:03:36 +08:00
BTMuli
1d3b1ae78e 完成单个成就状态修改
close #60
2023-11-18 21:51:04 +08:00
BTMuli
312436b4e2 💄 增加 icon 清晰度 2023-11-18 21:01:47 +08:00
BTMuli
d98663dccb 🐛 完善数据库检测机制
fix #62
2023-11-18 20:46:14 +08:00
BTMuli
653073e684 🚨 修复 qodana 报错 2023-11-18 12:29:35 +08:00
BTMuli
1d09f4817b ️ 调整部分 ui 2023-11-18 00:28:12 +08:00
BTMuli
6ca5de28ac 🌱 初步迁移非重复数据库操作 #60 2023-11-17 23:47:31 +08:00
BTMuli
ff649c2426 🐛 初始化数据库仅保留手动方式 #62 2023-11-17 23:24:17 +08:00
BTMuli
1c5bebf75e ️ 输入框自动聚焦,回车自动确认 2023-11-17 23:22:32 +08:00
BTMuli
b60718aa62 ️ 优化 share 样式 2023-11-17 22:26:29 +08:00
BTMuli
c018638e4a 💄 微调样式 2023-11-17 21:55:31 +08:00
BTMuli
1906e911c7 💄 调整 finish icon color 2023-11-17 21:12:53 +08:00
BTMuli
fbb66b3964 🎨 内存不是问题x 2023-11-17 18:05:29 +08:00
BTMuli
561f34cf8b ⬆️ 更新依赖 2023-11-17 13:26:59 +08:00
BTMuli
5241c08c33 👽️ 大别野也有咨讯区了 2023-11-17 00:19:17 +08:00
BTMuli
630a64323d 🐛 我真傻,真的 2023-11-16 23:58:17 +08:00
BTMuli
54d2e27054 🐛 修复一些 bug
* 米社子窗口关闭后无法再次创建
* 保存图片回调默认路径错误
* 调整 closePage 逻辑
2023-11-16 22:38:42 +08:00
BTMuli
1c69cf07a5 ⬆️ 米社 salt 更新,2.59.1 → 2.63.1 2023-11-16 22:01:10 +08:00
BTMuli
715c206945 素材日历添加“留影叙佳期”入口
close #61
2023-11-16 22:00:35 +08:00
BTMuli
219286f6a1 🎨 完善 fp 更新逻辑 2023-11-16 20:59:32 +08:00
BTMuli
fc3d417961 🎨 微调逻辑 2023-11-16 14:44:23 +08:00
BTMuli
712a09131e 实装 getDeviceFp
close #58
2023-11-16 14:23:30 +08:00
BTMuli
13e9440c6f ✏️ 完善类型 #51 2023-11-16 00:21:36 +08:00
BTMuli
bed0e528b0 ✏️ extends BaseWithData #51 2023-11-16 00:16:04 +08:00
BTMuli
43c282efd2 📝 更新 UIGF 文档 2023-11-15 21:02:13 +08:00
BTMuli
b74a3b0bbf 🎨 格式化 2023-11-15 14:04:13 +08:00
BTMuli
7c0239391e 🔧 调整部分配置 2023-11-15 14:00:52 +08:00
BTMuli
b1060f76c5 ⬆️ 更新依赖,开写 v0.3.6 2023-11-15 14:00:08 +08:00
BTMuli
66137cf5b6 🚀 v0.3.5 2023-11-11 12:15:18 +08:00
BTMuli
69668f5ada ✏️ 类型修正 2023-11-10 22:06:26 +08:00
BTMuli
a5cff46efb ⬆️ 更新 UIGF v2.3 → v2.4
close #59
2023-11-10 20:53:18 +08:00
BTMuli
8037b635ba 👽️ 适应 endpoint 变更 2023-11-10 00:46:42 +08:00
BTMuli
7a060a71f0 💄 适应游戏样式变更 ui 2023-11-09 17:21:48 +08:00
BTMuli
d583247630 🍱 添加遗漏 contentId
close #57
2023-11-09 00:40:10 +08:00
BTMuli
c1a7e8448a 🐛 更新数据库时更新 buildTime 2023-11-06 23:02:10 +08:00
BTMuli
349bab1173 ✏️ itemType 收束 2023-11-06 22:49:42 +08:00
BTMuli
db36d18df2 💄 修复 hint 被 overlay 遮挡的问题 2023-11-06 22:41:03 +08:00
BTMuli
f619ccc64b 🍱 更新部分 4.2 资源文件 #57 2023-11-06 22:30:50 +08:00
BTMuli
8ae1d578ff 🐛 修复图片保存失败(真)
fix #56
2023-11-03 00:13:16 +08:00
BTMuli
e6eaa2e293 💄 调整基准背景色 2023-11-02 23:39:55 +08:00
BTMuli
a9912cf42b 🌱 准备 beta 版本的角色详情页面 2023-11-02 17:32:13 +08:00
BTMuli
4aa9319799 ⚰️ 删除无用代码 2023-11-02 17:14:16 +08:00
BTMuli
38ef0bfcca 🚚 稍微调整了下位置 2023-11-02 17:10:34 +08:00
BTMuli
c55f5b9ab8 👷 更新 template 2023-11-02 11:53:12 +08:00
BTMuli
468db1171e 🌱 share 延时 3s 自动退出 2023-11-02 11:47:31 +08:00
BTMuli
a474b96280 🎨 调整 hideSideBar 逻辑 2023-11-02 11:26:29 +08:00
BTMuli
149c7b3f27 增加工具箱入口,隐藏生日入口 2023-11-02 11:21:45 +08:00
BTMuli
abca5bd2f9 🐛 修复图片保存失败
fix #56
2023-11-02 11:07:44 +08:00
BTMuli
89d3a172b8 ⬆️ 更新依赖,开写 0.3.5 2023-11-02 10:16:26 +08:00
BTMuli
f318fca8ad 🚀 v0.3.4 版本发布 2023-10-28 19:29:50 +08:00
BTMuli
0b041a3f01 添加酒馆、留影叙佳期入口 2023-10-28 18:58:52 +08:00
BTMuli
2a8b97abe9 🎨 调整解析顺序 2023-10-28 14:35:59 +08:00
BTMuli
b390c7851e 🐛 处理不存在结构化数据 2023-10-28 14:31:20 +08:00
BTMuli
ecb0f1a793 ♻️ 重构结构化帖子类型 #51 2023-10-28 14:21:40 +08:00
BTMuli
b65afba30b 高级语法(bushi) 2023-10-27 19:12:27 +08:00
BTMuli
d4295c7dc9 🎨 避免关闭卡顿 2023-10-27 18:06:01 +08:00
BTMuli
e1e4f805ea ✏️ fix typo 2023-10-27 13:07:03 +08:00
BTMuli
b6624e8e3b 💄 样式微改 2023-10-27 13:01:17 +08:00
BTMuli
79fd18ea3b 💄 头部添加更多信息 2023-10-27 12:37:33 +08:00
BTMuli
3db8008f3a 💄 给 v-select 添加 theme
*找子元素实在找不到 T_T
2023-10-27 11:06:36 +08:00
BTMuli
8ce6c547bd 🎨 简化代码,统一返回数组 2023-10-27 10:48:25 +08:00
BTMuli
dc9bfe793a 🎨 参数调整,修复分享图模糊问题 2023-10-26 23:37:33 +08:00
BTMuli
9e4270603f 增加读取缓存目录大小
close #55
2023-10-26 22:59:31 +08:00
BTMuli
56c6c4f70f 🌱 尝试缓存清除 #55 2023-10-26 21:55:00 +08:00
BTMuli
803705218b 💚 完善模板 2023-10-26 19:32:01 +08:00
BTMuli
8ab7735cad 🎨 fmt 2023-10-26 19:07:40 +08:00
BTMuli
9a221f9b64 ✏️ 完善基本 Response 类型 #51 2023-10-26 18:59:29 +08:00
BTMuli
1d408b5d24 🐛 Tauri.Genshin → TeyvatGuide 2023-10-26 18:08:49 +08:00
BTMuli
7349f120e4 🎨 完善 dialog.open/save 配置 2023-10-26 17:56:58 +08:00
BTMuli
598a18557f 🔇 清除部分无用 todo 2023-10-26 17:49:19 +08:00
BTMuli
69ac285ee2 设置默认导出文件名称 2023-10-26 17:47:38 +08:00
BTMuli
93800a15ce ♻️ 返回完整数据 2023-10-26 17:34:41 +08:00
BTMuli
a9f92a6042 ✏️ 修正类型 2023-10-26 17:22:24 +08:00
BTMuli
2b9db5b5e0 🏷️ 完善 getActionTicket 类型 2023-10-26 17:01:04 +08:00
BTMuli
6dd98fbe98 👷 增加 profile.release 配置
https://course.rs/cargo/reference/profiles.html
2023-10-26 14:08:15 +08:00
BTMuli
ac9851aab0 ♻️ 将更新检测上移到 app 层 #45 2023-10-26 13:55:29 +08:00
BTMuli
824297142a 🔇 删除部分 todo 2023-10-26 13:47:04 +08:00
BTMuli
043fda9e33 💬 增加显示信息 2023-10-26 13:45:53 +08:00
BTMuli
90872a4917 ✏️ 完善类型 2023-10-26 13:31:26 +08:00
BTMuli
f84c4b93ea ✏️ 修正 Input Res 2023-10-26 13:23:54 +08:00
BTMuli
84b98e4ade 🔥 移除 geetest 相关代码 2023-10-26 13:19:24 +08:00
BTMuli
5992567d55 🎨 删除完整性检测,隐藏数据库重置 2023-10-26 13:17:58 +08:00
BTMuli
9be40181a7 🎨 v-if → v-show 2023-10-26 13:12:33 +08:00
BTMuli
0e864fc04f 💄 outer-text 居中 2023-10-26 12:57:38 +08:00
BTMuli
0d4fdecd5d 🎨 增加排序,精简代码 2023-10-26 12:56:59 +08:00
BTMuli
24aa355f3b 🐛 更改登录按钮显示条件 2023-10-25 21:00:18 +08:00
BTMuli
d422e308fb 🎨 添加粗略登录校验 2023-10-25 20:51:35 +08:00
BTMuli
f54ab8daa4 实装 JSBridge
fix #47
2023-10-25 20:49:14 +08:00
BTMuli
d0c0f40638 🐛 修复含视频分享图生成异常
fix #54
2023-10-25 18:37:57 +08:00
BTMuli
73bf525d42 🚨 修复 qodana 报错 2023-10-25 18:32:33 +08:00
BTMuli
efa2156fb2 支持 Mac 平台 2023-10-25 18:31:49 +08:00
BTMuli
1dad91dd95 🔧 继续修改 ci 2023-10-25 18:00:26 +08:00
BTMuli
3c4e30d0f5 🔧 解决 pnpm 未安装问题 2023-10-25 17:56:19 +08:00
BTMuli
5411ae013f 🔧 添加跨平台编译命令 2023-10-25 17:45:05 +08:00
目棃
20113cf81f Merge pull request #53 from AuroraZiling/master
feat: add cross-platform support for Mac
2023-10-25 17:23:56 +08:00
BTMuli
4fc77b60ad 🔧 完善 ISSUE_TEMPLATE 2023-10-25 15:49:41 +08:00
AuroraZiling
1bcc5a625b feat: add cross-platform support for Mac 2023-10-25 13:12:59 +08:00
BTMuli
6792c0ac0a 🎨 完善页面处理 2023-10-25 00:30:22 +08:00
BTMuli
16999f2e58 成功完成签到&战绩页面渲染 2023-10-24 21:51:37 +08:00
BTMuli
1914261e80 ♻️ 重构窗口创建逻辑,采用 rust invoke
https://github.com/tauri-apps/tauri/issues/5380
2023-10-24 14:24:52 +08:00
BTMuli
35dc972841 🌱 完善 jsBridge 2023-10-24 00:38:41 +08:00
BTMuli
7ef89c33f1 🔧 无边框 2023-10-23 22:47:48 +08:00
BTMuli
77d3ecbeca 🌱 完善窗口处理,代码格式化 2023-10-23 19:38:03 +08:00
BTMuli
d18e463f7b 🔧 完善 lint-staged script 2023-10-20 14:08:11 +08:00
BTMuli
9b57909dc4 🔧 添加 rust 格式化配置 2023-10-20 14:01:22 +08:00
BTMuli
c12461ad43 🌱 更新依赖,开写 v0.3.4 2023-10-19 21:43:34 +08:00
BTMuli
287af4031d 🔧 修改默认端口为 4000 2023-10-19 19:34:03 +08:00
BTMuli
8a3358a355 🔧 增加限制 2023-10-19 19:29:53 +08:00
BTMuli
f2db40e3c4 💚 重新激活 Qodana
https://github.com/JetBrains/Qodana/discussions/201
2023-10-19 17:49:06 +08:00
BTMuli
2ef2513fcf 📝 采用 sm.ms 图床 2023-10-19 17:38:17 +08:00
BTMuli
6e18331f2f 📝 修复 icon 链接错误 2023-10-19 17:33:49 +08:00
BTMuli
cf801314a1 🚀 v0.3.3 版本更新 2023-10-19 13:51:22 +08:00
BTMuli
06f4d26184 💄 角色详情深色模式样式完善 2023-10-19 13:38:17 +08:00
BTMuli
518af605de 💄 隐藏滚动条 2023-10-19 13:19:13 +08:00
目棃
1aa3ba3792 Merge pull request #52 from BTMuli/dependabot/cargo/src-tauri/rustix-0.37.25
Bump rustix from 0.37.23 to 0.37.25 in /src-tauri
2023-10-19 11:41:27 +08:00
dependabot[bot]
fc0b165c8f Bump rustix from 0.37.23 to 0.37.25 in /src-tauri
Bumps [rustix](https://github.com/bytecodealliance/rustix) from 0.37.23 to 0.37.25.
- [Release notes](https://github.com/bytecodealliance/rustix/releases)
- [Commits](https://github.com/bytecodealliance/rustix/compare/v0.37.23...v0.37.25)

---
updated-dependencies:
- dependency-name: rustix
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-18 18:43:44 +00:00
BTMuli
efec9f7917 🐛 修复 release 路径错误问题 2023-10-18 23:49:33 +08:00
BTMuli
c168a07640 🎨 调整分享图生成参数 2023-10-18 22:55:40 +08:00
BTMuli
839891448c 💄 完善视频截图处理 #44 2023-10-18 22:38:35 +08:00
BTMuli
cc475a5c50 🐛 修复遮挡问题 2023-10-18 21:58:44 +08:00
BTMuli
98189974b8 🔧 不设置 csp 2023-10-18 21:43:30 +08:00
BTMuli
f6e531909b ⬆️ 更新 pnpm 8.9.0 → 8.9.2 2023-10-18 21:06:07 +08:00
BTMuli
f7238186f4 🍱 添加米游社视频播放 svg 2023-10-18 20:59:58 +08:00
BTMuli
b71e21a131 🔧 修复样式错误 2023-10-18 19:35:07 +08:00
BTMuli
5c54fc255a 🔧 更新默认 release body 2023-10-18 19:20:40 +08:00
BTMuli
49b4ad53cd 🔧 开启 sourceMap 2023-10-18 19:19:50 +08:00
BTMuli
32a40b3cad 💄 修复左侧样式错误 2023-10-18 18:01:57 +08:00
BTMuli
b7a555c8b8 ✏️ 修复 eslint 报错 2023-10-18 17:48:16 +08:00
BTMuli
6ab1f5c842 🚨 解决 eslint 部分报错 2023-10-18 17:21:09 +08:00
BTMuli
98c3e1a468 🗃️ 完善数据库更新语句 2023-10-18 17:20:26 +08:00
BTMuli
90775f925b 🍱 更新部分缺漏数据 2023-10-18 16:51:11 +08:00
BTMuli
751372a4ba 🌱 完善客户端,获取 postMessage 2023-10-18 13:21:56 +08:00
BTMuli
4ab679dcea 🌱 初步构建米游社客户端子窗口 2023-10-18 01:24:19 +08:00
BTMuli
c41b89acf7 🎨 依赖结构调整 2023-10-17 22:57:41 +08:00
BTMuli
2f34b1be45 ♻️ 动态路由,提高加载速度 2023-10-17 20:32:13 +08:00
BTMuli
a7ad394c4f 🎨 提高主观启动速度(bushi) 2023-10-17 19:47:06 +08:00
BTMuli
a8bf9e3c3b 💄 当即将切换卡池时提供 switch btn 2023-10-17 18:05:10 +08:00
BTMuli
6af3461168 📝 修复仓库概况数据缺失问题 2023-10-17 13:39:39 +08:00
BTMuli
63f72a992c 💄 卡片样式调整 2023-10-17 13:33:02 +08:00
BTMuli
f7b60fb218 💄 snackbar 样式调整 2023-10-17 00:43:23 +08:00
BTMuli
7b76432b1e ♻️ 修改更新弹窗机制 #45 2023-10-15 17:34:18 +08:00
BTMuli
068f8c7647 🎨 nextTick 部分应用 2023-10-15 17:32:49 +08:00
BTMuli
c27d3af5db 💄 稍微调整了一下样式 2023-10-15 17:04:44 +08:00
BTMuli
3691397cec ♻️ 公告页样式重构 2023-10-15 16:57:48 +08:00
BTMuli
cbce3eda60 🎨 暂时就简化到这里吧 2023-10-14 15:01:45 +08:00
BTMuli
54a0cfd03f ♻️ 咨讯页路由路径变更 2023-10-14 14:52:07 +08:00
BTMuli
214991fdd9 ♻️ 二次简化 2023-10-14 14:46:58 +08:00
BTMuli
58e3c0e1a6 ♻️ 简化代码 2023-10-14 14:34:32 +08:00
BTMuli
358255d50a 💄 修复 text 过长时溢出容器问题 2023-10-14 01:33:41 +08:00
BTMuli
d2fa3529f8 🐛 definedProps无法引入外部Typescript类型定义 2023-10-13 23:19:05 +08:00
BTMuli
7dedcc4ea9 ✏️ 完善其余函数式组件类型 2023-10-13 23:09:08 +08:00
BTMuli
980b420eb1 ✏️ 完善 showConfirm 类型 2023-10-13 23:07:59 +08:00
BTMuli
c8c157852f 💚 调整运行条件 2023-10-13 14:02:08 +08:00
BTMuli
48a84918b6 💚 Qodana 暂时弃用
https://youtrack.jetbrains.com/issue/QD-7388/Maximum-number-of-licenses-is-exceeded-for-this-product
2023-10-13 13:15:51 +08:00
BTMuli
f6bea9b2e7 🐛 解决最小化无法唤起问题 2023-10-12 00:15:12 +08:00
BTMuli
3b0bc4b1a5 完成大别野卡片样式渲染 2023-10-11 23:45:33 +08:00
BTMuli
b484e745e0 🌱 初步完成大别野卡片的解析、渲染 2023-10-11 20:37:19 +08:00
BTMuli
74320f0e9a 🎨 优化 init_app 逻辑 2023-10-11 20:34:30 +08:00
BTMuli
adc96b7649 💄 美化未知类型样式 2023-10-11 18:25:40 +08:00
BTMuli
e32988f663 🐛 解决 account 初始化问题 2023-10-11 00:04:33 +08:00
BTMuli
1cd1c1f035 🚨 修复 eslint 报错 2023-10-10 23:31:37 +08:00
BTMuli
b04f49ec46 🎨 代码格式化 2023-10-10 23:15:26 +08:00
BTMuli
5c2bb4e5af 🔧 优化 eslint 配置 2023-10-10 22:30:38 +08:00
BTMuli
9a25e387aa ♻️ 数据库连接一直保持开启 #46 2023-10-10 21:23:25 +08:00
BTMuli
fa22a45bb1 粗略处理视频截图 #44 2023-10-10 13:59:36 +08:00
BTMuli
146f3404f0 💄 颜色变更 2023-10-09 19:19:30 +08:00
BTMuli
93fe738c97 ✏️ 完善极验类型 2023-10-09 19:07:40 +08:00
BTMuli
5272108e82 🎨 完善请求 2023-10-09 18:50:40 +08:00
BTMuli
faa1832c1e ♻️ 启动后只执行一次 2023-10-09 17:52:28 +08:00
BTMuli
5712d4b7fc ✏️ 修复 GCG 类型错误 2023-10-09 01:14:14 +08:00
BTMuli
1ad3506f66 ✏️ 修复 import.meta.env 引用 2023-10-09 01:05:52 +08:00
BTMuli
7841ea4a79 🎨 完善 ck,briefInfo 获取 2023-10-09 00:18:50 +08:00
BTMuli
66ecd9a91e ♻️ 通过 import.meta 获取当前环境 2023-10-08 23:52:37 +08:00
BTMuli
6e79c0a7e0 🎨 调整 deep link 响应方式 2023-10-08 23:42:30 +08:00
BTMuli
96ab38b932 💄 添加 finish icon 2023-10-08 23:38:37 +08:00
BTMuli
788560f536 🎨 添加成就系列信息 #19 2023-10-08 23:29:05 +08:00
BTMuli
fdfcc70bcb 🐛 修复部分成就版本错误
https://github.com/DGP-Studio/Snap.Hutao/issues/996
2023-10-08 23:22:45 +08:00
目棃
cbb2ddd8a2 Merge pull request #49 from BTMuli/dependabot/npm_and_yarn/postcss-8.4.31
Bump postcss from 8.4.29 to 8.4.31
2023-10-06 14:14:16 +08:00
dependabot[bot]
8aeaf30a89 Bump postcss from 8.4.29 to 8.4.31
Bumps [postcss](https://github.com/postcss/postcss) from 8.4.29 to 8.4.31.
- [Release notes](https://github.com/postcss/postcss/releases)
- [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/postcss/postcss/compare/8.4.29...8.4.31)

---
updated-dependencies:
- dependency-name: postcss
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-05 20:11:00 +00:00
BTMuli
fe1f16584e ♻️ 完善报错,默认刷新两期 #48 2023-10-05 20:23:04 +08:00
BTMuli
e974f30647 ♻️ 更新 salt 2023-10-05 15:43:32 +08:00
BTMuli
ce2ff5b6f5 📝 添加 JB OSS 说明 2023-10-04 21:24:18 +08:00
BTMuli
491cdf9af6 🚨 修复 Qodana 报错 2023-10-02 23:02:14 +08:00
BTMuli
49c716f009 🚧 继续调试验证 2023-10-02 22:56:12 +08:00
BTMuli
b3bbd4bed7 🔧 Qodana 又能用了 2023-10-02 22:22:37 +08:00
BTMuli
bbc2a3f845 🐛 修复控制台报错 2023-09-30 21:15:37 +08:00
BTMuli
3cd2586ed4 🎨 优化方法 2023-09-30 21:06:11 +08:00
BTMuli
dd940d5a2b ♻️ geetest 组件函数化调用 2023-09-30 19:08:21 +08:00
BTMuli
b57d34419f 🐛 修复依赖 2023-09-30 16:19:15 +08:00
BTMuli
bd67ee7a25 🌱 完成相关请求,待测试 #47 2023-09-30 16:13:05 +08:00
BTMuli
137180028e 🎨 更改引入方式 2023-09-30 13:38:38 +08:00
BTMuli
a513b38f14 🌱 初步引入 gt.js 2023-09-28 22:42:06 +08:00
BTMuli
2cbac71b36 🍱 替换默认头像 2023-09-28 21:44:16 +08:00
BTMuli
e95cf683aa ️ 优化错误处理 2023-09-28 19:08:46 +08:00
BTMuli
1fa97d0a6c 🌱 更新依赖,开写 v0.3.3 2023-09-28 19:01:43 +08:00
237 changed files with 8482 additions and 5244 deletions

View File

@@ -2,4 +2,9 @@
dist
src-tauri/target
# Submodules
TGAssistant
TGAssistant
# Package files
pnpm-lock.yaml
# lint files
!.prettierrc.yml
!.stylelintrc.yml

View File

@@ -3,37 +3,130 @@ env:
browser: true
es2021: true
extends:
- plugin:vue/vue3-recommended
- standard-with-typescript
- plugin:vue/vue3-essential
- prettier
- plugin:prettier/recommended
parser: vue-eslint-parser
- plugin:yml/standard
- plugin:jsonc/recommended-with-json
parserOptions:
extraFileExtensions:
- .vue
parser: "@typescript-eslint/parser"
project:
- tsconfig.json
- tsconfig.node.json
ecmaVersion: latest
sourceType: module
tsconfigRootDir: .
project: ./tsconfig.json
globals:
TGApp: readonly
plugins:
- vue
- prettier
window: readonly
rules:
# normal
array-callback-return: warn
# TypeScript
"@typescript-eslint/strict-boolean-expressions": off
"@typescript-eslint/consistent-type-assertions":
- warn
- assertionStyle: angle-bracket # 使用尖括号
"@typescript-eslint/naming-convention":
- warn
- selector: variableLike
format: ["camelCase", "UPPER_CASE", "PascalCase", "snake_case"]
leadingUnderscore: "allow"
trailingUnderscore: "allow"
"@typescript-eslint/no-non-null-assertion": warn
# Vue
"vue/multi-word-component-names": off
vue/multi-word-component-names: off
vue/valid-template-root: off
overrides:
- files: ["*.ts"]
extends: standard-with-typescript
# 将此处规则 copy 到下面的 .vue 文件的 rules 中
rules: &typescript-rules
import/order:
- error
- groups:
- builtin
- external
- [internal, parent, sibling, index]
- unknown
newlines-between: always
alphabetize:
order: asc
caseInsensitive: true
"@typescript-eslint/indent": off
"@typescript-eslint/quotes":
- error
- double
"@typescript-eslint/semi":
- error
- always
"@typescript-eslint/comma-dangle":
- error
- always-multiline
"@typescript-eslint/space-before-function-paren":
- error
- anonymous: always
named: never
asyncArrow: always
"@typescript-eslint/member-delimiter-style":
- error
- multiline:
delimiter: semi
requireLast: true
"@typescript-eslint/no-import-type-side-effects": error
"@typescript-eslint/strict-boolean-expressions": off
"@typescript-eslint/consistent-type-assertions":
- warn
- assertionStyle: angle-bracket # 使用尖括号
"@typescript-eslint/naming-convention":
- warn
- selector: variableLike
format: [camelCase, UPPER_CASE, PascalCase, snake_case]
leadingUnderscore: allow
trailingUnderscore: allow
"@typescript-eslint/no-non-null-assertion": warn
"@typescript-eslint/no-misused-promises": off
- files: ["*.vue"]
parser: vue-eslint-parser
parserOptions:
parser: "@typescript-eslint/parser"
extraFileExtensions: [.vue]
rules:
<<: *typescript-rules
- files: [package.json, tsconfig.json]
parser: jsonc-eslint-parser
rules:
jsonc/sort-array-values:
- error
- pathPattern: .*
order:
type: asc
jsonc/sort-keys:
- error
- pathPattern: ^$
order:
- name
- version
- description
- private
- packageManager
- scripts
- lint-staged
- keywords
- author
- license
- repository
- homepage
- bugs
- dependencies
- devDependencies
- files: ["*.yaml", "*.yml"]
parser: yaml-eslint-parser
parserOptions:
defaultYAMLVersion: "1.2"
rules:
yml/indent:
- error
- 2
yml/no-multiple-empty-lines: error
yml/key-spacing: error
yml/quotes:
- error
- prefer: double
avoidEscape: true
yml/sort-keys:
- error
- pathPattern: ^$
order:
- root
- env
- extends
- parserOptions
- globals
- rules
- overrides
- pathPattern: ^rules$
order:
type: asc

View File

@@ -4,9 +4,6 @@ title: "[Bug] "
labels:
- BUG
- Question
- 待处理
assignees:
- BTMuli
body:
- type: markdown
attributes:
@@ -17,24 +14,13 @@ body:
- 复现流程
- type: checkboxes
attributes:
label: Issue重复性检查
description: 我确认没有查找过相关的 Issue
label: Issue Check
options:
- label: 我确认没有查找过相关的 Issue
- label: 我确认查找过相关的 Issue
required: false
- type: checkboxes
attributes:
label: 问题必要性检查
description: 我确认这个问题是无用且不必要的
options:
- label: 我确认这个问题是无用且不必要的
- label: 我确认这个问题是影响使用的
required: false
- type: checkboxes
attributes:
label: 提问的艺术
description: 我没有阅读过[提问的艺术](https://github.com/betaseeker/How-To-Ask-Questions)
options:
- label: 我没有阅读过[提问的艺术](https://github.com/betaseeker/How-To-Ask-Questions)
- label: 我阅读过[提问的艺术](https://github.com/betaseeker/How-To-Ask-Questions)
required: false
- type: textarea
id: description
@@ -49,7 +35,7 @@ body:
attributes:
label: 当前使用版本
description: 请填写当前使用版本
placeholder: Alpha v0.2.0
placeholder: Beta v0.3.4
validations:
required: true
- type: textarea
@@ -67,4 +53,4 @@ body:
description: 请填写其他信息
placeholder: 请填写其他信息
validations:
required: true
required: false

View File

@@ -3,9 +3,6 @@ description: 提交新功能请求
title: "[Feat] "
labels:
- 新特性
- 待处理
assignees:
- BTMuli
body:
- type: markdown
attributes:
@@ -16,24 +13,13 @@ body:
- 当前使用版本
- type: checkboxes
attributes:
label: Issue重复性检查
description: 我确认没有查找过相关的 Issue
label: Issue Check
options:
- label: 我确认没有查找过相关的 Issue
- label: 我确认查找过相关的 Issue
required: false
- type: checkboxes
attributes:
label: 功能必要性检查
description: 我确认这个功能是无用且不必要的
options:
- label: 我确认这个功能是无用且不必要的
- label: 我确认这个问题是影响使用的
required: false
- type: checkboxes
attributes:
label: 提问的艺术
description: 我没有阅读过[提问的艺术](https://github.com/betaseeker/How-To-Ask-Questions)
options:
- label: 我没有阅读过[提问的艺术](https://github.com/betaseeker/How-To-Ask-Questions)
- label: 我阅读过[提问的艺术](https://github.com/betaseeker/How-To-Ask-Questions)
required: false
- type: textarea
id: description
@@ -48,7 +34,7 @@ body:
attributes:
label: 当前使用版本
description: 请填写当前使用版本
placeholder: Alpha v0.2.0
placeholder: Beta v0.3.4
validations:
required: true
- type: textarea

View File

@@ -2,10 +2,7 @@ name: 开发目标(开发人员)
description: 用于开发人员 Issue 的模板
title: "[TODO] "
labels:
- 计划中
- 新特性
assignees:
- BTMuli
body:
- type: markdown
attributes:
@@ -28,7 +25,7 @@ body:
attributes:
label: 预期版本
description: 请填写预期版本
placeholder: Alpha v0.2.0
placeholder: Beta v0.3.x
validations:
required: true
- type: input
@@ -36,7 +33,7 @@ body:
attributes:
label: 当前提交
description: 请填写当前提交
placeholder: 0.2.0
placeholder: Beta v0.3.4
validations:
required: true
- type: textarea

View File

@@ -11,11 +11,20 @@ jobs:
strategy:
fail-fast: false
matrix:
platform: [windows-latest]
platform: [windows-latest, macos-latest]
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v3
- name: Checkout
uses: actions/checkout@v3
- name: Rust setup
uses: dtolnay/rust-toolchain@stable
- name: Rust cache
uses: swatinem/rust-cache@v2
with:
workspaces: "./src-tauri -> target"
- name: setup node
uses: actions/setup-node@v3
with:
@@ -23,23 +32,12 @@ jobs:
- name: setup pnpm
uses: pnpm/action-setup@v2
with:
version: 8.6.7
- name: Install Rust stable
uses: dtolnay/rust-toolchain@stable
- name: Restore Cargo dependencies
uses: actions/cache@v3
with:
path: |
~/src-tauri/.cargo/bin/
~/src-tauri/.cargo/registry/index/
~/src-tauri/.cargo/registry/cache/
~/src-tauri/.cargo/git/db/
~/src-tauri/target/
key: ${{ runner.os }}-cargo-${{ hashFiles('src-tauri/Cargo.lock') }}
restore-keys: ${{ runner.os }}-cargo-
version: 8.10.5
- name: Install frontend dependencies
run: pnpm install --frozen-lockfile
- uses: tauri-apps/tauri-action@dev
run: pnpm install
- name: Build app
uses: tauri-apps/tauri-action@dev
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
@@ -47,6 +45,11 @@ jobs:
with:
tagName: v__VERSION__ # the action automatically replaces \_\_VERSION\_\_ with the app version
releaseName: v__VERSION__-beta
releaseBody: https://github.com/BTMuli/Tauri.Genshin/releases/tag/v__VERSION__
releaseBody: |
> Windows 平台用户建议通过微软应用商店下载MacOS 平台仅在此发布Linux 平台暂不支持。
<a href="https://apps.microsoft.com/store/detail/9NLBNNNBNSJN?launch=true&cid=BTMuli&mode=mini">
<img src="https://get.microsoft.com/images/zh-cn%20dark.svg" alt="download"/>
</a>
releaseDraft: true
prerelease: false

View File

@@ -1,7 +1,10 @@
name: Qodana
on:
workflow_dispatch:
pull_request:
push:
branches:
- master
jobs:
qodana:
runs-on: ubuntu-latest

View File

@@ -5,4 +5,4 @@ useTabs: false
tabWidth: 2
bracketSpacing: true
endOfLine: auto
trailingComma: "all"
trailingComma: all

View File

@@ -8,4 +8,4 @@ plugins:
- stylelint-prettier
- stylelint-order
rules:
"prettier/prettier": true
prettier/prettier: true

View File

@@ -2,12 +2,129 @@
Author: 目棃
Description: CHANGELOG
Date: 2023-09-08
Update: 2023-09-27
Update: 2023-11-28
---
> 本文档 [`Frontmatter`](https://github.com/BTMuli/MuCli#Frontmatter) 由 [MuCli](https://github.com/BTMuli/Mucli) 自动生成于 `2023-09-08 09:45:17 `
>
> 更新于 `2023-09-27 08:41:43`
> 更新于 `2023-11-28 15:18:27`
## [0.3.6](https://github.com/BTMuli/TeyvatGuide/releases/v0.3.6) (2023-11-25)
### Feat
- 应用:实装 `device_fp`,有效降低 `1034` 错误 [`#58`](https://github.com/BTMuli/TeyvatGuide/issues/58)
- 首页:今日素材组件添加留影叙佳期入口,角色生日时颜色变更 [`#61`](https://github.com/BTMuli/TeyvatGuide/issues/61)
- 组件:优化 showConfirm 组件 input 模式下的体验
- 成就:支持单个成就完成状态修改 [`#60`](https://github.com/BTMuli/TeyvatGuide/issues/60)
- 成就:支持隐藏已完成成就 [`#19`](https://github.com/BTMuli/TeyvatGuide/issues/19)
- 角色:角色详情页 UI 迭代,支持角色卡片分享 [`#20`](https://github.com/BTMuli/TeyvatGuide/issues/20)
### Fix
- JSBridge修复窗口关闭后无法再次创建的问题
- JSBridge修复保存图片默认路径错误
- JSBridge调整 closePage 逻辑
- 应用:在生成分享图时忽略某些元素
- 应用:完善数据库检测机制 [`#62`](https://github.com/BTMuli/TeyvatGuide/issues/62)
- JSBridge应用启动时关闭隐藏的子窗口
- 应用:完善登录态检测机制
### Change
- 应用:米游社 salt 版本更新到 2.63.1
- 咨讯:大别野版块不再忽略咨讯区
- 分享:提高生成分享图的清晰度
- 成就:调整完成 icon 的颜色
- 组件:增加素材日历组件 overlay 国家 icon 清晰度
- 成就:重构成就页面代码,优化性能
## [0.3.5](https://github.com/BTMuli/TeyvatGuide/releases/v0.3.5) (2023-11-11)
### Feat
- 资源:更新至 4.2 版本 [`#57`](https://github.com/BTMuli/TeyvatGuide/issues/57)
- 祈愿:支持 UIGF v2.4 [`#59`](https://github.com/BTMuli/TeyvatGuide/issues/59)
### Fix
- JSBridge修复图片保存失败 [`#56`](https://github.com/BTMuli/TeyvatGuide/issues/56)
- JSBridge: 调整 hideSideBar 逻辑 [`a474b962`](https://github.com/BTMuli/TeyvatGuide/commit/a474b962)
- 组件:修复 `snackbar` 组件被 `overlay` 遮挡问题 [`db36d18d`](https://github.com/BTMuli/TeyvatGuide/commit/db36d18d)
- 数据库:更新数据库时同时更新 `buildTime` [`c1a7e844`](https://github.com/BTMuli/TeyvatGuide/commit/c1a7e844)
### Change
- JSBridge留影叙佳期入口改为工具箱入口 [`149c7b3f`](https://github.com/BTMuli/TeyvatGuide/commit/149c7b3f)
- Post调整基准背景色 [`e6eaa2e2`](https://github.com/BTMuli/TeyvatGuide/commit/e6eaa2e2)
- 组件:适应游戏 UI 变更 `confirm` 组件样式 [`7a060a71`](https://github.com/BTMuli/TeyvatGuide/commit/7a060a71)
- API更新祈愿记录获取 `endpoint` [`8037b635`](https://github.com/BTMuli/TeyvatGuide/commit/8037b635)
## [0.3.4](https://github.com/BTMuli/TeyvatGuide/releases/v0.3.4) (2023-10-28)
### Feat
- 应用Awesome Tauri[`tauri-apps/awesome-tauri#226`](https://github.com/tauri-apps/awesome-tauri/pull/226)
- 应用:支持 MacOS 平台 [`#53`](https://github.com/BTMuli/TeyvatGuide/pull/53)
- 应用:实装米游社 JSBridge支持战绩、签到、酒馆、留影叙佳期等功能 [`#47`](https://github.com/BTMuli/TeyvatGuide/issues/47)
- 导出:设置默认导出文件名称
- 应用:支持缓存检测&清理 [`#55`](https://github.com/BTMuli/TeyvatGuide/issues/55)
- 帖子:展示更多相关信息 [`79fd18ea`](https://github.com/BTMuli/TeyvatGuide/commit/79fd18ea)
### Fix
- 应用:窗口创建逻辑重构 [`1914261e`](https://github.com/BTMuli/TeyvatGuide/commit/1914261e)
- 分享:修复含视频分享图生成异常 [`#54`](https://github.com/BTMuli/TeyvatGuide/issues/54)
- 应用:更新检测上移到应用初始化 [`#45`](https://github.com/BTMuli/TeyvatGuide/issues/45)
- 应用:将部分未更正的 `Tauri.Genshin` 改为 `Teyvat Guide`
- 应用:`v-select` 样式适应主题变更 [`3db8008f`](https://github.com/BTMuli/TeyvatGuide/commit/3db8008f)
- 应用:修复关闭卡顿 [`d4295c7d`](https://github.com/BTMuli/TeyvatGuide/commit/d4295c7d)
### Change
- 角色:对获取到的数据进行排序 [`0d4fdecd`](https://github.com/BTMuli/TeyvatGuide/commit/0d4fdecd)
- 组件Confirm 组件渲染调整 `v-if` -> `v-show` [`9be40181`](https://github.com/BTMuli/TeyvatGuide/commit/9be40181)
- 设置:删除数据库完整性检测,隐藏数据库重置 [`5992567d`](https://github.com/BTMuli/TeyvatGuide/commit/5992567d)
- 极验:移除极验验证相关代码 [`84b98e4a`](https://github.com/BTMuli/TeyvatGuide/commit/84b98e4a)
- 战绩:角色数据添加 `title` 属性,展示部分角色信息 [`043fda9e`](https://github.com/BTMuli/TeyvatGuide/commit/043fda9e)
- 重构:对基本 `Response` 类型进行重构 [`9a221f9b`](https://github.com/BTMuli/TeyvatGuide/commit/9a221f9b)
- 重构:对米游社帖子结构化类型进行重构 [`ecb0f1a7`](https://github.com/BTMuli/TeyvatGuide/commit/ecb0f1a7)
FullCommits: [`v0.3.3...v0.3.4`](https://BTMuli/TeyvatGuide/compare/v0.3.3...v0.3.4)
## [0.3.3](https://github.com/BTMuli/TeyvatGuide/releases/v0.3.3) (2023-10-19)
### Feat
- 应用:支持含视频帖子分享图生成 [`#44`](https://github.com/BTMuli/TeyvatGuide/issues/44)
- 帖子:新增对于大别野卡片 `VillaCard` 的解析渲染
- 应用:公告页样式美化
- 应用:采取动态路由,提高加载速度
- 角色:完善深色模式角色详情页样式
### Fix
- 深渊:默认刷新两期 [`#48`](https://github.com/BTMuli/TeyvatGuide/issues/48)
- 成就:修复部分成就版本错误 [`DGP-Studio/Snap.Hutao#996`](https://github.com/BTMuli/TeyvatGuide/commit/fdfcc70b)
- 应用:完善 DeepLink 处理
- 应用:完善 Cookie,BriefInfo 数据获取
- 应用:数据库链接保持开启,[`#46`](https://github.com/BTMuli/TeyvatGuide/issues/46)
- 组件:修复 `showConfirm` 文字过长时溢出容器问题 [`358255d5`](https://github.com/BTMuli/TeyvatGuide/commit/358255d5)
- 应用:更改弹窗弹出机制 [`#45`](https://github.com/BTMuli/TeyvatGuide/issues/45)
- 数据:补充 4.1 版本缺漏数据
### Change
- 图像:未登录时的默认头像变更 [`2cbac71b`](https://github.com/BTMuli/TeyvatGuide/commit/2cbac71b)
- 成就:添加 Finish Icon [`96ab38b9`](https://github.com/BTMuli/TeyvatGuide/commit/96ab38b9)
- 应用:浅色主题样式调整
- 帖子:未知结构化数据类型样式调整 [`adc96b76`](https://github.com/BTMuli/TeyvatGuide/commit/adc96b76)
- 应用:重构创建帖子子窗口代码
- 应用:咨讯页路由变更
- 组件:`showSnackbar` 样式调整
- 应用:重构咨讯页代码
FullCommits: [`v0.3.2...v0.3.3`](https://BTMuli/TeyvatGuide/compare/v0.3.2...v0.3.3)
## [0.3.2](https://github.com/BTMuli/TeyvatGuide/releases/v0.3.2) (2023-9-27)

View File

@@ -2,36 +2,26 @@
Author: 目棃
Description: 说明文档
Date: 2023-03-05
Update: 2023-09-27
Update: 2023-11-15
---
> 本文档 [`Frontmatter`](https://github.com/BTMuli/MuCli#Frontmatter) 由 [MuCli](https://github.com/BTMuli/Mucli) 自动生成于 `2023-03-05 14:41:55`
>
> 更新于 `2023-09-27 08:43:41`
> 更新于 `2023-11-15 21:01:51`
![](https://img.shields.io/github/last-commit/BTMuli/TeyvatGuide?style=for-the-badge) ![](https://img.shields.io/github/commits-since/BTMuli/TeyvatGuide/latest?include_prereleases&style=for-the-badge)
![](https://img.shields.io/badge/UIAF-v1.1-orange?style=for-the-badge) ![](https://img.shields.io/badge/UIGF-v2.3-red?style=for-the-badge) ![](https://img.shields.io/github/license/BTMuli/TeyvatGuide?style=for-the-badge)
![](https://img.shields.io/badge/UIAF-v1.1-orange?style=for-the-badge) ![](https://img.shields.io/badge/UIGF-v2.4-red?style=for-the-badge) ![](https://img.shields.io/github/license/BTMuli/TeyvatGuide?style=for-the-badge)
<div style="width: 100%; text-align: center; margin: 0 auto;">
<img alt="icon" src="https://s2.loli.net/2023/10/19/Y5DpBQRy3usLHEb.png" />
</div>
# Teyvat Guide
基于 Tauri 的原神助手应用
基于 Tauri 的原神工具应用,支持 Windows 和 MacOS 平台
A Genshin Impact assistant app based on Tauri.
## 声明 / Declaration
本项目仅供个人学习交流使用。请勿用于任何商业或违法违规用途。
本项目涉及到的隐私数据,如 Cookie、Token 等,仅用于获取相关数据,不会被上传至任何服务器。
深渊页面的上传功能,采用的是 [Hutao API](https://hut.ao/zh/development/platform.html) 提供的接口,仅上传如下数据:
- 用户的游戏 UID
- 用户的深境螺旋记录
- 用户的角色信息及其装备的武器和圣遗物信息
该功能为用户主动上传,不会在用户不知情的情况下上传数据。
Game Tool for Genshin Impact player, supports Windows and MacOS.
## 下载 / Download
@@ -41,18 +31,13 @@ A Genshin Impact assistant app based on Tauri.
<img src="https://get.microsoft.com/images/zh-cn%20dark.svg" alt="download"/>
</a>
> 不推荐通过 GitHub Release 下载,但仍保留了该功能。
> MacOS 用户可以通过 Github Release 下载
[![GitHub release (latest by date including pre-releases)](https://img.shields.io/github/v/release/BTMuli/TeyvatGuide?include_prereleases&style=for-the-badge)](https://github.com/BTMuli/TeyvatGuide/releases/latest)
## 技术栈 / Tech Stack
## 仓库概况 / Repo Stats
- [Tauri](https://github.com/tauri-apps/tauri)
- [Vue3](https://github.com/vuejs/core)
- [Vite](https://github.com/vitejs/vite)
- [TypeScript](https://github.com/microsoft/TypeScript)
- [Vuetify](https://github.com/vuetifyjs/vuetify)
- [Echarts](https://echarts.apache.org/zh/index.html)
![Status](https://repobeats.axiom.co/api/embed/345d4bae5dc7e5184af4452b9dad01a671e220b3.svg "Repobeats analytics image")
## 功能 / Features
@@ -74,16 +59,20 @@ A Genshin Impact assistant app based on Tauri.
- Wiki 功能:
- [x] 深渊数据库Hutao API
- [x] 角色数据库(开发中)
- [x] 武器数据库(开发中)
- [x] 卡牌数据库(开发中)
- [ ] 角色数据库(开发中)
- [ ] 武器数据库(开发中)
- [ ] 卡牌数据库(开发中)
- 应用功能:
- [x] 浅色/深色主题切换
- [x] 米游社 JSBridge
## 仓库概况 / Repo Stats
## 贡献者 / Contributors
![Status](https://repobeats.axiom.co/api/embed/0edac184a5892f2520e83e3fe6519c4168db2e1b.svg "Repobeats analytics image")
- [BTMuli](https://github.com/BTMuli)
- [舰队的偶像岛风酱!](https://github.com/frg2089)
- [jerry765](https://github.com/jerry765)
- [AuroraZiling](https://github.com/AuroraZiling)
## UI 参考 / UI Reference
@@ -97,13 +86,21 @@ A Genshin Impact assistant app based on Tauri.
- Changelog: [CHANGELOG](CHANGELOG.md)
- 资源来源:[项目资源说明](docs/项目资源说明.md)
- UIAF[UIAF v1.1](docs/UIAF.md)
- UIGF[UIGF v2.3](docs/UIGF.md)
- UIGF[UIGF v2.4](docs/UIGF.md)
## 贡献者 / Contributors
## 特定项目 / Special Project
- [BTMuli](https://github.com/BTMuli)
- [舰队的偶像岛风酱!](https://github.com/frg2089)
- [jerry765](https://github.com/jerry765)
- [MuCli](https://github.com/BTMuli/MuCli):基于 NodeJS 的命令行工具,用于生成项目文档。
- [TGAssistant](https://github.com/BTMuli/TGAssistant)Teyvat Guide 的资源获取、解析、处理仓库。
- [WhiteTea](https://github.com/BTMuli/WhiteTea)Github Bot自动化处理 Teyvat Guide 的 Issue 和 Pull Request。
## 技术栈 / Tech Stack
- [Tauri](https://github.com/tauri-apps/tauri)
- [Vue3](https://github.com/vuejs/core)
- [Vite](https://github.com/vitejs/vite)
- [Vuetify](https://github.com/vuetifyjs/vuetify)
- [Echarts](https://echarts.apache.org/zh/index.html)
## 协议 / License
@@ -111,6 +108,8 @@ A Genshin Impact assistant app based on Tauri.
应用版本号遵循 [Semantic Versioning 2.0.0](https://semver.org/lang/zh-CN/) 规范。
隐私政策:[Privacy](https://app.btmuli.ink/docs/privacy.html)
## 鸣谢 / Thanks
本项目在开发过程中参考了诸多相关开源项目,特此鸣谢。
@@ -122,3 +121,7 @@ A Genshin Impact assistant app based on Tauri.
- [gs-helper](https://github.com/vikiboss/gs-helper)
- [paimon-moe](https://github.com/MadeBaruna/paimon-moe)
- [Adachi-BOT](https://github.com/Arondight/Adachi-BOT)
感谢 JetBrains 提供的开源许可证。
[![JetBrains](https://www.jetbrains.com/company/brand/img/jetbrains_logo.png)](https://www.jetbrains.com/?from=TeyvatGuide)

View File

@@ -1,50 +1,77 @@
---
Author: 目棃
Date: 2023-04-07
Description: UIGF v2.3 Backup
Update: 2023-04-07
Description: UIGF v2.4 Backup
Date: 2023-11-15
Update: 2023-11-15
---
> 本文档 [`Front-matter`](https://github.com/BTMuli/Mucli#FrontMatter) 由 [MuCli](https://github.com/BTMuli/Mucli) 自动生成于`2023-04-07 19:51:40`
> 本文档 [`Frontmatter`](https://github.com/BTMuli/MuCli#Frontmatter) 由 [MuCli](https://github.com/BTMuli/Mucli) 自动生成于 `2023-11-15 20:58:36`
>
> 更新于 `2023-04-07 19:51:40`
> 更新于 `2023-11-15 20:58:36`
>
> 本文档为 [UIGF v2.4](https://github.com/UIGF-org/UIGF-org.github.io/blob/main/docs/zh/standards/UIGF.md) 的备份,仅供参考。
> 本文档为 [`UIGF`](https://github.com/UIGF-org/UIGF-org.github.io/blob/main/docs/zh/standards/UIGF-pre-release.md) 的备份。
# 统一可交换抽卡记录标准 v2.4
# 统一可交换祈愿记录标准 v2.3
> Uniformed Interchangeable GachaLog Format standard (UIGF) v2.4 <Badge text="Current" type="message" />
>
> ::: warning UIGF 标准使用声明
> 应用必须在同时支持 UIGF 数据格式**导入**和**导出**功能并在相关功能区域或文档中提供跳转至 [UIGF-Org](https://uigf.org) 的超链接后声明支持 UIGF 格式
> Uniformed Interchangeable GachaLog Format standard (UIGF) v2.3
仅包含导入功能降低了用户数据可流通性,且将数据至于用户不可控的风险中,不符合 UIGF-Org 设计的初衷。
:::
## 更新记录
| 版本 | 说明 | 兼容 |
| ------ | ------------------------------------------------ | -------------- |
| `v2.0` | 首个正式版本 | v2.0 |
| `v2.1` | 简化了部分语言表述,与 v2.0 在数据格式上完全一致 | v2.1 and lower |
| `v2.2` | 新增 `info.export_timestamp` 填充 UNIX 时间戳 | v2.2 and lower |
| `v2.3` | 扩充至非中文语境,使用 Json Schema 表述 | v2.3 and lower |
| 版本 | 说明 | 兼容 |
| ----------------------------- | ---------------------------------------------------------- | -------------- |
| `v2.0` | 首个正式版本 | v2.0 |
| `v2.1` | 简化了部分语言表述,与 v2.0在数据格式上完全一致 | v2.1 and lower |
| [`v2.2`](UIGF-legacy-v2.2.md) | 新增 `info.export_timestamp` 填充 UNIX 时间戳 | v2.2 and lower |
| [`v2.3`](UIGF-legacy-v2.3.md) | 扩充至非中文语境,使用 Json Schema 表述。移除了 Excel 格式 | v2.3 and lower |
| `v2.4` | 新增 `info.region_time_zone` 支持时区处理 | v2.4 and lower |
## Id
### v2.4 更新内容
原神的祈愿记录物品内包含了一项较为特殊的字段: `id` ,该值在 1.3 版本后加入
所以**先前查询出的物品**若无特殊兼容性修改则不会包含相应的 `id`
App 导出 UIGF 时
- 国际化兼容性增强
-`info` 对象中新增了 `region_time_zone` 字段
- 需要确保每个物品的 `id` 的有效性。
- 从最后一个自带有效 `id` 的物品开始,向前(相对于时间)依次递减 `id` 的值,每次递减的值应保持为 `1`
## `info` 字段说明
导入 UIGF 到 App 时
### `region_time_zone`
- App 不应假设所有的 `gacha_item` 都有有效的 `id`
- App 应具有处理 `id` 字段为 `null`或 `` 空字符串的能力
由于在获取祈愿记录时得到的`time`为服务器时间,为了准确判断时间的时区偏移,引入此字段。
## GachaType
与 SRGF 不同,由于无法直接从服务器获取`region_time_zone`,在导出方未提供此字段时,需要根据 `uid` 进行推断。
祈愿包含了会共享保底与概率的卡池,所以需要一个额外的字段来界定
我们在`UIGF`的所有格式中注入了`uigf_gacha_type`字段
在导出到`UIGF`格式时需要注意添加对应的`uigf_gacha_type`字段
#### 映射关系
### 映射关系
| `uid`首个字符 | `region_time_zone` | 游戏服务器 |
| ------------- | ------------------ | --------------------------------- |
| `'6'` | `-5` | os_usa |
| `'7'` | `1` | os_euro |
| 剩余情况 | `8` | os_cht, os_asia, cn_gf01, cn_qd01 |
App 不应假定 `region_time_zone` 的值为上表中给出的值,应具有处理非标准 `region_time_zone` 值的能力。
`region_time_zone` 的值与 `uid` 推断结果不一致,则优先选择 `region_time_zone` 给出的值。
## `list` 字段说明
### `id`
物品内包含了一项较为特殊的字段: `id`,为原神官方 API 中包含的,代表每条抽卡记录唯一性的 `id`。App 导出 UIGF 时
- 需要确保每个物品都有一个有效的唯一 `id`
- 若有记录中不包含`id`,则应从下一个自带有效 `id` 的物品开始,为每条缺失`id`字段的数据补全`id`
赋值数据向前(时间排序)依次递减,每次递减的值应保持为 `1`
### `gacha_type`
由于存在会共享保底与概率的卡池,所以需要一个额外的字段来界定
我们在 `UIGF` 的所有格式中注入了 `uigf_gacha_type` 字段
在导出到 `UIGF` 格式时需要注意添加对应的 `uigf_gacha_type` 字段
#### 映射关系
| `uigf_gacha_type` | `gacha_type` |
| ----------------- | -------------- |
@@ -53,225 +80,112 @@ App 导出 UIGF 时
| `301` | `301` or `400` |
| `302` | `302` |
## Json 格式
### `item_id`
> Uniformed Interchangeable GachaLog Format standard of Json (UIGF.J)
> Json 格式 由于 与从官方接口获取到的格式一致
> 更便于各 App 的导入与导出,我们也在此做出规范
> 该格式应仅用于各 App 间的数据互通
物品游戏内ID你可以通过 [UIGF API](../API.md) 获取这一数据
### 导出的格式
## Json Schema
> UIGF-Org 提供[Json Schema](/schema/uigf.json) 用于验证
```json
{
"type": "object",
"title": "UIGF object",
"properties": {
"info": {
"type": "object",
"properties": {
"uid": {
"type": "string",
"title": "Uid",
"description": "Uid"
},
"lang": {
"type": "string",
"title": "Language",
"description": "语言 ISO 3166"
},
"uigf_version": {
"type": "string",
"title": "UIGF Version",
"description": "UIGF 版本号"
},
"export_timestamp": {
"type": "number",
"title": "Export Timestamp",
"description": "导出时间戳(秒)"
},
"export_time": {
"type": "string",
"description": "导出时间",
"format": "date-time",
"pattern": "yyyy-MM-dd HH:mm:ss",
"title": "Export Time"
},
"export_app": {
"type": "string",
"title": "Export App",
"description": "导出应用"
},
"export_app_version": {
"type": "string",
"title": "Export App Version",
"description": "导出应用版本"
}
},
"title": "Infomation",
"required": ["uid", "lang", "uigf_version"],
"description": "包含导出方定义的基本信息"
},
"list": {
"type": "array",
"items": {
"root": {
"type": "object",
"properties": {
"info": {
"type": "object",
"properties": {
"gacha_type": {
"uid": {
"type": "string",
"description": "祈愿类型"
"title": "导出记录的 UID"
},
"item_id": {
"lang": {
"type": "string",
"title": "Item Id",
"description": "空字符串"
"title": "语言 languagecode2-country/regioncode2"
},
"count": {
"type": "string",
"title": "Count",
"description": "数量"
"export_timestamp": {
"type": "number",
"title": "导出 UNIX 时间戳(秒)"
},
"time": {
"export_time": {
"type": "string",
"title": "Time",
"description": "物品获取时间",
"pattern": "yyyy-MM-dd HH:mm:ss",
"format": "date-time"
"title": "导出时间",
"description": "yyyy-MM-dd HH:mm:ss"
},
"name": {
"export_app": {
"type": "string",
"title": "Name",
"description": "名称"
"title": "导出 App 名称"
},
"item_type": {
"export_app_version": {
"type": "string",
"title": "Item Type",
"description": "物品类型"
"title": "导出 App 版本"
},
"rank_type": {
"uigf_version": {
"type": "string",
"title": "Item Quality",
"description": "物品星级"
"title": "UIGF 版本号",
"pattern": "v\\d+\\.\\d+"
},
"id": {
"type": "string",
"title": "Id",
"description": "内部数据库Id"
},
"uigf_gacha_type": {
"type": "string",
"title": "Query Type",
"description": "向接口查询时需要的 gacha_type"
"region_time_zone": {
"type": "number",
"title": "区域时区偏移"
}
},
"required": ["gacha_type", "name", "id", "uigf_gacha_type", "time"],
"title": "Gacha Item",
"description": "祈愿物品"
"required": ["uid", "uigf_version"],
"title": "UIGF 导出信息"
},
"title": "List",
"description": "物品列表"
}
},
"required": ["info", "list"],
"description": "UIGF 根对象"
"list": {
"type": "array",
"items": {
"type": "object",
"properties": {
"uigf_gacha_type": {
"type": "string",
"title": "UIGF 卡池类型",
"description": "用于区分卡池类型不同,但卡池保底计算相同的物品"
},
"gacha_type": {
"type": "string",
"title": "卡池类型"
},
"item_id": {
"type": "string",
"title": "物品的内部 ID"
},
"count": {
"type": "string",
"title": "个数",
"description": "一般为1"
},
"time": {
"type": "string",
"title": "获取物品的时间"
},
"name": {
"type": "string",
"title": "物品名称"
},
"item_type": {
"type": "string",
"title": "物品类型"
},
"rank_type": {
"type": "string",
"title": "物品等级"
},
"id": {
"type": "string",
"title": "记录内部 ID"
}
},
"required": ["uigf_gacha_type", "gacha_type", "id", "item_id", "time"],
"title": "UIGF 物品"
},
"title": "物品列表"
}
},
"required": ["info", "list"],
"title": "UIGF 根对象"
}
}
```
## Excel 工作簿 (Workbook Format)
> Uniformed Interchangeable GachaLog Format standard of Workbook (UIGF.W)
### 单元格的格式
- 在填充单元格内的数据时,应统一转换到 `String` 字符串类型后填入
### 表名及内容
| 表名 | 内容 | 类型 | 是否必要 |
| ------------ | -------------------------------------- | ------ | ------------------------------ |
| 统计分析 | 统计分析内容等 | 任意 | 否 |
| 角色活动祈愿 | `gacha_type` : `301 or 400` 的祈愿数据 | 祈愿表 | 否,但是应该导出 |
| 武器活动祈愿 | `gacha_type` : `302` 的祈愿数据 | 祈愿表 | 否,但是应该导出 |
| 常驻祈愿 | `gacha_type` : `200` 的祈愿数据 | 祈愿表 | 否,但是应该导出 |
| 新手祈愿 | `gacha_type` : `100` 的祈愿数据 | 祈愿表 | 否,但是应该导出 |
| 原始数据 | 全部祈愿数据 | 数据表 | **详见下方原始数据表结构说明** |
- 表的顺序可以是任意的
- 可以隐藏部分表,防止用户随意篡改数据
- Sheet 的名称应与游戏内祈愿记录页面显示的名称保持一致
> App 间应依据 `原始数据表` 的内容,来进行数据互通
### 祈愿表结构
本节内容是为了规范兼容分析类 App
- 表头对应的内容填充**顺序需要严格按照下方说明**排布
- **共享保底的卡池**按祈愿类型 (`gacha_type`) 区分
- 此类 `Sheet` 存在的目的,是为了便于用户观看与祈愿分析工具的分析
| 表头 | 内容 | 是否必要 |
| -------- | ---------------------------------------- | ---------------------------- |
| 时间 | `yyyy-MM-dd HH:mm:ss` 格式的 `time` 时间 | 是 |
| 名称 | `name`物品名称 | 是 |
| 物品类型 | `item_type` | 是 |
| 星级 | `rank_type` | 是 |
| 祈愿类型 | `gacha_type` 的转义名称 | 是,尽管部分工具不会分析此项 |
| ... | ... | 否 |
> 如果你认为有必要的话,可以额外增加其他表头,但请确保表头的前几列为上表规范的内容
> 表内的数据通常按祈愿 Id 升序或降序排列,分析 App 不应假设表内的顺序为特定的升序与降序
#### `gacha_type` 转义名称
| gacha_type | 名称 |
| ---------- | -------------- |
| 100 | 新手祈愿 |
| 200 | 常驻祈愿 |
| 301 | 角色活动祈愿 |
| 400 | 角色活动祈愿-2 |
| 302 | 武器活动祈愿 |
#### 示例
| 时间 | 名称 | 类别 | 星级 | 祈愿类型 | ... |
| ------------------- | -------- | ---- | ---- | -------------- | --- |
| 2021-02-17 18:45:09 | 以理服人 | 武器 | 3 | 角色活动祈愿-2 | ... |
| ... | ... | ... | ... | ... | ... |
### 原始数据表结构
导出时
- App 在导出时应尽可能询问用户是否应包含原始数据表
- 一旦在工作簿内包含了名为 `原始数据` 的表,即表示支持本格式
- 该表内的内容应严格按照本格式所述填充
- **表头的顺序需严格按照下表设置**。
- 现有的字段采用**字典顺序**递增排序,后续新增的字段依添加的顺序排在后侧。
- 若无特殊需求,我们建议导出所有 json 数据内包含的字段
导入时
- 强烈建议您编写不依赖于列的顺序位置便可实现导入的程序,以达到最大化的兼容。
- 如果省略了其中某些非必要字段的值,请保持表头存在,对应的列则空置。
| 表头 | 是否必要 |
| ----------------- | ---------------------------------------------------- |
| `count` | 否,但是建议保留,不排除后续会有`count`不为 1 的情况 |
| `gacha_type` | 是 |
| `id` | 是,且大部分 App 按此字段排序数据 |
| `item_id` | 否,目前官方已经弃用了此字段 |
| `item_type` | 是 |
| `lang` | 否,但建议保留,以便国际化 |
| `name` | 是 |
| `rank_type` | 否,但建议保留,以便分析 |
| `time` | 否,但建议保留,以便分析 |
| `uid` | 否,但建议将选择权交予用户,保留以便分析 |
| `uigf_gacha_type` | 是 |
#### 示例
| count | gacha_type | id | item_id | item_type | lang | name | rank_type | time | uid | uigf_gacha_type |
| ----- | ---------- | ------------------- | ------- | --------- | ----- | -------- | --------- | ------------------- | --------- | --------------- |
| 1 | 301 | 1613556360008291100 | | 武器 | zh-cn | 以理服人 | 3 | 2021-02-17 18:45:09 | 123456789 | 301 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |

View File

@@ -4,7 +4,7 @@
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/icon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Tauri.Genshin</title>
<title>TeyvatGuide</title>
</head>
<body>

View File

@@ -1,10 +1,9 @@
{
"name": "TeyvatGuide",
"description": "A Genshin Tool build with Tauri",
"version": "0.3.6",
"description": "Game Tool for Genshin Impact player",
"private": true,
"version": "0.3.2",
"author": "BTMuli <bt-muli@outlook.com>",
"packageManager": "pnpm@8.7.6",
"packageManager": "pnpm@8.10.5",
"scripts": {
"build": "tauri build",
"debug": "tauri build --debug",
@@ -15,79 +14,103 @@
"lint:code:fix": "eslint . --fix",
"lint:style": "stylelint \"src/**/*.{vue,css}\"",
"lint:style:fix": "pnpm lint:style --fix",
"lint:rust:fix": "cd src-tauri && cargo fmt",
"prettier": "prettier . --write",
"tauri": "tauri",
"tauri:icon": "tauri icon ./public/icon.png",
"vite:dev": "vite dev",
"vite:build": "vite build",
"prepare": "husky install"
"prepare": "husky install",
"fix:pnpm": "pnpm add https://github.com/tauri-apps/tauri-plugin-sql#v1"
},
"lint-staged": {
"*.{ts,vue}": "eslint --fix",
"*.{vue,css}": "stylelint --fix",
"*.{ts,vue,css,yml,json,md}": "prettier --write"
"*.ts": [
"eslint --fix",
"prettier --write"
],
"*.vue": [
"eslint --fix",
"prettier --write",
"stylelint --fix"
],
"*.css": [
"prettier --write",
"stylelint --fix"
],
"*.{yml,json,md}": [
"prettier --write"
],
"*.rs": [
"rustfmt"
]
},
"keywords": [
"Genshin Impact",
"Node",
"Tauri",
"Typescript",
"Vite",
"Vuetify"
],
"author": "BTMuli <bt-muli@outlook.com>",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/BTMuli/TeyvatGuide.git"
},
"homepage": "https://github.com/BTMuli/TeyvatGuide#readme",
"bugs": {
"url": "https://github.com/BTMuli/TeyvatGuide/issues"
},
"directories": {
"doc": "docs"
},
"homepage": "https://github.com/BTMuli/TeyvatGuide#readme",
"keywords": [
"Tauri",
"Node",
"Typescript",
"Vite",
"Genshin Impact",
"Vuetify"
],
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/BTMuli/TeyvatGuide.git"
},
"dependencies": {
"@mdi/font": "7.2.96",
"@tauri-apps/api": "^1.4.0",
"@mdi/font": "7.3.67",
"@tauri-apps/api": "^1.5.1",
"clipboard": "^2.0.11",
"color-convert": "^2.0.1",
"echarts": "^5.4.3",
"html2canvas": "^1.4.1",
"js-md5": "^0.7.3",
"pinia": "^2.1.6",
"js-md5": "^0.8.3",
"pinia": "^2.1.7",
"pinia-plugin-persistedstate": "^3.2.0",
"qrcode.vue": "^3.4.1",
"tauri-plugin-sql-api": "github:tauri-apps/tauri-plugin-sql#v1",
"vue": "^3.3.4",
"uuid": "^9.0.1",
"vue": "^3.3.8",
"vue-echarts": "^6.6.1",
"vue-json-viewer": "^3.0.4",
"vue-router": "^4.2.5",
"vuetify": "^3.3.17",
"vuetify": "^3.4.2",
"wcag-color": "^1.1.1"
},
"devDependencies": {
"@tauri-apps/cli": "^1.4.0",
"@types/color-convert": "^2.0.1",
"@types/js-md5": "^0.7.0",
"@types/node": "^20.6.3",
"@typescript-eslint/eslint-plugin": "^6.7.2",
"@typescript-eslint/parser": "^6.7.2",
"@vitejs/plugin-vue": "^4.3.4",
"@vue/devtools": "^6.5.0",
"concurrently": "^8.2.1",
"eslint": "^8.50.0",
"@tauri-apps/cli": "^1.5.6",
"@types/color-convert": "^2.0.3",
"@types/js-md5": "^0.7.2",
"@types/node": "^20.9.1",
"@types/uuid": "^9.0.7",
"@typescript-eslint/eslint-plugin": "^6.11.0",
"@typescript-eslint/parser": "^6.11.0",
"@vitejs/plugin-vue": "^4.5.0",
"@vue/devtools": "^6.5.1",
"concurrently": "^8.2.2",
"eslint": "^8.53.0",
"eslint-config-prettier": "^9.0.0",
"eslint-config-standard-with-typescript": "^39.1.0",
"eslint-plugin-import": "^2.28.1",
"eslint-plugin-n": "^16.1.0",
"eslint-plugin-prettier": "^5.0.0",
"eslint-config-standard-with-typescript": "^39.1.1",
"eslint-plugin-import": "^2.29.0",
"eslint-plugin-jsonc": "^2.10.0",
"eslint-plugin-n": "^16.3.1",
"eslint-plugin-prettier": "^5.0.1",
"eslint-plugin-promise": "^6.1.1",
"eslint-plugin-vue": "^9.17.0",
"eslint-plugin-vue": "^9.18.1",
"eslint-plugin-yml": "^1.10.0",
"husky": "^8.0.3",
"lint-staged": "^14.0.1",
"prettier": "3.0.3",
"stylelint": "^15.10.3",
"jsonc-eslint-parser": "^2.4.0",
"lint-staged": "^15.1.0",
"prettier": "3.1.0",
"stylelint": "^15.11.0",
"stylelint-config-idiomatic-order": "^9.0.0",
"stylelint-config-standard-vue": "^1.0.0",
"stylelint-declaration-block-no-ignored-properties": "^2.7.0",
@@ -95,7 +118,8 @@
"stylelint-order": "^6.0.3",
"stylelint-prettier": "^4.0.2",
"typescript": "^5.2.2",
"vite": "^4.4.9",
"vite-plugin-vuetify": "^1.0.2"
"vite": "^5.0.0",
"vite-plugin-vuetify": "^1.0.2",
"yaml-eslint-parser": "^1.2.2"
}
}

1233
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 896 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

View File

@@ -1,32 +1,32 @@
#-------------------------------------------------------------------------------#
# -------------------------------------------------------------------------------#
# Qodana analysis is configured by qodana.yaml file #
# https://www.jetbrains.com/help/qodana/qodana-yaml.html #
#-------------------------------------------------------------------------------#
# -------------------------------------------------------------------------------#
version: "1.0"
#Specify inspection profile for code analysis
# Specify inspection profile for code analysis
profile:
name: qodana.starter
#Enable inspections
#include:
# Enable inspections
# include:
# - name: <SomeEnabledInspectionId>
#Disable inspections
#exclude:
# Disable inspections
# exclude:
# - name: <SomeDisabledInspectionId>
# paths:
# - <path/where/not/run/inspection>
#The following options are only applied in CI/CD environment
#These options are ignored during local run
# The following options are only applied in CI/CD environment
# These options are ignored during local run
#Execute shell command before Qodana execution
#bootstrap: sh ./prepare-qodana.sh
# Execute shell command before Qodana execution
# bootstrap: sh ./prepare-qodana.sh
#Install IDE plugins before Qodana execution
#plugins:
# Install IDE plugins before Qodana execution
# plugins:
# - id: <plugin.id> #(plugin id can be found at https://plugins.jetbrains.com)
#Specify Qodana linter for analysis
# Specify Qodana linter for analysis
linter: jetbrains/qodana-js:latest

1356
src-tauri/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,8 +1,8 @@
[package]
name = "TeyvatGuide"
version = "0.3.2"
description = "A Genshin Tool build with Tauri"
authors = ["BTMuli<bt-muli@outlook.com>"]
version = "0.3.6"
description = "Game Tool for Genshin Impact player"
authors = ["BTMuli <bt-muli@outlook.com>"]
license = "MIT"
repository = "https://github.com/BTMuli/TeyvatGuide"
edition = "2021"
@@ -13,16 +13,30 @@ edition = "2021"
tauri-build = { version = "1.4", features = [] }
[dependencies]
tauri = { version = "1.4", features = ["api-all"] }
tauri = { version = "1.4", features = [ "dialog-message", "process-exit", "fs-read-dir", "window-hide", "os-all", "clipboard-all", "dialog-open", "dialog-save", "fs-create-dir", "fs-remove-dir", "fs-write-file", "fs-remove-file", "fs-read-file", "path-all", "fs-exists", "window-close", "window-set-title", "window-unminimize", "window-show", "window-set-focus", "http-request", "shell-open"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
tauri-plugin-deep-link="0.1.2"
url = "2.4.1"
tauri-utils = "1.5.0"
walkdir = "2"
# sqlite 插件
[dependencies.tauri-plugin-sql]
git = "https://github.com/tauri-apps/plugins-workspace"
branch = "v1"
features = ["sqlite"]
# deep link 插件
[dependencies.tauri-plugin-deep-link]
version = "0.1.2"
# 用于打包
[profile.release]
codegen-units = 1
lto = true
opt-level = "s"
panic = "abort"
[features]
# this feature is used for production builds or when `devPath` points to the filesystem
# DO NOT REMOVE!!

Binary file not shown.

9
src-tauri/rustfmt.toml Normal file
View File

@@ -0,0 +1,9 @@
# File rustfmt.toml
# Desc Rust 格式化配置文件
# Since Beta v0.3.3
max_width = 100
tab_spaces = 2
edition = "2018"
use_small_heuristics = "Max"
newline_style = "Auto"

55
src-tauri/src/client.rs Normal file
View File

@@ -0,0 +1,55 @@
//! @file src/client.rs
//! @desc 客户端模块,负责操作米游社客户端
//! @since Beta v0.3.6
use tauri::{AppHandle, Manager, WindowBuilder, WindowUrl};
use url::Url;
// 获取米游社客户端入口地址
fn get_mhy_client_url(func: String) -> WindowUrl {
let mut url_res: Url = "https://bbs.mihoyo.com/ys/".parse().unwrap();
if func == "sign_in" {
url_res = "https://webstatic.mihoyo.com/bbs/event/signin-ys/index.html?act_id=e202009291139501"
.parse()
.unwrap();
} else if func == "game_record" {
url_res =
"https://webstatic.mihoyo.com/app/community-game-records/index.html?bbs_presentation_style=fullscreen".parse().unwrap();
} else if func == "birthday" {
url_res = "https://webstatic.mihoyo.com/ys/event/e20220303-birthday/index.html?activity_id=20220301153521"
.parse()
.unwrap();
}
return WindowUrl::External(url_res);
}
// 操作米游社客户端
#[tauri::command]
pub async fn create_mhy_client(handle: AppHandle, func: String, url: String) {
let mut mhy_window_config = handle.config().tauri.windows.get(1).unwrap().clone();
// 如果没有传入 url 参数,则使用默认的米游社客户端入口地址
if url != "" {
mhy_window_config.url = WindowUrl::External(url.parse().unwrap());
} else {
mhy_window_config.url = get_mhy_client_url(func.clone());
}
if func == "birthday"
|| url.starts_with("https://webstatic.mihoyo.com/ys/event/e20220303-birthday/index.html")
{
mhy_window_config.width = 1280.0;
mhy_window_config.height = 720.0;
}
let has_mhy_client = handle.get_window("mhy_client").is_some();
if has_mhy_client {
dbg!("mhy_client exists");
return;
}
let mhy_client = WindowBuilder::from_config(&handle, mhy_window_config).build().unwrap();
let js_bridge = r#"
window.MiHoYoJSInterface = {
postMessage: function(arg) { window.__TAURI__.event.emit('post_mhy_client', arg) },
closePage: function() { this.postMessage('{"method":"closePage"}') },
};
"#;
mhy_client.eval(&js_bridge).ok().unwrap();
}

View File

@@ -1,60 +1,131 @@
//! @file src/main.rs
//! @desc 主模块,用于启动应用
//! @since Beta v0.3.4
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
use tauri::Manager;
use tauri::{Manager, WindowBuilder};
use tauri_utils::config::WindowConfig;
mod client;
// 放一个常数,用来判断是否注册deep link
// 放一个常数,用来判断应用是否初始化
static mut APP_INITIALIZED: bool = false;
static mut DEEP_LINK_REGISTERED: bool = false;
#[tauri::command]
async fn init_app(app_handle: tauri::AppHandle) {
dbg!("init_app");
unsafe {
if APP_INITIALIZED == true && DEEP_LINK_REGISTERED == true {
return;
}
}
app_handle.emit_all("initApp", ()).unwrap();
unsafe {
APP_INITIALIZED = true;
}
}
#[tauri::command]
async fn register_deep_link(app_handle: tauri::AppHandle) {
unsafe {
if DEEP_LINK_REGISTERED {
return;
}
dbg!("register_deep_link");
unsafe {
if DEEP_LINK_REGISTERED == true {
return;
}
tauri_plugin_deep_link::register(
"teyvatguide",
move |request| {
dbg!(&request);
app_handle.emit_all("active_deep_link", request).unwrap();
},
)
.unwrap();
unsafe {
DEEP_LINK_REGISTERED = true;
}
tauri_plugin_deep_link::register("teyvatguide", move |request| {
dbg!(&request);
app_handle.emit_all("active_deep_link", request).unwrap();
})
.unwrap();
unsafe {
DEEP_LINK_REGISTERED = true;
}
}
// 执行 js
#[tauri::command]
async fn execute_js(app_handle: tauri::AppHandle, label: String, js: String) {
let window = app_handle.get_window(&label).unwrap();
dbg!(&js);
window.eval(&js).ok().unwrap();
}
// 创建窗口
#[tauri::command]
async fn create_window(app_handle: tauri::AppHandle, label: String, mut option: WindowConfig) {
let window_old = app_handle.get_window(&label);
option.label = label.clone();
if window_old.is_some() {
dbg!("window exists");
window_old.unwrap().close().unwrap();
return;
}
let window_new =
Some(WindowBuilder::from_config(&app_handle, option).build().expect("failed to create window"));
window_new.unwrap();
}
// 读取目录大小
#[tauri::command]
async fn get_dir_size(path: String) -> u64 {
dbg!(&path);
let walk_dir = walkdir::WalkDir::new(path);
let mut size = 0;
for entry in walk_dir {
let entry = entry.unwrap();
let file_type = entry.file_type();
if file_type.is_file() {
size += entry.metadata().unwrap().len();
}
}
size
}
fn main() {
tauri_plugin_deep_link::prepare("teyvatguide");
tauri::Builder::default()
.on_window_event(|event| {
match event.event() {
tauri::WindowEvent::CloseRequested { api, .. } => {
api.prevent_close();
let window = event.window().clone();
if window.label() == "TeyvatGuide" {
// 子窗口 label 的数组
const SUB_WINDOW_LABELS: [&str; 2] = ["Sub_window", "Dev_JSON"];
for label in SUB_WINDOW_LABELS.iter() {
let sub = window.get_window(label).unwrap();
sub.close().unwrap();
}
}
window.close().unwrap();
},
_ => {}
tauri_plugin_deep_link::prepare("teyvatguide");
tauri::Builder::default()
.on_window_event(|event| {
match event.event() {
tauri::WindowEvent::CloseRequested { api, .. } => {
api.prevent_close();
let window = event.window().clone();
if window.label() == "TeyvatGuide" {
// 子窗口 label 的数组
const SUB_WINDOW_LABELS: [&str; 3] = ["Sub_window", "Dev_JSON", "mhy_client"];
for label in SUB_WINDOW_LABELS.iter() {
let sub = window.get_window(label);
if sub.is_some() {
sub.unwrap().close().unwrap();
}
}
})
.plugin(tauri_plugin_sql::Builder::default().build())
.invoke_handler(tauri::generate_handler![register_deep_link])
.setup(|_app| {
let _window = _app.get_window("TeyvatGuide").unwrap();
#[cfg(debug_assertions)] // only include this code on debug builds
_window.open_devtools(); // open the devtools on startup
Ok(())
})
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
window.close().unwrap();
}
_ => {}
}
})
.plugin(tauri_plugin_sql::Builder::default().build())
.invoke_handler(tauri::generate_handler![
register_deep_link,
init_app,
execute_js,
create_window,
get_dir_size,
client::create_mhy_client,
])
.setup(|_app| {
let _window = _app.get_window("TeyvatGuide").unwrap();
let _mhy = _app.get_window("mhy_client");
if _mhy.is_some() {
_mhy.unwrap().close().unwrap();
}
#[cfg(debug_assertions)] // only include this code on debug builds
_window.open_devtools(); // open the devtools on startup
Ok(())
})
.run(tauri::generate_context!())
.expect("error while running tauri application");
}

View File

@@ -2,77 +2,117 @@
"build": {
"beforeDevCommand": "pnpm vite:dev",
"beforeBuildCommand": "pnpm vite:build",
"devPath": "http://localhost:3000",
"devPath": "http://localhost:4000",
"distDir": "../dist",
"withGlobalTauri": true
},
"package": {
"productName": "TeyvatGuide",
"version": "0.3.2"
"version": "0.3.6"
},
"tauri": {
"allowlist": {
"all": true,
"all": false,
"fs": {
"all": true,
"all": false,
"exists": true,
"readFile": true,
"readDir": true,
"writeFile": true,
"createDir": true,
"removeDir": true,
"removeFile": true,
"scope": ["**", "**/*"]
},
"http": {
"all": true,
"all": false,
"request": true,
"scope": [
"https://api-takumi.mihoyo.com/*",
"https://api-takumi-record.mihoyo.com/*",
"https://api-static.mihoyo.com/*",
"https://bbs-api.mihoyo.com/*",
"https://bbs-api.miyoushe.com/*",
"https://bbs-api-static.miyoushe.com/*",
"https://bbs.mihoyo.com/*",
"https://hk4e-api.mihoyo.com/*",
"https://hk4e-sdk.mihoyo.com/*",
"https://passport-api.mihoyo.com/*",
"https://passport-api.miyoushe.com/*",
"https://passport-api-v4.mihoyo.com/*",
"https://act-webstatic.mihoyo.com/*",
"https://sdk-webstatic.mihoyo.com/*",
"https://homa.snapgenshin.com/*",
"https://enka-api.hut.ao/*"
]
"scope": ["http://**", "https://**"]
},
"shell": {
"all": false,
"open": true
},
"dialog": {
"all": false,
"open": true,
"save": true,
"message": true
},
"clipboard": {
"all": true
},
"path": {
"all": true
},
"window": {
"all": false,
"setTitle": true,
"unminimize": true,
"show": true,
"close": true,
"setFocus": true,
"hide": true
},
"os": {
"all": true
},
"process": {
"all": false,
"exit": true
}
},
"bundle": {
"active": true,
"icon": [
"icons/32x32.png",
"icons/128x128.png",
"icons/128x128@2x.png",
"icons/icon.icns",
"icons/icon.ico",
"icons/icon.png",
"icons/Square30x30Logo.png",
"icons/Square44x44Logo.png",
"icons/Square71x71Logo.png",
"icons/Square89x89Logo.png",
"icons/32x32.png",
"icons/Square107x107Logo.png",
"icons/Square142x142Logo.png",
"icons/Square150x150Logo.png",
"icons/Square284x284Logo.png",
"icons/Square30x30Logo.png",
"icons/Square310x310Logo.png",
"icons/StoreLogo.png"
"icons/Square44x44Logo.png",
"icons/Square71x71Logo.png",
"icons/Square89x89Logo.png",
"icons/StoreLogo.png",
"icons/icon.ico",
"icons/icon.png"
],
"identifier": "TeyvatGuide",
"targets": ["msi"],
"targets": ["msi", "app", "dmg"],
"windows": {
"wix": {
"language": "zh-CN"
}
}
},
"macOS": {}
},
"security": {
"dangerousRemoteDomainIpcAccess": [
{
"domain": "act.mihoyo.com",
"windows": ["mhy_client"],
"enableTauriAPI": true
},
{
"domain": "m.miyoushe.com",
"windows": ["mhy_client"],
"enableTauriAPI": true
},
{
"domain": "webstatic.mihoyo.com",
"windows": ["mhy_client"],
"enableTauriAPI": true
},
{
"domain": "api-takumi-record.mihoyo.com",
"windows": ["mhy_client"],
"enableTauriAPI": true
}
],
"csp": null
},
"updater": {
@@ -94,6 +134,20 @@
"height": 900,
"center": true,
"transparent": true
},
{
"fullscreen": false,
"resizable": false,
"title": "米游社",
"label": "mhy_client",
"url": "https://api-static.mihoyo.com/",
"userAgent": "Mozilla/5.0 (Linux; Android 12) Mobile miHoYoBBS/2.63.1",
"visible": false,
"width": 400,
"height": 800,
"center": true,
"decorations": true,
"closable": true
}
]
}

View File

@@ -11,19 +11,19 @@
</template>
<script lang="ts" setup>
// vue
import { app, event, fs, tauri, window as TauriWindow } from "@tauri-apps/api";
import { onBeforeMount, onMounted, ref } from "vue";
import { useRouter } from "vue-router";
import TSidebar from "./components/app/t-sidebar.vue";
import TBackTop from "./components/app/t-backTop.vue";
// tauri
import { app, event, fs, tauri, window as TauriWindow } from "@tauri-apps/api";
// store
import { useAppStore } from "./store/modules/app";
// utils
import { getEmojis } from "./plugins/Mys/request/getEmojis";
import TSidebar from "./components/app/t-sidebar.vue";
import showConfirm from "./components/func/confirm";
import showSnackbar from "./components/func/snackbar";
import { getEmojis } from "./plugins/Mys/request/getEmojis";
import TGSqlite from "./plugins/Sqlite";
import { useAppStore } from "./store/modules/app";
import { useUserStore } from "./store/modules/user";
import { getBuildTime } from "./utils/TGBuild";
const appStore = useAppStore();
const isMain = ref<boolean>(false);
@@ -36,11 +36,9 @@ onBeforeMount(async () => {
isMain.value = win.label === "TeyvatGuide";
if (isMain.value) {
const title = "Teyvat Guide v" + (await app.getVersion()) + " Beta";
await tauri.invoke("register_deep_link");
await getDeepLink();
await win.setTitle(title);
await emojiLoad();
await checkLoad();
await listenOnInit();
await tauri.invoke("init_app");
}
});
@@ -61,6 +59,19 @@ async function listenOnTheme(): Promise<void> {
});
}
// 启动后只执行一次的监听
async function listenOnInit(): Promise<void> {
await event.listen("initApp", async () => {
await tauri.invoke("register_deep_link");
await getDeepLink();
await emojiLoad();
await checkAppLoad();
await checkUserLoad();
await checkUpdate();
});
return;
}
async function emojiLoad(): Promise<void> {
const res = await getEmojis();
if ("retcode" in res) {
@@ -75,15 +86,79 @@ async function emojiLoad(): Promise<void> {
}
}
async function checkLoad(): Promise<void> {
async function checkAppLoad(): Promise<void> {
if (appStore.loading) {
console.info("数据已加载!");
return;
}
await createDataDir();
await initData();
appStore.loading = true;
console.info("数据加载完成!");
const checkDB = await TGSqlite.check();
if (!checkDB) {
await TGSqlite.reset();
showSnackbar({
text: "检测到数据库不完整!已重置数据库!",
color: "error",
timeout: 3000,
});
await createDataDir();
} else {
appStore.loading = true;
console.info("数据库已加载!");
}
}
// 检测 ck,info 数据
async function checkUserLoad(): Promise<void> {
if (!appStore.isLogin) {
console.info("未登录!");
return;
}
const userStore = useUserStore();
const ckLocal = userStore.cookie;
const ckDB = await TGSqlite.getCookie();
if (JSON.stringify(ckLocal) !== JSON.stringify(ckDB)) {
userStore.cookie = ckDB;
console.info("cookie 数据已更新!");
} else if (JSON.stringify(ckLocal) === "{}") {
await new Promise((resolve) => {
setTimeout(() => {
showSnackbar({
text: "获取 Cookie 失败!请重新登录!",
color: "error",
timeout: 3000,
});
resolve(true);
}, 3000);
});
} else {
console.info("cookie 数据已加载!");
}
const infoLocal = userStore.briefInfo;
const appData = await TGSqlite.getAppData();
const infoDB = appData.find((item) => item.key === "userInfo")?.value;
if (infoDB === undefined && JSON.stringify(infoLocal) !== "{}") {
await TGSqlite.saveAppData("userInfo", JSON.stringify(infoLocal));
} else if (infoDB !== undefined && infoLocal !== JSON.parse(infoDB)) {
userStore.setBriefInfo(JSON.parse(infoDB));
console.info("briefInfo 数据已更新!");
} else {
console.info("briefInfo 数据已加载!");
}
const accountLocal = userStore.getCurAccount();
const accountDB = await TGSqlite.getCurAccount();
if (accountDB === false) {
showSnackbar({
text: "获取 GameAccount 失败!请尝试更新数据库!",
color: "error",
timeout: 3000,
});
return;
}
if (accountDB !== accountLocal) {
userStore.setCurAccount(accountDB);
console.info("curAccount 数据已更新!");
} else {
console.info("curAccount 数据已加载!");
}
}
// 创建数据文件夹
@@ -94,49 +169,66 @@ async function createDataDir(): Promise<void> {
console.info("数据文件夹创建完成!");
}
// 初始化数据库
async function initData(): Promise<void> {
if (appStore.devEnv) {
console.info("开发环境,跳过数据库初始化!");
return;
}
await TGSqlite.reset();
showSnackbar({
text: "已成功初始化数据库!",
async function getDeepLink(): Promise<void> {
await event.listen("active_deep_link", async (e) => {
const windowGet = new TauriWindow.WebviewWindow("TeyvatGuide");
if (await windowGet.isMinimized()) {
await windowGet.unminimize();
}
await windowGet.setFocus();
if (typeof e.payload !== "string") {
showSnackbar({
text: "无效的 deep link",
color: "error",
timeout: 3000,
});
return;
}
if (e.payload === "") return;
// 导入格式: teyvatguide://import_uigf?app=appName
// 跳转格式: localhost:4000/achievements/?app=appName
if (e.payload.startsWith("teyvatguide://import_uigf")) {
const param = (<string>e.payload).split("teyvatguide://import_uigf/?")[1];
let appName = "";
if (param) {
appName = param.split("app=")[1];
}
if (appName === "") {
await router.push("/achievements");
} else {
await router.push("/achievements/?app=" + appName);
}
} else {
showSnackbar({
text: "无效的 deep link",
color: "error",
timeout: 3000,
});
}
});
console.info("已成功初始化数据库!");
}
async function getDeepLink(): Promise<void> {
await event.listen("active_deep_link", (e) => {
new TauriWindow.WebviewWindow("TeyvatGuide")
.setFocus()
.then(async () => {
// 导入格式: teyvatguide://import_uigf?app=appName
// 跳转格式: localhost:4000/achievements/?app=appName
if ((<string>e.payload).startsWith("teyvatguide://import_uigf")) {
const param = (<string>e.payload).split("teyvatguide://import_uigf/?")[1];
let appName = "";
if (param) {
appName = param.split("app=")[1];
}
if (appName === "") {
await router.push("/achievements");
} else {
await router.push("/achievements/?app=" + appName);
}
} else {
showSnackbar({
text: "无效的 deep link",
color: "error",
timeout: 3000,
});
}
})
.catch((e) => {
console.log(e);
// 检测更新
async function checkUpdate(): Promise<void> {
if (!appStore.loading) return;
const isProdEnv = import.meta.env.MODE === "production";
const needUpdate = await TGSqlite.checkUpdate();
if (needUpdate && isProdEnv) {
const confirm = await showConfirm({
title: "检测到版本更新",
text: "请到设置页手动更新版本,即将弹出更新说明子页面",
});
if (confirm) {
appStore.buildTime = getBuildTime();
window.open("https://app.btmuli.ink/docs/Changelogs.html");
} else {
showSnackbar({
text: "请到设置页手动更新版本!",
color: "error",
timeout: 3000,
});
});
}
}
}
</script>
<style lang="css">

View File

@@ -1,8 +1,7 @@
/*
* @file assets css post-parser.css
* @description 游戏公告解析 css
* @author BTMuli <bt-muli@outlook.com
* @since Alpha v0.2.0
* @since Beta v0.3.3
*/
.anno-body {
@@ -11,15 +10,19 @@
font-family: var(--font-text);
}
.anno-title {
.anno-title,
.anno-subtitle {
color: var(--common-text-title);
font-family: var(--font-title);
}
.anno-title {
font-size: 24px;
}
.anno-subtitle {
color: var(--common-text-quote);
font-size: 16px;
font-size: 18px;
opacity: 0.5;
}
.anno-img {
@@ -27,7 +30,7 @@
max-width: 100%;
height: auto;
border-radius: 10px;
margin-bottom: 10px;
margin: 10px auto;
}
.anno-content {

View File

@@ -1,33 +1,12 @@
/*
* @file assets css post-parser.css
* @file assets/css/post-parser.css
* @description 米游社解析 css
* @todo 需要完善
* @author BTMuli <bt-muli@outlook.com>
* @since Beta v0.3.0
* @since Beta v0.3.4
*/
.mys-post-body {
width: 800px;
margin: 0 auto;
font-family: var(--font-text);
}
.mys-post-title {
color: var(--common-text-title);
font-family: var(--font-title);
font-size: 20px;
}
.mys-post-subtitle {
font-size: 16px;
opacity: 0.6;
}
.mys-post-content {
line-height: 2;
}
:deep(.mys-post-div) {
position: relative;
margin: 10px auto;
}
@@ -73,6 +52,50 @@
border-radius: 10px;
}
:deep(.mys-post-vod-cover-div) {
position: absolute;
z-index: -1;
top: 0;
left: 0;
display: flex;
overflow: hidden;
width: 800px;
height: 450px;
align-items: center;
justify-content: center;
border: 1px solid var(--common-shadow-2);
border-radius: 10px;
}
:deep(.mys-post-vod-cover) {
max-width: 800px;
max-height: 450px;
object-fit: contain;
}
:deep(.mys-post-vod-icon) {
position: absolute;
top: calc(50% - 40px);
left: calc(50% - 40px);
width: 80px;
height: 80px;
font-size: 50px;
line-height: 80px;
text-align: center;
}
:deep(.mys-post-vod-time) {
position: absolute;
right: 10px;
bottom: 10px;
padding: 0 5px;
border-radius: 5px;
background: rgba(0, 0, 0, 0.5);
color: #ffffff;
font-family: var(--font-title);
font-size: 12px;
}
:deep(.mys-post-iframe) {
overflow: hidden;
width: 800px;
@@ -92,17 +115,6 @@
gap: 10px;
}
:deep(.mys-post-unknown) {
width: 800px;
padding: 10px;
border: 2px solid #485466;
border-radius: 10px;
margin: 10px auto;
background: #5b738f;
color: #faf7e8;
font-family: Consolas, monospace;
}
:deep(.mys-post-link-card-cover) {
width: auto;
height: 180px;
@@ -114,12 +126,6 @@
max-width: 400px;
height: 180px;
border-radius: 10px;
transition: all 0.5s;
}
:deep(.mys-post-link-card-cover):hover img {
transform: scale(1.05);
transition: all 0.5s;
}
:deep(.mys-post-link-card-content) {
@@ -132,9 +138,11 @@
}
:deep(.mys-post-link-card-title) {
width: 100%;
color: var(--common-text-title);
font-family: var(--font-title);
font-size: 20px;
text-align: left;
}
:deep(.mys-post-link-card-price) {
@@ -152,8 +160,145 @@
text-decoration: none;
}
/* 表情包 */
:deep(.mys-post-emoji) {
width: 45px;
height: 45px;
margin: 0 5px;
}
/* 大别野卡片 */
:deep(.mys-post-villa-card) {
position: relative;
overflow: hidden;
width: 800px;
border: 1px solid var(--common-shadow-2);
border-radius: 10px;
}
:deep(.mys-post-villa-card-bg) {
position: absolute;
top: -40px;
right: 0;
width: 100%;
border-radius: 10px;
object-fit: cover;
}
:deep(.mys-post-villa-card-bg-before) {
position: absolute;
top: 0;
right: 0;
width: 100%;
height: 100%;
background: linear-gradient(rgba(255, 255, 255, 0) 40px, var(--box-bg-1) 140px);
}
:deep(.mys-post-villa-card-flex) {
position: relative;
display: flex;
width: 100%;
flex-direction: column;
justify-content: space-between;
padding: 10px;
border-radius: 10px;
gap: 10px;
}
:deep(.mys-post-villa-card-top) {
display: flex;
width: 100%;
flex-direction: row;
align-items: center;
justify-content: flex-start;
gap: 10px;
}
:deep(.mys-post-villa-card-icon) {
width: 80px;
height: 80px;
border-radius: 5px;
}
:deep(.mys-post-villa-card-content) {
display: flex;
height: 80px;
flex-direction: column;
align-items: flex-start;
justify-content: space-between;
}
:deep(.mys-post-villa-card-title) {
padding: 0 5px;
border-radius: 5px;
backdrop-filter: blur(5px);
background: var(--common-shadow-t-4);
color: var(--common-text-title);
font-family: var(--font-title);
font-size: 20px;
}
:deep(.mys-post-villa-card-desc) {
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-start;
padding: 0 5px;
border-radius: 5px;
backdrop-filter: blur(5px);
background: var(--common-shadow-t-2);
gap: 5px;
}
:deep(.mys-post-villa-card-desc-icon) {
width: 30px;
height: 30px;
border-radius: 50%;
}
:deep(.mys-post-villa-card-desc-text) {
color: var(--common-text-content);
font-family: var(--font-text);
font-size: 14px;
}
:deep(.mys-post-villa-card-mid) {
display: flex;
width: 100%;
flex-direction: row;
align-items: center;
justify-content: flex-start;
gap: 10px;
}
:deep(.mys-post-villa-card-tag) {
padding: 2px 5px;
border-radius: 5px;
background: var(--box-bg-2);
color: var(--box-text-2);
font-family: var(--font-text);
font-size: 12px;
}
:deep(.mys-post-villa-card-bottom) {
width: 100%;
font-family: var(--font-title);
font-size: 20px;
text-align: left;
}
/* 未知类型 */
:deep(.mys-post-unknown) {
width: 800px;
padding: 10px;
border: 1px solid var(--common-shadow-2);
border-radius: 10px;
background: var(--box-bg-1);
color: var(--common-text-content);
}
:deep(.mys-post-unknown-code) {
font-family: var(--font-text);
white-space: pre-wrap;
word-break: break-all;
}

View File

@@ -1,8 +1,7 @@
/*
* @file assets index.css
* @file assets/index.css
* @description 全局样式文件
* @author BTMuli <bt-muli@outlook.com>
* @since Beta v0.3.0
* @since Beta v0.3.5
*/
@import "fonts/index.css";
@@ -36,6 +35,7 @@
--tgc-red-1: #e06c63; /* Mys bbs color */
--tgc-yellow-1: #ffcd0c; /* Genshin btn bg */
--tgc-yellow-2: #f4d8a8; /* Genshin confirm text */
--tgc-yellow-3: #e0c06b; /* Genshin btn border */
/* some css same in dark and default theme */
--tgc-btn-1: var(--tgc-dark-7); /* button bg */

View File

@@ -1,8 +1,7 @@
/**
* @file assets themes default.css
* @description 主题样式文件-默认(浅色)主题
* @author BTMuli <bt-muli@outlook.com>
* @since Beta v0.3.2
* @since Beta v0.3.3
*/
/* default(light) theme */
@@ -14,7 +13,7 @@ html.default {
--app-side-content: #222222;
/* box container */
--box-bg-1: #fffdfa;
--box-bg-1: #ebe7df;
--box-bg-2: #f2f9f6;
--box-bg-3: #dee4e9;
--box-bg-4: #f5f5f5;

View File

@@ -57,12 +57,12 @@
</template>
</v-list-item>
<v-divider />
<v-list-item v-show="appStore.devEnv" title="测试" value="test" :link="true" href="/test">
<v-list-item v-show="isDevEnv" title="测试" value="test" :link="true" href="/test">
<template #prepend>
<v-icon>mdi-test-tube</v-icon>
</template>
</v-list-item>
<v-divider v-show="appStore.devEnv" />
<v-divider v-show="isDevEnv" />
<v-list-group value="wiki" :fluid="true">
<template #activator="{ props }">
<v-list-item title="图鉴" v-bind="props">
@@ -93,16 +93,47 @@
</v-list-item>
</v-list-group>
<div class="bottom-menu">
<v-list-item>
<template #prepend>
<img :src="userInfo.avatar" alt="userIcon" class="side-icon" />
<v-menu open-on-click location="end">
<template #activator="{ props }">
<v-list-item :title="userInfo.nickname" v-bind="props">
<template #prepend>
<img :src="userInfo.avatar" alt="userIcon" class="side-icon" />
</template>
</v-list-item>
</template>
<template #default>
<v-list-item-title>
{{ userInfo.nickname }}
</v-list-item-title>
</template>
</v-list-item>
<v-list class="side-list-user" density="compact" :nav="true">
<v-list-item class="side-item-user" title="签到" @click="openClient('sign_in')">
<template #prepend>
<img src="/source/UI/userGacha.webp" class="side-icon-user" alt="sing_in" />
</template>
</v-list-item>
<v-list-item class="side-item-user" title="战绩" @click="openClient('game_record')">
<template #prepend>
<img src="/source/UI/userRecord.webp" class="side-icon-user" alt="game_record" />
</template>
</v-list-item>
<v-list-item class="side-item-user" title="酒馆" @click="openClient('tavern')">
<template #prepend>
<img src="/platforms/mhy/mys.webp" alt="酒馆" class="side-icon-user" />
</template>
</v-list-item>
<v-list-item class="side-item-user" title="工具箱" @click="openClient('toolbox')">
<template #prepend>
<img src="/source/UI/toolbox.webp" alt="工具箱" class="side-icon-user" />
</template>
</v-list-item>
<v-list-item
class="side-item-user"
title="登录"
@click="login"
v-show="userStore.cookie?.game_token === ''"
>
<template #prepend>
<img src="/source/UI/defaultUser.webp" class="side-icon-user" alt="login" />
</template>
</v-list-item>
</v-list>
</v-menu>
<v-list-item :title="themeTitle" @click="switchTheme()">
<template #prepend>
<v-icon>
@@ -121,23 +152,32 @@
</template>
<script lang="ts" setup>
// vue
import { computed, onMounted, ref } from "vue";
// tauri
import { event } from "@tauri-apps/api";
// store
import { computed, onMounted, ref } from "vue";
import { useAppStore } from "../../store/modules/app";
import { useUserStore } from "../../store/modules/user";
import mhyClient from "../../utils/TGClient";
import showSnackbar from "../func/snackbar";
const appStore = useAppStore();
const userStore = useUserStore();
const isDevEnv = ref<boolean>(import.meta.env.MODE === "development");
const userInfo = computed(() => {
const info = userStore.getBriefInfo();
return {
nickname: info.nickname || "未登录",
avatar: info.avatar || "/source/UI/defaultUser.webp",
};
if (appStore.isLogin) {
const info = userStore.getBriefInfo();
return {
nickname: info.nickname,
avatar: info.avatar,
};
} else {
return {
nickname: "未登录",
avatar: "/source/UI/defaultUser.webp",
};
}
});
const rail = ref(appStore.sidebar.collapse);
// theme
@@ -169,6 +209,7 @@ function collapse(): void {
onMounted(async () => {
await listenOnTheme();
await mhyClient.run();
});
async function listenOnTheme(): Promise<void> {
@@ -181,6 +222,20 @@ async function listenOnTheme(): Promise<void> {
async function switchTheme(): Promise<void> {
await event.emit("readTheme", themeGet.value === "default" ? "dark" : "default");
}
async function openClient(func: string): Promise<void> {
if (appStore.isLogin) {
await mhyClient.open(func);
} else {
login();
}
}
function login(): void {
showSnackbar({
text: "请前往设置页面扫码登录",
});
}
</script>
<style lang="css" scoped>
@@ -207,10 +262,21 @@ async function switchTheme(): Promise<void> {
margin-right: 32px;
}
.side-icon-mini {
width: 36px;
height: 36px;
margin-right: 20px;
transform: translateX(-6px);
.side-list-user {
background: var(--app-side-bg) !important;
color: var(--app-side-content) !important;
font-family: var(--font-title);
}
.side-item-user {
border: 1px solid var(--common-shadow-2);
background: var(--box-bg-1);
}
.side-icon-user {
width: 20px;
height: 20px;
border-radius: 5px;
margin-right: 10px;
}
</style>

View File

@@ -8,11 +8,9 @@
</div>
</template>
<script lang="ts" setup>
// vue
import { computed, onMounted } from "vue";
// tauri
import { event } from "@tauri-apps/api";
// store
import { computed, onMounted } from "vue";
import { useAppStore } from "../../store/modules/app";
// store

View File

@@ -0,0 +1,84 @@
<template>
<div class="duc-dolb-box">
<div
v-for="constellation in constellations"
:key="constellation.pos"
:title="constellation.name"
class="duc-dolb-item"
>
<div v-if="!constellation.active" class="duc-dolb-lock">
<v-icon color="white">mdi-lock</v-icon>
</div>
<img class="duc-dolb-icon" :src="constellation.icon" alt="constellation" />
</div>
</div>
</template>
<script lang="ts" setup>
import { onMounted, onUpdated, ref } from "vue";
import { saveImgLocal } from "../../utils/TGShare";
interface DucDetailOlbProps {
modelValue: TGApp.Sqlite.Character.RoleConstellation[];
}
const props = defineProps<DucDetailOlbProps>();
const constellations = ref<TGApp.Sqlite.Character.RoleConstellation[]>([]);
async function loadData() {
const tempConstellations = props.modelValue;
for (const constellation of tempConstellations) {
if (constellation.icon.startsWith("blob:")) return;
constellation.icon = await saveImgLocal(constellation.icon);
}
constellations.value = tempConstellations;
}
onMounted(async () => {
await loadData();
});
onUpdated(async () => {
await loadData();
});
</script>
<style>
.duc-dolb-box {
display: flex;
align-items: center;
justify-content: center;
column-gap: 10px;
}
.duc-dolb-item {
position: relative;
display: flex;
width: 60px;
height: 60px;
align-items: center;
justify-content: center;
border-radius: 50%;
backdrop-filter: blur(5px);
background: rgb(0 0 0/40%);
}
.duc-dolb-lock {
position: absolute;
display: flex;
width: 54px;
height: 54px;
align-items: center;
justify-content: center;
padding: 3px;
border-radius: 50%;
backdrop-filter: blur(5px);
background-color: rgb(0 0 0 / 40%);
}
.duc-dolb-icon {
width: 50px;
height: 50px;
padding: 5px;
border-radius: 50%;
}
</style>

View File

@@ -0,0 +1,109 @@
<template>
<div class="ddo-lt-box">
<div class="ddo-ltb-icon" :title="getTitle">
<TItemBox :model-value="boxData" />
</div>
<div class="ddo-ltb-info">
<span>{{ props.data.name }}</span>
<span>Lv.{{ props.data.level }}</span>
<span>{{ info }}</span>
</div>
</div>
</template>
<script lang="ts" setup>
import { computed } from "vue";
import TItemBox, { TItemBoxData } from "../main/t-itembox.vue";
type DucDetailOltProps =
| {
data: TGApp.Sqlite.Character.UserRole;
mode: "avatar";
}
| {
data: TGApp.Sqlite.Character.RoleWeapon;
mode: "weapon";
};
const props = defineProps<DucDetailOltProps>();
const getTitle = computed(() => {
if (props.mode === "avatar") {
return `${props.data.name}`;
} else {
const descriptionList = props.data.description.split("");
return descriptionList.reduce((prev: string, cur: string, index: number) => {
if (index % 10 === 0) {
return `${prev}\n${cur}`;
} else {
return `${prev}${cur}`;
}
}, "");
}
});
const boxData = computed<TItemBoxData>(() => {
if (props.mode === "avatar") {
return {
bg: `/icon/bg/${props.data.star}-Star.webp`,
icon: `/WIKI/character/icon/${props.data.cid}.webp`,
size: "100px",
height: "100px",
display: "inner",
innerHeight: 0,
innerText: "",
clickable: false,
lt: `/icon/element/${props.data.element}.webp`,
ltSize: "30px",
};
} else {
return {
bg: `/icon/bg/${props.data.star}-Star.webp`,
icon: `/WIKI/weapon/icon/${props.data.id}.webp`,
size: "100px",
height: "100px",
display: "inner",
innerHeight: 0,
innerText: "",
clickable: false,
lt: `/icon/weapon/${props.data.type}.webp`,
ltSize: "30px",
};
}
});
const info = computed(() => {
if (props.mode === "avatar") {
return `好感 ${props.data.fetter}`;
} else {
return `精炼 ${props.data.affix}`;
}
});
</script>
<style lang="css" scoped>
.ddo-lt-box {
display: flex;
flex-direction: row;
align-items: flex-start;
justify-content: flex-start;
column-gap: 10px;
}
.ddo-ltb-info {
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: space-between;
color: var(--tgc-white-1);
text-align: left;
}
.ddo-ltb-info :nth-child(1) {
margin-bottom: 10px;
font-family: var(--font-title);
font-size: 20px;
}
.ddo-ltb-info :not(:nth-child(1)) {
font-family: var(--font-text);
font-size: 16px;
opacity: 0.8;
}
</style>

View File

@@ -0,0 +1,75 @@
<template>
<div class="duc-dort-box">
<div :title="talent.name" v-for="talent in talents" :key="talent.pos" class="duc-dort-item">
<span>{{ talent.name }}</span>
<img :src="talent.icon" alt="talent" />
<span>Lv.{{ talent.level }}</span>
</div>
</div>
</template>
<script lang="ts" setup>
import { onMounted, onUpdated, ref } from "vue";
import { saveImgLocal } from "../../utils/TGShare";
interface DucDetailOrtProps {
modelValue: TGApp.Sqlite.Character.RoleTalent[];
}
const props = defineProps<DucDetailOrtProps>();
const talents = ref<TGApp.Sqlite.Character.RoleTalent[]>([]);
async function loadData(): Promise<void> {
const tempTalent = props.modelValue;
for (const talent of tempTalent) {
if (talent.icon.startsWith("blob:")) return;
talent.icon = await saveImgLocal(talent.icon);
}
talents.value = tempTalent;
}
onMounted(async () => {
await loadData();
});
onUpdated(async () => {
await loadData();
});
</script>
<style lang="css" scoped>
.duc-dort-box {
display: flex;
flex-direction: column;
row-gap: 10px;
}
.duc-dort-item {
display: flex;
justify-content: flex-end;
column-gap: 10px;
}
.duc-dort-item img {
width: 40px;
height: 40px;
padding: 5px;
border-radius: 50%;
backdrop-filter: blur(5px);
background: rgba(0 0 0 /40%);
}
.duc-dort-item span {
display: flex;
align-items: center;
justify-content: center;
color: var(--tgc-white-1);
font-family: var(--font-title);
font-size: 16px;
text-shadow: 0 0 5px rgba(0 0 0/40%);
}
.duc-dort-item :nth-last-child(1) {
width: 48px;
justify-content: flex-start;
}
</style>

View File

@@ -0,0 +1,199 @@
<template>
<TOverlay v-model="visible" hide :to-click="onOverlayCancel" blur-val="20px">
<div class="duc-do-box">
<!-- 左侧箭头 -->
<div class="duc-arrow-left" @click="handleClick('left')">
<img src="../../assets/icons/arrow-right.svg" alt="left" />
</div>
<!-- 中间内容 -->
<div class="duc-do-container">
<img :src="nameCard" class="duc-doc-bg" v-if="nameCard !== false" alt="bg" />
<div class="duc-doc-bgc" />
<!-- 左上角色跟武器 -->
<div class="duc-doc-lt">
<DucDetailOlt :data="props.dataVal" mode="avatar" />
<DucDetailOlt :data="JSON.parse(props.dataVal.weapon)" mode="weapon" />
<v-btn
class="duc-doc-btn"
@click="share"
variant="outlined"
:loading="loading"
data-html2canvas-ignore
>
<v-icon>mdi-share-variant</v-icon>
<span>分享</span>
</v-btn>
</div>
<!-- 右侧天赋 -->
<div class="duc-doc-rt">
<DucDetailOrt :model-value="JSON.parse(props.dataVal.talent)" />
</div>
<!-- 左下命座 -->
<div class="duc-doc-lb">
<DucDetailOlb :model-value="JSON.parse(props.dataVal.constellation)" />
</div>
</div>
<!-- 右侧箭头 -->
<div class="duc-arrow-right" @click="handleClick('right')">
<img src="../../assets/icons/arrow-right.svg" alt="right" />
</div>
</div>
</TOverlay>
</template>
<script lang="ts" setup>
import { computed, onMounted, onUpdated, ref } from "vue";
import DucDetailOlb from "./duc-detail-olb.vue";
import DucDetailOlt from "./duc-detail-olt.vue";
import DucDetailOrt from "./duc-detail-ort.vue";
import TGSqlite from "../../plugins/Sqlite";
import { generateShareImg } from "../../utils/TGShare";
import TOverlay from "../main/t-overlay.vue";
interface DucDetailOverlayProps {
modelValue: boolean;
dataVal: TGApp.Sqlite.Character.UserRole;
}
type DucDetailOverlayEmits = {
(e: "update:modelValue", value: boolean): void;
(e: "clickL"): void;
(e: "clickR"): void;
};
const props = defineProps<DucDetailOverlayProps>();
const emits = defineEmits<DucDetailOverlayEmits>();
const visible = computed({
get: () => props.modelValue,
set: (value) => {
emits("update:modelValue", value);
},
});
// share
const loading = ref<boolean>(false);
// 渲染数据
const nameCard = ref<string | false>(false);
function onOverlayCancel() {
visible.value = false;
emits("update:modelValue", false);
}
function handleClick(pos: "left" | "right") {
pos === "left" ? emits("clickL") : emits("clickR");
}
onMounted(async () => {
await loadData();
});
onUpdated(async () => {
await loadData();
});
async function loadData(): Promise<void> {
if (!props.modelValue) return;
if (props.dataVal.cid !== 10000005 && props.dataVal.cid !== 10000007) {
const role = await TGSqlite.getAppCharacter(props.dataVal.cid);
nameCard.value = `/source/nameCard/profile/${role.nameCard}.webp`;
}
}
async function share(): Promise<void> {
const detailBox = <HTMLElement>document.querySelector(".duc-do-container");
const fileName = `【角色详情】-${props.dataVal.name}`;
loading.value = true;
await generateShareImg(fileName, detailBox);
loading.value = false;
}
</script>
<style lang="css" scoped>
.duc-do-box {
display: flex;
align-items: center;
justify-content: space-between;
column-gap: 10px;
}
.duc-arrow-left,
.duc-arrow-right {
position: relative;
display: flex;
width: 30px;
height: 30px;
align-items: center;
justify-content: center;
cursor: pointer;
}
.dark .duc-arrow-left,
.dark .duc-arrow-right {
filter: invert(11%) sepia(73%) saturate(11%) hue-rotate(139deg) brightness(97%) contrast(81%);
}
.duc-arrow-left img {
width: 100%;
height: 100%;
transform: rotate(180deg);
}
.duc-arrow-right img {
width: 100%;
height: 100%;
}
.duc-do-container {
position: relative;
overflow: hidden;
width: 800px;
border-radius: 5px;
aspect-ratio: 21 / 10;
background: var(--box-bg-1);
}
.duc-doc-bg {
position: absolute;
right: 0;
left: 0;
width: 100%;
height: 100%;
}
.duc-doc-bgc {
position: absolute;
right: 0;
left: 0;
width: 100%;
height: 100%;
background: rgb(0 0 0 / 20%);
}
.duc-doc-lt {
position: absolute;
top: 10px;
left: 10px;
display: flex;
flex-direction: column;
justify-content: space-between;
padding: 5px;
row-gap: 10px;
}
.duc-doc-btn {
color: var(--tgc-white-1);
}
.duc-doc-rt {
position: absolute;
top: 10px;
right: 10px;
padding: 5px;
}
.duc-doc-lb {
position: absolute;
bottom: 10px;
left: 10px;
padding: 5px;
}
</style>

View File

@@ -1,17 +1,29 @@
/**
* @file component func confirm.ts
* @description 封装自定义 confirm 组件,通过函数调用的方式,简化 confirm 的使用
* @author BTMuli <bt-muli@outlook.com>
* @since Alpha v0.2.3
* @since Beta v0.3.4
*/
// vue
import { h, render, type VNode } from "vue";
// confirm
import { h, render } from "vue";
import type { ComponentInternalInstance, VNode } from "vue";
import confirm from "./confirm.vue";
const confirmId = "tg-func-confirm";
/**
* @description 自定义 confirm 组件
* @since Beta v0.3.4
* @extends ComponentInternalInstance
* @property {Function} exposeProxy.displayBox 显示 confirm
* @return ConfirmInstance
*/
interface ConfirmInstance extends ComponentInternalInstance {
exposeProxy: {
displayBox: (props: TGApp.Component.Confirm.Params) => Promise<string | boolean>;
};
}
const renderBox = (props: TGApp.Component.Confirm.Params): VNode => {
const container = document.createElement("div");
container.id = confirmId;
@@ -23,14 +35,17 @@ const renderBox = (props: TGApp.Component.Confirm.Params): VNode => {
let confirmInstance: VNode;
const showConfirm = async (props: TGApp.Component.Confirm.Params): Promise<string | boolean> => {
if (confirmInstance) {
const boxVue = confirmInstance.component;
return boxVue?.exposeProxy?.displayBox(props);
async function showConfirm(props: TGApp.Component.Confirm.ParamsConfirm): Promise<boolean>;
async function showConfirm(props: TGApp.Component.Confirm.ParamsInput): Promise<string | false>;
async function showConfirm(props: TGApp.Component.Confirm.Params): Promise<string | boolean>;
async function showConfirm(props: TGApp.Component.Confirm.Params): Promise<string | boolean> {
if (confirmInstance !== undefined) {
const boxVue = <ConfirmInstance>confirmInstance.component;
return await boxVue.exposeProxy.displayBox(props);
} else {
confirmInstance = renderBox(props);
return await showConfirm(props);
}
};
}
export default showConfirm;

View File

@@ -3,29 +3,26 @@
<div v-show="show || showOuter" class="confirm-overlay" @click.self.prevent="handleOuter">
<transition name="func-confirm-inner">
<div v-show="showInner" class="confirm-box">
<div class="confirm-title">
{{ data.title }}
</div>
<div v-if="data?.text !== '' && data.mode === 'confirm'" class="confirm-subtitle">
<div class="confirm-title">{{ data.title }}</div>
<div
v-show="data?.text !== '' && data.mode === 'confirm'"
class="confirm-subtitle"
:title="data.text"
>
{{ data.text }}
</div>
<div v-if="data?.text !== '' && data.mode === 'input'" class="confirm-input">
<div class="confirm-input-label">
{{ data.text }}
</div>
<input v-model="inputVal" class="confirm-input-box" />
<div v-show="data?.text !== '' && data.mode === 'input'" class="confirm-input">
<div class="confirm-input-label">{{ data.text }}</div>
<input
v-model="inputVal"
class="confirm-input-box"
ref="inputRef"
@keydown.enter="handleClick(true)"
/>
</div>
<div class="confirm-btn-box">
<button class="confirm-btn" @click="handleClick(false)">取消</button>
<button
class="confirm-btn"
:style="{
backgroundColor: 'var(--tgc-dark-2)',
}"
@click="handleClick(true)"
>
确定
</button>
<button class="confirm-btn no-btn" @click="handleClick(false)">取消</button>
<button class="confirm-btn ok-btn" @click="handleClick(true)">确定</button>
</div>
</div>
</transition>
@@ -34,8 +31,7 @@
</template>
<script lang="ts" setup>
// vue
import { onMounted, reactive, ref, watch } from "vue";
import { nextTick, onMounted, reactive, ref, watch } from "vue";
interface ConfirmProps {
title: string;
@@ -60,6 +56,7 @@ const showOuter = ref<boolean>(false);
const showInner = ref<boolean>(false);
const confirmVal = ref<boolean | string>(false);
const inputVal = ref<string>("");
const inputRef = ref<HTMLInputElement>();
watch(show, () => {
if (show.value) {
@@ -89,6 +86,14 @@ async function displayBox(params: TGApp.Component.Confirm.Params): Promise<strin
show.value = true;
// 等待确认框关闭返回关闭后的confirmVal
return await new Promise<string | boolean>((resolve) => {
nextTick(() => {
if (data.mode === "input") {
// 等待确认框打开,聚焦输入框
setTimeout(() => {
inputRef.value?.focus();
}, 100);
}
});
watch(show, () => {
// 等 0.5s 动画
setTimeout(() => {
@@ -175,7 +180,16 @@ defineExpose({
align-items: center;
justify-content: center;
backdrop-filter: blur(10px);
background: var(--common-shadow-t-1);
/* 颜色变量 */
--confirm-title: var(--tgc-dark-7);
--confirm-bg: var(--tgc-white-1);
}
/* 深色模式 */
.dark .confirm-overlay {
--confirm-title: var(--tgc-white-1);
--confirm-bg: var(--tgc-dark-7);
}
.confirm-box {
@@ -187,25 +201,30 @@ defineExpose({
justify-content: space-between;
padding: 10px;
border: 1px solid var(--common-shadow-1);
border-radius: 5px;
background: var(--box-bg-4);
color: var(--box-text-1);
border-radius: 15px;
background: var(--confirm-bg);
box-shadow: 0 0 10px var(--common-shadow-t-1);
color: var(--tgc-yellow-3);
}
.confirm-title {
width: 100%;
border-bottom: 1px solid var(--common-shadow-4);
border-bottom: 1px solid var(--confirm-title);
color: var(--confirm-title);
font-family: var(--font-title);
font-size: 30px;
text-align: center;
}
.confirm-subtitle {
overflow: hidden;
width: 100%;
color: var(--box-text-4);
font-family: var(--font-text);
font-size: 20px;
text-align: center;
text-overflow: ellipsis;
white-space: nowrap;
word-break: break-all;
}
.confirm-input {
@@ -222,10 +241,10 @@ defineExpose({
width: 50%;
height: 100%;
padding: 5px;
border: 1px solid var(--common-shadow-4);
border: 1px solid var(--confirm-title);
border-radius: 5px;
background: inherit;
color: var(--box-text-1);
color: var(--confirm-title);
}
.confirm-btn-box {
@@ -242,11 +261,17 @@ defineExpose({
height: 60px;
align-items: center;
justify-content: center;
border: 1px solid var(--common-shadow-4);
border-radius: 5px;
color: var(--btn-text);
border-radius: 15px;
cursor: pointer;
font-family: var(--font-title);
font-size: 20px;
}
.no-btn {
border: 1px solid var(--tgc-yellow-1);
}
.ok-btn {
background: var(--confirm-title);
}
</style>

View File

@@ -1,17 +1,29 @@
/**
* @file component func snackbar.ts
* @description 封装 vuetify 的 snackbar 组件,通过函数调用的方式,简化 snackbar 的使用
* @author BTMuli <bt-muli@outlook.com>
* @since Alpha v0.2.3
* @since Beta v0.3.4
*/
// vue
import { h, render, type VNode } from "vue";
// snackbar
import type { ComponentInternalInstance, VNode } from "vue";
import { h, render } from "vue";
import snackbar from "./snackbar.vue";
const snackbarId = "tg-func-snackbar";
/**
* @description 自定义 snackbar 组件
* @since Beta v0.3.3
* @extends ComponentInternalInstance
* @property {Function} exposeProxy.displayBox 显示 snackbar
* @return SnackbarInstance
*/
interface SnackbarInstance extends ComponentInternalInstance {
exposeProxy: {
displayBox: (props: TGApp.Component.Snackbar.Params) => void;
};
}
const renderBox = (props: TGApp.Component.Snackbar.Params): VNode => {
const container = document.createElement("div");
container.id = snackbarId;
@@ -23,14 +35,14 @@ const renderBox = (props: TGApp.Component.Snackbar.Params): VNode => {
let snackbarInstance: VNode;
const showSnackbar = (props: TGApp.Component.Snackbar.Params): void => {
if (snackbarInstance) {
const boxVue = snackbarInstance.component;
boxVue?.exposeProxy?.displayBox(props);
function showSnackbar(props: TGApp.Component.Snackbar.Params): void {
if (snackbarInstance !== undefined) {
const boxVue = <SnackbarInstance>snackbarInstance.component;
boxVue.exposeProxy.displayBox(props);
} else {
snackbarInstance = renderBox(props);
showSnackbar(props);
}
};
}
export default showSnackbar;

View File

@@ -1,15 +1,16 @@
<template>
<transition name="func-snackbar">
<div v-show="show" class="func-snackbar" :style="{ backgroundColor: data.color }">
<slot name="text">
<span class="func-snackbar-text">{{ data.text }}</span>
</slot>
<div class="func-snackbar-container" v-show="show">
<div class="func-snackbar" :style="{ backgroundColor: data.color }">
<slot name="text">
<span class="func-snackbar-text">{{ data.text }}</span>
</slot>
</div>
</div>
</transition>
</template>
<script lang="ts" setup>
// vue
import { ref, reactive, onMounted } from "vue";
import { onMounted, reactive, ref } from "vue";
interface SnackbarProps {
text: string;
@@ -41,13 +42,13 @@ function transColor(color: string): string {
}
switch (color) {
case "success":
return "#41b883";
return "#7ebd21";
case "error":
return "#ff4d4f";
return "#e63f5a";
case "warn":
return "#faad14";
case "info":
return "#1890ff";
return "#ecb349";
case "cancel":
return "#cdcbc4";
default:
return color;
}
@@ -77,20 +78,28 @@ defineExpose({
.func-snackbar-enter-from,
.func-snackbar-leave-to {
opacity: 0;
transform: translateX(-50%) translateY(20px);
transform: translateY(20px);
}
.func-snackbar-enter-to,
.func-snackbar-leave-from {
opacity: 1;
transform: translateX(-50%) translateY(0);
transform: translateY(0);
}
.func-snackbar-container {
position: fixed;
z-index: 999;
bottom: 0;
left: 0;
display: flex;
width: 100%;
height: 80px;
align-items: center;
justify-content: center;
}
.func-snackbar {
position: fixed;
z-index: 999;
bottom: 20px;
left: 50%;
display: flex;
min-width: 200px;
height: 40px;
@@ -99,10 +108,9 @@ defineExpose({
padding: 10px 20px;
border-radius: 5px;
box-shadow: 0 0 10px rgb(0 0 0 / 20%);
transform: translateX(-50%);
}
.func-snackbar .func-snackbar-text {
.func-snackbar-text {
color: #fff;
font-size: 16px;
font-weight: 500;

View File

@@ -2,22 +2,22 @@
<v-chart :option="getPoolData()" autoresize />
</template>
<script lang="ts" setup>
// vue
import { onMounted, provide } from "vue";
import VChart, { THEME_KEY } from "vue-echarts";
import showSnackbar from "../func/snackbar";
// echarts
import { use } from "echarts/core";
import type { EChartsOption } from "echarts";
import { PieChart } from "echarts/charts";
import {
LegendComponent,
TitleComponent,
TooltipComponent,
ToolboxComponent,
} from "echarts/components";
import { PieChart } from "echarts/charts";
import { use } from "echarts/core";
import { LabelLayout } from "echarts/features";
import { CanvasRenderer } from "echarts/renderers";
import { type EChartsOption } from "echarts";
import { onMounted, provide } from "vue";
import VChart, { THEME_KEY } from "vue-echarts";
import showSnackbar from "../func/snackbar";
// echarts
use([
TitleComponent,

View File

@@ -12,8 +12,8 @@
</div>
</template>
<script lang="ts" setup>
// vue
import { watch } from "vue";
import GroDataview from "./gro-dataview.vue";
interface GachaOverviewProps {

View File

@@ -2,9 +2,21 @@
<div class="calendar-box">
<div class="calendar-title">
<div class="calendar-title-left">
<v-icon size="small"> mdi-calendar-clock</v-icon>
<v-icon size="small" style="opacity: 0.8">mdi-calendar-clock</v-icon>
<span>今日素材</span>
<span>{{ dateNow }}</span>
<!-- 如果是某人生日礼物图标颜色为红色 -->
<span
v-if="birthInfo.isLogin"
@click="toBirthday"
class="calendar-title-gift"
:style="{
color: birthInfo.active ? 'var(--tgc-red-1)' : 'inherit',
}"
:title="birthInfo.text"
>
<v-icon size="small">mdi-gift</v-icon>
</span>
</div>
<div class="calendar-title-mid">
<v-btn
@@ -25,14 +37,14 @@
<div class="calendar-title-right">
<v-switch
class="calendar-title-switch"
color="grey"
color="var(--common-shadow-4)"
variant="outline"
:label="switchType === 'avatar' ? '角色' : '武器'"
@change="switchType = switchType === 'avatar' ? 'weapon' : 'avatar'"
/>
<v-btn class="calendar-title-btn" @click="share">
<v-btn class="calendar-title-btn" @click="share" data-html2canvas-ignore>
<template #prepend>
<v-icon> mdi-share-variant</v-icon>
<v-icon>mdi-share-variant</v-icon>
</template>
<span>分享</span>
</v-btn>
@@ -61,14 +73,15 @@
</div>
</template>
<script lang="ts" setup>
// vue
import { computed, onMounted, ref } from "vue";
import ToCalendar from "../overlay/to-calendar.vue";
import TibCalendarItem from "../itembox/tib-calendar-item.vue";
// data
import { AppCalendarData } from "../../data";
// utils
import TGSqlite from "../../plugins/Sqlite";
import { useAppStore } from "../../store/modules/app";
import TGClient from "../../utils/TGClient";
import { generateShareImg } from "../../utils/TGShare";
import TibCalendarItem from "../itembox/tib-calendar-item.vue";
import ToCalendar from "../overlay/to-calendar.vue";
// loading
const loading = ref<boolean>(true);
@@ -90,6 +103,13 @@ const switchType = ref<string>("avatar");
const selectedItem = ref<TGApp.App.Calendar.Item>(<TGApp.App.Calendar.Item>{});
const selectedType = ref<"avatar" | "weapon">("avatar");
// birthday
const birthInfo = ref({
isLogin: true,
active: false,
text: "点击前往留影叙佳期",
});
const btnText = [
{
week: 7,
@@ -127,7 +147,17 @@ defineExpose({
loading,
});
onMounted(() => {
onMounted(async () => {
const appStore = useAppStore();
if (appStore.isLogin) {
const birthRes = await TGSqlite.isBirthday();
if (birthRes !== false) {
birthInfo.value.active = true;
birthInfo.value.text = `今天是 ${birthRes} 的生日!`;
}
} else {
birthInfo.value.isLogin = false;
}
const dayNow = new Date().getDay() === 0 ? 7 : new Date().getDay();
const week = <{ week: number; text: string }>btnText.find((item) => item.week === dayNow);
dateNow.value =
@@ -171,12 +201,16 @@ function getContents(day: number): void {
}
async function share(): Promise<void> {
// todo 唤起外部 loading
const div = <HTMLElement>document.querySelector(".calendar-box");
const showType = switchType.value === "avatar" ? "角色" : "武器";
const title = `【今日素材】${showType}${btnNow.value}`;
await generateShareImg(title, div);
}
// 前往留影叙佳期
async function toBirthday(): Promise<void> {
await TGClient.open("birthday");
}
</script>
<style lang="css" scoped>
.calendar-box {
@@ -204,6 +238,13 @@ async function share(): Promise<void> {
column-gap: 10px;
}
.calendar-title-gift {
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
}
.calendar-title-mid {
display: flex;
align-items: center;

View File

@@ -1,12 +1,24 @@
<template>
<div class="pool-box">
<div class="pool-title">
<img src="../../assets/icons/icon-wish.svg" alt="wish" />
限时祈愿
<div class="pool-title-left">
<img src="../../assets/icons/icon-wish.svg" alt="wish" />
<span>限时祈愿</span>
</div>
<div class="pool-title-right">
<v-switch
class="pool-switch"
color="var(--common-shadow-4)"
variant="outline"
:label="showNew ? '查看当前祈愿' : '查看后续祈愿'"
v-show="hasNew"
@change="switchPool"
/>
</div>
</div>
<div v-if="!loading" class="pool-grid">
<div v-for="pool in poolCards" :key="pool.postId" class="pool-card">
<div class="pool-cover" @click="toPost(pool)">
<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" />
</div>
<div class="pool-bottom">
@@ -25,7 +37,7 @@
<div class="pool-time">
<div>
<v-icon>mdi-calendar-clock</v-icon>
{{ pool.time.start }}~{{ pool.time.end }}
{{ pool.time.str }}
</div>
<v-progress-linear :model-value="poolTimePass[pool.postId]" :rounded="true">
</v-progress-linear>
@@ -48,19 +60,12 @@
</div>
</template>
<script lang="ts" setup>
// vue
import { ref, onMounted, onUnmounted } from "vue";
import { useRouter } from "vue-router";
// store
import { useHomeStore } from "../../store/modules/home";
// utils
import { createTGWindow } from "../../utils/TGWindow";
import { stamp2LastTime } from "../../utils/toolFunc";
// plugins
import Mys from "../../plugins/Mys";
// vue
const router = useRouter();
import Mys from "../../plugins/Mys";
import { useHomeStore } from "../../store/modules/home";
import { createPost, createTGWindow } from "../../utils/TGWindow";
import { stamp2LastTime } from "../../utils/toolFunc";
// store
const homeStore = useHomeStore();
@@ -72,8 +77,12 @@ const showBar = ref<boolean>(false);
const barText = ref<string>("");
const barColor = ref<string>("error");
const hasNew = ref<boolean>(false);
const showNew = ref<boolean>(false);
// data
const poolCards = ref<TGApp.Plugins.Mys.Gacha.RenderCard[]>([]);
const poolSelect = ref<TGApp.Plugins.Mys.Gacha.RenderCard[]>([]);
const poolTimeGet = ref<Record<number, string>>({});
const poolTimePass = ref<Record<number, number>>({});
const timer = ref<Record<number, any>>({});
@@ -131,6 +140,7 @@ onMounted(async () => {
if (poolTimePass.value[pool.postId] <= 0) {
poolTimeGet.value[pool.postId] = "已结束";
poolTimePass.value[pool.postId] = 100;
showNew.value = false;
} else if (pool.time.startStamp - Date.now() > 0) {
poolTimeGet.value[pool.postId] = "未开始";
poolTimePass.value[pool.postId] = 100;
@@ -140,6 +150,17 @@ onMounted(async () => {
}, 1000);
return pool;
});
if (poolCards.value.length > 2) {
poolSelect.value = poolCards.value.filter(
(pool) =>
poolTimeGet.value[pool.postId] !== "未开始" && poolTimeGet.value[pool.postId] !== "已结束",
);
hasNew.value =
poolCards.value.filter((pool) => poolTimeGet.value[pool.postId] === "未开始").length > 0;
} else {
poolSelect.value = poolCards.value;
hasNew.value = false;
}
loading.value = false;
});
@@ -178,14 +199,19 @@ async function toOuter(url: string, title: string): Promise<void> {
createTGWindow(url, "Sub_window", `Pool_${title}`, 1200, 800, true, true);
}
function toPost(pool: TGApp.Plugins.Mys.Gacha.RenderCard): void {
const path = router.resolve({
name: "帖子详情",
params: {
post_id: pool.postId.toString(),
},
}).href;
createTGWindow(path, "Sub_window", `Post_${pool.postId} ${pool.title}`, 960, 720, false, false);
// 更换显示的卡池
async function switchPool(): Promise<void> {
showNew.value = !showNew.value;
if (showNew.value) {
poolSelect.value = poolCards.value.filter(
(pool) => poolTimeGet.value[pool.postId] === "未开始",
);
} else {
poolSelect.value = poolCards.value.filter(
(pool) =>
poolTimeGet.value[pool.postId] !== "未开始" && poolTimeGet.value[pool.postId] !== "已结束",
);
}
}
onUnmounted(() => {
@@ -207,32 +233,55 @@ onUnmounted(() => {
.pool-title {
display: flex;
color: var(--common-text-title);
align-items: center;
justify-content: space-between;
font-family: var(--font-title);
font-size: 20px;
}
.pool-title img {
.pool-title-left {
display: flex;
align-items: center;
justify-content: start;
color: var(--common-text-title);
column-gap: 10px;
}
.pool-title-left img {
width: 25px;
height: 25px;
border-radius: 50%;
margin-right: 10px;
background: var(--common-shadow-4);
transform: translate(0, 2px);
filter: brightness(0.8);
}
.pool-title-right {
display: flex;
align-items: center;
justify-content: start;
gap: 15px;
}
.pool-switch {
display: flex;
height: 36px;
align-items: center;
justify-content: center;
color: var(--box-text-1);
}
.pool-grid {
display: flex;
display: grid;
align-items: center;
justify-content: space-between;
gap: 10px;
grid-template-columns: repeat(2, 1fr);
}
.pool-card {
position: relative;
overflow: hidden;
width: 50%;
width: 100%;
border-radius: 5px;
aspect-ratio: 69 / 32;
box-shadow: 0 5px 5px var(--common-shadow-4);
}

View File

@@ -15,12 +15,14 @@
<v-list class="position-list">
<v-list-item :title="card.title" :subtitle="card.abstract">
<template #prepend>
<v-avatar rounded="0" @click="toPost(card)">
<v-avatar rounded="0" @click="createPost(card.postId, card.title)">
<v-img :src="card.icon" class="position-icon" />
</v-avatar>
</template>
<template #append>
<v-btn class="position-card-btn" @click="toPost(card)"> 查看 </v-btn>
<v-btn class="position-card-btn" @click="createPost(card.postId, card.title)">
查看
</v-btn>
</template>
</v-list-item>
</v-list>
@@ -32,7 +34,6 @@
</div>
<div class="position-card-text">
<v-icon>mdi-clock-outline</v-icon>
<span>剩余时间</span>
<span v-if="positionTimeGet[card.postId] !== '已结束'">{{
positionTimeGet[card.postId]
}}</span>
@@ -44,17 +45,11 @@
</div>
</template>
<script lang="ts" setup>
// vue
import { ref, onMounted, onUnmounted } from "vue";
import { useRouter } from "vue-router";
// utils
import { createTGWindow } from "../../utils/TGWindow";
import { stamp2LastTime } from "../../utils/toolFunc";
// plugins
import Mys from "../../plugins/Mys";
// vue
const router = useRouter();
import Mys from "../../plugins/Mys";
import { createPost } from "../../utils/TGWindow";
import { stamp2LastTime } from "../../utils/toolFunc";
// loading
const loading = ref<boolean>(true);
@@ -107,18 +102,6 @@ onMounted(async () => {
loading.value = false;
});
async function toPost(card: TGApp.Plugins.Mys.Position.RenderCard): Promise<void> {
// 获取路由路径
const path = router.resolve({
name: "帖子详情",
params: {
post_id: card.postId,
},
}).href;
// 打开新窗口
createTGWindow(path, "Sub_window", `Post_${card.postId} ${card.title}`, 960, 720, false, false);
}
onUnmounted(() => {
Object.keys(positionTimer.value).forEach((key) => {
clearInterval(positionTimer.value[Number(key)]);
@@ -146,6 +129,7 @@ onUnmounted(() => {
width: 20px;
height: 20px;
margin: 0 10px;
filter: brightness(0.9);
}
.position-grid {
@@ -198,5 +182,11 @@ onUnmounted(() => {
display: inline-block;
min-width: 200px;
align-items: flex-start;
margin-right: 5px;
}
.position-card-text :nth-child(1) {
color: var(--btn-text);
filter: brightness(0.8);
}
</style>

View File

@@ -29,8 +29,8 @@
</TOverlay>
</template>
<script lang="ts" setup>
// vue
import { computed } from "vue";
import TOverlay from "../main/t-overlay.vue";
interface HtaOverlayOverviewProps {

View File

@@ -45,8 +45,8 @@
</div>
</template>
<script lang="ts" setup>
// vue
import { onMounted, ref } from "vue";
import TibWikiAbyss2 from "../itembox/tib-wiki-abyss-2.vue";
interface HtaTabTeamProps {
@@ -104,14 +104,13 @@ onMounted(async () => {
.hta-tuf-box {
display: flex;
overflow: hidden auto;
width: 100%;
max-height: 100%;
flex-direction: column;
align-items: center;
justify-content: center;
border-radius: 5px;
overflow-x: hidden;
overflow-y: auto;
row-gap: 10px;
}

View File

@@ -20,8 +20,8 @@
</div>
</template>
<script lang="ts" setup>
// vue
import { onMounted, ref } from "vue";
import TibWikiAbyss from "../itembox/tib-wiki-abyss.vue";
interface HtaTabUseProps {

View File

@@ -20,8 +20,8 @@
</div>
</template>
<script lang="ts" setup>
// vue
import { onMounted, ref } from "vue";
import TibWikiAbyss from "../itembox/tib-wiki-abyss.vue";
interface HtaTabUseProps {

View File

@@ -2,12 +2,10 @@
<TItemBox :model-value="box" />
</template>
<script lang="ts" setup>
// vue
import { onMounted, ref } from "vue";
import TItemBox from "../main/t-itembox.vue";
// utils
import TGSqlite from "../../plugins/Sqlite";
// types
import TItemBox from "../main/t-itembox.vue";
import type { TItemBoxData } from "../main/t-itembox.vue";
interface TibAbyssDetailProps {

View File

@@ -1,13 +1,11 @@
<template>
<TItemBox :model-value="box" />
<TItemBox v-model="box" />
</template>
<script lang="ts" setup>
// vue
import { onMounted, ref } from "vue";
import TItemBox from "../main/t-itembox.vue";
// utils
import TGSqlite from "../../plugins/Sqlite";
// types
import TItemBox from "../main/t-itembox.vue";
import type { TItemBoxData } from "../main/t-itembox.vue";
interface TibAbyssOverviewProps {

View File

@@ -2,10 +2,9 @@
<TItemBox :model-value="box" />
</template>
<script lang="ts" setup>
// vue
import { computed } from "vue";
import { onMounted, ref } from "vue";
import TItemBox from "../main/t-itembox.vue";
// types
import type { TItemBoxData } from "../main/t-itembox.vue";
interface TibCalendarItemProps {
@@ -15,33 +14,48 @@ interface TibCalendarItemProps {
}
const props = defineProps<TibCalendarItemProps>();
const box = computed<TItemBoxData>(() => {
const box = ref<TItemBoxData>({
bg: "",
icon: "",
size: "100px",
height: "100px",
display: "inner",
clickable: false,
lt: "",
ltSize: "30px",
innerHeight: 25,
innerIcon: "",
innerText: "",
});
onMounted(() => {
if (props.model === "avatar") {
return {
box.value = {
bg: props.data.bg,
icon: props.data.icon,
size: "100px",
height: "100px",
display: "inner",
clickable: props.clickable,
lt: props.data.elementIcon,
lt: props.data.elementIcon ?? "",
ltSize: "30px",
innerHeight: 25,
innerIcon: props.data.weaponIcon,
innerText: props.data.name,
};
} else {
box.value = {
bg: props.data.bg,
icon: props.data.icon,
size: "100px",
height: "100px",
display: "inner",
clickable: props.clickable,
lt: props.data.weaponIcon,
ltSize: "30px",
innerHeight: 25,
innerText: props.data.name,
};
}
return {
bg: props.data.bg,
icon: props.data.icon,
size: "100px",
height: "100px",
display: "inner",
clickable: props.clickable,
lt: props.data.weaponIcon,
ltSize: "30px",
innerHeight: 25,
innerText: props.data.name,
};
});
</script>

View File

@@ -1,11 +1,10 @@
<template>
<TItemBox v-model="box" @click="showData" />
<TItemBox v-model="box" :title="hoverTitle" />
</template>
<script lang="ts" setup>
// vue
import { onMounted, ref } from "vue";
import TItemBox from "../main/t-itembox.vue";
// types
import type { TItemBoxData } from "../main/t-itembox.vue";
interface TibUrAvatarProps {
@@ -14,12 +13,13 @@ interface TibUrAvatarProps {
const props = defineProps<TibUrAvatarProps>();
const box = ref<TItemBoxData>(<TItemBoxData>{});
const hoverTitle = ref<string>("点击查看详情");
const getName = (): string => {
return props.modelValue.id === 10000005
? "旅行者-空"
: props.modelValue.id === 10000007
? "旅行者-荧"
: props.modelValue.name;
? "旅行者-荧"
: props.modelValue.name;
};
onMounted(async () => {
@@ -37,10 +37,6 @@ onMounted(async () => {
innerHeight: 20,
display: "inner",
};
hoverTitle.value = `等级:${props.modelValue.level}\n好感${props.modelValue.fetter}\n角色ID${props.modelValue.id}`;
});
function showData(): void {
// todo @click 应该出来的是一个弹窗,而不是 console
console.log(props.modelValue);
}
</script>

View File

@@ -2,11 +2,10 @@
<TItemBox :model-value="box" />
</template>
<script setup lang="ts">
// vue
import { ref, onMounted, computed } from "vue";
import TItemBox, { type TItemBoxData } from "../main/t-itembox.vue";
// plugins
import TGSqlite from "../../plugins/Sqlite";
import TItemBox, { type TItemBoxData } from "../main/t-itembox.vue";
interface TibWikiAbyssProps {
modelValue: string;

View File

@@ -2,11 +2,10 @@
<TItemBox :model-value="box" />
</template>
<script setup lang="ts">
// vue
import { ref, onMounted, computed } from "vue";
import TItemBox, { type TItemBoxData } from "../main/t-itembox.vue";
// plugins
import TGSqlite from "../../plugins/Sqlite";
import TItemBox, { type TItemBoxData } from "../main/t-itembox.vue";
interface TibWikiAbyssProps {
modelValue: {

View File

@@ -2,8 +2,8 @@
<TItemBox :model-value="box" />
</template>
<script lang="ts" setup>
// vue
import { computed } from "vue";
import TItemBox, { type TItemBoxData } from "../main/t-itembox.vue";
interface TibCalendarAvatarProps {

View File

@@ -2,10 +2,9 @@
<TItemBox :model-value="box" style="cursor: pointer" />
</template>
<script lang="ts" setup>
// vue
import { computed } from "vue";
import TItemBox from "../main/t-itembox.vue";
// types
import type { TItemBoxData } from "../main/t-itembox.vue";
interface TibCalendarWeaponProps {

View File

@@ -211,7 +211,10 @@ const props = defineProps<TItemBoxProps>();
.tib-outer {
position: absolute;
bottom: 0;
display: flex;
width: 100%;
align-items: center;
justify-content: center;
color: var(--common-text-title);
text-align: center;
}

View File

@@ -35,16 +35,14 @@
</TOverlay>
</template>
<script setup lang="ts">
// vue
import { computed } from "vue";
import Mys from "../../plugins/Mys";
import { createTGWindow } from "../../utils/TGWindow";
import showSnackbar from "../func/snackbar";
import TOverlay from "../main/t-overlay.vue";
import TibCalendarItem from "../itembox/tib-calendar-item.vue";
import TibCalendarMaterial from "../itembox/tib-calendar-material.vue";
// utils
import { createTGWindow } from "../../utils/TGWindow";
// plugins
import Mys from "../../plugins/Mys";
import TOverlay from "../main/t-overlay.vue";
interface ToCalendarProps {
modelValue: boolean;
@@ -76,7 +74,7 @@ const onCancel = (): void => {
function toDetail(item: TGApp.App.Calendar.Item): void {
if (item.contentId === 0) {
const itemType = item.itemType === "avatar" ? "角色" : "武器";
const itemType = item.itemType === "character" ? "角色" : "武器";
showSnackbar({
text: `[${itemType}] ${item.name} 暂无详情`,
color: "warn",
@@ -145,10 +143,13 @@ function toDetail(item: TGApp.App.Calendar.Item): void {
.toc-src-box :nth-child(2) {
height: 30px;
border-radius: 50%;
margin: 5px;
aspect-ratio: 1;
background: var(--common-shadow-4);
filter: invert(87%) sepia(14%) saturate(216%) hue-rotate(180deg) brightness(81%) contrast(87%);
}
.dark .toc-src-box :nth-child(2) {
filter: none;
}
.toc-src-text {

View File

@@ -24,9 +24,9 @@
</TOverlay>
</template>
<script lang="ts" setup>
// vue
import { computed } from "vue";
import { useRouter } from "vue-router";
import TOverlay from "../main/t-overlay.vue";
interface ToChannelProps {

View File

@@ -17,17 +17,15 @@
</TOverlay>
</template>
<script setup lang="ts">
// vue
import QrcodeVue from "qrcode.vue";
import { computed, reactive, ref, watch } from "vue";
import Mys from "../../plugins/Mys";
import TGSqlite from "../../plugins/Sqlite";
import { useUserStore } from "../../store/modules/user";
import TGRequest from "../../web/request/TGRequest";
import showSnackbar from "../func/snackbar";
import TOverlay from "../main/t-overlay.vue";
import QrcodeVue from "qrcode.vue";
// store
import { useUserStore } from "../../store/modules/user";
// utils
import Mys from "../../plugins/Mys";
import TGRequest from "../../web/request/TGRequest";
import TGSqlite from "../../plugins/Sqlite";
interface ToWebLoginProps {
modelValue: boolean;

View File

@@ -24,8 +24,8 @@
</TOverlay>
</template>
<script lang="ts" setup>
// vue
import { ref, watch } from "vue";
import TOverlay from "../main/t-overlay.vue";
interface LoadingProps {

View File

@@ -10,9 +10,8 @@
</div>
</template>
<script lang="ts" setup>
// vue
import TuaDetailTitle from "./tua-detail-title.vue";
import TuaDetailBattle from "./tua-detail-battle.vue";
import TuaDetailTitle from "./tua-detail-title.vue";
interface TuaDetailLevelProps {
modelValue: TGApp.Sqlite.Abyss.Level;

View File

@@ -15,8 +15,8 @@
</div>
</template>
<script lang="ts" setup>
import TuaDetailTitle from "./tua-detail-title.vue";
import TuaDetailLevel from "./tua-detail-level.vue";
import TuaDetailTitle from "./tua-detail-title.vue";
interface TuaDetailProps {
modelValue: TGApp.Sqlite.Abyss.Floor;

View File

@@ -22,8 +22,8 @@
</div>
</template>
<script lang="ts" setup>
// vue
import { computed } from "vue";
import TibAbyssOverview from "../itembox/tib-abyss-overview.vue";
interface TAOProps {

View File

@@ -24,9 +24,8 @@
</TucDetailDesc>
</template>
<script lang="ts" setup>
// vue
import TucDetailDesc from "./tuc-detail-desc.vue";
import TucDetailConstellation from "./tuc-detail-constellation.vue";
import TucDetailDesc from "./tuc-detail-desc.vue";
interface TucDetailDescConstellationProps {
modelValue: TGApp.Sqlite.Character.RoleConstellation;
@@ -38,10 +37,10 @@ const props = defineProps<TucDetailDescConstellationProps>();
function parseDesc(desc: string): string {
const reg = /<color=(.*?)>(.*?)<\/color>/g;
let match = reg.exec(desc);
while (match) {
while (match !== null) {
const color = match[1];
const text = match[2];
desc = desc.replace(match[0], `<span style="color: ${color}">${text}</span>`);
desc = desc.replace(match[0], `<span title="${text}" style="color: ${color};">${text}</span>`);
match = reg.exec(desc);
}
desc = desc.replace(/\\n/g, "<br />");
@@ -61,11 +60,12 @@ function parseDesc(desc: string): string {
.tuc-ddc-top {
height: 20px;
color: var(--tgc-blue-1);
color: var(--box-text-3);
}
.tuc-ddc-bottom {
height: 20px;
color: var(--box-text-1);
}
.tuc-ddc-bottom :nth-child(1) {

View File

@@ -48,7 +48,7 @@ const props = defineProps<TucDetailDescRelicProps>();
.tuc-ddrc-top {
height: 20px;
color: var(--tgc-dark-1);
color: var(--box-text-1);
}
.tuc-ddrc-top :nth-child(1) {
@@ -70,7 +70,7 @@ const props = defineProps<TucDetailDescRelicProps>();
}
.tuc-ddrd-title {
color: var(--tgc-dark-1);
color: var(--box-text-3);
font-family: var(--font-title);
font-size: 16px;
}

View File

@@ -24,8 +24,8 @@
</TucDetailDesc>
</template>
<script lang="ts" setup>
// vue
import { computed } from "vue";
import TucDetailDesc from "./tuc-detail-desc.vue";
import TucDetailItemBox from "./tuc-detail-itembox.vue";
@@ -49,7 +49,7 @@ const box = computed(() => {
align-items: start;
justify-content: space-around;
margin-left: 5px;
color: var(--tgc-dark-1);
color: var(--box-text-1);
}
.tuc-ddwc-top {

View File

@@ -15,14 +15,14 @@
<style lang="css" scoped>
.tuc-dd-box {
padding: 10px;
border: 1px solid rgb(0 0 0 /40%);
border: 1px solid var(--common-shadow-2);
border-radius: 5px;
background: var(--tgc-white-1);
background: var(--box-bg-2);
}
.tuc-dd-title {
width: 100%;
color: var(--tgc-dark-1);
color: var(--common-text-title);
font-family: var(--font-title);
font-size: 20px;
text-align: left;
@@ -32,8 +32,7 @@
width: 100%;
height: 1px;
margin: 5px 0;
background: var(--tgc-dark-1);
opacity: 0.5;
background: var(--common-shadow-2);
}
.tuc-dd-content {
@@ -48,12 +47,16 @@
width: 100%;
max-height: 50px;
margin-top: 5px;
color: var(--tgc-dark-1);
color: var(--box-text-4);
font-family: var(--font-text);
font-size: 14px;
overflow-x: hidden;
overflow-y: auto;
text-align: left;
word-break: break-all;
}
/* 隐藏 desc 侧面滚动条 */
.tuc-dd-desc::-webkit-scrollbar {
display: none;
}
</style>

View File

@@ -1,90 +1,101 @@
<template>
<TOverlay v-model="visible" hide :to-click="onCancel" blur-val="20px">
<div class="tuc-do-box">
<div class="tuc-do-bg">
<img
:src="data.bg"
alt="role"
:style="{
objectFit: data.bgFit,
}"
/>
<div class="tuc-do-div">
<!-- 左侧箭头 -->
<div class="tuc-arrow-left" @click="handleClick('left')">
<img src="../../assets/icons/arrow-right.svg" alt="left" />
</div>
<div class="tuc-do-quote">* 所有数据以游戏内为准此处仅供参考</div>
<!-- 衣装 -->
<div v-if="data.costume.length > 0" class="tuc-do-costume">
<v-switch v-model="showCostumeSwitch" color="#fb7299" @click="switchBg">
<template #label>
<v-icon>mdi-tshirt-crew-outline</v-icon>
</template>
</v-switch>
</div>
<div v-if="showCostumeSwitch" class="tuc-do-costume-name">
{{ data.costume[0].name }}
</div>
<div v-if="data" class="tuc-do-show">
<!-- 左侧武器跟圣遗物 -->
<div class="tuc-do-left">
<div
class="tuc-dol-item"
<!-- 中间内容 -->
<div class="tuc-do-box">
<div class="tuc-do-bg">
<img
:src="data.bg"
alt="role"
:style="{
opacity: selected.pos === 0 ? '1' : '0.5',
objectFit: data.bgFit,
}"
@click="showDetail(data.weapon, '武器', 0)"
>
<TucDetailItemBox v-model="weaponBox" />
</div>
<div
v-for="(item, index) in data.reliquary"
:key="index"
class="tuc-dol-item"
:style="{
cursor: item ? 'pointer' : 'default',
opacity: selected.pos === index + 1 ? '1' : item ? '0.5' : '1',
}"
@click="showDetail(item, '圣遗物', index + 1)"
>
<TucDetailRelic :model-value="item" :pos="index + 1" />
</div>
</div>
<!-- 右侧环状排列6个命座 -->
<div class="tuc-do-right">
<div class="tuc-dor-box">
<TucDetailConstellation
v-for="item in data.constellation"
:key="item.pos"
class="tuc-dor-item"
:model-value="item"
:style="{
border: selected.pos === item.pos + 5 ? '2px solid var(--tgc-yellow-1)' : '',
}"
@click="showDetail(item, '命座', item.pos + 5)"
/>
</div>
</div>
<!-- 底部说明 -->
<div class="tuc-do-bottom">
<TucDetailDescWeapon v-if="selected.type === '武器'" v-model="selectWeapon" />
<TucDetailDescConstellation
v-if="selected.type === '命座'"
v-model="selectConstellation"
/>
<TucDetailDescRelic v-if="selected.type === '圣遗物'" v-model="selectRelic" />
</div>
<div class="tuc-do-quote">* 所有数据以游戏内为准此处仅供参考</div>
<!-- 衣装 -->
<div v-if="data.costume.length > 0" class="tuc-do-costume">
<v-switch v-model="showCostumeSwitch" color="#fb7299" @click="switchBg">
<template #label>
<v-icon>mdi-tshirt-crew-outline</v-icon>
</template>
</v-switch>
</div>
<div v-if="showCostumeSwitch" class="tuc-do-costume-name">
{{ data.costume[0].name }}
</div>
<div v-if="data" class="tuc-do-show">
<!-- 左侧武器跟圣遗物 -->
<div class="tuc-do-left">
<div
class="tuc-dol-item"
:style="{
opacity: selected.pos === 0 ? '1' : '0.5',
}"
@click="showDetail(data.weapon, '武器', 0)"
>
<TucDetailItemBox v-model="weaponBox" />
</div>
<div
v-for="(item, index) in data.reliquary"
:key="index"
class="tuc-dol-item"
:style="{
cursor: item ? 'pointer' : 'default',
opacity: selected.pos === index + 1 ? '1' : item ? '0.5' : '1',
}"
@click="showDetail(item, '圣遗物', index + 1)"
>
<TucDetailRelic :model-value="item" :pos="index + 1" />
</div>
</div>
<!-- 右侧环状排列6个命座 -->
<div class="tuc-do-right">
<div class="tuc-dor-box">
<TucDetailConstellation
v-for="item in data.constellation"
:key="item.pos"
class="tuc-dor-item"
:model-value="item"
:style="{
border: selected.pos === item.pos + 5 ? '2px solid var(--tgc-yellow-1)' : '',
}"
@click="showDetail(item, '命座', item.pos + 5)"
/>
</div>
</div>
<!-- 底部说明 -->
<div class="tuc-do-bottom">
<TucDetailDescWeapon v-if="selected.type === '武器'" v-model="selectWeapon" />
<TucDetailDescConstellation
v-if="selected.type === '命座'"
v-model="selectConstellation"
/>
<TucDetailDescRelic v-if="selected.type === '圣遗物'" v-model="selectRelic" />
</div>
</div>
</div>
<!-- 右侧箭头 -->
<div class="tuc-arrow-right" @click="handleClick('right')">
<img src="../../assets/icons/arrow-right.svg" alt="right" />
</div>
</div>
</TOverlay>
</template>
<script lang="ts" setup>
// vue
import { computed, onMounted, onUpdated, ref } from "vue";
import TOverlay from "../main/t-overlay.vue";
import TucDetailDescWeapon from "./tuc-detail-desc-weapon.vue";
import TucDetailConstellation from "./tuc-detail-constellation.vue";
import TucDetailDescConstellation from "./tuc-detail-desc-constellation.vue";
import TucDetailDescRelic from "./tuc-detail-desc-relic.vue";
import TucDetailDescWeapon from "./tuc-detail-desc-weapon.vue";
import TucDetailItemBox from "./tuc-detail-itembox.vue";
import TucDetailConstellation from "./tuc-detail-constellation.vue";
import TucDetailRelic from "./tuc-detail-relic.vue";
import TOverlay from "../main/t-overlay.vue";
interface ToUcDetailProps {
modelValue: boolean;
@@ -94,7 +105,9 @@ interface ToUcDetailProps {
interface ToUcDetailEmits {
(e: "update:modelValue", value: boolean): void;
(e: "cancel"): void;
(e: "clickL"): void;
(e: "clickR"): void;
}
type fixedLenArray<T, N extends number> = [T, ...T[]] & { length: N };
@@ -214,9 +227,13 @@ const weaponBox = computed(() => {
const onCancel = (): void => {
visible.value = false;
emits("cancel");
emits("update:modelValue", false);
};
function handleClick(pos: "left" | "right") {
pos === "left" ? emits("clickL") : emits("clickR");
}
function showDetail(
item:
| TGApp.Sqlite.Character.RoleConstellation
@@ -259,13 +276,47 @@ function switchBg(): void {
}
</script>
<style lang="css" scoped>
.tuc-do-div {
display: flex;
align-items: center;
justify-content: space-between;
column-gap: 10px;
}
.tuc-arrow-left,
.tuc-arrow-right {
position: relative;
display: flex;
width: 30px;
height: 30px;
align-items: center;
justify-content: center;
cursor: pointer;
}
.dark .tuc-arrow-left,
.dark .tuc-arrow-right {
filter: invert(11%) sepia(73%) saturate(11%) hue-rotate(139deg) brightness(97%) contrast(81%);
}
.tuc-arrow-left img {
width: 100%;
height: 100%;
transform: rotate(180deg);
}
.tuc-arrow-right img {
width: 100%;
height: 100%;
}
.tuc-do-box {
position: relative;
width: 500px;
height: 620px;
padding: 10px;
border-radius: 5px;
background: var(--tgc-white-1);
background: var(--box-bg-1);
}
.tuc-do-bg {
@@ -290,7 +341,7 @@ function switchBg(): void {
bottom: 0;
padding: 2px 5px;
backdrop-filter: blur(10px);
background: rgb(0 0 0 /50%);
background: var(--common-shadow-2);
border-bottom-right-radius: 5px;
border-top-left-radius: 5px;
color: var(--tgc-white-1);

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