Compare commits

...

63 Commits

Author SHA1 Message Date
BTMuli
2ab31d8f5c 🚀 v0.8.6 2025-11-19 14:09:48 +08:00
BTMuli
1af990512d 📝 更新README 2025-11-19 13:55:53 +08:00
BTMuli
d96d451156 👽️ 调整读取格式 2025-11-19 13:50:47 +08:00
BTMuli
f029306ebb 🚸 添加跳转视频链接 2025-11-19 13:40:25 +08:00
BTMuli
d3c5baa0c2 📝 更新资源说明文档 2025-11-19 13:22:01 +08:00
BTMuli
ba0802752c 🔊 完善log 2025-11-19 00:51:49 +08:00
BTMuli
ff94e12ff5 🚸 调整默认文本 2025-11-19 00:07:13 +08:00
BTMuli
0fbf1f7c2a 🚸 添加AIGC相关注释 2025-11-18 23:01:51 +08:00
BTMuli
68809a93c6 支撑导入剧诗数据 2025-11-18 22:51:32 +08:00
BTMuli
0edcadef63 👽️ 移除剧诗概览,支撑导入剧诗数据 2025-11-18 22:42:46 +08:00
BTMuli
9f9c30914f 🔥 移除胡桃深渊统计页面 2025-11-18 22:29:24 +08:00
BTMuli
04cf372798 🎨 路由重定向 2025-11-18 22:27:27 +08:00
BTMuli
6617a26c90 👽️ 移除深渊上传,支撑导入胡桃深渊数据 2025-11-18 22:20:58 +08:00
BTMuli
d244423800 🚸 调整导入浮窗ui,显示导入进度 2025-11-18 22:02:07 +08:00
BTMuli
3366efaadd 🐛 处理拓展解析异常 2025-11-15 20:36:50 +08:00
BTMuli
d74e7a7a31 🥅 处理异常,清除缓存后重启 2025-11-15 14:54:06 +08:00
BTMuli
2d0b409813 🐛 修复图片渲染异常 2025-11-15 14:37:05 +08:00
BTMuli
942068faea 🚀 v0.8.5 2025-11-10 16:34:14 +08:00
BTMuli
0f0f7684d2 🍱 更新下半数据 2025-11-10 16:31:01 +08:00
BTMuli
531cb32f72 🚀 v0.8.4 2025-10-27 19:48:35 +08:00
BTMuli
a368223805 千星奇域页面 2025-10-27 19:42:02 +08:00
BTMuli
6eab6c81f1 🍱 增加千星奇域元数据 2025-10-27 17:26:33 +08:00
BTMuli
68594a2a76 🐛 剔除多余换行 2025-10-27 12:33:09 +08:00
BTMuli
5d5f22d76e 🚸 添加prefix 2025-10-25 23:05:13 +08:00
BTMuli
65e948c34c 添加getRegionRoleInfo事件处理 2025-10-25 21:04:04 +08:00
BTMuli
68dead3d84 🔥 考虑合并祈愿,不单独分页 2025-10-25 19:46:03 +08:00
BTMuli
babc6a9a75 嵌入祈愿详情 2025-10-25 19:45:17 +08:00
BTMuli
6db4ff5ac9 👽️ 移除非必需参数 2025-10-25 18:59:26 +08:00
BTMuli
ce1b6f365e 🏷️ 调整类型注释 2025-10-25 14:07:46 +08:00
BTMuli
b6ed9668ac 🚸 完善类型,添加交互 2025-10-25 13:00:10 +08:00
BTMuli
2a2a190f5f 🐛 修复部分帖子渲染异常 2025-10-25 12:31:03 +08:00
BTMuli
5d03a32362 💄 调整名片样式 2025-10-24 23:35:12 +08:00
BTMuli
33d9ba5c4d 重构帖子解析逻辑,增加新类型解析
*PostID:69886846,69915487
2025-10-24 22:21:03 +08:00
BTMuli
9020214d23 🐛 修复部分帖子解析异常 2025-10-24 20:13:51 +08:00
BTMuli
78c3f79bfd 🧑‍💻 JSON内容复制 2025-10-24 19:58:31 +08:00
BTMuli
ee0fc6dbae 完善投稿活动类型声明,渲染投稿活动&交互
*PostID:69823686
2025-10-24 19:27:49 +08:00
BTMuli
8c51b79558 🐛 修复浮窗显示异常 2025-10-24 18:05:50 +08:00
BTMuli
8c1899637f 🌱 暂时将千星奇域移到祈愿子tab 2025-10-24 16:21:30 +08:00
BTMuli
56df920a7d 嵌入官方公告页面(已登录) 2025-10-24 16:15:05 +08:00
BTMuli
64c6f4ab8f 🚸 兑换码浮窗显示游戏名称 2025-10-24 12:20:15 +08:00
BTMuli
d3902d6e31 🌱 千星奇域抽卡记录页面 2025-10-23 23:56:23 +08:00
BTMuli
01e355b0d6 千星奇域抽卡记录获取 2025-10-23 23:09:58 +08:00
BTMuli
c40b3c6ff0 👽️ 公告添加千星奇域分类 2025-10-23 22:40:30 +08:00
BTMuli
4305967ba9 🚀 v0.8.3 2025-10-22 13:59:10 +08:00
BTMuli
78f454bee5 🍱 更新卡池数据 2025-10-22 13:50:08 +08:00
BTMuli
e9a38e1474 👽️ 奇偶不lock好感卡片 2025-10-22 13:13:22 +08:00
BTMuli
9fb2aa6112 🍱 更新6.1资源 2025-10-22 12:34:43 +08:00
BTMuli
a0554e4355 🚸 首页活动组件(用户)分享图生成 2025-10-17 18:13:34 +08:00
BTMuli
f890165894 💄 微调月谕模式ui 2025-10-17 18:06:38 +08:00
BTMuli
bc22612da7 💄 调整布局 2025-10-15 11:09:42 +08:00
BTMuli
a9ec93b18d 🐛 修复JS脚本执行异常 2025-10-15 00:14:01 +08:00
BTMuli
651cbef0a0 ♻️ 提取剧诗Icon 2025-10-08 12:05:51 +08:00
BTMuli
41a144fec2 🚸 优化图片调整浮窗样式 2025-10-08 11:49:47 +08:00
BTMuli
3f219ebb82 ♻️ 重构gt返回逻辑 2025-10-08 10:36:17 +08:00
BTMuli
43c85afd1e 📝 移除oss认证 2025-10-08 10:08:54 +08:00
BTMuli
48771f57a0 🚸 降低验证触发概率 2025-10-04 12:09:17 +08:00
BTMuli
6e3884df58 💄 添加圣牌图标 2025-10-02 12:26:52 +08:00
BTMuli
7a6a06bb25 🐛 修正链接判断逻辑 2025-10-02 12:18:25 +08:00
BTMuli
eac3691d0b 🐛 修复切换角色导致ck对应异常 2025-10-01 11:10:12 +08:00
BTMuli
3ece987c80 👽️ 适配月谕圣牌模式 2025-10-01 10:37:46 +08:00
BTMuli
145438373b 🐛 修复下载链接异常 2025-09-28 23:23:40 +08:00
BTMuli
b62b0b4902 🐛 重构数据解析,修复HEIC格式图片渲染异常 2025-09-28 23:13:55 +08:00
BTMuli
ed878dea9e 💄 微调样式 2025-09-27 21:40:23 +08:00
172 changed files with 10850 additions and 4080 deletions

View File

@@ -1,8 +1,9 @@
name: Qodana
on:
push:
branches:
- master
workflow_dispatch:
# push:
# branches:
# - master
jobs:
qodana:

View File

@@ -2,12 +2,54 @@
Author: 目棃
Description: CHANGELOG
Date: 2025-09-09
Update: 2025-09-27
Update: 2025-11-19
---
> 本文档 [`Frontmatter`](https://github.com/BTMuli/MuCli#Frontmatter) 由 [MuCli](https://github.com/BTMuli/Mucli) 自动生成于 `2025-09-09 14:30:56`
>
> 更新于 `2025-09-27 10:18:07`
> 更新于 `2025-11-19 14:08:20`
## [0.8.6](https://github.com/BTMuli/TeyvatGuide/releases/v0.8.6) (2025-11-19)
> 关于胡桃数据库导入功能的说明请参考 [导入胡桃数据库](https://app.btmuli.ink/docs/TeyvatGuide/import-hutao-db.html)
- 👽️ 移除剧诗概览,支持导入胡桃剧诗数据
- 👽️ 移除深渊上传,支持导入胡桃深渊数据
- 🔥 移除胡桃深渊统计页面
- 🚸 调整导入祈愿记录浮窗ui显示导入进度
- 🐛 修复图片渲染异常
- 🥅 处理清除缓存异常,清除缓存后重启
- 🚸 帖子详情添加AIGC相关注释
- 🚸 添加跳转视频链接
- 📝 更新相关文档
## [0.8.5](https://github.com/BTMuli/TeyvatGuide/releases/v0.8.5) (2025-11-10)
- 🍱 更新下半数据
## [0.8.4](https://github.com/BTMuli/TeyvatGuide/releases/v0.8.4) (2025-10-27)
- 👽️ 公告添加千星奇域分类
- 🚸 兑换码浮窗显示游戏名称
- ✨ 嵌入官方公告页面(已登录)
- ✨ 嵌入官方祈愿详情(已登录)
- ✨ 完善投稿活动类型声明,渲染投稿活动&交互
- 🐛 修复部分帖子解析异常
- ✨ 重构帖子解析逻辑,增加新类型解析
- 💄 调整名片样式
- ✨ 添加getRegionRoleInfo事件处理
- 🐛 公告解析剔除多余换行
- ✨ 千星奇域祈愿页面草创
## [0.8.3](https://github.com/BTMuli/TeyvatGuide/releases/v0.8.3) (2025-10-22)
- 🍱 更新6.1版本数据
- 👽️ 适配月谕圣牌模式
- 🐛 重构帖子数据解析修复HEIC格式图片渲染异常
- 🐛 修复切换角色导致ck对应异常
- 🚸 优化图片调整浮窗样式
- ♻️ 重构gt返回逻辑
- 💄 调整布局
## [0.8.2](https://github.com/BTMuli/TeyvatGuide/releases/v0.8.2) (2025-09-27)

View File

@@ -2,12 +2,12 @@
Author: 目棃
Description: 说明文档
Date: 2023-03-05
Update: 2025-09-09
Update: 2025-11-19
---
> 本文档 [`Frontmatter`](https://github.com/BTMuli/MuCli#Frontmatter) 由 [MuCli](https://github.com/BTMuli/Mucli) 自动生成于 `2023-03-05 14:41:55`
>
> 更新于 `2025-09-09 14:37:02`
> 更新于 `2025-11-19 13:21:38`
[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/BTMuli/TeyvatGuide) ![](https://img.shields.io/github/last-commit/BTMuli/TeyvatGuide) ![](https://img.shields.io/github/commits-since/BTMuli/TeyvatGuide/latest?include_prereleases)
@@ -65,13 +65,13 @@ Game Tool for Genshin Impact player, supports Windows and macOS.
- [x] 真境剧诗
- [x] 幽境危战
- [x] 祈愿数据获取(近一年)
- [x] 千星奇域祈愿数据获取(近一年)
- [x] 用户收藏帖子获取
- [x] 用户关注帖子获取
- [x] 一键完成米游币每日任务
- [x] 一键完成游戏签到
- Wiki 功能:
- [x] 深渊数据库Hutao API
- [x] 角色图鉴
- [x] 武器图鉴
- [x] 名片图鉴
@@ -90,7 +90,7 @@ Game Tool for Genshin Impact player, supports Windows and macOS.
## UI 参考 / UI Reference
- [Snap.Hutao](https://github.com/DGP-Studio/Snap.Hutao)
- ~~[Snap.Hutao](https://github.com/DGP-Studio/Snap.Hutao)~~
- [Starward](https://github.com/Scighost/Starward)
- [米游社](https://www.miyoushe.com/ys/)
- [原神](https://yuanshen.com/)
@@ -102,6 +102,8 @@ Game Tool for Genshin Impact player, supports Windows and macOS.
- UIAF[UIAF v1.1](docs/standards/UIAF.md)
- UIGF[UIGF v3.0](docs/standards/UIGF3.md)[UIGF v4.0](docs/standards/UIGF.md)
- [macOS 平台门禁属性导致应用无法打开应用的修复指引](docs/macos-gatekeeper/README.md)
- [隐私政策](https://app.btmuli.ink/docs/TeyvatGuide/privacy.html)
- [如何导入胡桃数据库](https://app.btmuli.ink/docs/TeyvatGuide/import-hutao-db.html)
## 特定项目 / Special Project
@@ -136,7 +138,7 @@ Game Tool for Genshin Impact player, supports Windows and macOS.
本项目在开发过程中参考了诸多相关开源项目,特此鸣谢。
- [UIGF Organization](https://github.com/UIGF-org)
- [Snap.Hutao](https://github.com/DGP-Studio/Snap.Hutao)
- ~~[Snap.Hutao](https://github.com/DGP-Studio/Snap.Hutao)~~
- [StarWard](https://github.com/Scighost/Starward)
- [xunkong](https://github.com/xunkong/xunkong)
- [gs-helper](https://github.com/vikiboss/gs-helper)
@@ -146,8 +148,4 @@ Game Tool for Genshin Impact player, supports Windows and macOS.
- [MihoyoBBSTools](https://github.com/Womsxd/MihoyoBBSTools)
- [nonebot-plugin-mystool](https://github.com/Ljzd-PRO/nonebot-plugin-mystool)
感谢 JetBrains 提供的开源许可证。
![JetBrains logo](https://resources.jetbrains.com/storage/products/company/brand/logos/jetbrains.png)
[![Star History Chart](https://api.star-history.com/svg?repos=BTMuli/TeyvatGuide&type=Timeline)](https://star-history.com/#BTMuli/TeyvatGuide&Timeline)

View File

@@ -2,12 +2,12 @@
Author: 目棃
Description: 项目资源说明
Date: 2023-03-10
Update: 2025-02-28
Update: 2025-11-19
---
> 本文档 [`Frontmatter`](https://github.com/BTMuli/MuCli#Frontmatter) 由 [MuCli](https://github.com/BTMuli/Mucli) 自动生成于 `2023-03-10 22:05:44`
>
> 更新于 `2025-02-28 09:40:33`
> 更新于 `2025-11-19 12:31:22`
## 说明
@@ -40,8 +40,8 @@ Update: 2025-02-28
相关仓库:
- [TGAssistant](https://github.com/BTMuli/TGAssistant):项目下游仓库,用于处理项目数据。
- [Snap.Metadata](https://github.com/DGP-Studio/Snap.Metadata):胡桃元数据仓库,项目大部分数据来源于此。
- [Snap.Static](https://github.com/DGP-Studio/Snap.Static):胡桃静态资源仓库,项目部分图像资源来源于此。
- ~~[Snap.Metadata](https://github.com/DGP-Studio/Snap.Metadata)~~:胡桃元数据仓库,项目大部分数据来源于此。
- ~~[Snap.Static](https://github.com/DGP-Studio/Snap.Static)~~:胡桃静态资源仓库,项目部分图像资源来源于此。
- [amos-data](https://github.com/yuehaiteam/amos-data):成就数据仓库,成就数据的详细信息来源于此。
## 字体

View File

@@ -1,9 +1,9 @@
{
"name": "teyvatguide",
"version": "0.8.2",
"version": "0.8.6",
"description": "Game Tool for GenshinImpact player",
"private": true,
"packageManager": "pnpm@10.16.1",
"packageManager": "pnpm@10.22.0",
"type": "module",
"scripts": {
"build": "tauri build",
@@ -71,68 +71,66 @@
},
"dependencies": {
"@mdi/font": "7.4.47",
"@tauri-apps/api": "^2.8.0",
"@tauri-apps/plugin-deep-link": "^2.4.3",
"@tauri-apps/plugin-dialog": "^2.4.0",
"@tauri-apps/plugin-fs": "^2.4.2",
"@tauri-apps/plugin-http": "^2.5.2",
"@tauri-apps/plugin-log": "^2.7.0",
"@tauri-apps/plugin-opener": "^2.5.0",
"@tauri-apps/plugin-os": "^2.3.1",
"@tauri-apps/plugin-process": "^2.3.0",
"@tauri-apps/plugin-shell": "^2.3.1",
"@tauri-apps/plugin-sql": "^2.3.0",
"@tauri-apps/api": "^2.9.0",
"@tauri-apps/plugin-deep-link": "^2.4.5",
"@tauri-apps/plugin-dialog": "^2.4.2",
"@tauri-apps/plugin-fs": "^2.4.4",
"@tauri-apps/plugin-http": "^2.5.4",
"@tauri-apps/plugin-log": "^2.7.1",
"@tauri-apps/plugin-opener": "^2.5.2",
"@tauri-apps/plugin-os": "^2.3.2",
"@tauri-apps/plugin-process": "^2.3.1",
"@tauri-apps/plugin-shell": "^2.3.3",
"@tauri-apps/plugin-sql": "^2.3.1",
"ajv": "^8.17.1",
"artplayer": "^5.3.0",
"clipboard": "^2.0.11",
"color-convert": "^3.1.2",
"color-convert": "^3.1.3",
"echarts": "^6.0.0",
"html2canvas": "^1.4.1",
"js-md5": "^0.8.3",
"jsencrypt": "^3.5.4",
"pinia": "^3.0.3",
"pinia-plugin-persistedstate": "^4.5.0",
"pinia": "^3.0.4",
"pinia-plugin-persistedstate": "^4.7.1",
"qrcode.vue": "^3.6.0",
"sass-embedded": "^1.92.1",
"swiper": "^12.0.1",
"sass-embedded": "^1.93.3",
"swiper": "^12.0.3",
"uuid": "^13.0.0",
"vue": "^3.5.21",
"vue-echarts": "^7.0.3",
"vue-json-pretty": "^2.5.0",
"vue-router": "^4.5.1",
"vuetify": "^3.10.0",
"vue": "^3.5.24",
"vue-echarts": "^8.0.1",
"vue-json-pretty": "^2.6.0",
"vue-router": "^4.6.3",
"vuetify": "^3.10.11",
"wcag-color": "^1.1.1",
"xml-js": "^1.6.11"
},
"devDependencies": {
"@btmuli/stylelint-plugin-color": "^0.1.0",
"@eslint/eslintrc": "^3.3.1",
"@eslint/js": "^9.35.0",
"@tauri-apps/cli": "2.8.4",
"@eslint/js": "^9.39.1",
"@tauri-apps/cli": "2.9.4",
"@types/color-convert": "^2.0.4",
"@types/fs-extra": "^11.0.4",
"@types/js-md5": "^0.8.0",
"@types/node": "^24.4.0",
"@types/uuid": "^10.0.0",
"@typescript-eslint/parser": "^8.43.0",
"@typescript/native-preview": "7.0.0-dev.20250914.1",
"@vitejs/plugin-vue": "^6.0.1",
"@types/node": "^24.10.1",
"@typescript-eslint/parser": "^8.47.0",
"@typescript/native-preview": "7.0.0-dev.20251118.1",
"@vitejs/plugin-vue": "^6.0.2",
"app-root-path": "^3.1.0",
"concurrently": "^9.2.1",
"eslint": "^9.35.0",
"eslint": "^9.39.1",
"eslint-plugin-import": "^2.32.0",
"eslint-plugin-jsonc": "^2.20.1",
"eslint-plugin-jsonc": "^2.21.0",
"eslint-plugin-prettier": "^5.5.4",
"eslint-plugin-vue": "^10.4.0",
"eslint-plugin-yml": "^1.18.0",
"fs-extra": "^11.3.1",
"globals": "^16.4.0",
"eslint-plugin-vue": "^10.5.1",
"eslint-plugin-yml": "^1.19.0",
"fs-extra": "^11.3.2",
"globals": "^16.5.0",
"husky": "^9.1.7",
"jsonc-eslint-parser": "^2.4.0",
"lint-staged": "^16.1.6",
"oxlint": "^1.15.0",
"jsonc-eslint-parser": "^2.4.1",
"lint-staged": "^16.2.6",
"oxlint": "^1.29.0",
"prettier": "3.6.2",
"stylelint": "^16.24.0",
"stylelint": "^16.25.0",
"stylelint-config-idiomatic-order": "^10.0.0",
"stylelint-config-standard-scss": "^16.0.0",
"stylelint-config-standard-vue": "^1.0.0",
@@ -141,14 +139,14 @@
"stylelint-order": "^7.0.0",
"stylelint-prettier": "^5.0.3",
"stylelint-scss": "^6.12.1",
"tsx": "^4.20.5",
"typescript": "^5.9.2",
"typescript-eslint": "^8.43.0",
"vite": "^7.1.5",
"vite-plugin-vue-devtools": "^8.0.2",
"tsx": "^4.20.6",
"typescript": "^5.9.3",
"typescript-eslint": "^8.47.0",
"vite": "npm:rolldown-vite@^7.2.6",
"vite-plugin-vue-devtools": "^8.0.5",
"vite-plugin-vuetify": "^2.1.2",
"vue-eslint-parser": "^10.2.0",
"vue-tsc": "^3.0.7",
"vue-tsc": "^3.1.4",
"yaml-eslint-parser": "^1.3.0"
}
}

3328
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 836 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

1157
src-tauri/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
[package]
name = "TeyvatGuide"
version = "0.8.2"
version = "0.8.6"
description = "Game Tool for Genshin Impact player"
authors = ["BTMuli <bt-muli@outlook.com>"]
license = "MIT"
@@ -17,15 +17,15 @@ name = "teyvat_guide_lib"
crate-type = ["staticlib", "cdylib", "rlib"]
[build-dependencies]
tauri-build = { version = "2.4.1", features = [] }
tauri-build = { version = "2.5.2", features = [] }
[dependencies]
chrono = "0.4.42"
log = "0.4.28"
serde = { version = "1.0.221", features = ["derive"] }
serde_json = "1.0.144"
tauri = { version = "2.8.5", features = [] }
tauri-utils = "2.7.0"
serde = { version = "1.0.228", features = ["derive"] }
serde_json = "1.0.145"
tauri = { version = "2.9.3", features = [] }
tauri-utils = "2.8.0"
url = "2.5.7"
walkdir = "2.5.0"

View File

@@ -8,7 +8,7 @@ mod utils;
use tauri::{AppHandle, Manager, WebviewWindowBuilder};
use tauri_utils::config::WebviewUrl;
static BBS_VERSION: &'static str = "2.93.0";
static BBS_VERSION: &'static str = "2.95.1";
#[tauri::command]
pub async fn create_mhy_client(handle: AppHandle, func: String, url: String) {

View File

@@ -2,7 +2,7 @@
"$schema": "https://schema.tauri.app/config/2",
"productName": "TeyvatGuide",
"identifier": "TeyvatGuide",
"version": "0.8.2",
"version": "0.8.6",
"build": {
"beforeDevCommand": "pnpm vite:dev",
"beforeBuildCommand": "pnpm vite:build",

View File

@@ -7,7 +7,7 @@
<div v-if="hasNav" class="tgn-nav">
<v-icon size="25" @click="tryGetCode" title="查看兑换码">mdi-code-tags-check</v-icon>
</div>
<ToLivecode v-model="showOverlay" :data="codeData" v-model:actId="actId" />
<ToLivecode v-model="showOverlay" :gid="model" :data="codeData" :actId="actId" />
</div>
</template>
<script lang="ts" setup>

View File

@@ -112,7 +112,10 @@
}"
v-else
>
{{ props.modelValue.post.post_id }}
<span>{{ props.modelValue.post.post_id }}</span>
<template v-if="isDevEnv">
<span data-html2canvas-ignore>[{{ props.modelValue.post.view_type }}]</span>
</template>
</div>
</div>
</template>
@@ -154,7 +157,8 @@ export type RenderCard = {
topics: Array<TGApp.BBS.Post.Topic>;
reasons: Array<TGApp.BBS.Post.RecommendTags>;
};
// @ts-expect-error The import.meta meta-property is not allowed in files which will build into CommonJS output.
const isDevEnv = import.meta.env.MODE === "development";
const stats: Readonly<Array<RenderStatus>> = [
{ stat: 0, label: "未知", color: "var(--tgc-od-red)" },
{ stat: 1, label: "进行中", color: "var(--tgc-od-green)" },

View File

@@ -110,11 +110,6 @@
</v-list-item>
</template>
<v-list class="side-list-menu sub" density="compact" :nav="true">
<v-list-item class="side-item-menu" title="深渊数据库" :link="true" href="/wiki/abyss">
<template #prepend>
<img src="/source/UI/wikiAbyss.webp" alt="abyssIcon" class="side-icon-menu" />
</template>
</v-list-item>
<v-list-item class="side-item-menu" title="角色图鉴" :link="true" href="/wiki/character">
<template #prepend>
<img src="/source/UI/wikiAvatar.webp" alt="characterIcon" class="side-icon-menu" />

View File

@@ -2,7 +2,7 @@
<TOverlay v-model="visible" class="tolc-overlay">
<div class="tolc-box">
<div class="tolc-title">
<span>兑换码</span>
<span>{{ gameInfo?.name ?? "" }}兑换码</span>
<v-icon
size="18px"
title="share"
@@ -39,16 +39,28 @@
</template>
<script setup lang="ts">
import showSnackbar from "@comp/func/snackbar.js";
import useBBSStore from "@store/bbs.js";
import { generateShareImg } from "@utils/TGShare.js";
import { timestampToDate } from "@utils/toolFunc.js";
import { storeToRefs } from "pinia";
import { computed } from "vue";
import TMiImg from "./t-mi-img.vue";
import TOverlay from "./t-overlay.vue";
type ToLiveCodeProps = { data: Array<TGApp.BBS.Navigator.CodeData>; actId: string | undefined };
type ToLiveCodeProps = {
data: Array<TGApp.BBS.Navigator.CodeData>;
actId: string | undefined;
gid: number;
};
const { gameList } = storeToRefs(useBBSStore());
const props = defineProps<ToLiveCodeProps>();
const visible = defineModel<boolean>({ default: false });
const gameInfo = computed<TGApp.BBS.Game.Item | undefined>(() => {
return gameList.value.find((i) => i.id === props.gid);
});
function copy(code: string): void {
navigator.clipboard.writeText(code);

View File

@@ -1,40 +1,35 @@
<!-- 名片栏组件 -->
<template>
<div
class="top-nc-box"
@click="emit('selected', props.data)"
:class="props.finish ? '' : 'grey'"
:title.attr="props.data.name"
:style="{ backgroundImage: `url('/WIKI/nameCard/bg/${props.data.name}.webp')` }"
>
<v-list-item>
<template #title>
<div class="title">
<TwnTypeTag :type="props.data.type" />
<span>{{ props.data.name }}</span>
</div>
</template>
<template #subtitle>
<div class="top-nc-bgc" />
<div class="top-nc-prepend">
<img :src="`/WIKI/nameCard/icon/${props.data.name}.webp`" alt="icon" />
</div>
<div class="top-nc-info">
<div class="top-nc-title">
<TwnTypeTag :type="props.data.type" />
<span>{{ props.data.name }}</span>
</div>
<div class="top-nc-desc">
<span class="desc" :title="props.data.desc">{{ props.data.desc }}</span>
</template>
<template #prepend>
<img :src="`/WIKI/nameCard/icon/${props.data.name}.webp`" alt="icon" class="icon" />
</template>
</v-list-item>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import TwnTypeTag from "@comp/pageWiki/twn-type-tag.vue";
import { computed } from "vue";
type TopNameCardProps = { data: TGApp.App.NameCard.Item; finish?: boolean };
type TopNameCardEmits = (e: "selected", v: TGApp.App.NameCard.Item) => void;
const props = withDefaults(defineProps<TopNameCardProps>(), { finish: true });
const emit = defineEmits<TopNameCardEmits>();
const bgImage = computed<string>(() => {
if (props.data.name === "原神·印象") return "none;";
return `url("/WIKI/nameCard/bg/${props.data.name}.webp")`;
});
</script>
<style lang="scss" scoped>
@use "@styles/github.styles.scss" as github-styles;
@@ -42,17 +37,19 @@ const bgImage = computed<string>(() => {
.top-nc-box {
@include github-styles.github-card-shadow;
position: relative;
display: flex;
width: 100%;
height: 80px;
box-sizing: border-box;
align-items: center;
justify-content: flex-start;
border: 1px solid var(--common-shadow-1);
border-radius: 4px 50px 50px 4px;
background-color: var(--box-bg-1);
background-image: v-bind(bgImage); /* stylelint-disable-line value-keyword-case */
background-position: right;
background-repeat: no-repeat;
column-gap: 8px;
cursor: pointer;
font-family: var(--font-title);
transition: filter 0.5s ease-in-out;
@@ -70,19 +67,60 @@ const bgImage = computed<string>(() => {
@include github-styles.github-card-shadow("dark");
}
.icon {
height: 60px;
margin-right: 12px;
aspect-ratio: 23 / 15;
.top-nc-bgc {
position: absolute;
z-index: 0;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(to right, var(--box-bg-1) 40%, transparent 75%);
}
.title {
.top-nc-prepend {
position: relative;
z-index: 1;
display: flex;
height: 60px;
flex-shrink: 0;
align-items: center;
justify-content: center;
margin-left: 8px;
img {
height: 60px;
aspect-ratio: 23/15;
}
}
.top-nc-info {
position: relative;
z-index: 1;
display: flex;
overflow: hidden;
height: 100%;
flex-direction: column;
flex-grow: 1;
align-items: flex-start;
justify-content: center;
margin-right: 8px;
}
.top-nc-title {
display: flex;
align-items: center;
column-gap: 4px;
font-size: 16px;
}
.desc {
text-shadow: 0 0 2px var(--common-shadow-t-8);
.top-nc-desc {
position: relative;
overflow: hidden;
max-width: calc(100% - 16px);
font-size: 14px;
opacity: 0.75;
text-overflow: ellipsis;
text-shadow: 0 0 2px var(--box-bg-1);
white-space: nowrap;
}
</style>

View File

@@ -48,7 +48,7 @@ declare function initGeetest(
async function displayBox(
props: TGApp.BBS.Geetest.CreateRes,
): Promise<TGApp.BBS.Geetest.GeetestVerifyRes | false> {
return await new Promise<TGApp.BBS.Geetest.GeetestVerifyRes>((resolve) => {
return await new Promise<TGApp.BBS.Geetest.GeetestVerifyRes | false>((resolve) => {
initGeetest(
{
gt: props.gt,
@@ -64,11 +64,12 @@ async function displayBox(
geetestEl.value.innerHTML = "";
captchaObj.appendTo("#geetest");
captchaObj.onReady(() => (show.value = true));
captchaObj.onSuccess(() => {
captchaObj.onClose(() => {
const validate = captchaObj.getValidate();
show.value = false;
if (!validate) resolve(false);
resolve(validate);
});
captchaObj.onClose(() => (show.value = false));
},
);
});

View File

@@ -0,0 +1,105 @@
<!-- 游戏内公告浮窗 -->
<template>
<TOverlay v-model="visible">
<div class="tao-iframe-box">
<!-- TODO:加载完成后修改样式 -->
<iframe :src="link" class="tao-iframe" />
</div>
</TOverlay>
</template>
<script lang="ts" setup>
import TOverlay from "@comp/app/t-overlay.vue";
import showSnackbar from "@comp/func/snackbar.js";
import takumiReq from "@req/takumiReq.js";
import useAppStore from "@store/app.js";
import useUserStore from "@store/user.js";
import { storeToRefs } from "pinia";
import { onMounted, ref, watch } from "vue";
const { lang } = storeToRefs(useAppStore());
const { cookie, account } = storeToRefs(useUserStore());
const visible = defineModel<boolean>();
const authkey = ref<string>("");
const link = ref<string>("");
onMounted(async () => await refreshUrl());
watch(
() => lang.value,
async () => {
if (!visible.value) return;
await refreshUrl();
},
);
async function refreshUrl(): Promise<void> {
const res = await getUrl();
if (res === "") return;
link.value = res;
}
async function refreshAuthkey(): Promise<void> {
if (!cookie.value || !account.value) {
visible.value = false;
showSnackbar.warn("请先登录账号");
return;
}
const authkeyRes = await takumiReq.bind.authKey(cookie.value, account.value);
if (typeof authkeyRes === "string") {
authkey.value = authkeyRes;
} else {
showSnackbar.error("获取authkey失败");
visible.value = false;
return;
}
}
async function getUrl(): Promise<string> {
const path = "https://sdk.mihoyo.com/hk4e/announcement/index.html";
if (authkey.value === "") await refreshAuthkey();
if (authkey.value === "") return "";
const param: Record<string, string> = {
auth_appid: "announcement",
authkey_ver: "1",
bundle_id: "hk4e_cn",
channel_id: "14",
game: "hk4e",
game_biz: account.value.gameBiz,
lang: lang.value,
level: account.value.level,
platform: "pc",
region: account.value.region,
sdk_presentation_style: "fullscreen",
sdk_screen_transparent: "true",
sign_type: "2",
uid: account.value.gameUid,
timestamp: Math.floor(Date.now() / 1000).toString(),
authkey: authkey.value,
};
const targetLink = new URL(path);
for (const key in param) {
targetLink.searchParams.append(key, param[key]);
}
return targetLink.toString();
}
</script>
<style lang="scss" scoped>
.tao-iframe-box {
position: relative;
display: flex;
overflow: hidden;
width: 50vw;
align-items: center;
justify-content: center;
border-radius: 8px;
aspect-ratio: 16/9;
}
.tao-iframe {
width: 100%;
height: 100%;
border: none;
background-color: transparent;
}
</style>

View File

@@ -404,6 +404,9 @@ async function tryGetCaptcha(phone: string, aigis?: string): Promise<string | fa
if ("retcode" in captchaResp) {
if (!captchaResp.data || captchaResp.data === "") {
showSnackbar.error(`[${captchaResp.retcode}] ${captchaResp.message}`);
await TGLogger.Error(
`[tc-userBadge][tryGetCaptcha] ${captchaResp.retcode} ${captchaResp.message}`,
);
return false;
}
const aigisResp: TGApp.BBS.CaptchaLogin.CaptchaAigis = JSON.parse(captchaResp.data);
@@ -424,6 +427,9 @@ async function tryLoginByCaptcha(
if ("retcode" in loginResp) {
if (!loginResp.data || loginResp.data === "") {
showSnackbar.error(`[${loginResp.retcode}] ${loginResp.message}`);
await TGLogger.Error(
`[tc-userBadge][tryLoginByCaptcha] ${loginResp.retcode} ${loginResp.message}`,
);
return false;
}
const aigisResp: TGApp.BBS.CaptchaLogin.CaptchaAigis = JSON.parse(loginResp.data);

View File

@@ -0,0 +1,147 @@
<!-- 设置图片质量浮窗 -->
<template>
<TOverlay v-model="model" hide blur-val="10px">
<div class="toi-box">
<div class="toi-top">
<div class="toi-title">调整图片质量</div>
<div class="toi-desc">设置图片质量数值越大图片越清晰但也会占用更多空间</div>
</div>
<div class="toi-mid">
<v-slider
thumb-label="always"
color="var(--tgc-od-blue)"
thumb-color="var(--tgc-od-red)"
v-model="quality"
:max="100"
:min="5"
:step="1"
hide-details
/>
<v-number-input
class="toi-input"
v-model="quality"
control-variant="stacked"
density="compact"
variant="outlined"
style="max-width: 100px"
type="number"
:max="100"
:min="5"
:step="1"
/>
</div>
<div class="toi-bottom">
<button class="toi-btn no-btn" @click="onCancel()">取消</button>
<button class="toi-btn ok-btn" @click="onConfirm()">确定</button>
</div>
</div>
</TOverlay>
</template>
<script lang="ts" setup>
import TOverlay from "@comp/app/t-overlay.vue";
import showSnackbar from "@comp/func/snackbar.js";
import useAppStore from "@store/app.js";
import { storeToRefs } from "pinia";
import { ref } from "vue";
const { imageQualityPercent } = storeToRefs(useAppStore());
const model = defineModel<boolean>({ default: false });
const quality = ref<number>(imageQualityPercent.value);
function onCancel(): void {
model.value = false;
quality.value = imageQualityPercent.value;
}
async function onConfirm(): Promise<void> {
if (quality.value === imageQualityPercent.value) {
model.value = false;
showSnackbar.info(`图片质量未修改`);
return;
}
imageQualityPercent.value = quality.value;
model.value = false;
showSnackbar.success(`图片质量已修改为 ${quality.value}%`);
}
</script>
<style lang="scss" scoped>
.toi-box {
display: flex;
width: 400px;
flex-direction: column;
padding: 8px;
border-radius: 4px;
background-color: var(--box-bg-1);
color: var(--app-page-content);
gap: 12px;
}
.toi-top {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-family: var(--font-title);
row-gap: 4px;
text-align: center;
.toi-title {
color: var(--common-text-title);
font-size: 20px;
}
.toi-desc {
font-family: var(--font-text);
font-size: 12px;
}
}
.toi-mid {
display: flex;
width: 100%;
box-sizing: border-box;
align-items: center;
justify-content: center;
padding-left: 12px;
margin-top: 24px;
margin-bottom: 24px;
column-gap: 24px;
.toi-input {
max-width: 100px;
height: 48px;
}
}
.toi-bottom {
display: flex;
width: 100%;
align-items: center;
justify-content: flex-end;
gap: 16px;
}
.toi-btn {
position: relative;
display: flex;
box-sizing: border-box;
align-items: center;
justify-content: center;
padding: 4px 20px;
border-radius: 24px;
background: var(--tgc-btn-1);
color: var(--btn-text);
cursor: pointer;
font-family: var(--font-title);
&.no-btn {
opacity: 0.8;
}
&:hover {
opacity: 0.75;
}
}
</style>

View File

@@ -1,12 +1,13 @@
<!-- 近期活动卡片组件用户-->
<template>
<div class="ph-pos-user-card">
<div class="ph-pos-user-card" ref="posRef">
<div class="ph-puc-top">
<div class="title">
<v-icon title="已完成" color="var(--tgc-od-green)" v-if="props.pos.is_finished">
mdi-checkbox-marked-circle-outline
</v-icon>
<v-icon v-else title="未完成" color="var(--tgc-od-white)">mdi-circle</v-icon>
<span>{{ props.pos.name }}</span>
<span @click="sharePos()" title="点击分享">{{ props.pos.name }}</span>
</div>
<div class="subtitle">
<!-- 处理幽境危战 -->
@@ -29,13 +30,7 @@
<!-- 处理真境剧诗 -->
<template v-else-if="props.pos.type === ActCalendarTypeEnum.RoleCombat">
<div class="combat-append" @click="toCombat()" title="点击前往剧诗页面">
<template v-if="!props.pos.role_combat_detail.is_unlock">
<span>未解锁</span>
</template>
<template v-else-if="!props.pos.role_combat_detail.has_data">
<span>尚未挑战</span>
</template>
<span v-else>{{ props.pos.role_combat_detail.max_round_id }}</span>
<span>{{ getCombatStat(props.pos.role_combat_detail) }}</span>
</div>
</template>
<!-- 处理深境螺旋 -->
@@ -69,7 +64,7 @@
</div>
<div class="ph-puc-duration">
<template v-if="isStart">
<span title="剩余时间">{{ stamp2LastTime(restTs * 1000) }}</span>
<span title="剩余时间" data-html2canvas-ignore>{{ stamp2LastTime(restTs * 1000) }}</span>
<span title="活动时间">
{{ timestampToDate(Number(props.pos.start_timestamp) * 1000) }} ~
{{ timestampToDate(Number(props.pos.end_timestamp) * 1000) }}
@@ -98,8 +93,9 @@
import TMiImg from "@comp/app/t-mi-img.vue";
import { ActCalendarTypeEnum } from "@enum/game.js";
import { getHardChallengeDesc } from "@Sql/utils/transUserRecord.js";
import { generateShareImg } from "@utils/TGShare.js";
import { stamp2LastTime, timestampToDate } from "@utils/toolFunc.js";
import { computed, onMounted, onUnmounted, ref } from "vue";
import { computed, onMounted, onUnmounted, ref, useTemplateRef } from "vue";
import { useRouter } from "vue-router";
type PhCompPositionUserProps = { pos: TGApp.Game.ActCalendar.ActItem };
@@ -111,6 +107,7 @@ const router = useRouter();
const props = defineProps<PhCompPositionUserProps>();
const emits = defineEmits<PhCompPositionUserEmits>();
const posEl = useTemplateRef<HTMLDivElement>("posRef");
const endTs = ref<number>(0);
const restTs = ref<number>(0);
@@ -129,6 +126,10 @@ onMounted(() => {
timer = setInterval(handlePosition, 1000);
});
onUnmounted(() => {
if (timer !== null) clearInterval(timer);
});
function handlePosition(): void {
if (restTs.value < 1) {
if (timer !== null) clearInterval(timer);
@@ -155,9 +156,17 @@ function showMaterial(reward: TGApp.Game.ActCalendar.ActReward): void {
emits("clickM", reward);
}
onUnmounted(() => {
if (timer !== null) clearInterval(timer);
});
function getCombatStat(detail: TGApp.Game.ActCalendar.ActRoleCombat): string {
if (!detail.is_unlock) return "未解锁";
if (!detail.has_data) return "尚未挑战";
if (detail.difficulty_id < 5) return `${detail.max_round_id}`;
return `月谕模式·第${detail.max_round_id}幕·圣牌${detail.tarot_finished_cnt}`;
}
async function sharePos(): Promise<void> {
if (!posEl.value) return;
await generateShareImg(`活动-${props.pos.name}.png`, posEl.value, 2);
}
</script>
<style lang="scss" scoped>
.ph-pos-user-card {
@@ -190,6 +199,10 @@ onUnmounted(() => {
justify-content: flex-start;
column-gap: 4px;
font-family: var(--font-title);
span {
cursor: pointer;
}
}
.subtitle {
@@ -267,6 +280,7 @@ onUnmounted(() => {
}
.icon {
position: relative;
z-index: 1;
width: 40px;
height: 40px;

View File

@@ -11,7 +11,8 @@
@click="toChannel(item)"
>
<TMiImg :src="item.icon" alt="icon" :ori="true" />
<span>{{ item.title }}</span>
<span class="toc-list-title">{{ item.title }}</span>
<span class="toc-list-id">GID:{{ item.gid }}</span>
</div>
</div>
</div>
@@ -59,32 +60,38 @@ async function toChannel(item: ChannelItem): Promise<void> {
</script>
<style lang="css" scoped>
.toc-box {
padding: 10px;
border-radius: 5px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 8px;
border-radius: 4px;
background: var(--app-page-bg);
row-gap: 12px;
}
.toc-title {
color: var(--common-text-title);
font-family: var(--font-title);
font-size: 20px;
font-size: 18px;
}
.toc-list {
display: grid;
margin-top: 10px;
grid-gap: 10px;
grid-template-columns: repeat(2, 1fr);
gap: 8px;
grid-template-columns: repeat(3, 1fr);
}
.toc-list-item {
position: relative;
display: flex;
align-items: center;
justify-content: start;
border: 1px solid var(--common-shadow-1);
border-radius: 5px;
border-radius: 4px;
background: var(--box-bg-1);
color: var(--box-text-1);
column-gap: 8px;
cursor: pointer;
transition: all 0.5s linear;
@@ -95,17 +102,22 @@ async function toChannel(item: ChannelItem): Promise<void> {
}
img {
width: 45px;
height: 45px;
margin-right: 10px;
border-bottom-left-radius: 5px;
border-top-left-radius: 5px;
width: 48px;
height: 48px;
}
span {
margin-right: 10px;
.toc-list-title {
margin-right: 8px;
font-family: var(--font-title);
font-size: 16px;
}
.toc-list-id {
position: absolute;
right: 4px;
bottom: 2px;
font-size: 6px;
opacity: 0.3;
}
}
</style>

View File

@@ -107,9 +107,8 @@ const relicsBox = computed<AvatarRelics>(() => {
];
});
const isFetterMax = computed<boolean>(() => {
if (props.modelValue.avatar.id === 10000005 || props.modelValue.avatar.id === 10000007) {
return true;
}
const skipList = [10000005, 10000007, 10000117, 10000118];
if (skipList.includes(props.modelValue.avatar.id)) return true;
return props.modelValue.avatar.fetter === 10;
});
const skills = computed<Array<TGApp.Game.Avatar.Skill>>(() =>

View File

@@ -3,7 +3,9 @@
<div class="tucfi-label">
<slot name="label">{{ props.label }}</slot>
</div>
<div v-if="props.data === null"><span class="tucfi-data">暂无数据</span></div>
<div v-if="!props.data">
<span class="tucfi-data">暂无数据</span>
</div>
<div v-else-if="!Array.isArray(props.data)" class="tucfi-data">
<TItemBox :model-value="getBox(props.data)" />
</div>

View File

@@ -1,8 +1,8 @@
<template>
<div class="tuco-box">
<TucTile title="最佳记录" :val="props.data.max_round_id" />
<TucTile title="最佳记录" :val="getBestVal()" />
<TucTile :title="`获得星章-${props.data.medal_num}`" :val="props.data.get_medal_round_list" />
<TucTile :title="getTitle()" :val="`第${props.data.max_round_id}幕`" />
<TucTile :title="getRoundTitle()" :val="getRoundVal()" />
<TucTile title="消耗幻剧之花" :val="props.data.coin_num" />
<TucFight label="最快完成演出" :data="props.fights.shortest_avatar_list" />
<TucTile title="总耗时" :val="getTime()" />
@@ -21,7 +21,7 @@ type TucOverviewProps = { data: TGApp.Game.Combat.Stat; fights: TGApp.Game.Comba
const props = defineProps<TucOverviewProps>();
function getTitle(): string {
function getRoundTitle(): string {
switch (props.data.difficulty_id) {
case 0:
return "未选择";
@@ -33,11 +33,23 @@ function getTitle(): string {
return "困难模式";
case 4:
return "卓越模式";
case 5:
return "月谕模式";
default:
return `未知模式${props.data.difficulty_id}`;
}
}
function getBestVal(): string {
if (props.data.difficulty_id < 5) return `${props.data.max_round_id}`;
return `${props.data.max_round_id}幕·圣牌${props.data.tarot_finished_cnt}`;
}
function getRoundVal(): string {
if (props.data.difficulty_id < 5) return `${props.data.max_round_id}`;
return `${props.data.tarot_finished_cnt + props.data.max_round_id}`;
}
function getTime(): string {
const sec = props.fights.total_use_time % 60;
const min = (props.fights.total_use_time - sec) / 60;
@@ -48,7 +60,7 @@ function getTime(): string {
.tuco-box {
display: grid;
width: 100%;
grid-gap: 8px;
gap: 8px;
grid-template-columns: repeat(3, 1fr);
}
</style>

View File

@@ -1,19 +1,23 @@
<!-- 真境剧诗单轮次卡片组件 -->
<template>
<div class="tucr-box">
<div class="tucr-title">
<img :src="`/icon/star/combat${modelValue.is_get_medal ? 1 : 0}.webp`" alt="combat" />
<span class="main">{{ modelValue.round_id }}</span>
<span class="sub">{{ timestampToDate(Number(modelValue.finish_time) * 1000) }}</span>
<img :src="`/icon/combat/${getIcon()}.webp`" alt="combat" />
<span class="main" v-if="props.round.is_tarot">
圣牌挑战·{{ props.round.tarot_serial_no }}
</span>
<span class="main" v-else>{{ props.round.round_id }}</span>
<span class="sub">{{ timestampToDate(Number(props.round.finish_time) * 1000) }}</span>
</div>
<div class="tucr-content">
<TucSub title="出演角色" class="main">
<TucAvatars :model-value="modelValue.avatars" :detail="true" />
<TucAvatars :model-value="props.round.avatars" :detail="true" />
</TucSub>
<TucSub title="辉彩祝福" class="main">
<TucBuffs :model-value="modelValue.splendour_buff" />
<TucBuffs :model-value="props.round.splendour_buff" />
</TucSub>
<TucSub :title="`神秘收获(${modelValue.choice_cards.length})`" class="sub">
<TucCards :model-value="modelValue.choice_cards" />
<TucSub :title="`神秘收获(${props.round.choice_cards.length})`" class="sub">
<TucCards :model-value="props.round.choice_cards" />
</TucSub>
</div>
</div>
@@ -26,7 +30,12 @@ import TucBuffs from "./tuc-buffs.vue";
import TucCards from "./tuc-cards.vue";
import TucSub from "./tuc-sub.vue";
defineProps<{ modelValue: TGApp.Game.Combat.RoundData }>();
type TucRoundProps = { round: TGApp.Game.Combat.RoundData };
const props = defineProps<TucRoundProps>();
function getIcon(): string {
return `${props.round.is_tarot ? "tarot" : "star"}_${props.round.is_get_medal ? "1" : "0"}`;
}
</script>
<style lang="css" scoped>
.tucr-box {
@@ -49,8 +58,8 @@ defineProps<{ modelValue: TGApp.Game.Combat.RoundData }>();
column-gap: 4px;
img {
width: 30px;
aspect-ratio: 1;
height: 30px;
object-fit: contain;
}
.main {

View File

@@ -1,18 +1,29 @@
<!-- 真境剧诗概况卡片组件 -->
<template>
<div class="tuct-box">
<div class="tuct-title">
<slot name="title">{{ title }}</slot>
<slot name="title">{{ props.title }}</slot>
</div>
<div class="tuct-text" v-if="!Array.isArray(val)">
<slot name="text">{{ val }}</slot>
<div class="tuct-text" v-if="!Array.isArray(props.val)">
<slot name="text">{{ props.val }}</slot>
</div>
<div class="tuct-icons" v-else>
<img v-for="(v, idx) in val" :key="idx" :src="`/icon/star/combat${v}.webp`" :alt="`${v}`" />
<template v-for="(v, idx) in props.val" :key="idx">
<img
v-if="idx < 10"
:src="`/icon/combat/star_${v}.webp`"
:alt="`${v}`"
:title="`第${idx + 1}幕`"
/>
<img v-else :src="`/icon/combat/tarot_${v}.webp`" :alt="`${v}`" :title="`圣牌${idx - 9}`" />
</template>
</div>
</div>
</template>
<script lang="ts" setup>
defineProps<{ title: string; val: string | number | Array<number> }>();
type TucTileProps = { title: string; val: string | number | Array<number> };
const props = defineProps<TucTileProps>();
</script>
<style lang="css" scoped>
.tuct-box {
@@ -47,7 +58,7 @@ defineProps<{ title: string; val: string | number | Array<number> }>();
img {
height: 30px;
aspect-ratio: 1;
object-fit: contain;
}
}
</style>

View File

@@ -0,0 +1,144 @@
<!-- 千星奇域概览单项组件 -->
<template>
<div class="gbr-dl-box">
<div class="gbr-dl-progress" />
<div class="gbr-dl-icon">
<img :alt="props.data.name" :src="getIcon()" />
</div>
<div class="gbr-dl-base">
<div class="gbr-dl-name">{{ props.data.name }}</div>
<div class="gbr-dl-time">{{ props.data.time }}</div>
</div>
<div class="gbr-dl-info">
<div class="gbr-dl-cnt">{{ props.count }}</div>
<div class="gbr-dl-hint" v-if="hint !== ''">{{ hint }}</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { computed } from "vue";
export type GbrDataLineProps = { data: TGApp.Sqlite.GachaRecords.TableGachaB; count: number };
const props = defineProps<GbrDataLineProps>();
const hint = getEndHint();
function getIcon(): string {
console.log(props.data);
// const find = AppGachaBData.find((i) => i.id.toString() === props.data.itemId);
// if (!find) return `/source/UI/paimon.webp`;
// return `https://api.hakush.in/gi/UI/${find.icon}.webp`;
// TODO: 缺失元数据
return `/source/UI/paimon.webp`;
}
function getEndHint(): string {
if (props.data.gachaType === "1000") return "";
if (!props.data.isUp) return "歪";
return "";
}
const progressColor = computed<string>(() => {
if (hint === "UP" && props.data.rank === "5") return "#d19a66";
if (hint === "UP" && props.data.rank === "4") return "#c678dd";
if (hint === "歪") return "#e06c75";
return "#61afef";
});
const progressWidth = computed<string>(() => {
let final = 10;
if (props.data.rank === "5") {
if (props.data.gachaType === "302") final = 80;
else final = 90;
} else if (props.data.rank === "4") final = 10;
else return "0%";
return ((props.count / final) * 100).toFixed(2) + "%";
});
</script>
<style lang="scss" scoped>
.gbr-dl-box {
position: relative;
display: flex;
width: 100%;
height: 48px;
box-sizing: border-box;
align-items: center;
justify-content: flex-start;
padding: 8px;
border: 1px solid var(--common-shadow-1);
border-radius: 4px;
background: var(--box-bg-2);
column-gap: 4px;
}
.gbr-dl-progress {
position: absolute;
bottom: 0;
left: 0;
width: v-bind(progressWidth); /* stylelint-disable-line value-keyword-case */
max-width: 100%;
height: 4px;
border-radius: 4px;
background: v-bind(progressColor); /* stylelint-disable-line value-keyword-case */
}
.gbr-dl-icon {
display: flex;
width: 32px;
height: 32px;
flex-shrink: 0;
align-items: center;
justify-content: center;
img {
width: 100%;
height: 100%;
}
}
.gbr-dl-base {
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: center;
}
.gbr-dl-name {
color: var(--common-text-title);
font-family: var(--font-title);
font-size: 14px;
line-height: 18px;
}
.gbr-dl-time {
color: var(--box-text-7);
font-size: 12px;
line-height: 14px;
}
.gbr-dl-info {
display: flex;
align-items: center;
justify-content: center;
margin-left: auto;
column-gap: 4px;
}
.gbr-dl-cnt {
color: var(--common-text-title);
font-family: var(--font-title);
}
.gbr-dl-hint {
display: flex;
width: 32px;
height: 32px;
align-items: center;
justify-content: center;
padding: 4px;
border-radius: 50%;
background: var(--box-bg-3);
color: v-bind(progressColor); /* stylelint-disable-line value-keyword-case */
font-family: var(--font-title);
transform: rotate(25deg);
}
</style>

View File

@@ -0,0 +1,263 @@
<!-- 千星奇域概览数据视图组件 -->
<template>
<div class="gbr-dv-container">
<div class="gbr-dvt-title">
<span>{{ title }}</span>
<span>{{ props.dataVal.length }}</span>
</div>
<div class="gbr-dvt-subtitle">
<span v-show="props.dataVal.length === 0">暂无数据</span>
<span v-show="props.dataVal.length !== 0">{{ startDate }} ~ {{ endDate }}</span>
</div>
<div class="gbr-mid-list">
<div class="gbr-ml-item">
<span>4已垫</span>
<span>{{ reset4count - 1 }}</span>
</div>
<div class="gbr-ml-item">
<span>5已垫</span>
<span>{{ reset5count - 1 }}</span>
</div>
<div class="gbr-ml-item">
<span>5平均</span>
<span>{{ star5avg }}</span>
</div>
</div>
<div class="gbr-mid-list">
<div class="gbr-ml-item">
<span>5统计</span>
<span>{{ getTitle("5") }}</span>
</div>
<div class="gbr-ml-item">
<span>4统计</span>
<span>{{ getTitle("4") }}</span>
</div>
</div>
<!-- 这边放具体物品的列表 -->
<div class="gbr-bottom">
<v-tabs v-model="tab" density="compact">
<v-tab value="5">5</v-tab>
<v-tab value="4">4</v-tab>
<v-tab value="3">3</v-tab>
</v-tabs>
<v-window v-model="tab" class="gbr-bottom-window">
<v-window-item value="5" class="gbr-b-window-item">
<v-virtual-scroll :items="star5List" :item-height="48">
<template #default="{ item }">
<GbrDataLine :data="item.data" :count="item.count" />
</template>
</v-virtual-scroll>
</v-window-item>
<v-window-item value="4" class="gbr-b-window-item">
<v-virtual-scroll :items="star4List" :item-height="48">
<template #default="{ item }">
<GbrDataLine :data="item.data" :count="item.count" />
</template>
</v-virtual-scroll>
</v-window-item>
<v-window-item value="3" class="gbr-b-window-item">
<v-virtual-scroll :items="star3List" :item-height="48">
<template #default="{ item }">
<GbrDataLine :data="item.data" :count="item.count" />
</template>
</v-virtual-scroll>
</v-window-item>
</v-window>
</div>
</div>
</template>
<script lang="ts" setup>
import { onMounted, ref, shallowRef, watch } from "vue";
import GbrDataLine, { type GbrDataLineProps } from "./gbr-data-line.vue";
type GachaDataViewProps = {
dataType: "normal" | "boy" | "girl";
dataVal: Array<TGApp.Sqlite.GachaRecords.TableGachaB>;
};
const props = defineProps<GachaDataViewProps>();
// data
const loading = ref<boolean>(true); // 是否加载完
const title = ref<string>(""); // 卡片标题
const startDate = ref<string>(""); // 最早的时间
const endDate = ref<string>(""); // 最晚的时间
const star5List = shallowRef<Array<GbrDataLineProps>>([]); // 5星物品数据
const star4List = shallowRef<Array<GbrDataLineProps>>([]); // 4星物品数据
const star3List = shallowRef<Array<GbrDataLineProps>>([]);
const reset5count = ref<number>(1); // 5星垫抽数量
const reset4count = ref<number>(1); // 4星垫抽数量
const reset3count = ref<number>(1); // 3星垫抽数量
const star3count = ref<number>(0); // 3星物品数量
const star5avg = ref<string>(""); // 5星平均抽数
const tab = ref<string>("5"); // tab
onMounted(() => {
loadData();
loading.value = false;
});
function loadData(): void {
title.value = getTitle("top");
const tempData = props.dataVal;
const temp5Data: Array<GbrDataLineProps> = [];
const temp4Data: Array<GbrDataLineProps> = [];
const temp3Data: Array<GbrDataLineProps> = [];
// 按照 id 升序
tempData
.sort((a, b) => a.id.localeCompare(b.id))
.forEach((item) => {
// 处理时间
if (startDate.value === "" || item.time < startDate.value) startDate.value = item.time;
if (endDate.value === "" || item.time > endDate.value) endDate.value = item.time;
if (item.rank === "2") {
reset3count.value++;
reset4count.value++;
reset5count.value++;
} else if (item.rank === "3") {
reset4count.value++;
reset5count.value++;
temp3Data.push({ data: item, count: reset3count.value });
reset3count.value = 1;
} else if (item.rank === "4") {
reset5count.value++;
temp4Data.push({ data: item, count: reset4count.value });
reset4count.value = 1;
} else if (item.rank === "5") {
reset4count.value++;
temp5Data.push({ data: item, count: reset5count.value });
reset5count.value = 1;
}
});
star5List.value = temp5Data.reverse();
star4List.value = temp4Data.reverse();
star3List.value = temp3Data.reverse();
star5avg.value = getStar5Avg();
}
// 获取标题
function getTitle(type: "top" | "5" | "4" | "3"): string {
if (type === "top") {
if (props.dataType === "normal") return "常驻颂愿";
if (props.dataType === "boy") return "活动颂愿(男)";
if (props.dataType === "girl") return "活动颂愿(女)";
return "";
}
if (props.dataVal.length === 0) return "暂无数据";
if (type === "5") {
// 5星物品统计 00.00%
return `${star5List.value.length} [${((star5List.value.length * 100) / props.dataVal.length)
.toFixed(2)
.padStart(5, "0")}%]`;
}
if (type === "4") {
// 4星物品统计
return `${star4List.value.length} [${((star4List.value.length * 100) / props.dataVal.length)
.toFixed(2)
.padStart(5, "0")}%]`;
}
// 3星物品统计
return `${star3count.value} [${((star3count.value * 100) / props.dataVal.length)
.toFixed(2)
.padStart(5, "0")}%]`;
}
// 获取5星平均抽数
function getStar5Avg(): string {
const resetList = star5List.value.map((item) => item.count);
if (resetList.length === 0) return "0";
const total = resetList.reduce((a, b) => a + b);
return (total / star5List.value.length).toFixed(2);
}
// 监听数据变化
watch(
() => props.dataVal,
() => {
star5List.value = [];
star4List.value = [];
reset5count.value = 1;
reset4count.value = 1;
star3count.value = 1;
startDate.value = "";
endDate.value = "";
star5avg.value = "";
tab.value = "5";
loadData();
},
);
</script>
<style lang="css" scoped>
.gbr-dv-container {
height: 100%;
box-sizing: border-box;
padding: 8px;
border-radius: 4px;
background: var(--box-bg-1);
}
.gbr-dvt-title {
display: flex;
width: 100%;
align-items: center;
justify-content: space-between;
color: var(--common-text-title);
font-family: var(--font-title);
font-size: 18px;
}
.gbr-dvt-subtitle {
width: 100%;
font-family: var(--font-text);
font-size: 12px;
opacity: 0.6;
}
.gbr-mid-list {
padding-top: 4px;
padding-bottom: 4px;
border-top: 1px solid var(--common-shadow-4);
color: var(--box-text-7);
}
.gbr-ml-item {
display: flex;
width: 100%;
align-items: center;
justify-content: space-between;
font-family: var(--font-title);
font-size: 14px;
}
.gbr-bottom {
position: relative;
display: flex;
width: 100%;
height: calc(100% - 150px);
box-sizing: border-box;
flex-direction: column;
gap: 8px;
}
.gbr-bottom-window {
position: relative;
height: calc(100vh - 428px);
overflow-y: auto;
}
.gbr-b-window-item {
position: relative;
width: 100%;
box-sizing: border-box;
padding-right: 4px;
}
/* stylelint-disable selector-class-pattern */
:deep(.v-virtual-scroll__item + .v-virtual-scroll__item) {
margin-top: 8px;
}
/* stylelint-enable selector-class-pattern */
</style>

View File

@@ -0,0 +1,34 @@
<!-- 千星奇域祈愿概览组件 -->
<template>
<div class="gro-o-container">
<GbrDataView :data-val="normalData" data-type="normal" />
<GbrDataView :data-val="boyData" data-type="boy" />
<GbrDataView :data-val="girlData" data-type="girl" />
</div>
</template>
<script lang="ts" setup>
import { computed } from "vue";
import GbrDataView from "./gbr-data-view.vue";
type GachaOverviewProps = { modelValue: Array<TGApp.Sqlite.GachaRecords.TableGachaB> };
const props = defineProps<GachaOverviewProps>();
const normalData = computed<Array<TGApp.Sqlite.GachaRecords.TableGachaB>>(() =>
props.modelValue.filter((item) => item.opGachaType === "1000"),
);
const girlData = computed<Array<TGApp.Sqlite.GachaRecords.TableGachaB>>(() =>
props.modelValue.filter((item) => item.opGachaType === "20011" || item.opGachaType === "20012"),
);
const boyData = computed<Array<TGApp.Sqlite.GachaRecords.TableGachaB>>(() =>
props.modelValue.filter((item) => item.opGachaType === "20021" || item.opGachaType === "20022"),
);
</script>
<style lang="css" scoped>
.gro-o-container {
display: grid;
height: 100%;
column-gap: 8px;
grid-template-columns: repeat(3, 1fr);
}
</style>

View File

@@ -0,0 +1,62 @@
<!-- 千星奇域数据表格 -->
<template>
<v-data-table
:headers="headers"
:items="props.modelValue"
fixed-header
fixed-footer
class="gbr-t-box"
>
<template v-slot:item="{ item }">
<tr class="gbr-t-tr">
<td>{{ item.time }}</td>
<td>{{ getPool(item.opGachaType) }}</td>
<td>{{ item.type }}</td>
<td>{{ item.name }}</td>
<td>{{ item.rank }}</td>
</tr>
</template>
</v-data-table>
</template>
<script lang="ts" setup>
type GroTableProps = { modelValue: Array<TGApp.Sqlite.GachaRecords.TableGachaB> };
const props = defineProps<GroTableProps>();
const headers = <const>[
{ title: "时间", align: "center", key: "time" },
{ title: "卡池", align: "center", key: "opGachaType" },
{ title: "类型", align: "center", key: "type" },
{ title: "名称", align: "center", key: "name" },
{ title: "星级", align: "center", key: "rank" },
];
function getPool(type: string) {
switch (type) {
case "1000":
return "常驻颂愿";
case "2000":
return "活动颂愿";
case "20011":
case "20012":
return "活动颂愿-男";
case "20021":
case "20022":
return "活动颂愿-女";
default:
return "未知";
}
}
</script>
<style lang="css" scoped>
.gbr-t-box {
height: calc(100vh - 200px);
padding-right: 5px;
border-radius: 5px;
overflow-y: auto;
}
.gbr-t-tr {
text-align: center;
}
</style>

View File

@@ -20,7 +20,7 @@ import { computed } from "vue";
import { AppGachaData } from "@/data/index.js";
export type GroDataLineProps = { data: TGApp.Sqlite.GachaRecords.SingleTable; count: number };
export type GroDataLineProps = { data: TGApp.Sqlite.GachaRecords.TableGacha; count: number };
const props = defineProps<GroDataLineProps>();
const hint = getEndHint();

View File

@@ -64,7 +64,7 @@ import GroDataLine, { type GroDataLineProps } from "./gro-data-line.vue";
type GachaDataViewProps = {
dataType: "new" | "avatar" | "weapon" | "normal" | "mix";
dataVal: Array<TGApp.Sqlite.GachaRecords.SingleTable>;
dataVal: Array<TGApp.Sqlite.GachaRecords.TableGacha>;
};
const props = defineProps<GachaDataViewProps>();

View File

@@ -0,0 +1,134 @@
<!-- 嵌入游戏内容的iframe组件 -->
<template>
<div class="gro-iframe-container">
<v-tabs class="gro-ic-tabs" v-model="poolTab" align-tabs="start" direction="vertical">
<v-tab v-for="(item, index) in tabList" :key="index" :value="item.value">
<template v-if="item.beyond">
<img src="/icon/nation/千星奇域.webp" title="千星奇域" alt="beyond" />
</template>
{{ item.label }}
</v-tab>
</v-tabs>
<iframe class="gro-iframe" :src="link" style="width: 100%; height: 100%; border: none" />
</div>
</template>
<script lang="ts" setup>
import showSnackbar from "@comp/func/snackbar.js";
import takumiReq from "@req/takumiReq.js";
import useUserStore from "@store/user.js";
import { storeToRefs } from "pinia";
import { onMounted, ref, shallowRef, watch } from "vue";
/**
* 卡池类型-ID映射
* @remarks
* 目前缺失集录&新手池
* TODO: 动态获取当前卡池类型&ID映射
*/
const GachaIdMap: Record<string, string> = {
"200": "34ff1a235049182fd199d285110e3e7d292c50cd", // 常驻
"301": "182e725d99b742b14839117650d3e79628cc6221", //角色活动
"302": "8ff7a7d42bea79b0d54e92fdb58a20f971490372", // 武器活动
"400": "bb0486115a7e7c4bd2994135f7d212014b17173b", // 角色活动-2
"1000": "f3f5090a8ec0b28f15805c9969aa6c4ec357", // 千星奇域常驻
"20011": "a8d0a985efb4ed61eb2e73a86a57237bd116", // 千星奇域角色活动-男
"20021": "57016dec6b768231ba1342c01935417a799b", // 千星奇域角色活动-女
};
const tabNormal: ReadonlyArray<GroTab> = [
{ label: "常驻祈愿", value: "200" },
{ label: "角色活动祈愿", value: "301" },
{ label: "武器活动祈愿", value: "302" },
{ label: "角色活动祈愿-2", value: "400" },
];
const tabBeyond: ReadonlyArray<GroTab> = [
{ label: "常驻颂愿", value: "1000", beyond: true },
{ label: "活动颂愿-男", value: "20011", beyond: true },
{ label: "活动颂愿-女", value: "20021", beyond: true },
];
type GroTabKey = keyof typeof GachaIdMap;
type GroTab = { label: string; value: string; beyond?: boolean };
type GroIframeProps = { mode: "normal" | "beyond" };
const props = defineProps<GroIframeProps>();
const { cookie, account } = storeToRefs(useUserStore());
const authkey = ref<string>("");
const link = ref<string>("");
const poolTab = ref<GroTabKey>("200");
const tabList = shallowRef<ReadonlyArray<GroTab>>(props.mode === "beyond" ? tabBeyond : tabNormal);
onMounted(async () => {
link.value = await getUrl();
});
watch(
() => poolTab.value,
async () => {
link.value = await getUrl();
},
);
async function getUrl(): Promise<string> {
const path = "https://webstatic.mihoyo.com/hk4e/event/e20190909gacha-v3/index.html";
const pathB = "https://webstatic.mihoyo.com/hk4e/event/e20250716gacha/index.html";
const pathF = poolTab.value.length < 4 ? path : pathB;
if (authkey.value === "") await refreshAuthkey();
const param: Record<string, string> = {
win_mode: "fullscreen",
no_joypad_close: "1",
authkey_ver: "1",
sign_type: "2",
auth_appid: "webview_gacha",
gacha_id: GachaIdMap[poolTab.value],
timestamp: Math.floor(Date.now() / 1000).toString(),
lang: "zh-cn",
device_type: "pc",
region: account.value.region,
authkey: authkey.value,
game_biz: account.value.gameBiz,
};
const targetLink = new URL(pathF);
for (const key in param) {
targetLink.searchParams.append(key, param[key]);
}
return targetLink.toString();
}
async function refreshAuthkey(): Promise<void> {
if (!cookie.value || !account.value) {
return;
}
const authkeyRes = await takumiReq.bind.authKey(cookie.value, account.value);
if (typeof authkeyRes === "string") {
authkey.value = authkeyRes;
} else {
showSnackbar.error("获取authkey失败");
return;
}
}
</script>
<style lang="scss" scoped>
.gro-iframe-container {
display: flex;
width: 100%;
height: 100%;
align-items: flex-start;
justify-content: space-between;
}
.gro-ic-tabs {
height: 100%;
img {
width: 24px;
height: 24px;
margin-right: 4px;
}
}
.gro-ic-window {
height: 100%;
flex: 1;
}
</style>

View File

@@ -12,22 +12,22 @@ import { computed, ref, watch } from "vue";
import GroDataView from "./gro-data-view.vue";
type GachaOverviewProps = { modelValue: Array<TGApp.Sqlite.GachaRecords.SingleTable> };
type GachaOverviewProps = { modelValue: Array<TGApp.Sqlite.GachaRecords.TableGacha> };
const props = defineProps<GachaOverviewProps>();
const newData = computed<Array<TGApp.Sqlite.GachaRecords.SingleTable>>(() =>
const newData = computed<Array<TGApp.Sqlite.GachaRecords.TableGacha>>(() =>
props.modelValue.filter((item) => item.uigfType === "100"),
);
const normalData = computed<Array<TGApp.Sqlite.GachaRecords.SingleTable>>(() =>
const normalData = computed<Array<TGApp.Sqlite.GachaRecords.TableGacha>>(() =>
props.modelValue.filter((item) => item.uigfType === "200"),
);
const avatarData = computed<Array<TGApp.Sqlite.GachaRecords.SingleTable>>(() =>
const avatarData = computed<Array<TGApp.Sqlite.GachaRecords.TableGacha>>(() =>
props.modelValue.filter((item) => item.uigfType === "301"),
);
const weaponData = computed<Array<TGApp.Sqlite.GachaRecords.SingleTable>>(() =>
const weaponData = computed<Array<TGApp.Sqlite.GachaRecords.TableGacha>>(() =>
props.modelValue.filter((item) => item.uigfType === "302"),
);
const mixData = computed<Array<TGApp.Sqlite.GachaRecords.SingleTable>>(() =>
const mixData = computed<Array<TGApp.Sqlite.GachaRecords.TableGacha>>(() =>
props.modelValue.filter((item) => item.uigfType === "500"),
);
@@ -56,7 +56,7 @@ watch(
.gro-o-container {
display: grid;
height: 100%;
grid-column-gap: 8px;
column-gap: 8px;
grid-template-columns: v-bind(cnCols); /* stylelint-disable-line value-keyword-case */
}
</style>

View File

@@ -19,7 +19,7 @@
</v-data-table>
</template>
<script lang="ts" setup>
type GroTableProps = { modelValue: Array<TGApp.Sqlite.GachaRecords.SingleTable> };
type GroTableProps = { modelValue: Array<TGApp.Sqlite.GachaRecords.TableGacha> };
const props = defineProps<GroTableProps>();

View File

@@ -62,9 +62,11 @@ import { computed, onMounted, ref, shallowRef, watch } from "vue";
type UgoUidProps = { mode: "import" | "export" };
type UgoUidItem = { uid: string; length: number; time: string };
const fpEmptyText = "点击选择文件路径";
const props = defineProps<UgoUidProps>();
const visible = defineModel<boolean>();
const fp = ref<string>("未选择");
const fp = ref<string>(fpEmptyText);
const dataRaw = shallowRef<TGApp.Plugins.UIGF.Schema4>();
const data = shallowRef<Array<UgoUidItem>>([]);
const selectedData = shallowRef<Array<UgoUidItem>>([]);
@@ -91,7 +93,7 @@ async function refreshData(): Promise<void> {
data.value = [];
dataRaw.value = undefined;
if (props.mode === "import") {
fp.value = "未选择";
fp.value = fpEmptyText;
await handleImportData();
} else {
fp.value = await getDefaultSavePath();
@@ -118,7 +120,7 @@ async function selectFile(): Promise<void> {
}
async function handleImportData(): Promise<void> {
if (fp.value === "未选择") return;
if (fp.value === fpEmptyText) return;
try {
await showLoading.start("正在导入数据...", "正在验证数据...");
const check = await verifyUigfData(fp.value, true);
@@ -161,7 +163,7 @@ async function handleExportData(): Promise<void> {
data.value = tmpData;
}
function parseDataRaw(data: TGApp.Sqlite.GachaRecords.SingleTable[]): UgoUidItem {
function parseDataRaw(data: TGApp.Sqlite.GachaRecords.TableGacha[]): UgoUidItem {
const timeList = data.map((item) => new Date(item.time).getTime());
return {
uid: data[0].uid,
@@ -178,7 +180,7 @@ async function handleSelected(): Promise<void> {
async function handleImport(): Promise<void> {
if (!dataRaw.value) {
showSnackbar.error("未获取到数据!");
fp.value = "未选择";
fp.value = fpEmptyText;
return;
}
if (selectedData.value.length === 0) {
@@ -234,6 +236,7 @@ async function handleExport(): Promise<void> {
position: relative;
display: flex;
width: 100%;
flex-wrap: wrap;
align-items: flex-end;
justify-content: space-between;
column-gap: 10px;
@@ -249,6 +252,7 @@ async function handleExport(): Promise<void> {
color: var(--tgc-od-white);
cursor: pointer;
font-size: 12px;
word-break: break-all;
}
.ugo-header {

View File

@@ -278,6 +278,7 @@ async function autoSign(ck: TGApp.App.Account.Cookie, ch?: string): Promise<void
}
await TGLogger.Script("[米游币任务]正在执行打卡");
const ckSign = { stoken: ck.stoken, stuid: ck.stuid, mid: ck.mid };
await painterReq.forum.recent(26, 2, 1, undefined, 20, ckSign);
const resp = await apiHubReq.sign(ckSign, 2, ch);
if (resp.retcode !== 0) {
if (resp.retcode !== 1034) {

View File

@@ -1,11 +1,7 @@
<!-- TODO: 链接处理重构 -->
<template>
<div class="tp-image-box" v-if="localUrl !== undefined">
<img
:src="localUrl"
@click="showOverlay = true"
:alt="props.data.insert.image"
:title="getImageTitle()"
/>
<img :src="localUrl" @click="showOverlay = true" :alt="oriUrl" :title="getImageTitle()" />
<div
class="act"
@click.stop="showOri = true"
@@ -16,7 +12,7 @@
<v-icon size="16" color="white">mdi-magnify</v-icon>
</div>
</div>
<div v-else class="tp-image-load" :title="props.data.insert.image">
<div v-else class="tp-image-load" :title="oriUrl">
<v-progress-circular :indeterminate="true" color="primary" size="small" />
<span>加载中...</span>
</div>
@@ -26,6 +22,7 @@
v-model:link="localUrl"
v-model:ori="showOri"
v-model:bgColor="bgColor"
v-model:format="imgExt"
/>
</template>
<script lang="ts" setup>
@@ -39,7 +36,7 @@ import { computed, onMounted, onUnmounted, ref, watch } from "vue";
import VpOverlayImage from "./vp-overlay-image.vue";
export type TpImage = {
insert: { image: string };
insert: { image: string | TGApp.BBS.Post.Image };
attributes?: {
width: number;
height: number;
@@ -54,12 +51,14 @@ const appStore = useAppStore();
const { imageQualityPercent } = storeToRefs(appStore);
const props = defineProps<TpImageProps>();
const showOverlay = ref<boolean>(false);
const showOri = ref<boolean>(
props.data.insert.image.endsWith(".gif") || imageQualityPercent.value === 100,
);
const localUrl = ref<string>();
const bgColor = ref<string>("transparent");
const oriUrl = ref<string>("");
const imgExt = computed<string>(() => getImageExt());
const showOri = ref<boolean>(imgExt.value === "gif" || imageQualityPercent.value === 100);
const imgWidth = computed<string>(() => {
if (props.data.attributes === undefined) return "auto";
if (props.data.attributes.width >= 690) return "100%";
@@ -69,7 +68,8 @@ const imgWidth = computed<string>(() => {
console.log("tp-image", props.data.insert.image, props.data.attributes);
onMounted(async () => {
const link = appStore.getImageUrl(props.data.insert.image);
oriUrl.value = miniImgUrl();
const link = appStore.getImageUrl(oriUrl.value, imgExt.value);
localUrl.value = await saveImgLocal(link);
});
@@ -77,9 +77,12 @@ watch(
() => showOri.value,
async () => {
if (!showOri.value) return;
await showLoading.start("正在加载原图", props.data.insert.image);
await showLoading.start("正在加载原图", oriUrl.value);
if (localUrl.value) URL.revokeObjectURL(localUrl.value);
localUrl.value = await saveImgLocal(props.data.insert.image);
const ext = getImageExt();
if (!["png", "jpg", "jpeg", "gif", "webp"].includes(ext.toLowerCase())) {
localUrl.value = await saveImgLocal(`${oriUrl.value}?format=jpg`);
} else localUrl.value = await saveImgLocal(oriUrl.value);
await showLoading.end();
},
);
@@ -88,19 +91,49 @@ onUnmounted(() => {
if (localUrl.value) URL.revokeObjectURL(localUrl.value);
});
function miniImgUrl(): string {
let url: string;
if (typeof props.data.insert.image === "string") {
url = props.data.insert.image;
} else {
url = props.data.insert.image.url;
}
const link = new URL(url);
return `${link.origin}${link.pathname}`;
}
function getImageTitle(): string {
if (props.data.attributes == undefined) return "";
const res: string[] = [];
res.push(`宽度:${props.data.attributes.width}px`);
res.push(`度:${props.data.attributes.height}px`);
if (props.data.attributes.size) {
const size = bytesToSize(props.data.attributes.size);
res.push(`大小:${size}`);
if (props.data.attributes) {
res.push(`度:${props.data.attributes.width}px`);
res.push(`高度:${props.data.attributes.height}px`);
if (props.data.attributes.size) {
const size = bytesToSize(props.data.attributes.size);
res.push(`大小:${size}`);
}
res.push(`格式:${getImageExt()}`);
return res.join("\n");
}
if (props.data.attributes.ext) {
res.push(`格式${props.data.attributes.ext}`);
if (typeof props.data.insert.image !== "string") {
res.push(`宽度${props.data.insert.image.width}px`);
res.push(`高度:${props.data.insert.image.height}px`);
if (props.data.insert.image.size) {
const size = bytesToSize(Number(props.data.insert.image.size));
res.push(`大小:${size}`);
}
res.push(`格式:${getImageExt()}`);
return res.join("\n");
}
return res.join("\n");
return "";
}
function getImageExt(): string {
if (props.data.attributes && props.data.attributes.ext) return props.data.attributes.ext;
if (typeof props.data.insert.image !== "string") {
return props.data.insert.image.format;
}
const arr = oriUrl.value.split(".");
return arr[arr.length - 1];
}
</script>
<style lang="scss" scoped>

View File

@@ -7,6 +7,8 @@
/>
</template>
<script lang="ts" setup>
import TpUgcLevel from "@comp/viewPost/tp-ugc-level.vue";
import TpUgcTag from "@comp/viewPost/tp-ugc-tag.vue";
import type { Component } from "vue";
import TpBackupText from "./tp-backupText.vue";
@@ -93,9 +95,11 @@ function getParsedText(data: TpTextType): Array<TpTextType> {
function getTpName(tp: TGApp.BBS.SctPost.Base): Component {
if (tp.children) return TpTexts;
if (typeof tp.insert === "undefined") return TpUnknown;
if (typeof tp.insert === "string") return TpText;
// game_user_info属于backup_text的一种必须放在backup_text判断的前面
// 判断特殊backup_text
if ("game_user_info" in tp.insert) return TpUid;
if ("ugc_master_tag" in tp.insert) return TpUgcTag;
if ("backup_text" in tp.insert) {
if (tp.insert.backup_text === "[游戏卡片]" && "reception_card" in tp.insert) return TpGameCard;
if (tp.insert.backup_text === "[自定义表情]" && "custom_emoticon" in tp.insert) {
@@ -105,6 +109,7 @@ function getTpName(tp: TGApp.BBS.SctPost.Base): Component {
}
if ("divider" in tp.insert) return TpDivider;
if ("image" in tp.insert) return TpImage;
if ("level" in tp.insert) return TpUgcLevel;
if ("link_card" in tp.insert) return TpLinkCard;
if ("mention" in tp.insert) return TpMention;
if ("video" in tp.insert) return TpVideo;

View File

@@ -0,0 +1,151 @@
<!-- UGC关卡组件 TODO:UI调整-->
<template>
<div class="tul-card-box" @click="console.log(props.data)">
<TMiImg
@click="toLevel()"
class="tul-cover"
:src="props.data.insert.level.cover.url"
alt="cover"
/>
<div class="tul-content">
<div class="tul-top">
<div class="tul-title">{{ props.data.insert.level.level_name }}</div>
<div class="tul-sub">
<div class="tul-info-item" title="游戏类型">
<v-icon size="16" color="yellow">mdi-gamepad-variant</v-icon>
<span>{{ props.data.insert.level.play_type }}</span>
</div>
<div class="tul-info-item" title="游玩人数">
<v-icon size="16" color="blue">mdi-account-multiple</v-icon>
<span>{{ props.data.insert.level.show_limit_play_num_str }}</span>
</div>
</div>
<div class="tul-attach" v-if="props.data.insert.level.level_attachment">
<span></span>
<span>{{ props.data.insert.level.level_attachment.content }}</span>
</div>
</div>
<div class="tul-bottom">
<div class="tul-info">
<div class="tul-info-item" title="热度">
<v-icon size="16" color="orange">mdi-fire</v-icon>
<span>{{ props.data.insert.level.hot_score }}</span>
</div>
<div class="tul-info-item" title="点赞率">
<v-icon size="16" color="pink">mdi-thumb-up</v-icon>
<span>{{ props.data.insert.level.good_rate }}</span>
</div>
</div>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import TMiImg from "@comp/app/t-mi-img.vue";
import TGClient from "@utils/TGClient.js";
type TpUgcLevel = { insert: { level: TGApp.BBS.UGC.Level } };
type TpUgcLevelProps = { data: TpUgcLevel };
const props = defineProps<TpUgcLevelProps>();
async function toLevel(): Promise<void> {
let url = `https://act.miyoushe.com/ys/ugc_community/mx/#/pages/level-detail/index?`;
url = `${url}id=${props.data.insert.level.level_id}&region=${props.data.insert.level.region}`;
await TGClient.open("web_act_thin", url.toString());
}
</script>
<style lang="scss" scoped>
.tul-card-box {
display: flex;
width: 100%;
box-sizing: border-box;
padding: 8px;
border: 1px solid var(--common-shadow-1);
border-radius: 4px;
margin-bottom: 8px;
background: var(--box-bg-1);
column-gap: 8px;
}
.tul-cover {
max-width: 50%;
max-height: 180px;
border-radius: 4px;
cursor: pointer;
transition: all 0.5s;
&:hover {
scale: 0.9;
}
}
.tul-content {
display: flex;
width: 100%;
flex-direction: column;
align-items: center;
justify-content: space-between;
font-family: var(--font-title);
}
.tul-top {
display: flex;
width: 100%;
flex-direction: column;
align-items: flex-start;
justify-content: center;
}
.tul-title {
color: var(--tgc-od-red);
font-family: var(--font-title);
font-size: 20px;
}
.tul-sub {
display: flex;
width: 100%;
align-items: center;
justify-content: flex-start;
color: var(--tgc-od-white);
column-gap: 16px;
font-size: 16px;
}
.tul-attach {
margin-top: 8px;
color: var(--common-text-secondary);
font-size: 16px;
span {
color: var(--tgc-od-blue);
&:first-child {
font-family: Genshin, sans-serif;
font-size: 20px;
line-height: 14px;
vertical-align: top;
}
}
}
.tul-bottom {
display: flex;
width: 100%;
align-items: center;
justify-content: flex-end;
}
.tul-info {
display: flex;
gap: 16px;
}
.tul-info-item {
display: flex;
align-items: center;
font-size: 14px;
gap: 4px;
}
</style>

View File

@@ -0,0 +1,52 @@
<!-- ugc_master_tag backup_text -->
<template>
<span class="tut-box">
{{ tagName }}
</span>
</template>
<script lang="ts" setup>
import useAppStore from "@store/app.js";
import { str2Color } from "@utils/toolFunc.js";
import { storeToRefs } from "pinia";
import { computed } from "vue";
type TpUgcTag = {
insert: {
backup_text: string;
ugc_master_tag: {
id: number;
name: string;
is_available: boolean;
};
};
};
type TpUgcTagProps = { data: TpUgcTag };
const props = defineProps<TpUgcTagProps>();
const { theme } = storeToRefs(useAppStore());
const tagName = computed<string>(() => props.data.insert.ugc_master_tag.name);
const isDarkMode = computed<boolean>(() => theme.value === "dark");
const tagColor = computed<string>(() => tag2Color(tagName.value, isDarkMode.value));
const bgColor = computed<string>(() => `rgba(${tagColor.value.slice(4, -1)}, 0.18)`);
function tag2Color(str: string, isDarkMode: boolean = false): string {
const adjust = isDarkMode ? 80 : -40;
return str2Color(str, adjust);
}
</script>
<style lang="scss" scoped>
.tut-box {
display: inline-flex;
width: fit-content;
align-items: center;
justify-content: center;
padding: 0 6px;
border-radius: 12px;
margin-right: 4px;
background: v-bind(bgColor); /* stylelint-disable-line value-keyword-case */
color: v-bind(tagColor); /* stylelint-disable-line value-keyword-case */
font-family: var(--font-title);
font-size: 12px;
gap: 2px;
}
</style>

View File

@@ -23,6 +23,7 @@
import showLoading from "@comp/func/loading.js";
import useAppStore from "@store/app.js";
import { getCurrentWindow } from "@tauri-apps/api/window";
import { openUrl } from "@tauri-apps/plugin-opener";
import { getImageBuffer, saveCanvasImg, saveImgLocal } from "@utils/TGShare.js";
import { getVideoDuration } from "@utils/toolFunc.js";
import Artplayer, { type Option } from "artplayer";
@@ -106,7 +107,7 @@ onMounted(async () => {
name: "download-cover",
index: 0,
position: "right",
html: `<i class="mdi mdi-download"></i>`,
html: `<span class="mdi mdi-image-check"></span>`,
tooltip: "下载封面",
click: async () => {
await showLoading.start("正在下载封面", props.data.insert.vod.cover);
@@ -117,6 +118,17 @@ onMounted(async () => {
await showLoading.end();
},
},
{
name: "download-video",
index: 0,
position: "right",
html: `<span class="mdi mdi-video-check"></span>`,
tooltip: "下载视频",
click: async () => {
if (!container.value) return;
await openUrl(container.value.url);
},
},
],
};
container.value = new Artplayer(option);

View File

@@ -5,28 +5,46 @@
<img :src="localLink" alt="图片" @click="isOriSize = !isOriSize" />
</div>
<div class="tpoi-bottom">
<div class="tpoi-info" v-if="props.image.attributes">
<p v-if="props.image.attributes.size" class="tpoi-info-item">
<span>大小</span>
<span>{{ bytesToSize(props.image.attributes.size ?? 0) }}</span>
</p>
<p class="tpoi-info-item">
<span>尺寸</span>
<span>{{ props.image.attributes.width }}x{{ props.image.attributes.height }}</span>
</p>
<p class="tpoi-info-item">
<span>格式</span>
<span>{{ format }}</span>
</p>
</div>
<template v-if="typeof props.image.insert.image !== 'string'">
<div class="tpoi-info">
<p class="tpoi-info-item">
<span>大小</span>
<span>{{ bytesToSize(Number(props.image.insert.image.size) ?? 0) }}</span>
</p>
<p class="tpoi-info-item">
<span>尺寸</span>
<span>
{{ props.image.insert.image.width }}x{{ props.image.insert.image.height }}
</span>
</p>
<p class="tpoi-info-item">
<span>格式</span>
<span>{{ format }}</span>
</p>
</div>
</template>
<template v-else-if="props.image.attributes">
<div class="tpoi-info">
<p v-if="props.image.attributes.size" class="tpoi-info-item">
<span>大小</span>
<span>{{ bytesToSize(props.image.attributes.size ?? 0) }}</span>
</p>
<p class="tpoi-info-item">
<span>尺寸</span>
<span>{{ props.image.attributes.width }}x{{ props.image.attributes.height }}</span>
</p>
<p class="tpoi-info-item">
<span>格式</span>
<span>{{ format }}</span>
</p>
</div>
</template>
<div class="tpoi-tools">
<v-icon @click="setBlackBg" title="切换背景色" v-if="showOri">
mdi-format-color-fill
</v-icon>
<v-icon @click="showOri = true" title="查看原图" v-else>mdi-magnify</v-icon>
<v-icon @click="onCopy" title="复制到剪贴板" v-if="format !== 'gif'">
mdi-content-copy
</v-icon>
<v-icon @click="onCopy" title="复制到剪贴板" v-if="showCopy">mdi-content-copy</v-icon>
<v-icon @click="onDownload" title="下载到本地">mdi-download</v-icon>
<v-icon @click="visible = false" title="关闭浮窗">mdi-close</v-icon>
</div>
@@ -51,14 +69,14 @@ const visible = defineModel<boolean>();
const localLink = defineModel<string>("link");
const showOri = defineModel<boolean>("ori");
const bgColor = defineModel<string>("bgColor", { default: "transparent" });
const format = defineModel<string>("format", { default: "png" });
const bgMode = ref<number>(0); // 0: transparent, 1: black, 2: white
const isOriSize = ref<boolean>(false);
const buffer = shallowRef<Uint8Array | null>(null);
const format = computed<string>(() => {
if (props.image.attributes?.ext) return props.image.attributes.ext;
const imageFormat = props.image.insert.image.split(".").pop();
if (imageFormat !== undefined) return imageFormat;
return "png";
const oriLink = computed<string>(() => miniImgUrl());
const showCopy = computed<boolean>(() => {
// 只能显示 png/jpg/jpeg/webp 格式的复制按钮
return ["png", "jpg", "jpeg", "webp"].includes(format.value.toLowerCase());
});
function setBlackBg(): void {
@@ -80,8 +98,7 @@ async function onCopy(): Promise<void> {
await nextTick();
}
await showLoading.start("正在复制图片到剪贴板");
const image = props.image.insert.image;
if (buffer.value === null) buffer.value = await getImageBuffer(image);
if (buffer.value === null) buffer.value = await getImageBuffer(oriLink.value);
const size = bytesToSize(buffer.value.byteLength);
await copyToClipboard(buffer.value);
await showLoading.end();
@@ -93,18 +110,28 @@ async function onDownload(): Promise<void> {
showOri.value = true;
await nextTick();
}
const image = props.image.insert.image;
await showLoading.start("正在下载图片到本地", image);
if (buffer.value === null) buffer.value = await getImageBuffer(image);
await showLoading.start("正在下载图片到本地", oriLink.value);
if (buffer.value === null) buffer.value = await getImageBuffer(oriLink.value);
if (buffer.value.byteLength > 80000000) {
showSnackbar.warn("图片过大,无法下载到本地");
return;
}
let fileName = image.split("/").pop()?.split(".")[0];
let fileName = oriLink.value.split("/").pop()?.split(".")[0];
if (fileName === undefined) fileName = Date.now().toString();
await saveCanvasImg(buffer.value, fileName, format.value);
await showLoading.end();
}
function miniImgUrl(): string {
let url: string;
if (typeof props.image.insert.image === "string") {
url = props.image.insert.image;
} else {
url = props.image.insert.image.url;
}
const link = new URL(url);
return `${link.origin}${link.pathname}`;
}
</script>
<style lang="css" scoped>
.tpoi-box {

View File

@@ -91,7 +91,7 @@
"GroupId": 4922,
"Id": 492201,
"Name": "炎昼风物诗",
"Description": "施放<color=#FFD780FF>琉金云间草</color>后的15秒内附近的队伍中所有其他角色不包括宵宫自己攻击力提高10%。此外,依据宵宫自己施放<color=#FFD780FF>琉金云间草</color>时固有天赋「袖火百景图」的叠加层数将额外提升上述的攻击力效果每层提升1%攻击力。",
"Description": "施放<color=#FFD780FF>琉金云间草</color>后的15秒内附近的队伍中所有其他角色不包括宵宫自己攻击力提高10%。此外,依据宵宫自己施放<color=#FFD780FF>琉金云间草</color>时突破天赋「袖火百景图」的叠加层数将额外提升上述的攻击力效果每层提升1%攻击力。",
"Icon": "UI_Talent_S_Yoimiya_06"
},
{

View File

@@ -16,7 +16,7 @@
{
"Id": 672,
"Name": "漫行山薮",
"Description": "固有天赋「飞叶迴斜」转变为:\n<color=#FFD780FF>飞叶轮</color>返回时,将为角色赋予固有天赋「飞叶迴斜」的「新叶」状态持续对身边的敌人造成相当于柯莱攻击力40%的<color=#99FF88FF>草元素伤害</color>持续3秒。\n从施放<color=#FFD780FF>拂花偈叶</color>开始到此次新叶状态消失前如果队伍中自己的角色触发了燃烧、原激化、超激化、蔓激化、绽放、超绽放、烈绽放或月绽放反应则使此次新叶效果的持续时间延长3秒。\n新叶效果至多通过这种方式延长一次持续时间在持续期间再次产生新叶效果时将移除原有的效果。新叶效果造成的伤害视为元素战技伤害。\n需要解锁固有天赋「飞叶迴斜」。",
"Description": "突破天赋「飞叶迴斜」转变为:\n<color=#FFD780FF>飞叶轮</color>返回时,将为角色赋予突破天赋「飞叶迴斜」的「新叶」状态持续对身边的敌人造成相当于柯莱攻击力40%的<color=#99FF88FF>草元素伤害</color>持续3秒。\n从施放<color=#FFD780FF>拂花偈叶</color>开始到此次新叶状态消失前如果队伍中自己的角色触发了燃烧、原激化、超激化、蔓激化、绽放、超绽放、烈绽放或月绽放反应则使此次新叶效果的持续时间延长3秒。\n新叶效果至多通过这种方式延长一次持续时间在持续期间再次产生新叶效果时将移除原有的效果。新叶效果造成的伤害视为元素战技伤害。\n需要解锁突破天赋「飞叶迴斜」。",
"Icon": "UI_Talent_S_Collei_02"
},
{

View File

@@ -16,7 +16,7 @@
{
"Id": 702,
"Name": "星天的花雨",
"Description": "处于「金杯的丰馈」状态下的角色对敌人造成<color=#80C0FFFF>水元素伤害</color>后,该敌人的<color=#80C0FFFF>水元素抗性</color>降低35%持续10秒对敌人造成绽放或月绽放反应伤害后该敌人的<color=#99FF88FF>草元素抗性</color>降低35%持续10秒。\n需要解锁固有天赋「折旋落英之庭」。",
"Description": "处于「金杯的丰馈」状态下的角色对敌人造成<color=#80C0FFFF>水元素伤害</color>后,该敌人的<color=#80C0FFFF>水元素抗性</color>降低35%持续10秒对敌人造成绽放或月绽放反应伤害后该敌人的<color=#99FF88FF>草元素抗性</color>降低35%持续10秒。\n需要解锁突破天赋「折旋落英之庭」。",
"Icon": "UI_Talent_S_Nilou_02"
},
{
@@ -84,7 +84,7 @@
"GroupId": 7021,
"Id": 702101,
"Name": "折旋落英之庭",
"Description": "队伍中所有角色的元素类型均为<color=#99FF88FF>草元素</color>与<color=#80C0FFFF>水元素</color>,并且至少有一名草元素角色、一名水元素角色时:\n妮露完成<color=#FFD780FF>七域舞步</color>的第三段舞步会为附近的所有角色赋予持续30秒的「金杯的丰馈」状态。\n处于金杯的丰馈状态下的角色受到<color=#99FF88FF>草元素攻击</color>会使附近的所有角色元素精通提升100点持续10秒此外触发绽放或月绽放反应时将取代草原核产生「丰穰之核」。\n相比草原核丰穰之核在产生后将迅速迸发并且具有更大的影响范围。\n丰穰之核无法触发超绽放与烈绽放反应与草原核共享数量上限。丰穰之核的攻击视为绽放反应的草原核的攻击。\n当队伍中角色的元素类型不满足本固有天赋的条件时,将移除已有的金杯的丰馈效果。",
"Description": "队伍中所有角色的元素类型均为<color=#99FF88FF>草元素</color>与<color=#80C0FFFF>水元素</color>,并且至少有一名草元素角色、一名水元素角色时:\n妮露完成<color=#FFD780FF>七域舞步</color>的第三段舞步会为附近的所有角色赋予持续30秒的「金杯的丰馈」状态。\n处于金杯的丰馈状态下的角色受到<color=#99FF88FF>草元素攻击</color>会使附近的所有角色元素精通提升100点持续10秒此外触发绽放或月绽放反应时将取代草原核产生「丰穰之核」。\n相比草原核丰穰之核在产生后将迅速迸发并且具有更大的影响范围。\n丰穰之核无法触发超绽放与烈绽放反应与草原核共享数量上限。丰穰之核的攻击视为绽放反应的草原核的攻击。\n当队伍中角色的元素类型不满足本突破天赋的条件时,将移除已有的金杯的丰馈效果。",
"Icon": "UI_Talent_S_Nilou_05"
},
{

View File

@@ -10,7 +10,7 @@
{
"Id": 711,
"Name": "立仪·俯览昼冥",
"Description": "施放<color=#FFD780FF>圣仪·煟煌随狼行</color>后赛诺普通攻击的攻击速度提升20%持续10秒。若施放<color=#FFD780FF>秘仪·律渊渡魂</color>时触发了固有天赋「落羽的裁择」的「裁定」效果,将刷新该效果的持续时间。\n需要解锁固有天赋「落羽的裁择」。",
"Description": "施放<color=#FFD780FF>圣仪·煟煌随狼行</color>后赛诺普通攻击的攻击速度提升20%持续10秒。若施放<color=#FFD780FF>秘仪·律渊渡魂</color>时触发了突破天赋「落羽的裁择」的「裁定」效果,将刷新该效果的持续时间。\n需要解锁突破天赋「落羽的裁择」。",
"Icon": "UI_Talent_S_Cyno_01"
},
{
@@ -42,7 +42,7 @@
{
"Id": 716,
"Name": "羽仪·裁落钧衡",
"Description": "施放<color=#FFD780FF>圣仪·煟煌随狼行</color>或触发固有天赋「落羽的裁择」的「裁定」后赛诺将获得4层「豺祭」效果。赛诺的普通攻击命中敌人时将消耗一层「豺祭」效果并发射一道渡荒之雷。\n「豺祭」持续8秒至多叠加8层并将在圣仪·煟煌随狼行的启途誓使状态结束后移除。\n每0.4秒至多通过这种方式发射一道渡荒之雷。\n需要解锁固有天赋「落羽的裁择」。",
"Description": "施放<color=#FFD780FF>圣仪·煟煌随狼行</color>或触发突破天赋「落羽的裁择」的「裁定」后赛诺将获得4层「豺祭」效果。赛诺的普通攻击命中敌人时将消耗一层「豺祭」效果并发射一道渡荒之雷。\n「豺祭」持续8秒至多叠加8层并将在圣仪·煟煌随狼行的启途誓使状态结束后移除。\n每0.4秒至多通过这种方式发射一道渡荒之雷。\n需要解锁突破天赋「落羽的裁择」。",
"Icon": "UI_Talent_S_Cyno_04"
}
],
@@ -91,7 +91,7 @@
"GroupId": 7122,
"Id": 712201,
"Name": "九弓的执命",
"Description": "基于赛诺的元素精通,提高自身以下攻击造成的伤害值:\n·启途誓使状态下的普通攻击元素精通的150%\n·固有天赋「落羽的裁择」的渡荒之雷元素精通的250%。",
"Description": "基于赛诺的元素精通,提高自身以下攻击造成的伤害值:\n·启途誓使状态下的普通攻击元素精通的150%\n·突破天赋「落羽的裁择」的渡荒之雷元素精通的250%。",
"Icon": "UI_Talent_S_Cyno_06"
},
{

View File

@@ -4,7 +4,7 @@
"camp": "阿如村",
"constellation": "箭盾座",
"birth": "5月3日",
"cv": { "cn": "张琦", "jp": "柚木凉香", "en": "莎蕾·科尔比", "kr": "全暎洙" }
"cv": { "cn": "张琦", "jp": "柚木凉香", "en": "莎蕾·科尔比&匿名", "kr": "全暎洙" }
},
"constellation": [
{

View File

@@ -10,7 +10,7 @@
{
"Id": 751,
"Name": "初番·茂风流羽行",
"Description": "在<color=#FFD780FF>优风倾姿</color>状态下流浪者进行空居·不生断与空居·刀风界的攻击速度提升10%。\n此外固有天赋「梦迹一风」发射的风矢能额外造成25%攻击力的伤害,该效果需要解锁固有天赋「梦迹一风」。",
"Description": "在<color=#FFD780FF>优风倾姿</color>状态下流浪者进行空居·不生断与空居·刀风界的攻击速度提升10%。\n此外突破天赋「梦迹一风」发射的风矢能额外造成25%攻击力的伤害,该效果需要解锁突破天赋「梦迹一风」。",
"Icon": "UI_Talent_S_Wanderer_01"
},
{
@@ -29,7 +29,7 @@
{
"Id": 754,
"Name": "四番·花月歌浮舟",
"Description": "施放<color=#FFD780FF>羽画·风姿华歌</color>时,若触发了固有天赋「拾玉得花」依据元素类型强化的效果还将随机获得1种本次施放未触发的强化效果。同时至多获得3种元素对应的强化效果。\n需要解锁固有天赋「拾玉得花」。",
"Description": "施放<color=#FFD780FF>羽画·风姿华歌</color>时,若触发了突破天赋「拾玉得花」依据元素类型强化的效果还将随机获得1种本次施放未触发的强化效果。同时至多获得3种元素对应的强化效果。\n需要解锁突破天赋「拾玉得花」。",
"Icon": "UI_Talent_S_Wanderer_03"
},
{

View File

@@ -16,7 +16,7 @@
{
"Id": 802,
"Name": "伴行的旅路",
"Description": "<color=#FFD780FF>星霜的流旋</color>的霜流矢首次命中敌人时或是冰星信标命中敌人时将产生1层固有天赋「速射牵制」的侦明效果。\n需要解锁固有天赋「速射牵制」。",
"Description": "<color=#FFD780FF>星霜的流旋</color>的霜流矢首次命中敌人时或是冰星信标命中敌人时将产生1层突破天赋「速射牵制」的侦明效果。\n需要解锁突破天赋「速射牵制」。",
"Icon": "UI_Talent_S_Mika_02"
},
{
@@ -42,7 +42,7 @@
{
"Id": 806,
"Name": "依随的策援",
"Description": "<color=#FFD780FF>星霜的流旋</color>灵风状态下的侦明效果叠加层数上限提升1层。需要解锁固有天赋「速射牵制」。\n此外处于灵风状态下的当前场上角色其物理伤害的暴击伤害提高60%。",
"Description": "<color=#FFD780FF>星霜的流旋</color>灵风状态下的侦明效果叠加层数上限提升1层。需要解锁突破天赋「速射牵制」。\n此外处于灵风状态下的当前场上角色其物理伤害的暴击伤害提高60%。",
"Icon": "UI_Talent_S_Mika_04"
}
],
@@ -91,7 +91,7 @@
"GroupId": 8022,
"Id": 802201,
"Name": "地貌测绘",
"Description": "同时处于<color=#FFD780FF>苍翎的颂愿</color>的鹰翎状态与<color=#FFD780FF>星霜的流旋</color>的灵风状态下的当前场上角色在攻击造成暴击时灵风状态将产生1层固有天赋「速射牵制」的侦明效果。在一次灵风状态的持续期间内通过这种方式至多产生1层侦明效果。\n此外灵风状态下侦明效果叠加层数上限提升1层。\n需要解锁固有天赋「速射牵制」。",
"Description": "同时处于<color=#FFD780FF>苍翎的颂愿</color>的鹰翎状态与<color=#FFD780FF>星霜的流旋</color>的灵风状态下的当前场上角色在攻击造成暴击时灵风状态将产生1层突破天赋「速射牵制」的侦明效果。在一次灵风状态的持续期间内通过这种方式至多产生1层侦明效果。\n此外灵风状态下侦明效果叠加层数上限提升1层。\n需要解锁突破天赋「速射牵制」。",
"Icon": "UI_Talent_S_Mika_06"
},
{

View File

@@ -10,13 +10,13 @@
{
"Id": 861,
"Name": "予行恶者以惩惧",
"Description": "固有天赋「公理终有辩诉之日」的「恩典之诫」转变为:\n莱欧斯利的生命值低于60%,或在<color=#FFD780FF>冰牙突驰</color>的寒烈的惩裁状态持续期间斥逐拳的第五段攻击命中时将获得「恩典之诫」每2.5秒至多获得一次「恩典之诫」效果。\n\n此外惩戒·凌跃拳获得以下强化\n·造成的伤害提升进一步提升至200%\n·命中时若处于寒烈的惩裁状态下该状态的持续时间延长4秒在一次寒烈的惩裁状态持续期间至多通过这种方式延长一次。\n\n需要解锁固有天赋「公理终有辩诉之日」。",
"Description": "突破天赋「公理终有辩诉之日」的「恩典之诫」转变为:\n莱欧斯利的生命值低于60%,或在<color=#FFD780FF>冰牙突驰</color>的寒烈的惩裁状态持续期间斥逐拳的第五段攻击命中时将获得「恩典之诫」每2.5秒至多获得一次「恩典之诫」效果。\n\n此外惩戒·凌跃拳获得以下强化\n·造成的伤害提升进一步提升至200%\n·命中时若处于寒烈的惩裁状态下该状态的持续时间延长4秒在一次寒烈的惩裁状态持续期间至多通过这种方式延长一次。\n\n需要解锁突破天赋「公理终有辩诉之日」。",
"Icon": "UI_Talent_S_Wriothesley_01"
},
{
"Id": 862,
"Name": "予骄暴者以镣锁",
"Description": "施放<color=#FFD780FF>黑金狼噬</color>时,固有天赋「罪业终有报偿之时」的每层「检偿之敕」效果都将使造成的伤害提升40%。\n需要解锁固有天赋「罪业终有报偿之时」。",
"Description": "施放<color=#FFD780FF>黑金狼噬</color>时,突破天赋「罪业终有报偿之时」的每层「检偿之敕」效果都将使造成的伤害提升40%。\n需要解锁突破天赋「罪业终有报偿之时」。",
"Icon": "UI_Talent_S_Wriothesley_02"
},
{
@@ -29,7 +29,7 @@
{
"Id": 864,
"Name": "予贞苦者以拯赎",
"Description": "惩戒·凌跃拳为莱欧斯利恢复生命值的回复量提升为50%生命值上限,需要解锁固有天赋「公理终有辩诉之日」。\n此外莱欧斯利受到治疗时若此次治疗回复量溢出将根据莱欧斯利是否处于当前场上产生不同的效果处于当前场上时莱欧斯利的攻击速度提升20%持续4秒处于队伍后台时队伍中所有自己的角色攻击速度提升10%持续6秒。上述这两种攻击速度提升效果无法叠加。",
"Description": "惩戒·凌跃拳为莱欧斯利恢复生命值的回复量提升为50%生命值上限,需要解锁突破天赋「公理终有辩诉之日」。\n此外莱欧斯利受到治疗时若此次治疗回复量溢出将根据莱欧斯利是否处于当前场上产生不同的效果处于当前场上时莱欧斯利的攻击速度提升20%持续4秒处于队伍后台时队伍中所有自己的角色攻击速度提升10%持续6秒。上述这两种攻击速度提升效果无法叠加。",
"Icon": "UI_Talent_S_Wriothesley_03"
},
{
@@ -42,7 +42,7 @@
{
"Id": 866,
"Name": "予无罪者以念抚",
"Description": "惩戒·凌跃拳的暴击率提升10%暴击伤害提升80%并能够额外生成冰锥造成原本100%的<color=#99FFFFFF>冰元素伤害</color>,通过这种方式造成的伤害视为重击伤害。\n需要解锁固有天赋「公理终有辩诉之日」。",
"Description": "惩戒·凌跃拳的暴击率提升10%暴击伤害提升80%并能够额外生成冰锥造成原本100%的<color=#99FFFFFF>冰元素伤害</color>,通过这种方式造成的伤害视为重击伤害。\n需要解锁突破天赋「公理终有辩诉之日」。",
"Icon": "UI_Talent_S_Wriothesley_04"
}
],

View File

@@ -10,13 +10,13 @@
{
"Id": 871,
"Name": "尊荣的创定",
"Description": "那维莱特登场时,获得一层固有天赋「古海孑遗的权柄」的「遗龙之荣」。需要解锁固有天赋「古海孑遗的权柄」。\n此外进行重击蓄力·诉论心证与重击·衡平推裁时提高那维莱特的抗打断能力。",
"Description": "那维莱特登场时,获得一层突破天赋「古海孑遗的权柄」的「遗龙之荣」。需要解锁突破天赋「古海孑遗的权柄」。\n此外进行重击蓄力·诉论心证与重击·衡平推裁时提高那维莱特的抗打断能力。",
"Icon": "UI_Talent_S_Neuvillette_01"
},
{
"Id": 872,
"Name": "律法的命诫",
"Description": "固有天赋「古海孑遗的权柄」获得强化每存在一层「遗龙之荣」就使重击·衡平推裁的暴击伤害提升14%至多通过这种方式提升42%。\n需要解锁固有天赋「古海孑遗的权柄」。",
"Description": "突破天赋「古海孑遗的权柄」获得强化每存在一层「遗龙之荣」就使重击·衡平推裁的暴击伤害提升14%至多通过这种方式提升42%。\n需要解锁突破天赋「古海孑遗的权柄」。",
"Icon": "UI_Talent_S_Neuvillette_02"
},
{

View File

@@ -10,7 +10,7 @@
{
"Id": 901,
"Name": "稳固阵线的魄力",
"Description": "当前场上处于「协同战法」状态下的角色不包括夏沃蕾自己触发超载反应时将恢复6点元素能量。此效果每10秒至多触发一次。\n需要解锁固有天赋「尖兵协同战法」。",
"Description": "当前场上处于「协同战法」状态下的角色不包括夏沃蕾自己触发超载反应时将恢复6点元素能量。此效果每10秒至多触发一次。\n需要解锁突破天赋「尖兵协同战法」。",
"Icon": "UI_Talent_S_Chevreuse_01"
},
{
@@ -84,7 +84,7 @@
"GroupId": 9021,
"Id": 902101,
"Name": "尖兵协同战法",
"Description": "队伍中所有角色的元素类型均为<color=#FF9999FF>火元素</color>与<color=#FFACFFFF>雷元素</color>,并且至少有一名火元素角色、一名雷元素角色时:\n夏沃蕾将为队伍中附近的角色施加「协同战法」角色触发超载反应后受本次反应影响的敌人的<color=#FF9999FF>火元素</color>与<color=#FFACFFFF>雷元素</color>抗性降低40%持续6秒。\n当队伍中角色的元素类型不满足本固有天赋的条件时,将移除已有的协同战法效果。",
"Description": "队伍中所有角色的元素类型均为<color=#FF9999FF>火元素</color>与<color=#FFACFFFF>雷元素</color>,并且至少有一名火元素角色、一名雷元素角色时:\n夏沃蕾将为队伍中附近的角色施加「协同战法」角色触发超载反应后受本次反应影响的敌人的<color=#FF9999FF>火元素</color>与<color=#FFACFFFF>雷元素</color>抗性降低40%持续6秒。\n当队伍中角色的元素类型不满足本突破天赋的条件时,将移除已有的协同战法效果。",
"Icon": "UI_Talent_S_Chevreuse_05"
},
{

View File

@@ -16,7 +16,7 @@
{
"Id": 932,
"Name": "鹤唳远人间",
"Description": "施放<color=#FFD780FF>朝起鹤云</color>的步天梯后闲云的攻击力提升20%持续15秒。\n此外固有天赋「细想应是洞中仙」的效果获得提升:<color=#FFD780FF>暮集竹星</color>的竹星拥有仙力助推时附近的当前场上角色的下落攻击坠地冲击造成的伤害提升提升值相当于闲云的攻击力的400%。通过这种方式至多使附近的当前场上角色的下落攻击坠地冲击伤害提升18000。\n一次下落攻击坠地冲击伤害中只会对一名敌人造成上述伤害提升效果每个角色每0.4秒至多触发一次。\n该效果需要解锁固有天赋「细想应是洞中仙」。",
"Description": "施放<color=#FFD780FF>朝起鹤云</color>的步天梯后闲云的攻击力提升20%持续15秒。\n此外突破天赋「细想应是洞中仙」的效果获得提升:<color=#FFD780FF>暮集竹星</color>的竹星拥有仙力助推时附近的当前场上角色的下落攻击坠地冲击造成的伤害提升提升值相当于闲云的攻击力的400%。通过这种方式至多使附近的当前场上角色的下落攻击坠地冲击伤害提升18000。\n一次下落攻击坠地冲击伤害中只会对一名敌人造成上述伤害提升效果每个角色每0.4秒至多触发一次。\n该效果需要解锁突破天赋「细想应是洞中仙」。",
"Icon": "UI_Talent_S_Liuyun_02"
},
{

View File

@@ -10,7 +10,7 @@
{
"Id": 941,
"Name": "正绢六通",
"Description": "<color=#FFD780FF>羽袖一触</color>的自动制御人形「袖」的攻击范围提升50%。\n此外如果队伍中存在千织以外的<color=#FFE699FF>岩元素</color>角色,羽袖一触的突进结束时,还将触发以下效果:\n·额外唤出一名袖。通过这种方式唤出的千织自己的袖与通过岩元素创造物额外唤出的千织自己的袖至多同时存在一名\n·触发固有天赋「锦上添花」,此效果需要解锁固有天赋「锦上添花」。",
"Description": "<color=#FFD780FF>羽袖一触</color>的自动制御人形「袖」的攻击范围提升50%。\n此外如果队伍中存在千织以外的<color=#FFE699FF>岩元素</color>角色,羽袖一触的突进结束时,还将触发以下效果:\n·额外唤出一名袖。通过这种方式唤出的千织自己的袖与通过岩元素创造物额外唤出的千织自己的袖至多同时存在一名\n·触发、突破天赋「锦上添花」,此效果需要解锁突破天赋「锦上添花」。",
"Icon": "UI_Talent_S_Chiori_01"
},
{
@@ -29,7 +29,7 @@
{
"Id": 944,
"Name": "衣裁三礼",
"Description": "触发固有天赋「量体裁衣」的后续效果后的8秒内队伍中自己的当前场上角色的普通攻击、重击或下落攻击命中附近的敌人时将在敌人附近唤出一名简易自动制御人形「绢」。每1秒至多通过上述方式唤出一名绢一次固有天赋「量体裁衣」的当意即妙或裁锦持续期间至多通过上述方式唤出3名绢。上述效果每15秒最多触发一次。 \n需要解锁固有天赋「量体裁衣」。",
"Description": "触发突破天赋「量体裁衣」的后续效果后的8秒内队伍中自己的当前场上角色的普通攻击、重击或下落攻击命中附近的敌人时将在敌人附近唤出一名简易自动制御人形「绢」。每1秒至多通过上述方式唤出一名绢一次突破天赋「量体裁衣」的当意即妙或裁锦持续期间至多通过上述方式唤出3名绢。上述效果每15秒最多触发一次。 \n需要解锁突破天赋「量体裁衣」。",
"Icon": "UI_Talent_S_Chiori_02"
},
{
@@ -42,7 +42,7 @@
{
"Id": 946,
"Name": "万理一空",
"Description": "触发固有天赋「量体裁衣」的后续效果后,千织自己的<color=#FFD780FF>羽袖一触</color>的冷却时间减少12秒。需要解锁固有天赋「量体裁衣」。\n此外千织自己的普通攻击造成的伤害提升提升值相当于千织自己的防御力的235%。",
"Description": "触发突破天赋「量体裁衣」的后续效果后,千织自己的<color=#FFD780FF>羽袖一触</color>的冷却时间减少12秒。需要解锁突破天赋「量体裁衣」。\n此外千织自己的普通攻击造成的伤害提升提升值相当于千织自己的防御力的235%。",
"Icon": "UI_Talent_S_Chiori_04"
}
],

View File

@@ -10,7 +10,7 @@
{
"Id": 951,
"Name": "「最快乐的精灵,可否懂得焦虑」",
"Description": "<color=#FFD780FF>弹跳水疗法</color>的激愈水球能额外弹跳3次前3次弹跳不会使较大激愈水球变小。\n此外固有天赋「应有适当的休憩」的效果获得提升激愈水球弹跳时将为希格雯赋予1层静养计数并使静养计数产生的伤害值提升改为希格雯的生命值上限超过30000的部分每1000点生命值上限将提升100点通过这种方式至多提升3500点伤害。此效果需要解锁固有天赋「应有适当的休憩」。",
"Description": "<color=#FFD780FF>弹跳水疗法</color>的激愈水球能额外弹跳3次前3次弹跳不会使较大激愈水球变小。\n此外突破天赋「应有适当的休憩」的效果获得提升激愈水球弹跳时将为希格雯赋予1层静养计数并使静养计数产生的伤害值提升改为希格雯的生命值上限超过30000的部分每1000点生命值上限将提升100点通过这种方式至多提升3500点伤害。此效果需要解锁突破天赋「应有适当的休憩」。",
"Icon": "UI_Talent_S_Sigewinne_01"
},
{

View File

@@ -16,7 +16,7 @@
{
"Id": 962,
"Name": "「所有的赏与罚皆自我出…」",
"Description": "<color=#FFD780FF>血偿勒令</color>施加时即为血偿勒令·结。\n阿蕾奇诺回收血偿勒令·结时将在前方唤出厄月血火造成相当于她的攻击力900%的<color=#FF9999FF>火元素范围伤害</color>并使她的所有元素抗性与物理抗性提升20%持续15秒。此效果每10秒至多触发一次。\n需要解锁固有天赋「唯苦痛可偿还」。",
"Description": "<color=#FFD780FF>血偿勒令</color>施加时即为血偿勒令·结。\n阿蕾奇诺回收血偿勒令·结时将在前方唤出厄月血火造成相当于她的攻击力900%的<color=#FF9999FF>火元素范围伤害</color>并使她的所有元素抗性与物理抗性提升20%持续15秒。此效果每10秒至多触发一次。\n需要解锁突破天赋「唯苦痛可偿还」。",
"Icon": "UI_Talent_S_Arlecchino_02"
},
{

View File

@@ -16,7 +16,7 @@
{
"Id": 972,
"Name": "寂秘纸草经",
"Description": "满足以下任意一项条件时,赛索斯的<color=#FFACFFFF>雷元素伤害加成</color>提升15%持续10秒至多叠加2层每层独立计时\n·通过瞄准射击消耗元素能量此条件需要解锁固有天赋「黑鸢的密喻」;\n·通过<color=#FFD780FF>古仪·鸣砂掣雷</color>触发元素反应恢复元素能量;\n·施放<color=#FFD780FF>秘仪·瞑光贯影</color>。",
"Description": "满足以下任意一项条件时,赛索斯的<color=#FFACFFFF>雷元素伤害加成</color>提升15%持续10秒至多叠加2层每层独立计时\n·通过瞄准射击消耗元素能量此条件需要解锁突破天赋「黑鸢的密喻」;\n·通过<color=#FFD780FF>古仪·鸣砂掣雷</color>触发元素反应恢复元素能量;\n·施放<color=#FFD780FF>秘仪·瞑光贯影</color>。",
"Icon": "UI_Talent_S_Sethos_02"
},
{
@@ -42,7 +42,7 @@
{
"Id": 976,
"Name": "巡日塔门书",
"Description": "<color=#FFD780FF>贯影箭</color>命中敌人后,将返还因固有天赋「黑鸢的密喻」消耗的元素能量此效果每15秒至多触发一次需要解锁固有天赋「黑鸢的密喻」。",
"Description": "<color=#FFD780FF>贯影箭</color>命中敌人后,将返还因突破天赋「黑鸢的密喻」消耗的元素能量此效果每15秒至多触发一次需要解锁突破天赋「黑鸢的密喻」。",
"Icon": "UI_Talent_S_Sethos_04"
}
],

View File

@@ -16,7 +16,7 @@
{
"Id": 982,
"Name": "「自此,直面长夜之危」",
"Description": "固有天赋「破夜的明焰」的效果获得强化:队伍中附近的角色对敌人触发<color=#FFACFFFF>雷元素相关反应</color>后将基于克洛琳德攻击力的30%,提升克洛琳德的普通攻击与<color=#FFD780FF>残光将终</color>造成的<color=#FFACFFFF>雷元素伤害</color>。此效果持续15秒至多叠加3层每层独立计时。处于3层状态下时克洛琳德的抗打断能力提升。通过这种方式至多使克洛琳德的上述攻击造成的伤害提升2700。\n需要解锁固有天赋「破夜的明焰」。",
"Description": "突破天赋「破夜的明焰」的效果获得强化:队伍中附近的角色对敌人触发<color=#FFACFFFF>雷元素相关反应</color>后将基于克洛琳德攻击力的30%,提升克洛琳德的普通攻击与<color=#FFD780FF>残光将终</color>造成的<color=#FFACFFFF>雷元素伤害</color>。此效果持续15秒至多叠加3层每层独立计时。处于3层状态下时克洛琳德的抗打断能力提升。通过这种方式至多使克洛琳德的上述攻击造成的伤害提升2700。\n需要解锁突破天赋「破夜的明焰」。",
"Icon": "UI_Talent_S_Clorinde_02"
},
{

View File

@@ -10,13 +10,13 @@
{
"Id": 991,
"Name": "淡香浸析",
"Description": "<color=#FFD780FF>撷萃调香</color>与固有天赋「余薰」的清露香氛造成的伤害提升20%,后者需要解锁固有天赋「余薰」。\n此外队伍中附近的角色对敌人触发燃烧反应或对处于燃烧状态下的敌人造成<color=#99FF88FF>草元素伤害</color>时,会额外生成一枚<color=#FFD780FF>香韵</color>。此效果每2.9秒至多触发一次。",
"Description": "<color=#FFD780FF>撷萃调香</color>与突破天赋「余薰」的清露香氛造成的伤害提升20%,后者需要解锁突破天赋「余薰」。\n此外队伍中附近的角色对敌人触发燃烧反应或对处于燃烧状态下的敌人造成<color=#99FF88FF>草元素伤害</color>时,会额外生成一枚<color=#FFD780FF>香韵</color>。此效果每2.9秒至多触发一次。",
"Icon": "UI_Talent_S_Emilie_01"
},
{
"Id": 992,
"Name": "湖光顶调",
"Description": "<color=#FFD780FF>撷萃调香</color>、<color=#FFD780FF>香氛演绎</color>或固有天赋「余薰」的清露香氛(需解锁该固有天赋)命中敌人时,该敌人的<color=#99FF88FF>草元素抗性</color>降低30%持续10秒。",
"Description": "<color=#FFD780FF>撷萃调香</color>、<color=#FFD780FF>香氛演绎</color>或突破天赋「余薰」的清露香氛(需解锁该突破天赋)命中敌人时,该敌人的<color=#99FF88FF>草元素抗性</color>降低30%持续10秒。",
"Icon": "UI_Talent_S_Emilie_02"
},
{

View File

@@ -47,7 +47,7 @@
{
"Id": 1016,
"Name": "瑞兽之形",
"Description": "迴猎贯鳞炮在命中敌人后将在敌人间弹跳一次造成基尼奇的攻击力700%的<color=#99FF88FF>草元素伤害</color>。\n如果本次迴猎贯鳞炮触发了固有天赋「焰灵的契约」或命之座「星虎之掌」对迴猎贯鳞炮的增强效果,它所引发的弹跳攻击也会获得上述效果的增强。",
"Description": "迴猎贯鳞炮在命中敌人后将在敌人间弹跳一次造成基尼奇的攻击力700%的<color=#99FF88FF>草元素伤害</color>。\n如果本次迴猎贯鳞炮触发了突破天赋「焰灵的契约」或命之座「星虎之掌」对迴猎贯鳞炮的增强效果,它所引发的弹跳攻击也会获得上述效果的增强。",
"Icon": "UI_Talent_S_Kinich_04"
}
],

View File

@@ -16,7 +16,7 @@
{
"Id": 1022,
"Name": "全力以赴玛拉妮!",
"Description": "进入夜魂加持状态时玛拉妮获得2层<color=#FFD780FF>踏鲨破浪</color>的浪势充能玛拉妮获得豚豚球时获得1层浪势充能。\n此外玛拉妮在一次夜魂加持期间获得2个豚豚球的2秒内额外恢复12点夜魂值。该效果需要解锁固有天赋「耐热型淡水豚豚」。",
"Description": "进入夜魂加持状态时玛拉妮获得2层<color=#FFD780FF>踏鲨破浪</color>的浪势充能玛拉妮获得豚豚球时获得1层浪势充能。\n此外玛拉妮在一次夜魂加持期间获得2个豚豚球的2秒内额外恢复12点夜魂值。该效果需要解锁突破天赋「耐热型淡水豚豚」。",
"Icon": "UI_Talent_S_Mualani_02"
},
{
@@ -29,7 +29,7 @@
{
"Id": 1024,
"Name": "鲨鲨主食是豚豚。",
"Description": "获得豚豚球时玛拉妮恢复8点元素能量该效果需要解锁固有天赋「耐热型淡水豚豚」。\n此外<color=#FFD780FF>爆瀑飞弹</color>造成的伤害提升75%。",
"Description": "获得豚豚球时玛拉妮恢复8点元素能量该效果需要解锁突破天赋「耐热型淡水豚豚」。\n此外<color=#FFD780FF>爆瀑飞弹</color>造成的伤害提升75%。",
"Icon": "UI_Talent_S_Mualani_03"
},
{

View File

@@ -70,7 +70,7 @@
"GroupId": 10332,
"Id": 11032,
"Name": "音火锻淬",
"Description": "让热情的律动响彻大地!希诺宁切换至用于高速战斗的刃轮装束,并向前突进一段距离,基于希诺宁的防御力,造成具有夜魂性质的<color=#FFE699FF>岩元素伤害</color>。\n施放后希诺宁将获得45点夜魂值进入夜魂加持状态在这种状态下希诺宁将切换至「刃轮巡猎」模式。\n\n<color=#FFD780FF>源音采样</color>\n希诺宁拥有三枚采样器根据队伍中其他角色的元素类型能够呈现出不同的「音型」并在激活时降低附近的敌人的对应元素抗性\n三枚采样器初始记录的「源音采样」均为<color=#FFE699FF>岩元素</color>,队伍中每存在一名<color=#FF9999FF>火元素</color>、<color=#80C0FFFF>水元素</color>、<color=#99FFFFFF>冰元素</color>或<color=#FFACFFFF>雷元素</color>角色,一枚<color=#FFE699FF>岩元素</color>采样便将转变为对应的元素类型;\n处于夜魂加持状态下时希诺宁携带的<color=#FFE699FF>岩元素</color>「源音采样」将始终保持激活状态;解锁固有天赋「四境四象回声」后,希诺宁可以通过触发固有天赋「四境四象回声」的效果为自己恢复夜魂值在夜魂值达到上限时希诺宁将会消耗全部夜魂值激活希诺宁自己携带的三枚「源音采样」持续15秒\n「源音采样」激活时附近的敌人的对应元素抗性降低同元素类型「源音采样」的效果不能叠加希诺宁处于队伍后台时同样能产生该效果。\n\n<color=#FFD780FF>夜魂加持·希诺宁</color>\n持续消耗夜魂值。夜魂值耗尽时或是再次施放时希诺宁的夜魂加持将会结束。夜魂加持状态具有如下特性\n·切换至「刃轮巡猎」模式提升希诺宁的移动速度与攀爬速度且攀爬时能够以豹猫形态进行高速腾跃\n·希诺宁的夜魂加持状态具有如下限制处于夜魂加持状态下时希诺宁的夜魂值有9秒的时间限制超过时间限制后希诺宁的夜魂值将立刻耗竭。\n\n处于夜魂加持状态下时夜魂值耗竭后希诺宁将无法通过固有天赋「四境四象回声」产生夜魂值。\n\n<i>从某种意义上来说,锻造宝石与混音的原理或许有一定相通之处:滤除杂质,只保留最为闪耀之物。\n不过希诺宁向来是无所谓的毕竟两者对她而言都是无比快乐的事情嘛。</i>",
"Description": "让热情的律动响彻大地!希诺宁切换至用于高速战斗的刃轮装束,并向前突进一段距离,基于希诺宁的防御力,造成具有夜魂性质的<color=#FFE699FF>岩元素伤害</color>。\n施放后希诺宁将获得45点夜魂值进入夜魂加持状态在这种状态下希诺宁将切换至「刃轮巡猎」模式。\n\n<color=#FFD780FF>源音采样</color>\n希诺宁拥有三枚采样器根据队伍中其他角色的元素类型能够呈现出不同的「音型」并在激活时降低附近的敌人的对应元素抗性\n三枚采样器初始记录的「源音采样」均为<color=#FFE699FF>岩元素</color>,队伍中每存在一名<color=#FF9999FF>火元素</color>、<color=#80C0FFFF>水元素</color>、<color=#99FFFFFF>冰元素</color>或<color=#FFACFFFF>雷元素</color>角色,一枚<color=#FFE699FF>岩元素</color>采样便将转变为对应的元素类型;\n处于夜魂加持状态下时希诺宁携带的<color=#FFE699FF>岩元素</color>「源音采样」将始终保持激活状态;解锁突破天赋「四境四象回声」后,希诺宁可以通过触发突破天赋「四境四象回声」的效果为自己恢复夜魂值在夜魂值达到上限时希诺宁将会消耗全部夜魂值激活希诺宁自己携带的三枚「源音采样」持续15秒\n「源音采样」激活时附近的敌人的对应元素抗性降低同元素类型「源音采样」的效果不能叠加希诺宁处于队伍后台时同样能产生该效果。\n\n<color=#FFD780FF>夜魂加持·希诺宁</color>\n持续消耗夜魂值。夜魂值耗尽时或是再次施放时希诺宁的夜魂加持将会结束。夜魂加持状态具有如下特性\n·切换至「刃轮巡猎」模式提升希诺宁的移动速度与攀爬速度且攀爬时能够以豹猫形态进行高速腾跃\n·希诺宁的夜魂加持状态具有如下限制处于夜魂加持状态下时希诺宁的夜魂值有9秒的时间限制超过时间限制后希诺宁的夜魂值将立刻耗竭。\n\n处于夜魂加持状态下时夜魂值耗竭后希诺宁将无法通过突破天赋「四境四象回声」产生夜魂值。\n\n<i>从某种意义上来说,锻造宝石与混音的原理或许有一定相通之处:滤除杂质,只保留最为闪耀之物。\n不过希诺宁向来是无所谓的毕竟两者对她而言都是无比快乐的事情嘛。</i>",
"Icon": "Skill_S_Xilonen_01"
},
{

View File

@@ -10,13 +10,13 @@
{
"Id": 1041,
"Name": "弹匣,躁动的轮盘",
"Description": "触发固有天赋「子弹的戏法」中的「附灵转化」时,会额外使第二枚装入弹匣的<color=#FFD780FF>追影弹</color>转化为<color=#FFD780FF>焕光追影弹</color>。\n触发「附灵转化」的几率增加若队伍中存在符合元素转化条件的元素类型的角色则触发「附灵转化」的几率提升33.3%至多提升至100%。\n\n需要解锁固有天赋「子弹的戏法」。\n此外不处于战斗状态下时恰斯卡的夜魂加持状态消耗的夜魂值与燃素降低30%。",
"Description": "触发突破天赋「子弹的戏法」中的「附灵转化」时,会额外使第二枚装入弹匣的<color=#FFD780FF>追影弹</color>转化为<color=#FFD780FF>焕光追影弹</color>。\n触发「附灵转化」的几率增加若队伍中存在符合元素转化条件的元素类型的角色则触发「附灵转化」的几率提升33.3%至多提升至100%。\n\n需要解锁突破天赋「子弹的戏法」。\n此外不处于战斗状态下时恰斯卡的夜魂加持状态消耗的夜魂值与燃素降低30%。",
"Icon": "UI_Talent_S_Chasca_01"
},
{
"Id": 1042,
"Name": "枪口,灼烧的轻烟",
"Description": "恰斯卡登场时,获得一层固有天赋「子弹的戏法」的「焕影之灵」。该效果需要解锁固有天赋「子弹的戏法」。\n此外施放元素战技<color=#FFD780FF>灵缰追影</color>中的<color=#FFD780FF>多重瞄准</color>时,恰斯卡的<color=#FFD780FF>焕光追影弹</color>命中敌人时,会根据<color=#FFD780FF>焕光追影弹</color>的元素类型造成相当于恰斯卡攻击力400%的对应元素范围伤害,该伤害视为重击伤害。每次施放<color=#FFD780FF>多重瞄准</color>至多触发一次该效果。",
"Description": "恰斯卡登场时,获得一层突破天赋「子弹的戏法」的「焕影之灵」。该效果需要解锁突破天赋「子弹的戏法」。\n此外施放元素战技<color=#FFD780FF>灵缰追影</color>中的<color=#FFD780FF>多重瞄准</color>时,恰斯卡的<color=#FFD780FF>焕光追影弹</color>命中敌人时,会根据<color=#FFD780FF>焕光追影弹</color>的元素类型造成相当于恰斯卡攻击力400%的对应元素范围伤害,该伤害视为重击伤害。每次施放<color=#FFD780FF>多重瞄准</color>至多触发一次该效果。",
"Icon": "UI_Talent_S_Chasca_02"
},
{
@@ -42,7 +42,7 @@
{
"Id": 1046,
"Name": "相决,斗争的荣光",
"Description": "恰斯卡进行<color=#FFD780FF>多重瞄准</color>所需的蓄力时间减少,且触发固有天赋「子弹的戏法」中的「附灵转化」后恰斯卡将获得「命袭」状态在接下来的3秒内恰斯卡下一次施放元素战技<color=#FFD780FF>灵缰追影</color>中的<color=#FFD780FF>多重瞄准</color>时,会立即完成蓄力,并且本次<color=#FFD780FF>多重瞄准</color>中的<color=#FFD780FF>追影弹</color>和<color=#FFD780FF>焕光追影弹</color>的暴击伤害提升120%。每3秒至多获得一次「命袭」效果。\n\n上述效果需要解锁固有天赋「子弹的戏法」。",
"Description": "恰斯卡进行<color=#FFD780FF>多重瞄准</color>所需的蓄力时间减少,且触发突破天赋「子弹的戏法」中的「附灵转化」后恰斯卡将获得「命袭」状态在接下来的3秒内恰斯卡下一次施放元素战技<color=#FFD780FF>灵缰追影</color>中的<color=#FFD780FF>多重瞄准</color>时,会立即完成蓄力,并且本次<color=#FFD780FF>多重瞄准</color>中的<color=#FFD780FF>追影弹</color>和<color=#FFD780FF>焕光追影弹</color>的暴击伤害提升120%。每3秒至多获得一次「命袭」效果。\n\n上述效果需要解锁突破天赋「子弹的戏法」。",
"Icon": "UI_Talent_S_Chasca_04"
}
],

View File

@@ -10,7 +10,7 @@
{
"Id": 1051,
"Name": "林雾间的行迹",
"Description": "元素战技<color=#FFD780FF>暝色缒索</color>中的<color=#FFD780FF>宿灵球</color>能额外弹跳2次。\n此外<color=#FFD780FF>宿灵球</color>命中敌人后将为该敌人施加「夜暝」效果持续12秒对处于「夜暝」效果的影响下的敌人固有天赋「夜翳的通感」触发的「显象超感」造成的伤害提升50%,该效果需要解锁固有天赋「夜翳的通感」。",
"Description": "元素战技<color=#FFD780FF>暝色缒索</color>中的<color=#FFD780FF>宿灵球</color>能额外弹跳2次。\n此外<color=#FFD780FF>宿灵球</color>命中敌人后将为该敌人施加「夜暝」效果持续12秒对处于「夜暝」效果的影响下的敌人突破天赋「夜翳的通感」触发的「显象超感」造成的伤害提升50%,该效果需要解锁突破天赋「夜翳的通感」。",
"Icon": "UI_Talent_S_Olorun_01"
},
{
@@ -42,7 +42,7 @@
{
"Id": 1056,
"Name": "致深泉的颂赞",
"Description": "触发固有天赋「夜翳的通感」的「显象超感」后会使队伍中自己的当前场上角色的攻击力提升10%持续9秒。该效果至多叠加3层每层独立计算持续时间。\n此外施放元素爆发<color=#FFD780FF>黯声回响</color>时将触发一次等同于「显象超感」的效果造成原本200%的伤害。\n该效果需要解锁固有天赋「夜翳的通感」。",
"Description": "触发突破天赋「夜翳的通感」的「显象超感」后会使队伍中自己的当前场上角色的攻击力提升10%持续9秒。该效果至多叠加3层每层独立计算持续时间。\n此外施放元素爆发<color=#FFD780FF>黯声回响</color>时将触发一次等同于「显象超感」的效果造成原本200%的伤害。\n该效果需要解锁突破天赋「夜翳的通感」。",
"Icon": "UI_Talent_S_Olorun_04"
}
],

View File

@@ -29,7 +29,7 @@
{
"Id": 1064,
"Name": "「领袖」的觉悟",
"Description": "固有天赋「基扬戈兹」的效果获得提升:\n施放元素爆发<color=#FFD780FF>燔天之时</color>后的伤害提升效果不再随时间降低并额外获得10%伤害加成。\n需要解锁固有天赋「基扬戈兹」。",
"Description": "突破天赋「基扬戈兹」的效果获得提升:\n施放元素爆发<color=#FFD780FF>燔天之时</color>后的伤害提升效果不再随时间降低并额外获得10%伤害加成。\n需要解锁突破天赋「基扬戈兹」。",
"Icon": "UI_Talent_S_Mavuika_03"
},
{

View File

@@ -16,7 +16,7 @@
{
"Id": 1072,
"Name": "吞心者的巡行",
"Description": "茜特菈莉的元素精通提升125点处于白曜护盾的庇护下或是伊兹帕帕跟随的其他角色的元素精通提升250点。\n此外施放元素战技<color=#FFD780FF>霜昼黑星</color>时,也会为附近的当前场上角色赋予白曜护盾;\n\n此外固有天赋「五重天的寒雨」的效果获得提升:\n伊兹帕帕存在期间队伍中附近的角色触发冻结反应或融化反应后受本次反应影响的敌人的<color=#FF9999FF>火元素</color>与<color=#80C0FFFF>水元素</color>抗性还会额外降低20%持续12秒该效果需要解锁固有天赋「五重天的寒雨」。",
"Description": "茜特菈莉的元素精通提升125点处于白曜护盾的庇护下或是伊兹帕帕跟随的其他角色的元素精通提升250点。\n此外施放元素战技<color=#FFD780FF>霜昼黑星</color>时,也会为附近的当前场上角色赋予白曜护盾;\n\n此外突破天赋「五重天的寒雨」的效果获得提升:\n伊兹帕帕存在期间队伍中附近的角色触发冻结反应或融化反应后受本次反应影响的敌人的<color=#FF9999FF>火元素</color>与<color=#80C0FFFF>水元素</color>抗性还会额外降低20%持续12秒该效果需要解锁突破天赋「五重天的寒雨」。",
"Icon": "UI_Talent_S_Citlali_02"
},
{

View File

@@ -10,7 +10,7 @@
{
"Id": 1081,
"Name": "「若有人兮云之际」",
"Description": "触发固有天赋「四戈封刀灵卜」中的元素转化后,蓝砚在这次元素战技<color=#FFD780FF>凤缕随翦舞</color>中向敌人抛出翦月环时,将额外抛出一枚翦月环。\n需要解锁固有天赋「四戈封刀灵卜」。",
"Description": "触发突破天赋「四戈封刀灵卜」中的元素转化后,蓝砚在这次元素战技<color=#FFD780FF>凤缕随翦舞</color>中向敌人抛出翦月环时,将额外抛出一枚翦月环。\n需要解锁突破天赋「四戈封刀灵卜」。",
"Icon": "UI_Talent_S_Lanyan_01"
},
{

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