🎨 代码格式化

This commit is contained in:
BTMuli
2023-06-25 16:15:15 +08:00
parent 7982649bbc
commit 7567bba147
174 changed files with 29970 additions and 29840 deletions

View File

@@ -70,10 +70,10 @@ Tauri 练手项目,子模块:[TGAssistant](https://github.com/BTMuli/TGAssis
## 相关文档
+ Changelog: [CHANGELOG](CHANGELOG.md)
+ 资源来源:[项目资源说明](docs/项目资源说明.md)
+ UIAF[UIAF v1.1](docs/UIAF.md)
+ UIGF[UIGF v2.3](docs/UIGF.md)
- Changelog: [CHANGELOG](CHANGELOG.md)
- 资源来源:[项目资源说明](docs/项目资源说明.md)
- UIAF[UIAF v1.1](docs/UIAF.md)
- UIGF[UIGF v2.3](docs/UIGF.md)
## Contributors

View File

@@ -113,7 +113,6 @@ FullCommits: [`v0.1.2 ~ v0.1.3`](https://github.com/BTMuli/Tauri.Genshin/compare
- 咨讯:支持多种游戏咨讯查看
- 米游社解析:添加 `mention` 类型解析
### Fix
- 首页:近期活动、限时祈愿结束处理

View File

@@ -14,18 +14,19 @@ Update: 2023-03-07
# 统一可交换成就标准 v1.1
> Uniformed Interchangeable Achievement Format standard (UIAF)
## 前言
由于原神的第三方成就识别、导出、记录软件越来越多,在有了 UIGF 的经验后,
我们
* [babalae/genshin achievement toy](https://github.com/babalae/genshin-achievement-toy)
* [DGP Studio/Snap.Genshin](https://github.com/DGP-Studio/Snap.Genshin)
* [HolographicHat/genshin achievement export](https://github.com/HolographicHat/genshin-achievement-export)
* [YuehaiTeam/cocogoat](https://github.com/YuehaiTeam/cocogoat)
- [babalae/genshin achievement toy](https://github.com/babalae/genshin-achievement-toy)
- [DGP Studio/Snap.Genshin](https://github.com/DGP-Studio/Snap.Genshin)
- [HolographicHat/genshin achievement export](https://github.com/HolographicHat/genshin-achievement-export)
- [YuehaiTeam/cocogoat](https://github.com/YuehaiTeam/cocogoat)
(上述名称以字典顺序排序,不代表其他任何意义)
在此一起制定了此项标准旨在加强各个原神相关的App间的数据可交换性。
在此一起,制定了此项标准,旨在加强各个原神相关的 App 间的数据可交换性。
## 注意事项
@@ -61,10 +62,7 @@ Update: 2023-03-07
"description": "导出UNIX时间戳"
}
},
"required": [
"export_app",
"uiaf_version"
],
"required": ["export_app", "uiaf_version"],
"description": "包含导出方定义的基本信息"
},
"list": {
@@ -83,12 +81,7 @@ Update: 2023-03-07
"status": {
"type": "number",
"description": "完成状态",
"enum": [
0,
1,
2,
3
],
"enum": [0, 1, 2, 3],
"enumDesc": "ACHIEVEMENT_INVALID = 0; ACHIEVEMENT_UNFINISHED = 1; ACHIEVEMENT_FINISHED = 2;ACHIEVEMENT_POINT_TAKEN = 3;"
},
"timestamp": {
@@ -96,21 +89,13 @@ Update: 2023-03-07
"description": "完成的时间"
}
},
"required": [
"id",
"current",
"status",
"timestamp"
],
"required": ["id", "current", "status", "timestamp"],
"description": "表示一个成就"
},
"description": "包含完成或未完成的成就"
}
},
"required": [
"info",
"list"
]
"required": ["info", "list"]
}
```
@@ -118,46 +103,48 @@ Update: 2023-03-07
可以包含我们认可的以下字段
|字段名|值|说明|
|-|-|-|
|`export_timestamp`|导出UNIX时间戳||
|`export_app_version`|导出此份记录的App版本号||
|`uiaf_version`|所应用的 `UIAF` 的版本,包含此字段以防 `UIAF` 出现中断性变更时App无法处理||
| 字段名 | 值 | 说明 |
| -------------------- | --------------------------------------------------------------------------- | ---- |
| `export_timestamp` | 导出 UNIX 时间戳 | |
| `export_app_version` | 导出此份记录的 App 版本号 | |
| `uiaf_version` | 所应用的 `UIAF` 的版本,包含此字段以防 `UIAF` 出现中断性变更时App 无法处理 | |
#### `uiaf_version`
合法值
|值|说明|向下兼容的最低版本|
|-|-|-|
|`v1.0`|首个正式版本|v1.0|
|`v1.1`|`achievement` 中引入了 `status` 字段,指示成就的完成情况|v1.1|
| 值 | 说明 | 向下兼容的最低版本 |
| ------ | ----------------------------------------------------------- | ------------------ |
| `v1.0` | 首个正式版本 | v1.0 |
| `v1.1` | `achievement` 中引入了 `status` 字段,指示成就的完成情况 | v1.1 |
#### `export_app`
未实现导出支持的以 `-` 代替
|导出 App|`export_app` 的值|
|-|-|
|Empty|Empty|
| 导出 App | `export_app` 的值 |
| -------- | ----------------- |
| Empty | Empty |
### `achievement`
#### `id`
原神的成就在游戏内部带有Id对于扫描类导出软件在取得成就的外在表现形式呈现文本便可对应到内部的Id
原神的成就在游戏内部带有 Id对于扫描类导出软件在取得成就的外在表现形式呈现文本便可对应到内部的 Id
> 导入/导出软件应自行负责Id与呈现文本间的转换
> 导入/导出软件应自行负责 Id 与呈现文本间的转换
> 成就的信息可以从 [Dimbreath/GenshinData](https://github.com/Dimbreath/GenshinData) 库中获取
#### `timestamp`
* 对于识别成功的值直接将时间转换为对应的UNIX 时间戳(秒)
- 对于识别成功的值,直接将时间转换为对应的 UNIX 时间戳(秒)
* 对于识别失败的值,直接将时间设置为 `9999-12-31 23:59:59`253402271999
- 对于识别失败的值,直接将时间设置为 `9999-12-31 23:59:59`253402271999
#### `current`
* 对于识别成功的值,如 30/40 `current` 的值应设置为30
- 对于识别成功的值,如 30/40 `current` 的值应设置为 30
> 因为始终可以从原神的数据中找到目标达成值
* 对于识别失败的值,请将该字段的值设为 `0`
- 对于识别失败的值,请将该字段的值设为 `0`

View File

@@ -17,24 +17,26 @@ Update: 2023-04-07
## 更新记录
|版本|说明|兼容|
|-|-|-|
|`v2.0`| 首个正式版本 | v2.0 |
|`v2.1`| 简化了部分语言表述,与 v2.0在数据格式上完全一致 | v2.1 and lower |
|`v2.2`| 新增 `info.export_timestamp` 填充 UNIX 时间戳 | v2.2 and lower |
|`v2.3`| 扩充至非中文语境,使用 Json Schema 表述 | v2.3 and lower |
| 版本 | 说明 | 兼容 |
| ------ | ------------------------------------------------ | -------------- |
| `v2.0` | 首个正式版本 | v2.0 |
| `v2.1` | 简化了部分语言表述,与 v2.0 在数据格式上完全一致 | v2.1 and lower |
| `v2.2` | 新增 `info.export_timestamp` 填充 UNIX 时间戳 | v2.2 and lower |
| `v2.3` | 扩充至非中文语境,使用 Json Schema 表述 | v2.3 and lower |
## Id
原神的祈愿记录物品内包含了一项较为特殊的字段: `id` ,该值在 1.3版本后加入
原神的祈愿记录物品内包含了一项较为特殊的字段: `id` ,该值在 1.3 版本后加入
所以**先前查询出的物品**若无特殊兼容性修改则不会包含相应的 `id`
App 导出 UIGF 时
* 需要确保每个物品的 `id` 的有效性。
* 从最后一个自带有效 `id` 的物品开始,向前(相对于时间)依次递减 `id` 的值,每次递减的值应保持为 `1`
- 需要确保每个物品的 `id` 的有效性。
- 从最后一个自带有效 `id` 的物品开始,向前(相对于时间)依次递减 `id` 的值,每次递减的值应保持为 `1`
导入 UIGF 到 App 时
* App不应假设所有的 `gacha_item` 都有有效的 `id`
* App应具有处理 `id` 字段为 `null`或 `` 空字符串的能力
- App 不应假设所有的 `gacha_item` 都有有效的 `id`
- App 应具有处理 `id` 字段为 `null`或 `` 空字符串的能力
## GachaType
@@ -44,19 +46,19 @@ App 导出 UIGF 时
### 映射关系
|`uigf_gacha_type`|`gacha_type`|
|-|-|
|`100`|`100`|
|`200`|`200`|
|`301`|`301` or `400`|
|`302`|`302`|
| `uigf_gacha_type` | `gacha_type` |
| ----------------- | -------------- |
| `100` | `100` |
| `200` | `200` |
| `301` | `301` or `400` |
| `302` | `302` |
## Json 格式
> Uniformed Interchangeable GachaLog Format standard of Json (UIGF.J)
Json 格式 由于 与从官方接口获取到的格式一致
更便于各App的导入与导出我们也在此做出规范
该格式应仅用于各App间的数据互通
> Json 格式 由于 与从官方接口获取到的格式一致
> 更便于各 App 的导入与导出,我们也在此做出规范
> 该格式应仅用于各 App 间的数据互通
### 导出的格式
@@ -107,11 +109,7 @@ Json 格式 由于 与从官方接口获取到的格式一致
}
},
"title": "Infomation",
"required": [
"uid",
"lang",
"uigf_version"
],
"required": ["uid", "lang", "uigf_version"],
"description": "包含导出方定义的基本信息"
},
"list": {
@@ -166,13 +164,7 @@ Json 格式 由于 与从官方接口获取到的格式一致
"description": "向接口查询时需要的 gacha_type"
}
},
"required": [
"gacha_type",
"name",
"id",
"uigf_gacha_type",
"time"
],
"required": ["gacha_type", "name", "id", "uigf_gacha_type", "time"],
"title": "Gacha Item",
"description": "祈愿物品"
},
@@ -180,10 +172,7 @@ Json 格式 由于 与从官方接口获取到的格式一致
"description": "物品列表"
}
},
"required": [
"info",
"list"
],
"required": ["info", "list"],
"description": "UIGF 根对象"
}
```
@@ -194,95 +183,95 @@ Json 格式 由于 与从官方接口获取到的格式一致
### 单元格的格式
* 在填充单元格内的数据时,应统一转换到 `String` 字符串类型后填入
- 在填充单元格内的数据时,应统一转换到 `String` 字符串类型后填入
### 表名及内容
|表名|内容|类型|是否必要|
|-|-|-|-|
|统计分析|统计分析内容等|任意|否|
|角色活动祈愿|`gacha_type` : `301 or 400` 的祈愿数据|祈愿表|否,但是应该导出|
|武器活动祈愿|`gacha_type` : `302` 的祈愿数据|祈愿表|否,但是应该导出|
|常驻祈愿|`gacha_type` : `200` 的祈愿数据|祈愿表|否,但是应该导出|
|新手祈愿|`gacha_type` : `100` 的祈愿数据|祈愿表|否,但是应该导出|
|原始数据|全部祈愿数据|数据表|**详见下方原始数据表结构说明**|
| 表名 | 内容 | 类型 | 是否必要 |
| ------------ | -------------------------------------- | ------ | ------------------------------ |
| 统计分析 | 统计分析内容等 | 任意 | 否 |
| 角色活动祈愿 | `gacha_type` : `301 or 400` 的祈愿数据 | 祈愿表 | 否,但是应该导出 |
| 武器活动祈愿 | `gacha_type` : `302` 的祈愿数据 | 祈愿表 | 否,但是应该导出 |
| 常驻祈愿 | `gacha_type` : `200` 的祈愿数据 | 祈愿表 | 否,但是应该导出 |
| 新手祈愿 | `gacha_type` : `100` 的祈愿数据 | 祈愿表 | 否,但是应该导出 |
| 原始数据 | 全部祈愿数据 | 数据表 | **详见下方原始数据表结构说明** |
* 表的顺序可以是任意的
* 可以隐藏部分表,防止用户随意篡改数据
* Sheet 的名称应与游戏内祈愿记录页面显示的名称保持一致
- 表的顺序可以是任意的
- 可以隐藏部分表,防止用户随意篡改数据
- Sheet 的名称应与游戏内祈愿记录页面显示的名称保持一致
> App间应依据 `原始数据表` 的内容,来进行数据互通
> App 间应依据 `原始数据表` 的内容,来进行数据互通
### 祈愿表结构
本节内容是为了规范兼容分析类App
本节内容是为了规范兼容分析类 App
* 表头对应的内容填充**顺序需要严格按照下方说明**排布
* **共享保底的卡池**按祈愿类型 (`gacha_type`) 区分
* 此类 `Sheet` 存在的目的,是为了便于用户观看与祈愿分析工具的分析
- 表头对应的内容填充**顺序需要严格按照下方说明**排布
- **共享保底的卡池**按祈愿类型 (`gacha_type`) 区分
- 此类 `Sheet` 存在的目的,是为了便于用户观看与祈愿分析工具的分析
|表头|内容|是否必要|
|-|-|-|
|时间|`yyyy-MM-dd HH:mm:ss` 格式的 `time` 时间|是|
|名称|`name`物品名称|是|
|物品类型|`item_type`|是|
|星级|`rank_type`|是|
|祈愿类型|`gacha_type` 的转义名称|是,尽管部分工具不会分析此项|
|...|...|否|
| 表头 | 内容 | 是否必要 |
| -------- | ---------------------------------------- | ---------------------------- |
| 时间 | `yyyy-MM-dd HH:mm:ss` 格式的 `time` 时间 | 是 |
| 名称 | `name`物品名称 | 是 |
| 物品类型 | `item_type` | 是 |
| 星级 | `rank_type` | 是 |
| 祈愿类型 | `gacha_type` 的转义名称 | 是,尽管部分工具不会分析此项 |
| ... | ... | 否 |
> 如果你认为有必要的话,可以额外增加其他表头,但请确保表头的前几列为上表规范的内容
> 表内的数据通常按祈愿Id升序或降序排列分析App不应假设表内的顺序为特定的升序与降序
> 表内的数据通常按祈愿 Id 升序或降序排列,分析 App 不应假设表内的顺序为特定的升序与降序
#### `gacha_type` 转义名称
|gacha_type|名称|
|-|-|
|100|新手祈愿|
|200|常驻祈愿|
|301|角色活动祈愿|
|400|角色活动祈愿-2|
|302|武器活动祈愿|
| gacha_type | 名称 |
| ---------- | -------------- |
| 100 | 新手祈愿 |
| 200 | 常驻祈愿 |
| 301 | 角色活动祈愿 |
| 400 | 角色活动祈愿-2 |
| 302 | 武器活动祈愿 |
#### 示例
|时间|名称|类别|星级|祈愿类型|...|
|-|-|-|-|-|-|
|2021-02-17 18:45:09|以理服人|武器|3|角色活动祈愿-2|...|
|...|...|...|...|...|...|
| 时间 | 名称 | 类别 | 星级 | 祈愿类型 | ... |
| ------------------- | -------- | ---- | ---- | -------------- | --- |
| 2021-02-17 18:45:09 | 以理服人 | 武器 | 3 | 角色活动祈愿-2 | ... |
| ... | ... | ... | ... | ... | ... |
### 原始数据表结构
导出时
* App 在导出时应尽可能询问用户是否应包含原始数据表
* 一旦在工作簿内包含了名为 `原始数据` 的表,即表示支持本格式
* 该表内的内容应严格按照本格式所述填充
* **表头的顺序需严格按照下表设置**。
* 现有的字段采用**字典顺序**递增排序,后续新增的字段依添加的顺序排在后侧。
* 若无特殊需求我们建议导出所有json 数据内包含的字段
- App 在导出时应尽可能询问用户是否应包含原始数据表
- 一旦在工作簿内包含了名为 `原始数据` 的表,即表示支持本格式
- 该表内的内容应严格按照本格式所述填充
- **表头的顺序需严格按照下表设置**。
- 现有的字段采用**字典顺序**递增排序,后续新增的字段依添加的顺序排在后侧。
- 若无特殊需求,我们建议导出所有 json 数据内包含的字段
导入时
* 强烈建议您编写不依赖于列的顺序位置便可实现导入的程序,以达到最大化的兼容。
* 如果省略了其中某些非必要字段的值,请保持表头存在,对应的列则空置。
- 强烈建议您编写不依赖于列的顺序位置便可实现导入的程序,以达到最大化的兼容。
- 如果省略了其中某些非必要字段的值,请保持表头存在,对应的列则空置。
|表头|是否必要|
|-|-|
|`count`|否,但是建议保留,不排除后续会有`count`不为1的情况|
|`gacha_type`|是|
|`id`|且大部分App按此字段排序数据|
|`item_id`|否,目前官方已经弃用了此字段|
|`item_type`|是|
|`lang`|否,但建议保留,以便国际化|
|`name`|是|
|`rank_type`|否,但建议保留,以便分析|
|`time`|否,但建议保留,以便分析|
|`uid`|否,但建议将选择权交予用户,保留以便分析|
|`uigf_gacha_type`|是|
| 表头 | 是否必要 |
| ----------------- | ---------------------------------------------------- |
| `count` | 否,但是建议保留,不排除后续会有`count`不为 1 的情况 |
| `gacha_type` | 是 |
| `id` | 是,且大部分 App 按此字段排序数据 |
| `item_id` | 否,目前官方已经弃用了此字段 |
| `item_type` | 是 |
| `lang` | 否,但建议保留,以便国际化 |
| `name` | 是 |
| `rank_type` | 否,但建议保留,以便分析 |
| `time` | 否,但建议保留,以便分析 |
| `uid` | 否,但建议将选择权交予用户,保留以便分析 |
| `uigf_gacha_type` | 是 |
#### 示例
|count|gacha_type|id|item_id|item_type|lang|name|rank_type|time|uid|uigf_gacha_type|
|-|-|-|-|-|-|-|-|-|-|-|
|1|301|1613556360008291100||武器|zh-cn|以理服人|3|2021-02-17 18:45:09|123456789|301|
|...|...|...|...|...|...|...|...|...|...|...|
| count | gacha_type | id | item_id | item_type | lang | name | rank_type | time | uid | uigf_gacha_type |
| ----- | ---------- | ------------------- | ------- | --------- | ----- | -------- | --------- | ------------------- | --------- | --------------- |
| 1 | 301 | 1613556360008291100 | | 武器 | zh-cn | 以理服人 | 3 | 2021-02-17 18:45:09 | 123456789 | 301 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |

View File

@@ -48,7 +48,7 @@ Update: 2023-05-19
### 图像资源
- [36691244|米游社](https://www.miyoushe.com/ys/article/36691244)
- [成就系统|原神WIKI](https://wiki.biligame.com/ys/%E6%88%90%E5%B0%B1%E7%B3%BB%E7%BB%9F)
- [成就系统|原神 WIKI](https://wiki.biligame.com/ys/%E6%88%90%E5%B0%B1%E7%B3%BB%E7%BB%9F)
- [HoneyHunterWorld](https://genshin.honeyhunterworld.com/?lang=EN)
### 格式说明
@@ -57,8 +57,8 @@ Update: 2023-05-19
关于图像资源格式,涉及到的图像类型及格式如下:
- 原石图标:`source/material/原石.webp`256x25632位深度
- 成就系列图标:`source/achievementSeries/${series_id}.webp`256x25632位深度
- 原石图标:`source/material/原石.webp`256x25632 位深度
- 成就系列图标:`source/achievementSeries/${series_id}.webp`256x25632 位深度
- 成就系列对应名片图标:见 [名片相关](#名片相关)
## 名片相关
@@ -70,7 +70,7 @@ Update: 2023-05-19
参考:
- [HoneyHunterWorld](https://genshin.honeyhunterworld.com/?lang=CHS)
- [原神WIKI|名片](https://wiki.biligame.com/ys/%E5%90%8D%E7%89%87)
- [原神 WIKI|名片](https://wiki.biligame.com/ys/%E5%90%8D%E7%89%87)
### 图像资源
@@ -78,13 +78,13 @@ Update: 2023-05-19
### 格式说明
- 名片图标:`source/nameCard/icon/${card.name}.webp`250x16532位深度
- 名片bg`source/nameCard/bg/${card.name}.webp`880x14032位深度
- 名片profile`source/nameCard/profile/${card.name}.webp`840x40032位深度
- 名片图标:`source/nameCard/icon/${card.name}.webp`250x16532 位深度
- 名片 bg`source/nameCard/bg/${card.name}.webp`880x14032 位深度
- 名片 profile`source/nameCard/profile/${card.name}.webp`840x40032 位深度
### 格式说明
- 卡牌图标:`source/gcg/card/${cardType}/${cardName}.webp`420x72032位深度
- 卡牌图标:`source/gcg/card/${cardType}/${cardName}.webp`420x72032 位深度
## 素材日历相关
@@ -101,9 +101,9 @@ Update: 2023-05-19
> 角色&武器资源见 [Wiki](#wiki)。
- 素材图标:`source/calendar/material/${sourceName}.webp`128x12832位深度
- 星级图标:`icon/star/${star}.webp`133x2932位深度
- 地区图标: `icon/nation/${nation}.webp`256x25632位深度
- 素材图标:`source/calendar/material/${sourceName}.webp`128x12832 位深度
- 星级图标:`icon/star/${star}.webp`133x2932 位深度
- 地区图标: `icon/nation/${nation}.webp`256x25632 位深度
## Wiki
@@ -131,9 +131,8 @@ Update: 2023-05-19
>
> iconType 为 `normal` 或 `special`,目前只有 `normal`。
- 角色图鉴:`WIKI/character/icon/${avatarId}.webp`256x25632位深度
- 武器图鉴:`WIKI/weapon/icon/${weaponId}.webp`256x25632位深度
- 角色图鉴:`WIKI/character/icon/${avatarId}.webp`256x25632 位深度
- 武器图鉴:`WIKI/weapon/icon/${weaponId}.webp`256x25632 位深度
- 卡牌图鉴:
- 卡牌背景:`WIKI/GCG/${iconType}/${cardName}.webp`420x72032位深度
- 卡牌图标:`WIKI/GCG/bg.${bgType}.webp`420x72032位深度
- 卡牌背景:`WIKI/GCG/${iconType}/${cardName}.webp`420x72032 位深度
- 卡牌图标:`WIKI/GCG/bg.${bgType}.webp`420x72032 位深度

View File

@@ -1,14 +1,14 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/icon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Tauri.Genshin</title>
</head>
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/icon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Tauri.Genshin</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

View File

@@ -1,11 +1,11 @@
{
"version": "0.2.0",
"notes": "https://github.com/BTMuli/Tauri.Genshin/releases/tag/v0.2.0",
"pub_date": "2023-06-19T07:08:41.112Z",
"platforms": {
"windows-x86_64": {
"signature": "dW50cnVzdGVkIGNvbW1lbnQ6IHNpZ25hdHVyZSBmcm9tIHRhdXJpIHNlY3JldCBrZXkKUlVSWkdzekNNZjh1aGpkUzdSRHAvMnQ0M1hIRVAwOFZxOTNqOTIyQ0dyWlY4YVFVRjNMTyttNWp6SndvK0RnVGgvOFBBNFhCSjZHeVpGUEE2elZzWmlVeFZUSVRqUllPdXdvPQp0cnVzdGVkIGNvbW1lbnQ6IHRpbWVzdGFtcDoxNjg3MTU4NTAzCWZpbGU6dGF1cmktZ2Vuc2hpbl8wLjIuMF94NjRfemgtQ04ubXNpLnppcApmK3hKeWh4WE91SFFmWnFOeTd0aTlJMnlXakFNSVBPRnREY1pUbmcybngwdFNTa1FtZGF4czU5Qm04YjN6RllvRXUxM3c1ZTQxUlZVZE5nYzNKY0JEUT09Cg==",
"url": "https://github.com/BTMuli/Tauri.Genshin/releases/download/v0.2.0/tauri-genshin_0.2.0_x64_zh-CN.msi.zip"
}
}
"version": "0.2.0",
"notes": "https://github.com/BTMuli/Tauri.Genshin/releases/tag/v0.2.0",
"pub_date": "2023-06-19T07:08:41.112Z",
"platforms": {
"windows-x86_64": {
"signature": "dW50cnVzdGVkIGNvbW1lbnQ6IHNpZ25hdHVyZSBmcm9tIHRhdXJpIHNlY3JldCBrZXkKUlVSWkdzekNNZjh1aGpkUzdSRHAvMnQ0M1hIRVAwOFZxOTNqOTIyQ0dyWlY4YVFVRjNMTyttNWp6SndvK0RnVGgvOFBBNFhCSjZHeVpGUEE2elZzWmlVeFZUSVRqUllPdXdvPQp0cnVzdGVkIGNvbW1lbnQ6IHRpbWVzdGFtcDoxNjg3MTU4NTAzCWZpbGU6dGF1cmktZ2Vuc2hpbl8wLjIuMF94NjRfemgtQ04ubXNpLnppcApmK3hKeWh4WE91SFFmWnFOeTd0aTlJMnlXakFNSVBPRnREY1pUbmcybngwdFNTa1FtZGF4czU5Qm04YjN6RllvRXUxM3c1ZTQxUlZVZE5nYzNKY0JEUT09Cg==",
"url": "https://github.com/BTMuli/Tauri.Genshin/releases/download/v0.2.0/tauri-genshin_0.2.0_x64_zh-CN.msi.zip"
}
}
}

View File

@@ -1,76 +1,76 @@
{
"build": {
"beforeDevCommand": "npm run vite:dev",
"beforeBuildCommand": "npm run vite:build",
"devPath": "http://localhost:3000",
"distDir": "../dist",
"withGlobalTauri": true
},
"package": {
"productName": "tauri-genshin",
"version": "0.2.1"
},
"tauri": {
"allowlist": {
"all": true,
"fs": {
"all": true,
"scope": ["**", "**/*"]
},
"http": {
"all": true,
"request": true,
"scope": [
"https://api-takumi.mihoyo.com/*",
"https://api-takumi-record.mihoyo.com/*",
"https://api-static.mihoyo.com/*",
"https://bbs-api.mihoyo.com/*",
"https://bbs-api.miyoushe.com/*",
"https://bbs.mihoyo.com/*",
"https://hk4e-api.mihoyo.com/*",
"https://passport-api.mihoyo.com/*",
"https://passport-api-v4.mihoyo.com/*",
"https://act-webstatic.mihoyo.com/*",
"https://sdk-webstatic.mihoyo.com/*",
"https://homa.snapgenshin.com/*"
]
},
"shell": {
"all": false,
"open": true
}
},
"bundle": {
"active": true,
"icon": ["icons/icon.png", "icons/icon.ico"],
"identifier": "tauri-genshin",
"targets": "all",
"windows": {
"wix": {
"language": "zh-CN"
}
}
},
"security": {
"csp": null
},
"updater": {
"active": true,
"dialog": true,
"endpoints": ["https://raw.fastgit.org/BTMuli/Tauri.Genshin/master/latest.json"],
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDg2MkVGRjMxQzJDQzFBNTkKUldSWkdzekNNZjh1aHJGRXBEOGtwbUxLaU1wdWNVeUJaeGhoV2ZlZ3VlYmQ0b2tYZWQwODdnTHkK"
},
"windows": [
{
"fullscreen": false,
"resizable": true,
"title": "Tauri.Genshin",
"label": "tauri-genshin",
"width": 1600,
"height": 900,
"center": true,
"transparent": true
}
]
}
"build": {
"beforeDevCommand": "npm run vite:dev",
"beforeBuildCommand": "npm run vite:build",
"devPath": "http://localhost:3000",
"distDir": "../dist",
"withGlobalTauri": true
},
"package": {
"productName": "tauri-genshin",
"version": "0.2.1"
},
"tauri": {
"allowlist": {
"all": true,
"fs": {
"all": true,
"scope": ["**", "**/*"]
},
"http": {
"all": true,
"request": true,
"scope": [
"https://api-takumi.mihoyo.com/*",
"https://api-takumi-record.mihoyo.com/*",
"https://api-static.mihoyo.com/*",
"https://bbs-api.mihoyo.com/*",
"https://bbs-api.miyoushe.com/*",
"https://bbs.mihoyo.com/*",
"https://hk4e-api.mihoyo.com/*",
"https://passport-api.mihoyo.com/*",
"https://passport-api-v4.mihoyo.com/*",
"https://act-webstatic.mihoyo.com/*",
"https://sdk-webstatic.mihoyo.com/*",
"https://homa.snapgenshin.com/*"
]
},
"shell": {
"all": false,
"open": true
}
},
"bundle": {
"active": true,
"icon": ["icons/icon.png", "icons/icon.ico"],
"identifier": "tauri-genshin",
"targets": "all",
"windows": {
"wix": {
"language": "zh-CN"
}
}
},
"security": {
"csp": null
},
"updater": {
"active": true,
"dialog": true,
"endpoints": ["https://raw.fastgit.org/BTMuli/Tauri.Genshin/master/latest.json"],
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDg2MkVGRjMxQzJDQzFBNTkKUldSWkdzekNNZjh1aHJGRXBEOGtwbUxLaU1wdWNVeUJaeGhoV2ZlZ3VlYmQ0b2tYZWQwODdnTHkK"
},
"windows": [
{
"fullscreen": false,
"resizable": true,
"title": "Tauri.Genshin",
"label": "tauri-genshin",
"width": 1600,
"height": 900,
"center": true,
"transparent": true
}
]
}
}

View File

@@ -42,7 +42,7 @@ onMounted(async () => {
});
// 监听主题变化
async function listenOnTheme () {
async function listenOnTheme() {
await event.listen("readTheme", (e) => {
const themeGet = e.payload as string;
if (theme.value !== themeGet) {
@@ -52,7 +52,7 @@ async function listenOnTheme () {
});
}
async function checkLoad () {
async function checkLoad() {
if (appStore.loading) {
console.info("数据已加载!");
return;
@@ -63,13 +63,13 @@ async function checkLoad () {
}
// 创建数据文件夹
async function createDataDir () {
async function createDataDir() {
console.info("开始创建数据文件夹...");
// 如果不存在则创建
if (!await fs.exists("userData", { dir: fs.BaseDirectory.AppLocalData })) {
if (!(await fs.exists("userData", { dir: fs.BaseDirectory.AppLocalData }))) {
await fs.createDir("userData", { dir: fs.BaseDirectory.AppLocalData, recursive: true });
}
if (!await fs.exists("tempData", { dir: fs.BaseDirectory.AppLocalData })) {
if (!(await fs.exists("tempData", { dir: fs.BaseDirectory.AppLocalData }))) {
await fs.createDir("tempData", { dir: fs.BaseDirectory.AppLocalData, recursive: true });
}
console.info("数据文件夹创建完成!");

View File

@@ -6,21 +6,21 @@
*/
@font-face {
font-family: "Genshin";
src: url("./汉仪文黑-85W.ttf") format("truetype");
font-family: "Genshin";
src: url("./汉仪文黑-85W.ttf") format("truetype");
}
@font-face {
font-family: "Genshin-Light";
src: url("./汉仪文黑-55W.ttf") format("truetype");
font-family: "Genshin-Light";
src: url("./汉仪文黑-55W.ttf") format("truetype");
}
@font-face {
font-family: "JetBrians mono";
src: url("./JetBrainsMono-Regular.ttf") format("truetype");
font-family: "JetBrians mono";
src: url("./JetBrainsMono-Regular.ttf") format("truetype");
}
@font-face {
font-family: "JetBrians mono Bold";
src: url("./JetBrainsMono-Bold.ttf") format("truetype");
font-family: "JetBrians mono Bold";
src: url("./JetBrainsMono-Bold.ttf") format("truetype");
}

View File

@@ -10,65 +10,64 @@
@import url("themes/dark.css");
:root {
/* font */
--font-text: "JetBrians mono", "Genshin-Light", sans-serif;
--font-title: "JetBrians mono Bold", "Genshin", serif;
/* color */
--common-color-white: #faf7e8;
--common-color-black: #333333;
--common-color-blue: #485466;
--common-color-blue-2: #546d8b;
--common-color-blue-3: #5b738f;
--common-color-yellow: #fec90b;
--common-color-grey: #96979a;
--common-color-grey-2: #b3b3b3;
/* font */
--font-text: "JetBrians mono", "Genshin-Light", sans-serif;
--font-title: "JetBrians mono Bold", "Genshin", serif;
/* color */
--common-color-white: #faf7e8;
--common-color-black: #333333;
--common-color-blue: #485466;
--common-color-blue-2: #546d8b;
--common-color-blue-3: #5b738f;
--common-color-yellow: #fec90b;
--common-color-grey: #96979a;
--common-color-grey-2: #b3b3b3;
}
html {
font-family: var(--font-text);
font-size: 16px;
font-family: var(--font-text);
font-size: 16px;
}
/*
* @description 侧边滚动条样式
* @since Alpha v0.2.1
*/
::-webkit-scrollbar {
width: 8px;
border-radius: 5px;
width: 8px;
border-radius: 5px;
}
::-webkit-scrollbar-track {
background: #847c74; /* 夜灰 */
border-radius: 5px;
background: #847c74; /* 夜灰 */
border-radius: 5px;
}
::-webkit-scrollbar-thumb {
background: #c7d2d4; /* 鸥蓝 */
border-radius: 5px;
background: #c7d2d4; /* 鸥蓝 */
border-radius: 5px;
}
/* card action 内的按钮 */
.card-btn {
background: #4a5366;
color: #ece5d8;
border-radius: 50px;
margin-left: 5px;
background: #4a5366;
color: #ece5d8;
border-radius: 50px;
margin-left: 5px;
}
.card-btn img {
width: 20px;
height: 20px;
margin: 5px;
width: 20px;
height: 20px;
margin: 5px;
}
.card-dev-btn {
background: #546d8b;
color: #faf7e8;
background: #546d8b;
color: #faf7e8;
}
.card-dev-btn img {
width: 18px;
height: 18px;
width: 18px;
height: 18px;
}

View File

@@ -12,7 +12,7 @@ html.default {
--common-bg-4: rgb(0 0 0 / 40%);
--common-bg-8: rgb(0 0 0 / 80%);
--common-text: var(--common-color-blue); /* title */
--common-text: var(--common-color-blue); /* title */
--common-text-2: var(--common-color-blue-2); /* text */
--common-text-3: var(--common-color-blue-3); /* quote */

View File

@@ -29,7 +29,7 @@ const visible = computed({
set: (value) => emits("update:modelValue", value),
});
function onCancel () {
function onCancel() {
visible.value = false;
}
</script>

View File

@@ -7,7 +7,7 @@ import { computed } from "vue";
import TItemBox, { TItemBoxData } from "../main/t-itembox.vue";
interface TibCalendarAvatarProps {
modelValue: TGApp.App.Calendar.Item
modelValue: TGApp.App.Calendar.Item;
}
const props = defineProps<TibCalendarAvatarProps>();

View File

@@ -7,19 +7,17 @@ import { onMounted, ref } from "vue";
import TItemBox, { TItemBoxData } from "../main/t-itembox.vue";
interface TibUrAvatarProps {
modelValue: TGApp.Sqlite.Record.Avatar
modelValue: TGApp.Sqlite.Record.Avatar;
}
const props = defineProps<TibUrAvatarProps>();
const box = ref({} as TItemBoxData);
const getName = () => {
return (
props.modelValue.id === 10000005
? "旅行者-空"
: props.modelValue.id === 10000007
? "旅行者-荧"
: props.modelValue.name
);
return props.modelValue.id === 10000005
? "旅行者-空"
: props.modelValue.id === 10000007
? "旅行者-荧"
: props.modelValue.name;
};
onMounted(async () => {
@@ -38,7 +36,7 @@ onMounted(async () => {
};
});
function showData () {
function showData() {
// todo @click 应该出来的是一个弹窗,而不是 console
console.log(props.modelValue);
}

View File

@@ -1,5 +1,5 @@
<template>
<TItemBox :model-value="box" style="cursor: pointer;" />
<TItemBox :model-value="box" style="cursor: pointer" />
</template>
<script lang="ts" setup>
// vue
@@ -7,7 +7,7 @@ import { computed } from "vue";
import TItemBox, { TItemBoxData } from "../main/t-itembox.vue";
interface TibCalendarWeaponProps {
modelValue: TGApp.App.Weapon.WikiBriefInfo
modelValue: TGApp.App.Weapon.WikiBriefInfo;
}
const props = defineProps<TibCalendarWeaponProps>();

View File

@@ -1,7 +1,7 @@
<template>
<transition name="fade">
<div v-show="canTop" class="back-top" @click="handleScrollTop">
<img src="../../assets/icons/arrow-top.svg" alt="back-icon">
<img src="../../assets/icons/arrow-top.svg" alt="back-icon" />
</div>
</transition>
</template>
@@ -13,7 +13,7 @@ const scrollTop = ref(0); // 滚动条距离顶部的距离
const canTop = ref(false); // 默认不显示
// 监听滚动事件
function handleScroll () {
function handleScroll() {
scrollTop.value = document.documentElement.scrollTop || document.body.scrollTop;
// 超过500px显示回到顶部按钮
canTop.value = scrollTop.value > 500;
@@ -26,10 +26,10 @@ function handleScroll () {
}
// 点击回到顶部
function handleScrollTop () {
function handleScrollTop() {
let timer = 0;
cancelAnimationFrame(timer);
timer = requestAnimationFrame(function fn () {
timer = requestAnimationFrame(function fn() {
if (scrollTop.value > 0) {
scrollTop.value -= 50;
document.body.scrollTop = document.documentElement.scrollTop = scrollTop.value;

View File

@@ -2,9 +2,7 @@
<div class="calendar-box">
<div class="calendar-title">
<div class="calendar-title-left">
<v-icon size="small">
mdi-calendar-clock
</v-icon>
<v-icon size="small"> mdi-calendar-clock </v-icon>
<span>今日素材</span>
<span>{{ dateNow }}</span>
</div>
@@ -119,7 +117,7 @@ defineExpose({
});
onMounted(() => {
const dayNow = (new Date().getDay()) === 0 ? 7 : (new Date().getDay());
const dayNow = new Date().getDay() === 0 ? 7 : new Date().getDay();
weekNow.value = dayNow;
btnNow.value = dayNow;
calendarNow.value = getCalendar(dayNow);
@@ -129,17 +127,17 @@ onMounted(() => {
});
// 获取当前日历
function getCalendar (day: number) {
function getCalendar(day: number) {
return calendarData.value.filter((item) => item.dropDays.includes(day));
}
function selectContent (item: TGApp.App.Calendar.Item, type: string) {
function selectContent(item: TGApp.App.Calendar.Item, type: string) {
selectedItem.value = item;
selectedType.value = type;
showItem.value = true;
}
function getContents (day: number) {
function getContents(day: number) {
btnNow.value = day;
calendarNow.value = getCalendar(day);
characterCards.value = calendarNow.value.filter((item) => item.itemType === "character");
@@ -159,7 +157,7 @@ function getContents (day: number) {
height: 45px;
font-size: 20px;
display: flex;
color:rgb(255 255 255 / 80%);
color: rgb(255 255 255 / 80%);
}
.calendar-title-left {
@@ -182,8 +180,8 @@ function getContents (day: number) {
}
.calendar-title-btn {
margin-left: 10px;
border-radius: 5px;
margin-left: 10px;
border-radius: 5px;
}
.calendar-divider {
@@ -194,12 +192,12 @@ function getContents (day: number) {
}
.calendar-sub {
margin: 5px;
margin: 5px;
}
.cards-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
grid-gap: 8px;
display: grid;
grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
grid-gap: 8px;
}
</style>

View File

@@ -3,13 +3,13 @@
<div class="tib2-left">
<slot name="left">
<div v-if="props.modelValue.bg !== undefined" class="tib2-bg">
<img :src="props.modelValue.bg" alt="bg">
<img :src="props.modelValue.bg" alt="bg" />
</div>
<div class="tib2-icon">
<img :src="props.modelValue.icon" alt="icon">
<img :src="props.modelValue.icon" alt="icon" />
</div>
<div v-if="props.modelValue.star !== undefined" class="tib2-star">
<img :src="props.modelValue.star" alt="element">
<img :src="props.modelValue.star" alt="element" />
</div>
</slot>
</div>
@@ -19,18 +19,17 @@
</div>
</template>
<script lang="ts" setup>
export interface TItemBox2Data {
width: string,
height: string,
bg?: string,
icon: string,
star?: string,
name: string,
width: string;
height: string;
bg?: string;
icon: string;
star?: string;
name: string;
}
interface TItemBox2Props {
modelValue: TItemBox2Data,
modelValue: TItemBox2Data;
}
const props = defineProps<TItemBox2Props>();
@@ -38,16 +37,16 @@ const props = defineProps<TItemBox2Props>();
<style lang="css" scoped>
.tib2-box {
position: relative;
height: v-bind(props["modelValue"]["height"]);
width: v-bind(props["modelValue"]["width"]);
height: v-bind(props[ "modelValue"][ "height"]);
width: v-bind(props[ "modelValue"][ "width"]);
display: flex;
border-radius: 5px;
background: rgb(20 20 20 / 30%);
}
.tib2-left {
width: v-bind(props["modelValue"]["height"]);
height: v-bind(props["modelValue"]["height"]);
width: v-bind(props[ "modelValue"][ "height"]);
height: v-bind(props[ "modelValue"][ "height"]);
border-top-left-radius: 5px;
border-bottom-left-radius: 5px;
overflow: hidden;
@@ -57,8 +56,8 @@ const props = defineProps<TItemBox2Props>();
.tib2-icon {
position: absolute;
top: 0;
width: v-bind(props["modelValue"]["height"]);
height: v-bind(props["modelValue"]["height"]);
width: v-bind(props[ "modelValue"][ "height"]);
height: v-bind(props[ "modelValue"][ "height"]);
border-top-left-radius: 5px;
border-bottom-left-radius: 5px;
overflow: hidden;
@@ -74,7 +73,7 @@ const props = defineProps<TItemBox2Props>();
.tib2-star {
position: absolute;
bottom: -5px;
width: v-bind(props["modelValue"]["height"]);
width: v-bind(props[ "modelValue"][ "height"]);
height: auto;
}
@@ -87,7 +86,7 @@ const props = defineProps<TItemBox2Props>();
display: flex;
align-items: center;
justify-content: center;
font-size: calc(0.2 * v-bind(props["modelValue"]["height"]));
font-size: calc(0.2 * v-bind(props[ "modelValue"][ "height"]));
color: var(--common-color-white);
overflow: hidden;
}

View File

@@ -2,31 +2,31 @@
<div class="tib-box">
<div class="tib-bg">
<slot name="bg">
<img :src="modelValue.bg" alt="bg">
<img :src="modelValue.bg" alt="bg" />
</slot>
</div>
<div class="tib-icon">
<slot name="icon">
<img :src="modelValue.icon" alt="icon">
<img :src="modelValue.icon" alt="icon" />
</slot>
</div>
<div class="tib-cover">
<div class="tib-lt">
<img :src="modelValue.lt" alt="lt">
<img :src="modelValue.lt" alt="lt" />
</div>
<div v-show="modelValue.rt" class="tib-rt">
{{ modelValue.rt }}
</div>
<div class="tib-inner">
<slot name="inner-icon">
<img v-show="modelValue.innerIcon" :src="modelValue.innerIcon" alt="inner-icon">
<img v-show="modelValue.innerIcon" :src="modelValue.innerIcon" alt="inner-icon" />
</slot>
<slot name="inner-text">
<span>{{ modelValue.innerText }}</span>
</slot>
</div>
</div>
<div v-if="modelValue.display==='outer'" class="tib-outer">
<div v-if="modelValue.display === 'outer'" class="tib-outer">
<slot name="outer-text">
<span>{{ modelValue.outerText }}</span>
</slot>
@@ -37,24 +37,24 @@
import { computed } from "vue";
export interface TItemBoxData {
bg: string,
icon: string,
size: string,
height: string,
display: "inner" | "outer",
lt: string,
ltSize: string,
rt?: string,
rtSize?: string,
innerHeight?: number,
innerIcon?: string,
innerText: string,
outerHeight?: number,
outerText?: string,
bg: string;
icon: string;
size: string;
height: string;
display: "inner" | "outer";
lt: string;
ltSize: string;
rt?: string;
rtSize?: string;
innerHeight?: number;
innerIcon?: string;
innerText: string;
outerHeight?: number;
outerText?: string;
}
interface TItemBoxProps {
modelValue: TItemBoxData,
modelValue: TItemBoxData;
}
const props = withDefaults(defineProps<TItemBoxProps>(), {
@@ -78,16 +78,16 @@ const getOuterFont = computed(() => `${props.modelValue.outerHeight / 2}px`);
<style lang="css" scoped>
.tib-box {
position: relative;
width: v-bind(props["modelValue"]["size"]);
height: v-bind(props["modelValue"]["height"]);
width: v-bind(props[ "modelValue"][ "size"]);
height: v-bind(props[ "modelValue"][ "height"]);
}
.tib-bg {
position: absolute;
top: 0;
left: 0;
width: v-bind(props["modelValue"]["size"]);
height: v-bind(props["modelValue"]["size"]);
width: v-bind(props[ "modelValue"][ "size"]);
height: v-bind(props[ "modelValue"][ "size"]);
border-radius: 5px;
overflow: hidden;
}
@@ -100,8 +100,8 @@ const getOuterFont = computed(() => `${props.modelValue.outerHeight / 2}px`);
.tib-icon {
position: relative;
width: v-bind(props["modelValue"]["size"]);
height: v-bind(props["modelValue"]["size"]);
width: v-bind(props[ "modelValue"][ "size"]);
height: v-bind(props[ "modelValue"][ "size"]);
overflow: hidden;
border-radius: 5px;
}
@@ -116,8 +116,8 @@ const getOuterFont = computed(() => `${props.modelValue.outerHeight / 2}px`);
position: absolute;
top: 0;
left: 0;
width: v-bind(props["modelValue"]["size"]);
height: v-bind(props["modelValue"]["size"]);
width: v-bind(props[ "modelValue"][ "size"]);
height: v-bind(props[ "modelValue"][ "size"]);
border-radius: 5px;
display: flex;
justify-content: center;
@@ -130,8 +130,8 @@ const getOuterFont = computed(() => `${props.modelValue.outerHeight / 2}px`);
top: 0;
left: 0;
padding: 5px;
width: v-bind(props["modelValue"]["ltSize"]);
height: v-bind(props["modelValue"]["ltSize"]);
width: v-bind(props[ "modelValue"][ "ltSize"]);
height: v-bind(props[ "modelValue"][ "ltSize"]);
display: flex;
justify-content: center;
align-items: center;
@@ -147,8 +147,8 @@ const getOuterFont = computed(() => `${props.modelValue.outerHeight / 2}px`);
position: absolute;
top: 0;
right: 0;
width: v-bind(props["modelValue"]["rtSize"]);
height: v-bind(props["modelValue"]["rtSize"]);
width: v-bind(props[ "modelValue"][ "rtSize"]);
height: v-bind(props[ "modelValue"][ "rtSize"]);
background: rgb(0 0 0 / 40%);
border-top-right-radius: 5px;
border-bottom-left-radius: 5px;

View File

@@ -26,19 +26,21 @@ const props = withDefaults(defineProps<TolProps>(), {
blurVal: "20px",
});
watch(() => props.modelValue, () => {
if (props.modelValue) {
showTolo.value = true;
showToli.value = true;
} else {
setTimeout(() => {
showToli.value = false;
}, 100);
setTimeout(() => {
showTolo.value = false;
}, 300);
}
},
watch(
() => props.modelValue,
() => {
if (props.modelValue) {
showTolo.value = true;
showToli.value = true;
} else {
setTimeout(() => {
showToli.value = false;
}, 100);
setTimeout(() => {
showTolo.value = false;
}, 300);
}
},
);
</script>
<style lang="css" scoped>

View File

@@ -1,19 +1,15 @@
<template>
<div class="pool-box">
<div class="pool-title">
<img src="../../assets/icons/icon-wish.svg" alt="wish">
<img src="../../assets/icons/icon-wish.svg" alt="wish" />
限时祈愿
</div>
<div v-if="!loading" class="pool-grid">
<v-card
v-for="pool in poolCards"
:key="pool.post_id"
class="pool-card"
>
<v-card v-for="pool in poolCards" :key="pool.post_id" class="pool-card">
<v-list class="pool-list">
<v-list-item :title="pool.title" :subtitle="pool.subtitle">
<template #prepend>
<img :src="pool.voice.icon" class="pool-sideIcon" alt="icon">
<img :src="pool.voice.icon" class="pool-sideIcon" alt="icon" />
</template>
<template v-if="pool.voice.url" #append>
<audio :src="pool.voice.url" controls />
@@ -21,7 +17,7 @@
</v-list-item>
</v-list>
<div class="pool-cover" @click="toPost(pool)">
<img :src="pool.cover" alt="cover">
<img :src="pool.cover" alt="cover" />
</div>
<div class="pool-character">
<div class="pool-icon-grid">
@@ -31,11 +27,16 @@
class="pool-icon"
@click="toOuter(character.url, pool.title)"
>
<img :src="character.icon" alt="character">
<img :src="character.icon" alt="character" />
</div>
</div>
<div class="pool-clock">
<v-progress-circular :model-value="poolTimePass[pool.post_id]" size="100" width="10" :color="poolColor[pool.post_id]">
<v-progress-circular
:model-value="poolTimePass[pool.post_id]"
size="100"
width="10"
:color="poolColor[pool.post_id]"
>
{{ poolTimeGet[pool.post_id] }}
</v-progress-circular>
</div>
@@ -92,7 +93,7 @@ defineExpose({
loading,
});
function poolLastInterval (postId: number) {
function poolLastInterval(postId: number) {
const pool = poolCards.value.find((pool) => pool.post_id === postId);
if (!pool) return;
if (poolTimeGet.value[postId] === "未开始") {
@@ -105,7 +106,7 @@ function poolLastInterval (postId: number) {
const isEnd = pool.time.end_stamp - Date.now();
poolTimeGet.value[postId] = getLastPoolTime(isEnd);
poolTimePass.value[postId] =
((pool.time.end_stamp - Date.now()) / (pool.time.end_stamp - pool.time.start_stamp)) * 100;
((pool.time.end_stamp - Date.now()) / (pool.time.end_stamp - pool.time.start_stamp)) * 100;
if (isEnd >= 0) return;
clearInterval(timer.value[postId]);
timer.value[postId] = null;
@@ -156,7 +157,7 @@ onMounted(async () => {
});
// 检测是否有新的限时祈愿
function checkCover (data: GachaData[]) {
function checkCover(data: GachaData[]) {
// 如果没有缓存
if (!homeStore.poolCover || Object.keys(homeStore.poolCover).length === 0) {
return false;
@@ -180,7 +181,7 @@ function checkCover (data: GachaData[]) {
});
}
async function toOuter (url: string, title: string) {
async function toOuter(url: string, title: string) {
if (!url) {
barText.value = "链接为空!";
barColor.value = "error";
@@ -190,14 +191,14 @@ async function toOuter (url: string, title: string) {
createTGWindow(url, "祈愿", title, 1200, 800, true, true);
}
function getLastPoolTime (time: number) {
function getLastPoolTime(time: number) {
const hour = Math.floor(time / 1000 / 60 / 60);
const minute = Math.floor((time / 1000 / 60 / 60 - hour) * 60);
const second = Math.floor(((time / 1000 / 60 / 60 - hour) * 60 - minute) * 60);
return `${hour}:${minute.toFixed(0).padStart(2, "0")}:${second.toFixed(0).padStart(2, "0")}`;
}
function toPost (pool: GachaCard) {
function toPost(pool: GachaCard) {
const path = router.resolve({
name: "帖子详情",
params: {
@@ -225,7 +226,7 @@ onUnmounted(() => {
.pool-title {
color: var(--common-text);
font-family: var(--font-title);
font-family: var(--font-title);
font-size: 20px;
display: flex;
}
@@ -235,7 +236,7 @@ onUnmounted(() => {
height: 25px;
transform: translate(0, 2px);
margin-right: 10px;
border-radius: 50%;
border-radius: 50%;
background: var(--common-bg-2);
}
@@ -247,19 +248,19 @@ onUnmounted(() => {
}
.pool-card {
background: #45b787; /* 蛙绿 */
color: #eef7f2; /* 丹白 */
border-radius: 5px;
background: #45b787; /* 蛙绿 */
color: #eef7f2; /* 丹白 */
border-radius: 5px;
}
.dark .pool-card {
background: #1f2623; /* 苷蓝绿 */
background: #1f2623; /* 苷蓝绿 */
}
.pool-list {
font-family: var(--font-title);
background: inherit;
color: inherit;
font-family: var(--font-title);
background: inherit;
color: inherit;
}
.pool-sideIcon {
@@ -267,7 +268,7 @@ onUnmounted(() => {
width: 40px;
height: 40px;
border-radius: 50%;
background: var(--common-bg-2);
background: var(--common-bg-2);
}
.pool-cover {
@@ -282,7 +283,7 @@ onUnmounted(() => {
}
.pool-cover img {
width: 100%;
width: 100%;
transition: all 0.5s;
border-radius: 10px;
}
@@ -301,22 +302,22 @@ onUnmounted(() => {
}
.pool-icon-grid {
display: grid;
grid-template-columns: repeat(4,70px);
grid-column-gap: 10px;
display: grid;
grid-template-columns: repeat(4, 70px);
grid-column-gap: 10px;
}
.pool-icon {
width: 70px;
height: 70px;
border-radius: 5px;
width: 70px;
height: 70px;
border-radius: 5px;
}
.pool-icon img {
width: 100%;
height: 100%;
border-radius: 5px;
cursor: pointer;
width: 100%;
height: 100%;
border-radius: 5px;
cursor: pointer;
}
.pool-clock {

View File

@@ -1,15 +1,11 @@
<template>
<div class="position-box">
<div class="position-title">
<img src="../../assets/icons/board.svg" alt="act">
<img src="../../assets/icons/board.svg" alt="act" />
<span>近期活动</span>
</div>
<div v-if="!loading" class="position-grid">
<v-card
v-for="card in positionCards"
:key="card.post_id"
class="position-card"
>
<v-card v-for="card in positionCards" :key="card.post_id" class="position-card">
<v-list class="position-list">
<v-list-item :title="card.title" :subtitle="card.abstract">
<template #prepend>
@@ -18,9 +14,7 @@
</v-avatar>
</template>
<template #append>
<v-btn variant="outlined" @click="toPost(card)">
查看
</v-btn>
<v-btn variant="outlined" @click="toPost(card)"> 查看 </v-btn>
</template>
</v-list-item>
</v-list>
@@ -38,7 +32,9 @@
positionTimeGet[card.post_id]
}}</span>
<!-- 粉红 -->
<span v-if="positionTimeGet[card.post_id] === '已结束'" style="color: #f2b9b2">已结束</span>
<span v-if="positionTimeGet[card.post_id] === '已结束'" style="color: #f2b9b2"
>已结束</span
>
</div>
</v-card-text>
</v-card>
@@ -74,7 +70,7 @@ defineExpose({
loading,
});
function positionLastInterval (postId: number) {
function positionLastInterval(postId: number) {
const timeGet = positionTimeGet.value[postId];
if (timeGet === "未知" || timeGet === "已结束") {
clearInterval(positionTimer.value[postId]);
@@ -110,17 +106,17 @@ onMounted(async () => {
loading.value = false;
});
function getLastPositionTime (time: number) {
function getLastPositionTime(time: number) {
const day = Math.floor(time / (24 * 3600 * 1000));
const hour = Math.floor((time % (24 * 3600 * 1000)) / (3600 * 1000));
const minute = Math.floor((time % (3600 * 1000)) / (60 * 1000));
const second = Math.floor((time % (60 * 1000)) / 1000);
return `${day}${hour.toFixed(0).padStart(2, "0")}:${minute.toFixed(0).padStart(2, "0")}:${second
return `${day}${hour.toFixed(0).padStart(2, "0")}:${minute
.toFixed(0)
.padStart(2, "0")}`;
.padStart(2, "0")}:${second.toFixed(0).padStart(2, "0")}`;
}
async function toPost (card: PositionCard) {
async function toPost(card: PositionCard) {
// 获取路由路径
const path = router.resolve({
name: "帖子详情",
@@ -173,7 +169,7 @@ onUnmounted(() => {
.position-card {
background: #45b787; /* 蛙绿 */
color: #eef7f2; /* 月白 */
border-radius: 5px
border-radius: 5px;
}
.dark .position-card {

View File

@@ -1,9 +1,7 @@
<template>
<div class="share-box">
<div class="share-btn" @click="shareContent()">
<v-icon style="color:var(--theme-switch-icon)">
mdi-share-variant
</v-icon>
<v-icon style="color: var(--theme-switch-icon)"> mdi-share-variant </v-icon>
</div>
</div>
</template>
@@ -18,7 +16,7 @@ interface TShareBtnProps {
const props = defineProps<TShareBtnProps>();
async function shareContent () {
async function shareContent() {
await generateShareImg(props.title, props.modelValue);
}
</script>

View File

@@ -17,38 +17,38 @@
<!-- 菜单项 -->
<v-list-item value="home" title="首页" :link="true" href="/">
<template #prepend>
<img src="/source/UI/paimon.webp" alt="homeIcon" class="side-icon">
<img src="/source/UI/paimon.webp" alt="homeIcon" class="side-icon" />
</template>
</v-list-item>
<v-list-item title="公告" value="announcements" :link="true" href="/announcements">
<template #prepend>
<img src="../../assets/icons/board.svg" alt="annoIcon" class="side-icon">
<img src="../../assets/icons/board.svg" alt="annoIcon" class="side-icon" />
</template>
</v-list-item>
<v-list-item title="咨讯" value="news" :link="true" href="/news/2">
<template #prepend>
<img src="/platforms/mhy/mys.webp" alt="mihoyo" class="side-icon">
<img src="/platforms/mhy/mys.webp" alt="mihoyo" class="side-icon" />
</template>
</v-list-item>
<v-list-item title="成就" value="achievements" :link="true" href="/achievements">
<template #prepend>
<img src="../../assets/icons/achievements.svg" alt="achievementsIcon" class="side-icon">
<img src="../../assets/icons/achievements.svg" alt="achievementsIcon" class="side-icon" />
</template>
</v-list-item>
<v-divider />
<v-list-item title="原神战绩" value="record" :link="true" href="/user/record">
<template #prepend>
<img src="/source/UI/userRecord.webp" alt="record" class="side-icon">
<img src="/source/UI/userRecord.webp" alt="record" class="side-icon" />
</template>
</v-list-item>
<v-list-item title="我的角色" value="character" :link="true" href="/user/characters">
<template #prepend>
<img src="/source/UI/userAvatar.webp" alt="characters" class="side-icon">
<img src="/source/UI/userAvatar.webp" alt="characters" class="side-icon" />
</template>
</v-list-item>
<v-list-item title="深渊记录" value="abyss" :link="true" href="/user/abyss">
<template #prepend>
<img src="/source/UI/userAbyss.webp" alt="abyss" class="side-icon">
<img src="/source/UI/userAbyss.webp" alt="abyss" class="side-icon" />
</template>
</v-list-item>
<!-- todo -->
@@ -68,35 +68,35 @@
<template #activator="{ props }">
<v-list-item title="图鉴" v-bind="props">
<template #prepend>
<img src="/source/UI/guideMini.webp" alt="wikiIcon" class="side-icon-mini">
<img src="/source/UI/guideMini.webp" alt="wikiIcon" class="side-icon-mini" />
</template>
</v-list-item>
</template>
<v-list-item title="深渊数据库" value="wiki-abyss" :link="true" href="/wiki/abyss">
<template #prepend>
<img src="/icon/star/Abyss.webp" alt="abyssIcon" class="side-icon">
<img src="/icon/star/Abyss.webp" alt="abyssIcon" class="side-icon" />
</template>
</v-list-item>
<v-list-item title="GCG" value="wiki-GCG" :link="true" href="/wiki/GCG">
<template #prepend>
<img src="../../assets/icons/GCG.svg" alt="gcgIcon" class="side-icon">
<img src="../../assets/icons/GCG.svg" alt="gcgIcon" class="side-icon" />
</template>
</v-list-item>
<v-list-item title="角色图鉴" value="wiki-character" :link="true" href="/wiki/character">
<template #prepend>
<img src="/source/UI/avatarMini.webp" alt="characterIcon" class="side-icon-mini">
<img src="/source/UI/avatarMini.webp" alt="characterIcon" class="side-icon-mini" />
</template>
</v-list-item>
<v-list-item title="武器图鉴" value="wiki-weapon" :link="true" href="/wiki/weapon">
<template #prepend>
<img src="/source/UI/weaponMini.webp" alt="weaponIcon" class="side-icon-mini">
<img src="/source/UI/weaponMini.webp" alt="weaponIcon" class="side-icon-mini" />
</template>
</v-list-item>
</v-list-group>
<div class="bottom-menu">
<v-list-item>
<template #prepend>
<img :src="userInfo.avatar" alt="userIcon" class="side-icon">
<img :src="userInfo.avatar" alt="userIcon" class="side-icon" />
</template>
<template #default>
<v-list-item-title>
@@ -113,7 +113,7 @@
</v-list-item>
<v-list-item title="设置" value="config" :link="true" href="/config">
<template #prepend>
<img src="../../assets/icons/setting.svg" alt="setting" class="side-icon">
<img src="../../assets/icons/setting.svg" alt="setting" class="side-icon" />
</template>
</v-list-item>
</div>
@@ -143,10 +143,10 @@ const userInfo = computed(() => {
const rail = ref(appStore.sidebar.collapse);
// theme
const themeGet = computed({
get () {
get() {
return appStore.theme;
},
set (value: string) {
set(value: string) {
appStore.theme = value;
},
});
@@ -155,15 +155,15 @@ const themeTitle = computed(() => {
});
const open = computed({
get () {
get() {
return appStore.getSubmenu();
},
set (value: string[]) {
set(value: string[]) {
appStore.sidebar.submenu.wiki = value.includes("wiki");
},
});
function collapse () {
function collapse() {
rail.value = !rail.value;
appStore.sidebar.collapse = rail.value;
}
@@ -172,14 +172,14 @@ onMounted(async () => {
await listenOnTheme();
});
async function listenOnTheme () {
async function listenOnTheme() {
await event.listen("readTheme", (e) => {
const theme = e.payload as string;
themeGet.value = theme === "default" ? "default" : "dark";
});
}
async function switchTheme () {
async function switchTheme() {
await event.emit("readTheme", themeGet.value === "default" ? "dark" : "default");
}
</script>

View File

@@ -1,6 +1,6 @@
<template>
<div class="tsl-box">
<img src="/src/assets/icons/arrow-right.svg" alt="right">
<img src="/src/assets/icons/arrow-right.svg" alt="right" />
<slot>
{{ title }}
</slot>

View File

@@ -1,8 +1,8 @@
<template>
<div class="switch-box">
<div class="switch-btn" @click="switchTheme()">
<v-icon style="color:var(--theme-switch-icon)">
{{ themeGet === 'default' ? 'mdi-weather-night' : 'mdi-weather-sunny' }}
<v-icon style="color: var(--theme-switch-icon)">
{{ themeGet === "default" ? "mdi-weather-night" : "mdi-weather-sunny" }}
</v-icon>
</div>
</div>
@@ -19,10 +19,10 @@ import { useAppStore } from "../../store/modules/app";
const appStore = useAppStore();
// theme
const themeGet = computed({
get () {
get() {
return appStore.theme;
},
set (value: string) {
set(value: string) {
appStore.theme = value;
},
});
@@ -31,12 +31,12 @@ onMounted(async () => {
await listenOnTheme();
});
async function switchTheme () {
async function switchTheme() {
appStore.changeTheme();
await event.emit("readTheme", themeGet.value);
}
async function listenOnTheme () {
async function listenOnTheme() {
await event.listen("readTheme", (e) => {
const theme = e.payload as string;
themeGet.value = theme === "default" ? "default" : "dark";

View File

@@ -4,27 +4,33 @@
<div class="box-div">
<div class="toc-top">
<div class="toc-icon">
<TibCalendarAvatar v-if="itemType=== 'character'" v-model="itemVal" size="100px" style="cursor: default" />
<TibCalendarWeapon v-if="itemType=== 'weapon'" v-model="itemVal" size="100px" style="cursor: default" />
<TibCalendarAvatar
v-if="itemType === 'character'"
v-model="itemVal"
size="100px"
style="cursor: default"
/>
<TibCalendarWeapon
v-if="itemType === 'weapon'"
v-model="itemVal"
size="100px"
style="cursor: default"
/>
</div>
<div class="toc-material-grid">
<TibCalendarMaterial v-for="item in itemVal.materials" :item="item" />
</div>
</div>
<img src="/source/UI/item-line.webp" alt="line" class="toc-line">
<img src="/source/UI/item-line.webp" alt="line" class="toc-line" />
<div class="toc-bottom">
<div class="toc-src-box">
<div class="toc-src-text">
来源
</div>
<img :src="`/icon/nation/${itemVal.source.area}.webp`" alt="icon">
<div class="toc-src-text">
{{ itemVal.source.area }} - {{ itemVal.source.name }}
</div>
<div class="toc-src-text">来源</div>
<img :src="`/icon/nation/${itemVal.source.area}.webp`" alt="icon" />
<div class="toc-src-text">{{ itemVal.source.area }} - {{ itemVal.source.name }}</div>
</div>
<v-btn variant="outlined" @click="toDetail(itemVal)">
<template #append>
<img src="../../assets/icons/arrow-right.svg" alt="right" class="toc-btn-img">
<img src="../../assets/icons/arrow-right.svg" alt="right" class="toc-btn-img" />
</template>
详情
</v-btn>
@@ -83,7 +89,7 @@ const onCancel = () => {
emits("cancel");
};
function toDetail (item: TGApp.App.Calendar.Item) {
function toDetail(item: TGApp.App.Calendar.Item) {
if (item.contentId === 0) {
snackbar.value = true;
return;

View File

@@ -2,12 +2,10 @@
<TOverlay v-model="visible" hide :to-click="onCancel" blur-val="20px">
<div class="toc-box">
<div class="toc-top">
<div class="toc-title">
请选择要跳转的频道
</div>
<div class="toc-title">请选择要跳转的频道</div>
<div class="toc-list">
<div v-for="item in channelList" class="toc-list-item" @click="toChannel(item.link)">
<img :src="item.icon" alt="icon">
<img :src="item.icon" alt="icon" />
<span>{{ item.title }}</span>
</div>
</div>
@@ -86,13 +84,14 @@ const channelList: ToChannelItem[] = [
title: "大别野",
icon: "/platforms/mhy/dby.webp",
link: "/news/5",
}];
},
];
function onCancel () {
function onCancel() {
visible.value = false;
}
function toChannel (link: string) {
function toChannel(link: string) {
visible.value = false;
router.push(link);
setTimeout(() => {

View File

@@ -5,21 +5,26 @@
<div class="confirm-title">
{{ title }}
</div>
<div v-show="subtitle!=='' && !isInput" class="confirm-subtitle">
<div v-show="subtitle !== '' && !isInput" class="confirm-subtitle">
{{ subtitle }}
</div>
<div v-show="isInput" class="confirm-input">
<v-text-field v-model="inputVal" :label="subtitle||''" hide-details="auto" @keyup.enter="onConfirm" />
<v-text-field
v-model="inputVal"
:label="subtitle || ''"
hide-details="auto"
@keyup.enter="onConfirm"
/>
</div>
<div class="confirm-btn-box">
<button class="confirm-btn" @click="onCancel">
<img class="btn-icon" src="../../assets/icons/circle-cancel.svg" alt="cancel">
<img class="btn-icon" src="../../assets/icons/circle-cancel.svg" alt="cancel" />
<span class="btn-text">
{{ cancel }}
</span>
</button>
<button class="confirm-btn" @click="onConfirm">
<img class="btn-icon" src="../../assets/icons/circle-check.svg" alt="confirm">
<img class="btn-icon" src="../../assets/icons/circle-check.svg" alt="confirm" />
<span class="btn-text">
{{ confirm }}
</span>

View File

@@ -15,8 +15,8 @@
</div>
<div class="tol-img">
<slot name="img">
<img v-if="!empty" src="/source/UI/loading.webp" alt="loading">
<img v-else src="/source/UI/empty.webp" alt="empty">
<img v-if="!empty" src="/source/UI/loading.webp" alt="loading" />
<img v-else src="/source/UI/empty.webp" alt="empty" />
</slot>
</div>
</div>
@@ -43,9 +43,12 @@ const props = withDefaults(defineProps<LoadingProps>(), {
empty: false,
});
watch(() => props.modelValue, (v) => {
show.value = v;
});
watch(
() => props.modelValue,
(v) => {
show.value = v;
},
);
</script>
<style lang="css" scoped>
.tol-div {

View File

@@ -5,7 +5,8 @@
<div class="tud-db-icons-grid">
<TibAbyssDetail
v-for="avatar in props.modelValue.characters"
:key="avatar.id" :model-value="avatar"
:key="avatar.id"
:model-value="avatar"
/>
</div>
</template>

View File

@@ -6,7 +6,7 @@
</slot>
</div>
<div class="tud-t-val">
<img src="/icon/star/Abyss.webp" alt="Abyss">
<img src="/icon/star/Abyss.webp" alt="Abyss" />
<slot name="val">
<span>{{ props.val }}</span>
</slot>
@@ -53,7 +53,7 @@ const getFontSize: ComputedRef<string> = computed(() => {
display: flex;
align-items: center;
font-family: var(--font-text);
font-size: v-bind(getFontSize);
font-size: v-bind(getFontSize);
color: var(--common-color-white);
text-shadow: 0 0 10px var(--common-color-yellow);
}

View File

@@ -1,18 +1,25 @@
<template>
<div class="tuad-box">
<TuaDetailTitle :val="props.modelValue.winStar" :name="`第${props.modelValue.id}层`" mode="floor" />
<TuaDetailTitle
:val="props.modelValue.winStar"
:name="`第${props.modelValue.id}层`"
mode="floor"
/>
<div class="tuad-index-box">
<TuaDetailLevel v-for="level in props.modelValue.levels" :key="level.id" :model-value="level" />
<TuaDetailLevel
v-for="level in props.modelValue.levels"
:key="level.id"
:model-value="level"
/>
</div>
</div>
</template>
<script lang="ts" setup>
import TuaDetailTitle from "./tua-detail-title.vue";
import TuaDetailLevel from "./tua-detail-level.vue";
interface TuaDetailProps {
modelValue: TGApp.Sqlite.Abyss.Floor
modelValue: TGApp.Sqlite.Abyss.Floor;
}
const props = defineProps<TuaDetailProps>();

View File

@@ -14,7 +14,8 @@
<slot name="val-icons">
<TibAbyssOverview
v-for="avatar in JSON.parse(props.valIcons) as TGApp.Sqlite.Abyss.Character[]"
:key="avatar.id" :model-value="avatar"
:key="avatar.id"
:model-value="avatar"
/>
</slot>
</div>
@@ -25,10 +26,10 @@
import TibAbyssOverview from "../itembox/tib-abyss-overview.vue";
interface TAOProps {
title: string,
valText?: string | number,
valIcons?: string,
iconNum: number,
title: string;
valText?: string | number;
valIcons?: string;
iconNum: number;
}
const props = withDefaults(defineProps<TAOProps>(), {

View File

@@ -1,18 +1,16 @@
<template>
<div class="tuc-dc-box">
<div v-if="!modelValue.active" class="tuc-dc-lock">
<v-icon color="white">
mdi-lock
</v-icon>
<v-icon color="white"> mdi-lock </v-icon>
</div>
<div class="tuc-dc-icon">
<img :src="modelValue.icon" alt="constellation">
<img :src="modelValue.icon" alt="constellation" />
</div>
</div>
</template>
<script lang="ts" setup>
interface TucDetailConstellationProps {
modelValue: TGApp.Sqlite.Character.RoleConstellation
modelValue: TGApp.Sqlite.Character.RoleConstellation;
}
defineProps<TucDetailConstellationProps>();

View File

@@ -34,7 +34,7 @@ interface TucDetailDescConstellationProps {
const props = defineProps<TucDetailDescConstellationProps>();
// 解析描述
function parseDesc (desc: string): string {
function parseDesc(desc: string): string {
const reg = /<color=(.*?)>(.*?)<\/color>/g;
let match = reg.exec(desc);
while (match) {

View File

@@ -12,14 +12,12 @@
<span>{{ props.modelValue.level }}</span>
</div>
<div class="tuc-ddrc-bottom">
<img :src="`/icon/star/${props.modelValue.star}.webp`" alt="star">
<img :src="`/icon/star/${props.modelValue.star}.webp`" alt="star" />
</div>
</div>
</template>
<template #desc>
<div class="tuc-ddrd-title">
{{ props.modelValue.set.name }}
</div>
<div class="tuc-ddrd-title">{{ props.modelValue.set.name }}</div>
<div v-for="desc in props.modelValue.set.effect" class="tuc-ddrc-desc">
<span>{{ desc.active }}件套</span>
<span>{{ desc.description }}</span>

View File

@@ -14,7 +14,7 @@
<span></span>
</div>
<div class="tuc-ddwc-bottom">
<img :src="`/icon/star/${props.modelValue.star}.webp`" alt="star">
<img :src="`/icon/star/${props.modelValue.star}.webp`" alt="star" />
</div>
</div>
</template>

View File

@@ -1,19 +1,18 @@
<template>
<div class="tuc-dib-box">
<div v-if="modelValue.bg" class="tuc-dib-bg">
<img :src="modelValue.bg" alt="bg">
<img :src="modelValue.bg" alt="bg" />
</div>
<div v-if="modelValue.icon" class="tuc-dib-icon">
<img :src="modelValue.icon" alt="icon">
<img :src="modelValue.icon" alt="icon" />
</div>
</div>
</template>
<script lang="ts" setup>
interface TucDetailItemBoxProps {
modelValue: {
icon?: string,
bg?: string,
icon?: string;
bg?: string;
};
}

View File

@@ -2,13 +2,11 @@
<TOverlay v-model="visible" hide :to-click="onCancel" blur-val="20px">
<div class="tuc-do-box">
<div class="tuc-do-bg">
<img :src="data.bg" alt="role">
</div>
<div class="tuc-do-quote">
* 所有数据以游戏内为准此处仅供参考
<img :src="data.bg" alt="role" />
</div>
<div class="tuc-do-quote">* 所有数据以游戏内为准此处仅供参考</div>
<!-- 衣装 -->
<div v-if="data.costume.length>0" class="tuc-do-costume">
<div v-if="data.costume.length > 0" class="tuc-do-costume">
<v-switch v-model="showCostumeSwitch" color="#faf7e8" @click="switchBg">
<template #label>
<v-icon>mdi-tshirt-crew-outline</v-icon>
@@ -26,7 +24,7 @@
:style="{
border: selected.pos === 0 ? '2px solid var(--common-color-yellow)' : '',
}"
@click="showDetail(data.weapon,'武器',0)"
@click="showDetail(data.weapon, '武器', 0)"
>
<TucDetailItemBox v-model="weaponBox" />
</div>
@@ -37,7 +35,7 @@
cursor: item ? 'pointer' : 'default',
border: selected.pos === index + 1 ? '2px solid var(--common-color-yellow)' : '',
}"
@click="showDetail(item,'圣遗物',index + 1)"
@click="showDetail(item, '圣遗物', index + 1)"
>
<TucDetailRelic :model-value="item" :pos="index.toString()" />
</div>
@@ -50,9 +48,9 @@
class="tuc-dor-item"
:model-value="item"
:style="{
border: selected.pos === item.pos+5 ? '2px solid var(--common-color-yellow)' : '',
border: selected.pos === item.pos + 5 ? '2px solid var(--common-color-yellow)' : '',
}"
@click="showDetail(item, '命座', item.pos+5)"
@click="showDetail(item, '命座', item.pos + 5)"
/>
</div>
</div>
@@ -123,18 +121,21 @@ const data = ref({
bg: "" as string,
});
const selected = ref({
data: {} as TGApp.Sqlite.Character.RoleConstellation
| TGApp.Sqlite.Character.RoleWeapon
| TGApp.Sqlite.Character.RoleReliquary,
data: {} as
| TGApp.Sqlite.Character.RoleConstellation
| TGApp.Sqlite.Character.RoleWeapon
| TGApp.Sqlite.Character.RoleReliquary,
type: "武器" || "命之座" || "圣遗物",
pos: 0, // 用于标记选中的是哪个位置
});
// 加载数据
function loadData () {
function loadData() {
if (!props.modelValue) return;
data.value.weapon = JSON.parse(props.dataVal.weapon) as TGApp.Sqlite.Character.RoleWeapon;
data.value.constellation = JSON.parse(props.dataVal.constellation) as TGApp.Sqlite.Character.RoleConstellation[];
data.value.constellation = JSON.parse(
props.dataVal.constellation,
) as TGApp.Sqlite.Character.RoleConstellation[];
if (props.dataVal.reliquary !== "") {
const relics = JSON.parse(props.dataVal.reliquary) as TGApp.Sqlite.Character.RoleReliquary[];
relics.map((item) => {
@@ -173,12 +174,12 @@ const onCancel = () => {
emits("cancel");
};
function showDetail (
function showDetail(
item:
TGApp.Sqlite.Character.RoleConstellation |
TGApp.Sqlite.Character.RoleWeapon |
TGApp.Sqlite.Character.RoleReliquary |
false,
| TGApp.Sqlite.Character.RoleConstellation
| TGApp.Sqlite.Character.RoleWeapon
| TGApp.Sqlite.Character.RoleReliquary
| false,
type: string,
pos: number = 0,
) {
@@ -190,7 +191,7 @@ function showDetail (
};
}
function switchBg () {
function switchBg() {
if (data.value.bg === props.dataVal.img) {
data.value.bg = data.value.costume[0].icon;
} else {

View File

@@ -1,13 +1,13 @@
<template>
<div class="tuc-dr-box">
<div class="tuc-dr-bg">
<img :src="`/icon/relic/${props.pos}.webp`" alt="relic">
<img :src="`/icon/relic/${props.pos}.webp`" alt="relic" />
</div>
<div v-if="props.modelValue" class="tuc-dr-bg">
<img :src="`/icon/bg/${props.modelValue.star}-Star.webp`" alt="bg">
<img :src="`/icon/bg/${props.modelValue.star}-Star.webp`" alt="bg" />
</div>
<div v-if="props.modelValue" class="tuc-dr-icon">
<img :src="props.modelValue.icon" alt="relic">
<img :src="props.modelValue.icon" alt="relic" />
</div>
</div>
</template>

View File

@@ -6,21 +6,19 @@
</div>
<div class="tuc-rb-bottom">
<!-- bg 好感名片 -->
<div v-if="nameCard!==false" class="tuc-rbb-bg">
<img :src="nameCard" alt="nameCard">
<div v-if="nameCard !== false" class="tuc-rbb-bg">
<img :src="nameCard" alt="nameCard" />
</div>
<!-- 表面 lock -->
<div v-if="props.modelValue.fetter!==10" class="tuc-rbb-lock">
<v-icon size="20" color="var(--page-bg)">
mdi-lock
</v-icon>
<div v-if="props.modelValue.fetter !== 10" class="tuc-rbb-lock">
<v-icon size="20" color="var(--page-bg)"> mdi-lock </v-icon>
</div>
<!-- 左上角好感等级 -->
<div class="tuc-rbb-fetter">
<img src="/icon/material/105.webp" alt="fetter">
<img src="/icon/material/105.webp" alt="fetter" />
<span>{{ props.modelValue.fetter }}</span>
<!-- 衣装 icon -->
<span v-if="props.modelValue.costume!=='[]'">
<span v-if="props.modelValue.costume !== '[]'">
<v-icon>mdi-tshirt-crew-outline</v-icon>
</span>
</div>
@@ -83,14 +81,12 @@ onMounted(async () => {
nameCard.value = `/source/nameCard/profile/${role.nameCard}.webp`;
});
function getAvatarName () {
return (
props.modelValue.cid === 10000005
? "旅行者-空"
: props.modelValue.cid === 10000007
? "旅行者-荧"
: props.modelValue.name
);
function getAvatarName() {
return props.modelValue.cid === 10000005
? "旅行者-空"
: props.modelValue.cid === 10000007
? "旅行者-荧"
: props.modelValue.name;
}
</script>
<style lang="css" scoped>

View File

@@ -1,7 +1,5 @@
<template>
<div v-if="props.modelValue===undefined">
暂无数据
</div>
<div v-if="props.modelValue === undefined">暂无数据</div>
<div v-else class="tur-ag-box">
<TibUrAvatar v-for="avatar in data" :key="avatar.id" :model-value="avatar" />
</div>

View File

@@ -1,7 +1,5 @@
<template>
<div v-if="props.modelValue===undefined">
暂无数据
</div>
<div v-if="props.modelValue === undefined">暂无数据</div>
<div v-else class="tur-hg-box">
<TurHomeSub v-for="home in homes" :data="home" />
</div>

View File

@@ -11,7 +11,7 @@
</div>
<div class="tur-hs-title">
<!-- canvas -->
<img :src="getUrl.icon" alt="comfort">
<img :src="getUrl.icon" alt="comfort" />
{{ data.comfortName }}
</div>
<div class="tur-hs-text-grid">

View File

@@ -1,7 +1,5 @@
<template>
<div v-if="props.modelValue===undefined">
暂无数据
</div>
<div v-if="props.modelValue === undefined">暂无数据</div>
<div v-else class="tur-og-box">
<TurOverviewSub title="活跃天数" :text="data.activeDays" />
<TurOverviewSub title="成就达成数" :text="data.achievementNumber" />
@@ -26,13 +24,11 @@ import { computed } from "vue";
import TurOverviewSub from "./tur-overview-sub.vue";
interface TurOverviewGridProps {
modelValue?: string
modelValue?: string;
}
const props = defineProps<TurOverviewGridProps>();
const data = computed(() =>
JSON.parse(<string>props.modelValue) as TGApp.Sqlite.Record.Stats,
);
const data = computed(() => JSON.parse(<string>props.modelValue) as TGApp.Sqlite.Record.Stats);
</script>
<style lang="css" scoped>
.tur-og-box {

View File

@@ -14,8 +14,8 @@
</template>
<script lang="ts" setup>
interface TAOProps {
title: string,
text: string | number,
title: string;
text: string | number;
}
defineProps<TAOProps>();

View File

@@ -1,7 +1,5 @@
<template>
<div v-if="props.modelValue===undefined">
暂无数据
</div>
<div v-if="props.modelValue === undefined">暂无数据</div>
<div v-else class="tur-wg-box">
<TurWorldSub v-for="area in getData()" :data="area" :theme="theme" />
</div>
@@ -25,7 +23,7 @@ const theme = computed(() => {
}
});
function getData () {
function getData() {
return JSON.parse(<string>props.modelValue) as TGApp.Sqlite.Record.WorldExplore[];
}
</script>

View File

@@ -5,11 +5,11 @@
backgroundImage: `url('${getUrl.bg}')`,
backgroundPositionX: 'right',
backgroundSize: 'cover',
backgroundRepeat: 'no-repeat'
backgroundRepeat: 'no-repeat',
}"
>
<div class="tur-ws-icon">
<img :src="getUrl.icon" alt="icon">
<img :src="getUrl.icon" alt="icon" />
</div>
<div class="tur-ws-content">
<div class="tur-ws-title">
@@ -20,13 +20,13 @@
<span>{{ data.exploration / 10 }}</span>
<span>%</span>
</div>
<div v-if="data.type==='Reputation'" class="tur-ws-sub">
<div v-if="data.type === 'Reputation'" class="tur-ws-sub">
<span>声望等级</span>
<span>{{ data.level }}</span>
<span></span>
</div>
<div v-if="data.offerings.length>0" class="tur-ws-sub">
<img :src="getUrl.offer" alt="offer">
<div v-if="data.offerings.length > 0" class="tur-ws-sub">
<img :src="getUrl.offer" alt="offer" />
<span>{{ data.offerings[0].name }}等级</span>
<span>{{ data.offerings[0].level }}</span>
<span></span>
@@ -35,7 +35,6 @@
</div>
</template>
<script lang="ts" setup>
// vue
import { onMounted, ref } from "vue";
// tauri
@@ -44,8 +43,8 @@ import { event } from "@tauri-apps/api";
import { saveImgLocal } from "../../utils/TGShare";
interface TurWorldSubProps {
theme: "default" | "dark",
data: TGApp.Sqlite.Record.WorldExplore
theme: "default" | "dark";
data: TGApp.Sqlite.Record.WorldExplore;
}
const props = defineProps<TurWorldSubProps>();
@@ -66,11 +65,11 @@ onMounted(async () => {
getUrl.value.offer = await saveImgLocal(props.data.offerings[0].icon);
}
props.theme === "dark"
? getUrl.value.icon = getUrl.value.iconLight
: getUrl.value.icon = getUrl.value.iconDark;
? (getUrl.value.icon = getUrl.value.iconLight)
: (getUrl.value.icon = getUrl.value.iconDark);
});
async function listenOnTheme () {
async function listenOnTheme() {
await event.listen("readTheme", (e) => {
const theme = e.payload as string;
if (theme === "dark") {

File diff suppressed because it is too large Load Diff

View File

@@ -1,330 +1,330 @@
[
{
"id": 0,
"order": 1,
"name": "天地万象",
"version": "3.6",
"card": "",
"icon": "/source/achievementSeries/0.webp"
},
{
"id": 17,
"order": 2,
"name": "心跳的记忆",
"version": "3.7",
"card": "",
"icon": "/source/achievementSeries/17.webp"
},
{
"id": 1,
"order": 3,
"name": "尘世巡游·第一辑",
"version": "1.0",
"card": "成就·游遍",
"icon": "/source/achievementSeries/1.webp"
},
{
"id": 22,
"order": 4,
"name": "尘世巡游·第二辑",
"version": "2.0",
"card": "成就·遍历",
"icon": "/source/achievementSeries/22.webp"
},
{
"id": 32,
"order": 5,
"name": "尘世巡游·第三辑",
"version": "3.0",
"card": "成就·漫行",
"icon": "/source/achievementSeries/32.webp"
},
{
"id": 2,
"order": 6,
"name": "冒险手艺",
"version": "1.0",
"card": "成就·殊技",
"icon": "/source/achievementSeries/2.webp"
},
{
"id": 3,
"order": 7,
"name": "英雄之旅",
"version": "1.0",
"card": "成就·侠行",
"icon": "/source/achievementSeries/3.webp"
},
{
"id": 4,
"order": 8,
"name": "蒙德·风与牧歌的城邦",
"version": "1.0",
"card": "蒙德·风吟",
"icon": "/source/achievementSeries/4.webp"
},
{
"id": 5,
"order": 9,
"name": "璃月·岩与契约的海港",
"version": "1.0",
"card": "璃月·岩寂",
"icon": "/source/achievementSeries/5.webp"
},
{
"id": 6,
"order": 10,
"name": "元素专家·第一辑",
"version": "1.0",
"card": "成就·虹色",
"icon": "/source/achievementSeries/6.webp"
},
{
"id": 36,
"order": 11,
"name": "元素专家·第二辑",
"version": "3.1",
"card": "成就·七芒",
"icon": "/source/achievementSeries/36.webp"
},
{
"id": 7,
"order": 12,
"name": "神射手",
"version": "1.0",
"card": "成就·强弓",
"icon": "/source/achievementSeries/7.webp"
},
{
"id": 8,
"order": 13,
"name": "挑战者·第一辑",
"version": "1.0",
"card": "成就·挑战",
"icon": "/source/achievementSeries/8.webp"
},
{
"id": 14,
"order": 14,
"name": "挑战者·第二辑",
"version": "1.1",
"card": "成就·挑战·其二",
"icon": "/source/achievementSeries/14.webp"
},
{
"id": 15,
"order": 15,
"name": "挑战者·第三辑",
"version": "1.2",
"card": "成就·挑战·其三",
"icon": "/source/achievementSeries/15.webp"
},
{
"id": 20,
"order": 16,
"name": "挑战者·第四辑",
"version": "2.0",
"card": "成就·石龙",
"icon": "/source/achievementSeries/20.webp"
},
{
"id": 29,
"order": 17,
"name": "挑战者·第五辑",
"version": "2.6",
"card": "成就·雷音",
"icon": "/source/achievementSeries/29.webp"
},
{
"id": 34,
"order": 18,
"name": "挑战者·第六辑",
"version": "3.1",
"card": "成就·镜梦",
"icon": "/source/achievementSeries/34.webp"
},
{
"id": 39,
"order": 19,
"name": "挑战者·第七辑",
"version": "3.6",
"card": "成就·槿暮",
"icon": "/source/achievementSeries/39.webp"
},
{
"id": 9,
"order": 20,
"name": "秘境与深境螺旋·第一辑",
"version": "1.0",
"card": "成就·深秘",
"icon": "/source/achievementSeries/9.webp"
},
{
"id": 10,
"order": 21,
"name": "Olah第一辑",
"version": "1.0",
"card": "成就·山民",
"icon": "/source/achievementSeries/10.webp"
},
{
"id": 11,
"order": 22,
"name": "至冬国不相信眼泪·第一辑",
"version": "1.0",
"card": "成就·雪乡",
"icon": "/source/achievementSeries/11.webp"
},
{
"id": 12,
"order": 23,
"name": "岩港往事·第一辑",
"version": "1.0",
"card": "成就·合扇",
"icon": "/source/achievementSeries/12.webp"
},
{
"id": 13,
"order": 24,
"name": "异世相逢·第一辑",
"version": "1.0",
"card": "成就·相逢",
"icon": "/source/achievementSeries/13.webp"
},
{
"id": 21,
"order": 25,
"name": "异世相逢·第二辑",
"version": "2.1",
"card": "成就·门扉",
"icon": "/source/achievementSeries/21.webp"
},
{
"id": 33,
"order": 26,
"name": "异世相逢·第三辑",
"version": "3.0",
"card": "成就·逢缘",
"icon": "/source/achievementSeries/33.webp"
},
{
"id": 16,
"order": 27,
"name": "雪山上的来客",
"version": "1.2",
"card": "成就·雪峰",
"icon": "/source/achievementSeries/16.webp"
},
{
"id": 18,
"order": 28,
"name": "世外洞天·第一辑",
"version": "1.5",
"card": "成就·壶歌",
"icon": "/source/achievementSeries/18.webp"
},
{
"id": 19,
"order": 29,
"name": "世外洞天·第二辑",
"version": "1.6",
"card": "成就·旅居",
"icon": "/source/achievementSeries/19.webp"
},
{
"id": 23,
"order": 30,
"name": "世外洞天·第三辑",
"version": "2.0",
"card": "成就·繁花",
"icon": "/source/achievementSeries/23.webp"
},
{
"id": 24,
"order": 31,
"name": "稻妻·雷与永恒的群岛·其之一",
"version": "2.0",
"card": "稻妻·九条之纹",
"icon": "/source/achievementSeries/24.webp"
},
{
"id": 26,
"order": 32,
"name": "稻妻·雷与永恒的群岛·其之二",
"version": "2.1",
"card": "稻妻·珊瑚宫之纹",
"icon": "/source/achievementSeries/26.webp"
},
{
"id": 27,
"order": 33,
"name": "雾海纪行",
"version": "2.2",
"card": "稻妻·鹫羽",
"icon": "/source/achievementSeries/27.webp"
},
{
"id": 25,
"order": 34,
"name": "提瓦特钓鱼指南·第一辑",
"version": "2.1",
"card": "成就·敲针",
"icon": "/source/achievementSeries/25.webp"
},
{
"id": 28,
"order": 35,
"name": "白昼之光",
"version": "2.4",
"card": "稻妻·常世",
"icon": "/source/achievementSeries/28.webp"
},
{
"id": 30,
"order": 36,
"name": "岩窟流明",
"version": "2.6",
"card": "成就·层岩",
"icon": "/source/achievementSeries/30.webp"
},
{
"id": 31,
"order": 37,
"name": "须弥·玄识深藏的雨林",
"version": "3.0",
"card": "须弥·瑶林",
"icon": "/source/achievementSeries/31.webp"
},
{
"id": 35,
"order": 38,
"name": "须弥·饰金砂原·其之一",
"version": "3.1",
"card": "须弥·踏沙",
"icon": "/source/achievementSeries/35.webp"
},
{
"id": 38,
"order": 39,
"name": "须弥·饰金砂原·其之二",
"version": "3.4",
"card": "须弥·砂岚",
"icon": "/source/achievementSeries/38.webp"
},
{
"id": 37,
"order": 40,
"name": "七圣召唤",
"version": "3.3",
"card": "成就·七圣",
"icon": "/source/achievementSeries/37.webp"
},
{
"id": 41,
"order": 41,
"name": "佑灵砾漠",
"version": "3.6",
"card": "成就·万种",
"icon": "/source/achievementSeries/41.webp"
}
{
"id": 0,
"order": 1,
"name": "天地万象",
"version": "3.6",
"card": "",
"icon": "/source/achievementSeries/0.webp"
},
{
"id": 17,
"order": 2,
"name": "心跳的记忆",
"version": "3.7",
"card": "",
"icon": "/source/achievementSeries/17.webp"
},
{
"id": 1,
"order": 3,
"name": "尘世巡游·第一辑",
"version": "1.0",
"card": "成就·游遍",
"icon": "/source/achievementSeries/1.webp"
},
{
"id": 22,
"order": 4,
"name": "尘世巡游·第二辑",
"version": "2.0",
"card": "成就·遍历",
"icon": "/source/achievementSeries/22.webp"
},
{
"id": 32,
"order": 5,
"name": "尘世巡游·第三辑",
"version": "3.0",
"card": "成就·漫行",
"icon": "/source/achievementSeries/32.webp"
},
{
"id": 2,
"order": 6,
"name": "冒险手艺",
"version": "1.0",
"card": "成就·殊技",
"icon": "/source/achievementSeries/2.webp"
},
{
"id": 3,
"order": 7,
"name": "英雄之旅",
"version": "1.0",
"card": "成就·侠行",
"icon": "/source/achievementSeries/3.webp"
},
{
"id": 4,
"order": 8,
"name": "蒙德·风与牧歌的城邦",
"version": "1.0",
"card": "蒙德·风吟",
"icon": "/source/achievementSeries/4.webp"
},
{
"id": 5,
"order": 9,
"name": "璃月·岩与契约的海港",
"version": "1.0",
"card": "璃月·岩寂",
"icon": "/source/achievementSeries/5.webp"
},
{
"id": 6,
"order": 10,
"name": "元素专家·第一辑",
"version": "1.0",
"card": "成就·虹色",
"icon": "/source/achievementSeries/6.webp"
},
{
"id": 36,
"order": 11,
"name": "元素专家·第二辑",
"version": "3.1",
"card": "成就·七芒",
"icon": "/source/achievementSeries/36.webp"
},
{
"id": 7,
"order": 12,
"name": "神射手",
"version": "1.0",
"card": "成就·强弓",
"icon": "/source/achievementSeries/7.webp"
},
{
"id": 8,
"order": 13,
"name": "挑战者·第一辑",
"version": "1.0",
"card": "成就·挑战",
"icon": "/source/achievementSeries/8.webp"
},
{
"id": 14,
"order": 14,
"name": "挑战者·第二辑",
"version": "1.1",
"card": "成就·挑战·其二",
"icon": "/source/achievementSeries/14.webp"
},
{
"id": 15,
"order": 15,
"name": "挑战者·第三辑",
"version": "1.2",
"card": "成就·挑战·其三",
"icon": "/source/achievementSeries/15.webp"
},
{
"id": 20,
"order": 16,
"name": "挑战者·第四辑",
"version": "2.0",
"card": "成就·石龙",
"icon": "/source/achievementSeries/20.webp"
},
{
"id": 29,
"order": 17,
"name": "挑战者·第五辑",
"version": "2.6",
"card": "成就·雷音",
"icon": "/source/achievementSeries/29.webp"
},
{
"id": 34,
"order": 18,
"name": "挑战者·第六辑",
"version": "3.1",
"card": "成就·镜梦",
"icon": "/source/achievementSeries/34.webp"
},
{
"id": 39,
"order": 19,
"name": "挑战者·第七辑",
"version": "3.6",
"card": "成就·槿暮",
"icon": "/source/achievementSeries/39.webp"
},
{
"id": 9,
"order": 20,
"name": "秘境与深境螺旋·第一辑",
"version": "1.0",
"card": "成就·深秘",
"icon": "/source/achievementSeries/9.webp"
},
{
"id": 10,
"order": 21,
"name": "Olah第一辑",
"version": "1.0",
"card": "成就·山民",
"icon": "/source/achievementSeries/10.webp"
},
{
"id": 11,
"order": 22,
"name": "至冬国不相信眼泪·第一辑",
"version": "1.0",
"card": "成就·雪乡",
"icon": "/source/achievementSeries/11.webp"
},
{
"id": 12,
"order": 23,
"name": "岩港往事·第一辑",
"version": "1.0",
"card": "成就·合扇",
"icon": "/source/achievementSeries/12.webp"
},
{
"id": 13,
"order": 24,
"name": "异世相逢·第一辑",
"version": "1.0",
"card": "成就·相逢",
"icon": "/source/achievementSeries/13.webp"
},
{
"id": 21,
"order": 25,
"name": "异世相逢·第二辑",
"version": "2.1",
"card": "成就·门扉",
"icon": "/source/achievementSeries/21.webp"
},
{
"id": 33,
"order": 26,
"name": "异世相逢·第三辑",
"version": "3.0",
"card": "成就·逢缘",
"icon": "/source/achievementSeries/33.webp"
},
{
"id": 16,
"order": 27,
"name": "雪山上的来客",
"version": "1.2",
"card": "成就·雪峰",
"icon": "/source/achievementSeries/16.webp"
},
{
"id": 18,
"order": 28,
"name": "世外洞天·第一辑",
"version": "1.5",
"card": "成就·壶歌",
"icon": "/source/achievementSeries/18.webp"
},
{
"id": 19,
"order": 29,
"name": "世外洞天·第二辑",
"version": "1.6",
"card": "成就·旅居",
"icon": "/source/achievementSeries/19.webp"
},
{
"id": 23,
"order": 30,
"name": "世外洞天·第三辑",
"version": "2.0",
"card": "成就·繁花",
"icon": "/source/achievementSeries/23.webp"
},
{
"id": 24,
"order": 31,
"name": "稻妻·雷与永恒的群岛·其之一",
"version": "2.0",
"card": "稻妻·九条之纹",
"icon": "/source/achievementSeries/24.webp"
},
{
"id": 26,
"order": 32,
"name": "稻妻·雷与永恒的群岛·其之二",
"version": "2.1",
"card": "稻妻·珊瑚宫之纹",
"icon": "/source/achievementSeries/26.webp"
},
{
"id": 27,
"order": 33,
"name": "雾海纪行",
"version": "2.2",
"card": "稻妻·鹫羽",
"icon": "/source/achievementSeries/27.webp"
},
{
"id": 25,
"order": 34,
"name": "提瓦特钓鱼指南·第一辑",
"version": "2.1",
"card": "成就·敲针",
"icon": "/source/achievementSeries/25.webp"
},
{
"id": 28,
"order": 35,
"name": "白昼之光",
"version": "2.4",
"card": "稻妻·常世",
"icon": "/source/achievementSeries/28.webp"
},
{
"id": 30,
"order": 36,
"name": "岩窟流明",
"version": "2.6",
"card": "成就·层岩",
"icon": "/source/achievementSeries/30.webp"
},
{
"id": 31,
"order": 37,
"name": "须弥·玄识深藏的雨林",
"version": "3.0",
"card": "须弥·瑶林",
"icon": "/source/achievementSeries/31.webp"
},
{
"id": 35,
"order": 38,
"name": "须弥·饰金砂原·其之一",
"version": "3.1",
"card": "须弥·踏沙",
"icon": "/source/achievementSeries/35.webp"
},
{
"id": 38,
"order": 39,
"name": "须弥·饰金砂原·其之二",
"version": "3.4",
"card": "须弥·砂岚",
"icon": "/source/achievementSeries/38.webp"
},
{
"id": 37,
"order": 40,
"name": "七圣召唤",
"version": "3.3",
"card": "成就·七圣",
"icon": "/source/achievementSeries/37.webp"
},
{
"id": 41,
"order": 41,
"name": "佑灵砾漠",
"version": "3.6",
"card": "成就·万种",
"icon": "/source/achievementSeries/41.webp"
}
]

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -33,6 +33,6 @@ const wikiFiles = import.meta.glob("./wiki/**/*.json");
* @param {string} name 文件名
* @returns {Promise<any>} 文件内容
*/
export async function getWikiData (dir: string, name: string) {
export async function getWikiData(dir: string, name: string): Promise<any> {
return await wikiFiles[`./wiki/${dir}/${name}.json`]();
}

View File

@@ -14,30 +14,35 @@
@keyup.enter="searchCard"
/>
<template #append>
<v-btn prepend-icon="mdi-import" class="ms-2 top-btn" @click="importJson">
导入
</v-btn>
<v-btn prepend-icon="mdi-export" class="ms-2 top-btn" @click="exportJson">
导出
</v-btn>
<v-btn prepend-icon="mdi-import" class="ms-2 top-btn" @click="importJson"> 导入 </v-btn>
<v-btn prepend-icon="mdi-export" class="ms-2 top-btn" @click="exportJson"> 导出 </v-btn>
</template>
</v-app-bar>
<TOLoading v-model="loading" :title="loadingTitle" />
<div class="wrap">
<!-- 左侧菜单 -->
<div class="left-wrap">
<v-list v-for="series in seriesList" :key="series.id" class="card-left" @click="selectSeries(series.id)">
<div class="version-icon-series">
v{{ series.version }}
</div>
<v-list
v-for="series in seriesList"
:key="series.id"
class="card-left"
@click="selectSeries(series.id)"
>
<div class="version-icon-series">v{{ series.version }}</div>
<v-list-item>
<template #prepend>
<v-img width="40px" style="margin-right: 10px" :src="`/source/achievement/${series.id}.webp`" />
<v-img
width="40px"
style="margin-right: 10px"
:src="`/source/achievement/${series.id}.webp`"
/>
</template>
<v-list-item-title>
{{ series.name }}
</v-list-item-title>
<v-list-item-subtitle> {{ series.finCount }} / {{ series.totalCount }} </v-list-item-subtitle>
<v-list-item-subtitle>
{{ series.finCount }} / {{ series.totalCount }}
</v-list-item-subtitle>
</v-list-item>
</v-list>
</div>
@@ -65,11 +70,13 @@
</template>
</v-list-item>
</v-list>
<div
class="list-empty"
:style="{height: `${emptyHeight}px`}"
>
<v-list v-for="achievement in renderAchievement" :key="achievement.id" class="card-right" :style="{Transform:`translateY(${translateY})`}">
<div class="list-empty" :style="{ height: `${emptyHeight}px` }">
<v-list
v-for="achievement in renderAchievement"
:key="achievement.id"
class="card-right"
:style="{ Transform: `translateY(${translateY})` }"
>
<div v-if="achievement.progress !== 0" class="achievement-progress">
{{ achievement.progress }}
</div>
@@ -86,7 +93,9 @@
</v-list-item-title>
<v-list-item-subtitle>{{ achievement.description }}</v-list-item-subtitle>
<template #append>
<span v-show="achievement.isCompleted" class="right-time">{{ achievement.completedTime }}</span>
<span v-show="achievement.isCompleted" class="right-time">{{
achievement.completedTime
}}</span>
<v-card class="reward-card">
<v-img src="/icon/material/201.webp" sizes="32" />
<div class="reward-num">
@@ -172,9 +181,12 @@ onMounted(async () => {
loading.value = false;
});
function handleScroll (e: Event) {
function handleScroll(e: Event) {
// 如果 scrollTop 到底部了
if ((e.target as HTMLElement).scrollTop + (e.target as HTMLElement).offsetHeight >= (e.target as HTMLElement).scrollHeight) {
if (
(e.target as HTMLElement).scrollTop + (e.target as HTMLElement).offsetHeight >=
(e.target as HTMLElement).scrollHeight
) {
// 如果 selectedAchievement 的长度小于 itemCount不进行偏移
if (selectedAchievement.value.length <= itemCount.value) {
window.requestAnimationFrame(() => {
@@ -210,7 +222,7 @@ function handleScroll (e: Event) {
}
// 渲染选中的成就系列
async function selectSeries (index: number) {
async function selectSeries(index: number) {
// 如果选中的是已经选中的系列,则不进行操作
if (selectedSeries.value === index) {
snackbarText.value = "已经选中该系列";
@@ -229,11 +241,11 @@ async function selectSeries (index: number) {
}
// 打开图片
function openImg () {
function openImg() {
createTGWindow(getCardImg.value.profile, "nameCard", getCardInfo.value.name, 840, 400, false);
}
async function searchCard () {
async function searchCard() {
if (search.value === "") {
snackbarColor.value = "#F5810A";
snackbarText.value = "请输入搜索内容";
@@ -252,7 +264,7 @@ async function searchCard () {
loading.value = false;
}
// 导入 UIAF 数据,进行数据合并、刷新
async function importJson () {
async function importJson() {
const selectedFile = await dialog.open({
multiple: false,
filters: [
@@ -281,7 +293,7 @@ async function importJson () {
}
// 导出
async function exportJson () {
async function exportJson() {
// 判断是否有数据
if (achievementsStore.finAchievements === 0) {
snackbarColor.value = "#F5810A";
@@ -398,7 +410,7 @@ async function exportJson () {
top: 0;
text-align: center;
width: 65px;
background: #8BA5C5;
background: #8ba5c5;
border-bottom-right-radius: 20px;
border-bottom: #fff 2px solid;
border-right: #fff 2px solid;

View File

@@ -1,12 +1,8 @@
<template>
<TLoading v-model="loading" :title="loadingTitle" />
<v-tabs v-model="tab" align-tabs="start" class="anno-tab">
<v-tab value="activity">
活动公告
</v-tab>
<v-tab value="game">
游戏公告
</v-tab>
<v-tab value="activity"> 活动公告 </v-tab>
<v-tab value="game"> 游戏公告 </v-tab>
<v-spacer />
<v-btn class="switch-btn" @click="switchNews">
<template #prepend>
@@ -20,7 +16,7 @@
<div class="anno-grid">
<v-card v-for="item in annoCards.activity" :key="item.id" class="anno-card" width="340">
<div class="anno-cover" @click="toPost(item)">
<img :src="item.banner" alt="cover">
<img :src="item.banner" alt="cover" />
</div>
<v-card-title>
{{ item.title }}
@@ -29,7 +25,7 @@
<v-card-actions>
<v-btn class="anno-btn" @click="toPost(item)">
<template #prepend>
<img :src="item.tagIcon || '../assets/icons/arrow-right.svg'" alt="right">
<img :src="item.tagIcon || '../assets/icons/arrow-right.svg'" alt="right" />
</template>
查看
</v-btn>
@@ -38,12 +34,10 @@
{{ item.startTime.split(" ")[0] }} -
{{ item.endTime.split(" ")[0] }}
</v-card-subtitle>
<v-card-subtitle v-show="appStore.devMode">
id: {{ item.id }}
</v-card-subtitle>
<v-card-subtitle v-show="appStore.devMode"> id: {{ item.id }} </v-card-subtitle>
<v-btn v-show="appStore.devMode" class="card-dev-btn" @click="toJson(item)">
<template #prepend>
<img src="../assets/icons/arrow-right.svg" alt="right">
<img src="../assets/icons/arrow-right.svg" alt="right" />
</template>
查看数据
</v-btn>
@@ -55,14 +49,14 @@
<div class="anno-grid">
<v-card v-for="item in annoCards.game" :key="item.id" class="anno-card" width="340">
<div class="anno-cover" @click="toPost(item)">
<img :src="item.banner" alt="cover">
<img :src="item.banner" alt="cover" />
</div>
<v-card-title>{{ item.title }}</v-card-title>
<v-card-subtitle>{{ item.subtitle }}</v-card-subtitle>
<v-card-actions>
<v-btn class="anno-btn" @click="toPost(item)">
<template #prepend>
<img :src="item.tagIcon || '../assets/icons/arrow-right.svg'" alt="right">
<img :src="item.tagIcon || '../assets/icons/arrow-right.svg'" alt="right" />
</template>
查看
</v-btn>
@@ -71,12 +65,10 @@
{{ item.startTime.split(" ")[0] }} -
{{ item.endTime.split(" ")[0] }}
</v-card-subtitle>
<v-card-subtitle v-show="appStore.devMode">
id: {{ item.id }}
</v-card-subtitle>
<v-card-subtitle v-show="appStore.devMode"> id: {{ item.id }} </v-card-subtitle>
<v-btn v-show="appStore.devMode" class="card-dev-btn" @click="toJson(item)">
<template #prepend>
<img src="../assets/icons/arrow-right.svg" alt="right">
<img src="../assets/icons/arrow-right.svg" alt="right" />
</template>
查看数据
</v-btn>
@@ -131,11 +123,11 @@ onMounted(async () => {
loading.value = false;
});
async function switchNews () {
async function switchNews() {
await router.push("/news/2");
}
async function toPost (item: TGApp.App.Announcement.ListCard) {
async function toPost(item: TGApp.App.Announcement.ListCard) {
const path = router.resolve({
name: "游戏内公告",
params: {
@@ -146,7 +138,7 @@ async function toPost (item: TGApp.App.Announcement.ListCard) {
createTGWindow(path, "游戏内公告", item.title, 960, 720, false, false);
}
async function toJson (item: TGApp.App.Announcement.ListCard) {
async function toJson(item: TGApp.App.Announcement.ListCard) {
const path = router.resolve({
name: "游戏内公告JSON",
params: {
@@ -162,7 +154,7 @@ async function toJson (item: TGApp.App.Announcement.ListCard) {
.anno-tab {
font-family: Genshin, serif;
margin-bottom: 20px;
color: var(--content-text-3)
color: var(--content-text-3);
}
.anno-grid {

View File

@@ -1,13 +1,11 @@
<template>
<TOLoading v-model="loading" :title="loadingTitle" />
<v-list class="config-list">
<v-list-subheader inset class="config-header">
应用信息
</v-list-subheader>
<v-list-subheader inset class="config-header"> 应用信息 </v-list-subheader>
<v-divider inset class="border-opacity-75" />
<v-list-item title="Tauri 版本" @click="toOuter('https://next--tauri.netlify.app/')">
<template #prepend>
<img class="config-icon" src="/platforms/tauri.webp" alt="Tauri">
<img class="config-icon" src="/platforms/tauri.webp" alt="Tauri" />
</template>
<template #append>
<v-list-item-subtitle>{{ versionTauri }}</v-list-item-subtitle>
@@ -15,12 +13,13 @@
</v-list-item>
<v-list-item>
<template #prepend>
<img class="config-icon" src="/icon.webp" alt="App">
<img class="config-icon" src="/icon.webp" alt="App" />
</template>
<v-list-item-title>
应用版本
<v-btn
class="card-btn" size="small"
class="card-btn"
size="small"
@click="toOuter('https://github.com/BTMuli/Tauri.Genshin/releases/latest')"
>
Alpha
@@ -32,34 +31,32 @@
</v-list-item>
<v-list-item title="成就版本">
<template #prepend>
<img class="config-icon" src="../assets/icons/achievements.svg" alt="Achievements">
<img class="config-icon" src="../assets/icons/achievements.svg" alt="Achievements" />
</template>
<template #append>
<v-list-item-subtitle>{{ achievementsStore.lastVersion }}</v-list-item-subtitle>
</template>
</v-list-item>
<v-list-item title="登录信息">
<v-list-item-subtitle v-show="userInfo.nickname!=='未登录'">
<v-list-item-subtitle v-show="userInfo.nickname !== '未登录'">
{{ userInfo.nickname }} uid:{{ userInfo.uid }}
</v-list-item-subtitle>
<v-list-item-subtitle v-show="userInfo.nickname==='未登录'">
<v-list-item-subtitle v-show="userInfo.nickname === '未登录'">
未登录请输入 Cookie 登录
</v-list-item-subtitle>
<template #prepend>
<img class="config-icon" :src="userInfo.avatar" alt="Login">
<img class="config-icon" :src="userInfo.avatar" alt="Login" />
</template>
<template #append>
<v-btn class="card-btn" @click="tryConfirm('refreshUser')">
<template #prepend>
<img src="../assets/icons/circle-check.svg" alt="check">
<img src="../assets/icons/circle-check.svg" alt="check" />
刷新数据
</template>
</v-btn>
</template>
</v-list-item>
<v-list-subheader inset class="config-header">
系统信息
</v-list-subheader>
<v-list-subheader inset class="config-header"> 系统信息 </v-list-subheader>
<v-divider inset class="border-opacity-75" />
<v-list-item title="系统平台">
<template #prepend>
@@ -79,29 +76,43 @@
</v-list-item>
<v-list-item title="数据库更新时间" prepend-icon="mdi-database">
<template #append>
<v-list-item-subtitle>{{ dbInfo.find(item => item.key === "dataUpdated")?.value }}</v-list-item-subtitle>
<v-list-item-subtitle>{{
dbInfo.find((item) => item.key === "dataUpdated")?.value
}}</v-list-item-subtitle>
</template>
<v-list-item-subtitle>更新于 {{ dbInfo.find(item => item.key === "dataUpdated")?.updated }}</v-list-item-subtitle>
<v-list-item-subtitle
>更新于
{{ dbInfo.find((item) => item.key === "dataUpdated")?.updated }}</v-list-item-subtitle
>
</v-list-item>
<v-list-item title="数据库版本" prepend-icon="mdi-database">
<template #append>
<v-list-item-subtitle>{{ dbInfo.find(item => item.key === "appVersion")?.value }}</v-list-item-subtitle>
<v-list-item-subtitle>{{
dbInfo.find((item) => item.key === "appVersion")?.value
}}</v-list-item-subtitle>
</template>
<v-list-item-subtitle>更新于 {{ dbInfo.find(item => item.key === "appVersion")?.updated }}</v-list-item-subtitle>
<v-list-item-subtitle
>更新于
{{ dbInfo.find((item) => item.key === "appVersion")?.updated }}</v-list-item-subtitle
>
</v-list-item>
<v-list-subheader inset class="config-header">
设置
</v-list-subheader>
<v-list-subheader inset class="config-header"> 设置 </v-list-subheader>
<v-divider inset class="border-opacity-75" />
<v-list-item>
<template #prepend>
<v-icon>mdi-view-dashboard</v-icon>
</template>
<v-select v-model="showHome" :items="homeStore.getShowItems()" label="首页显示组件" multiple chips />
<v-select
v-model="showHome"
:items="homeStore.getShowItems()"
label="首页显示组件"
multiple
chips
/>
<template #append>
<v-btn class="card-btn" @click="submitHome">
<template #prepend>
<img src="../assets/icons/circle-check.svg" alt="check">
<img src="../assets/icons/circle-check.svg" alt="check" />
确定
</template>
</v-btn>
@@ -112,9 +123,7 @@
<v-list-item prepend-icon="mdi-delete" title="清除用户缓存" @click="tryConfirm('delUser')" />
<v-list-item prepend-icon="mdi-delete" title="清除临时数据" @click="tryConfirm('delTemp')" />
<v-list-item prepend-icon="mdi-cog" title="恢复默认设置" @click="tryConfirm('delApp')" />
<v-list-subheader inset class="config-header">
调试
</v-list-subheader>
<v-list-subheader inset class="config-header"> 调试 </v-list-subheader>
<v-divider inset class="border-opacity-75" />
<v-list-item v-if="appStore.devEnv" title="调试模式" subtitle="开启后将显示调试信息">
<template #prepend>
@@ -122,7 +131,10 @@
</template>
<template #append>
<v-switch
v-model="appStore.devMode" :label="appStore.devMode ? '开启' : '关闭'" inset color="#FAC51E"
v-model="appStore.devMode"
:label="appStore.devMode ? '开启' : '关闭'"
inset
color="#FAC51E"
@click="submitDevMode"
/>
</template>
@@ -135,16 +147,21 @@
<span style="cursor: pointer" @click="tryConfirm('inputCookie')">手动输入 Cookie</span>
</template>
<template #append>
<v-icon style="cursor: pointer" @click="toOuter('https://github.com/BTMuli/Tauri.Genshin/issues/18')">
<v-icon
style="cursor: pointer"
@click="toOuter('https://github.com/BTMuli/Tauri.Genshin/issues/18')"
>
mdi-help-circle-outline
</v-icon>
</template>
</v-list-item>
<v-list-item title="重置数据库" prepend-icon="mdi-delete" @click="tryConfirm('resetDB')" />
<v-list-item title="检测 SQLite 数据库完整性" prepend-icon="mdi-database-check" @click="tryConfirm('checkDB')" />
<v-list-subheader inset class="config-header">
路径
</v-list-subheader>
<v-list-item
title="检测 SQLite 数据库完整性"
prepend-icon="mdi-database-check"
@click="tryConfirm('checkDB')"
/>
<v-list-subheader inset class="config-header"> 路径 </v-list-subheader>
<v-divider inset class="border-opacity-75" />
<v-list-item prepend-icon="mdi-database">
<v-list-item-title>本地数据库路径</v-list-item-title>
@@ -165,8 +182,12 @@
</v-snackbar>
<!-- 确认弹窗 -->
<TOConfirm
v-model="confirmShow" :model-input="confirmInput" :title="confirmText"
:subtitle="confirmSub" :is-input="isConfirmInput" @confirm="doConfirm(confirmOper)"
v-model="confirmShow"
:model-input="confirmInput"
:title="confirmText"
:subtitle="confirmSub"
:is-input="isConfirmInput"
@confirm="doConfirm(confirmOper)"
/>
</template>
@@ -203,7 +224,7 @@ const buildTime = computed(() => appStore.buildTime);
// About OS
const osPlatform = ref("" as string);
const osVersion = ref("" as string);
const dbInfo = ref([] as { key: string, value: string, updated: string }[]);
const dbInfo = ref([] as { key: string; value: string; updated: string }[]);
// loading
const loading = ref(true as boolean);
@@ -251,12 +272,12 @@ onMounted(async () => {
});
// 打开外部链接
function toOuter (url: string) {
function toOuter(url: string) {
window.open(url);
}
// open confirm
function tryConfirm (oper: string) {
function tryConfirm(oper: string) {
confirmSub.value = "";
isConfirmInput.value = false;
switch (oper) {
@@ -317,7 +338,7 @@ function tryConfirm (oper: string) {
}
// transfer confirm oper
async function doConfirm (oper: string) {
async function doConfirm(oper: string) {
await new Promise((resolve) => setTimeout(resolve, 500));
switch (oper) {
case "backup":
@@ -356,7 +377,7 @@ async function doConfirm (oper: string) {
}
// confirmOper
async function backupData () {
async function backupData() {
loadingTitle.value = "正在备份数据...";
loading.value = true;
const achievements = await TGSqlite.getUIAF();
@@ -371,7 +392,7 @@ async function backupData () {
snackbar.value = true;
}
async function restoreData () {
async function restoreData() {
loadingTitle.value = "正在恢复数据...";
loading.value = true;
const fail = [];
@@ -399,7 +420,7 @@ async function restoreData () {
loading.value = false;
}
async function delTempData () {
async function delTempData() {
await fs.removeDir("tempData", {
dir: fs.BaseDirectory.AppLocalData,
recursive: true,
@@ -409,7 +430,7 @@ async function delTempData () {
snackbar.value = true;
}
async function delUserData () {
async function delUserData() {
await fs.removeDir("userData", {
dir: fs.BaseDirectory.AppLocalData,
recursive: true,
@@ -421,7 +442,7 @@ async function delUserData () {
}
// 恢复默认配置
function initAppData () {
function initAppData() {
appStore.init();
homeStore.init();
achievementsStore.init();
@@ -433,14 +454,16 @@ function initAppData () {
}
// 开启 dev 模式
function submitDevMode () {
appStore.devMode ? (snackbarText.value = "已关闭 dev 模式!") : (snackbarText.value = "已开启 dev 模式!");
function submitDevMode() {
appStore.devMode
? (snackbarText.value = "已关闭 dev 模式!")
: (snackbarText.value = "已开启 dev 模式!");
snackbarColor.value = "success";
snackbar.value = true;
}
// 修改首页显示
function submitHome () {
function submitHome() {
// 获取已选
const show = showHome.value;
if (show.length < 1) {
@@ -457,7 +480,7 @@ function submitHome () {
}
// 刷新用户数据
async function refreshUser () {
async function refreshUser() {
const ck = userStore.cookie;
let failCount = 0;
loadingTitle.value = "正在验证 ltoken...";
@@ -524,7 +547,7 @@ async function refreshUser () {
}
// 输入 Cookie
async function inputCookie () {
async function inputCookie() {
const cookie = confirmInput.value;
if (cookie === "") {
snackbarText.value = "Cookie 为空!";
@@ -533,7 +556,10 @@ async function inputCookie () {
return;
}
loadingTitle.value = "正在获取 tokens...";
const cookieObj = cookie.trim().split(";").map((item) => item.trim().split("="));
const cookieObj = cookie
.trim()
.split(";")
.map((item) => item.trim().split("="));
const ticket = cookieObj.find((item) => item[0] === "login_ticket")?.[1];
const uid = cookieObj.find((item) => item[0] === "login_uid")?.[1];
// 如果两者不存在
@@ -573,7 +599,7 @@ async function inputCookie () {
}
// 检查 SQLite 数据库
async function checkDB () {
async function checkDB() {
loadingTitle.value = "正在检查数据库表单完整性...";
loading.value = true;
const res = await TGSqlite.check();
@@ -610,7 +636,7 @@ async function checkDB () {
}
// 重置 SQLite 数据库
async function resetDB () {
async function resetDB() {
loadingTitle.value = "正在重置数据库...";
loading.value = true;
await TGSqlite.reset();
@@ -623,7 +649,7 @@ async function resetDB () {
}
// 更新 SQLite 数据库
async function updateDB () {
async function updateDB() {
await new Promise((resolve) => setTimeout(resolve, 500));
loadingTitle.value = "正在更新数据库...";
loading.value = true;

View File

@@ -34,7 +34,7 @@ const itemRefs = ref([] as any[]);
// 定时器
const timer = ref(null as any);
function readLoading (): void {
function readLoading(): void {
if (!loading.value) return;
const loadingMap = itemRefs.value.map((item) => {
return item.loading ? item.name : null;
@@ -82,7 +82,7 @@ onMounted(async () => {
timer.value = setInterval(readLoading, 100);
});
function setItemRef (item: any) {
function setItemRef(item: any) {
if (itemRefs.value.includes(item)) return;
itemRefs.value.push(item);
}

View File

@@ -1,7 +1,5 @@
<template>
<h1>测试页</h1>
</template>
<script lang="ts" setup>
</script>
<style lang="css" scoped>
</style>
<script lang="ts" setup></script>
<style lang="css" scoped></style>

View File

@@ -17,7 +17,12 @@
</div>
</v-tabs>
<v-window v-model="userTab" class="ua-window">
<v-window-item v-for="item in localAbyss" :key="item.id" :value="item.id" class="ua-window-item">
<v-window-item
v-for="item in localAbyss"
:key="item.id"
:value="item.id"
class="ua-window-item"
>
<div :ref="getAbyssRef">
<div class="uaw-title">
<span></span>
@@ -41,13 +46,16 @@
</div>
<TSubLine>详情</TSubLine>
<div class="uaw-d-box">
<TuaDetail v-for="floor in JSON.parse(item.floors) as TGApp.Sqlite.Abyss.Floor[]" :model-value="floor" />
<TuaDetail
v-for="floor in JSON.parse(item.floors) as TGApp.Sqlite.Abyss.Floor[]"
:model-value="floor"
/>
</div>
</div>
</v-window-item>
</v-window>
<div v-show="localAbyssID.length === 0" class="user-empty">
<img src="/source/UI/empty.webp" alt="empty">
<img src="/source/UI/empty.webp" alt="empty" />
<span>暂无数据请尝试刷新</span>
</div>
</div>
@@ -74,8 +82,7 @@ const loadingTitle = ref("");
// data
const userTab = ref(0);
const abyssCookie = ref(computed(
() => userStore.getCookieGroup4() as Record<string, string>));
const abyssCookie = ref(computed(() => userStore.getCookieGroup4() as Record<string, string>));
const user = computed(() => userStore.getCurAccount());
const localAbyss = ref([] as TGApp.Sqlite.Abyss.SingleTable[]);
@@ -89,7 +96,7 @@ onMounted(async () => {
loading.value = false;
});
async function initAbyssData () {
async function initAbyssData() {
localAbyss.value = await TGSqlite.getAbyss(user.value.gameUid);
localAbyss.value.forEach((item) => {
localAbyssID.value.push(item.id);
@@ -98,7 +105,7 @@ async function initAbyssData () {
userTab.value = localAbyssID.value[0];
}
async function getAbyssData (): Promise<void> {
async function getAbyssData(): Promise<void> {
loadingTitle.value = "正在获取深渊数据";
loading.value = true;
if (localAbyssID.value.length < 2) {
@@ -120,15 +127,15 @@ async function getAbyssData (): Promise<void> {
loading.value = false;
}
function toAbyss (id: number): void {
function toAbyss(id: number): void {
curAbyss.value = localAbyss.value.find((item) => item.id === id)!;
}
function getAbyssRef (el: HTMLElement): void {
function getAbyssRef(el: HTMLElement): void {
abyssRef.value = el;
}
async function shareAbyss (): Promise<void> {
async function shareAbyss(): Promise<void> {
const fileName = `【深渊数据】${curAbyss.value.id}-${user.value.gameUid}`;
await generateShareImg(fileName, abyssRef.value);
}

View File

@@ -70,13 +70,13 @@ onMounted(async () => {
loading.value = false;
});
function getGridGap () {
function getGridGap() {
const width = document.querySelector(".uc-grid")?.clientWidth - 20;
const count = Math.floor(width / 180);
gridGap.value = `${(width - count * 180) / (count - 1)}px`;
}
async function loadRole () {
async function loadRole() {
const roleData = await TGSqlite.getUserCharacter(user.value.gameUid);
if (roleData !== false) {
roleList.value = roleData;
@@ -84,7 +84,7 @@ async function loadRole () {
}
}
async function refresh () {
async function refresh() {
loadingTitle.value = "正在获取角色数据";
loading.value = true;
const res = await TGRequest.User.byLToken.getRoleList(roleCookie.value, user.value);
@@ -97,13 +97,13 @@ async function refresh () {
loading.value = false;
}
async function shareRoles () {
async function shareRoles() {
const rolesBox = document.querySelector(".uc-box") as HTMLElement;
const fileName = `【角色列表】-${user.value.gameUid}`;
await generateShareImg(fileName, rolesBox);
}
function getUpdateTime () {
function getUpdateTime() {
let lastUpdateTime = 0;
roleList.value.forEach((role) => {
const updateTime = new Date(role.updated).getTime();
@@ -114,7 +114,7 @@ function getUpdateTime () {
return new Date(lastUpdateTime).toLocaleString().replace(/\//g, "-");
}
function selectRole (role: TGApp.Sqlite.Character.UserRole) {
function selectRole(role: TGApp.Sqlite.Character.UserRole) {
dataVal.value = role;
visible.value = true;
}

View File

@@ -1,7 +1,5 @@
<template>
<h1>祈愿数据获取展示统计</h1>
</template>
<script lang="ts" setup>
</script>
<style lang="css" scoped>
</style>
<script lang="ts" setup></script>
<style lang="css" scoped></style>

View File

@@ -68,7 +68,7 @@ onMounted(async () => {
}, 3000);
});
async function initUserRecordData () {
async function initUserRecordData() {
const recordGet = await TGSqlite.getUserRecord(user.value.gameUid);
if (recordGet !== false) {
recordData.value = recordGet;
@@ -78,7 +78,7 @@ async function initUserRecordData () {
}
}
async function refresh () {
async function refresh() {
loadingTitle.value = "正在获取战绩数据";
loading.value = true;
const res = await TGRequest.User.getRecord(recordCookie.value, user.value);
@@ -93,18 +93,18 @@ async function refresh () {
loading.value = false;
}
function getTitle () {
function getTitle() {
const role = JSON.parse(recordData.value.role) as TGApp.Sqlite.Record.Role;
return `${role.nickname} Lv.${role.level}${recordData.value.uid}`;
}
async function shareRecord () {
async function shareRecord() {
const recordBox = document.querySelector(".ur-box") as HTMLElement;
const fileName = `【原神战绩】-${user.value.gameUid}`;
await generateShareImg(fileName, recordBox);
}
function getTheme () {
function getTheme() {
let theme = localStorage.getItem("theme");
if (theme) {
theme = JSON.parse(theme).theme;

View File

@@ -21,7 +21,7 @@ onMounted(async () => {
overview.value = await HutaoRequest.Abyss.getOverview();
});
function getUpdated () {
function getUpdated() {
return new Date(overview.value.timestamp)
.toLocaleString("zh-CN", { hour12: false })
.replace(/\//g, "-");

View File

@@ -3,9 +3,7 @@
<div v-for="item in cardsInfo" :key="item.id" class="card-box" @click="toOuter(item)">
<TibWikiAvatar size="128px" :model-value="item" />
</div>
<v-snackbar v-model="snackbar" timeout="1500" color="error">
该角色暂无详情
</v-snackbar>
<v-snackbar v-model="snackbar" timeout="1500" color="error"> 该角色暂无详情 </v-snackbar>
</div>
</template>
@@ -23,7 +21,7 @@ const snackbar = ref(false);
// data
const cardsInfo = computed(() => AppCharacterData);
function toOuter (item: TGApp.App.Character.WikiBriefInfo) {
function toOuter(item: TGApp.App.Character.WikiBriefInfo) {
if (item.contentId === 0) {
snackbar.value = true;
return;
@@ -31,7 +29,6 @@ function toOuter (item: TGApp.App.Character.WikiBriefInfo) {
const url = OBC_CONTENT_API.replace("{content_id}", item.contentId.toString());
createTGWindow(url, "角色详情", item.name, 1200, 800, true);
}
</script>
<style scoped>
.cards-grid {

View File

@@ -2,15 +2,9 @@
<TOLoading v-model="loading" title="正在加载卡牌列表" />
<v-tabs v-model="tab" align-tabs="start" class="cards-tab">
<div v-show="!doSearch">
<v-tab value="character">
角色牌
</v-tab>
<v-tab value="action">
行动牌
</v-tab>
<v-tab value="monster">
魔物牌
</v-tab>
<v-tab value="character"> 角色牌 </v-tab>
<v-tab value="action"> 行动牌 </v-tab>
<v-tab value="monster"> 魔物牌 </v-tab>
</div>
<v-spacer />
<v-text-field
@@ -28,14 +22,16 @@
<v-window-item value="character">
<div class="cards-grid">
<v-card
v-for="item in CardsInfoC" :key="item.contentId" class="card-cls"
v-for="item in CardsInfoC"
:key="item.contentId"
class="card-cls"
@click="toOuter(item.name, item.contentId)"
>
<div class="card-border">
<img src="/WIKI/GCG/bg/normal.webp" alt="border">
<img src="/WIKI/GCG/bg/normal.webp" alt="border" />
</div>
<div class="card-cover">
<img :src="item.icon" alt="cover">
<img :src="item.icon" alt="cover" />
</div>
<div class="card-content">
<span>{{ item.name }}</span>
@@ -46,14 +42,16 @@
<v-window-item value="action">
<div class="cards-grid">
<v-card
v-for="item in CardsInfoA" :key="item.contentId" class="card-cls"
v-for="item in CardsInfoA"
:key="item.contentId"
class="card-cls"
@click="toOuter(item.name, item.contentId)"
>
<div class="card-border">
<img src="/WIKI/GCG/bg/normal.webp" alt="border">
<img src="/WIKI/GCG/bg/normal.webp" alt="border" />
</div>
<div class="card-cover">
<img :src="item.icon" alt="cover">
<img :src="item.icon" alt="cover" />
</div>
<div class="card-content">
<span>{{ item.name }}</span>
@@ -64,14 +62,16 @@
<v-window-item value="monster">
<div class="cards-grid">
<v-card
v-for="item in CardsInfoM" :key="item.contentId" class="card-cls"
v-for="item in CardsInfoM"
:key="item.contentId"
class="card-cls"
@click="toOuter(item.name, item.contentId)"
>
<div class="card-border">
<img src="/WIKI/GCG/bg/normal.webp" alt="border">
<img src="/WIKI/GCG/bg/normal.webp" alt="border" />
</div>
<div class="card-cover">
<img :src="item.icon" alt="cover">
<img :src="item.icon" alt="cover" />
</div>
<div class="card-content">
<span>{{ item.name }}</span>
@@ -84,14 +84,16 @@
<div v-show="doSearch">
<div class="cards-grid">
<v-card
v-for="item in CardsInfoS" :key="item.contentId" class="card-cls"
v-for="item in CardsInfoS"
:key="item.contentId"
class="card-cls"
@click="toOuter(item.name, item.contentId)"
>
<div class="card-border">
<img src="/WIKI/GCG/bg/special.webp" alt="border">
<img src="/WIKI/GCG/bg/special.webp" alt="border" />
</div>
<div class="card-cover">
<img :src="item.icon" alt="cover">
<img :src="item.icon" alt="cover" />
</div>
<div class="card-content">
<span>{{ item.name }}</span>
@@ -99,9 +101,7 @@
</v-card>
</div>
</div>
<v-snackbar v-model="snackbar" timeout="1500" color="error">
未找到相关卡牌
</v-snackbar>
<v-snackbar v-model="snackbar" timeout="1500" color="error"> 未找到相关卡牌 </v-snackbar>
</template>
<script lang="ts" setup>
// vue
@@ -132,7 +132,7 @@ onMounted(async () => {
await loadData();
});
async function loadData () {
async function loadData() {
await Promise.allSettled(
allCards.value.map(async (item) => {
if (item.type === "角色牌") CardsInfoC.value.push(item);
@@ -143,12 +143,12 @@ async function loadData () {
loading.value = false;
}
function toOuter (cardName: string, cardId: number) {
function toOuter(cardName: string, cardId: number) {
const url = OBC_CONTENT_API.replace("{content_id}", cardId.toString());
createTGWindow(url, "GCG", cardName, 1200, 800, true);
}
async function searchCard () {
async function searchCard() {
loading.value = true;
if (search.value === "") {
setTimeout(() => {
@@ -159,7 +159,9 @@ async function searchCard () {
}
doSearch.value = true;
const res: TGApp.App.GCG.WikiBriefInfo[] = [];
await Promise.allSettled(allCards.value.map((item) => (item.name.includes(search.value) ? res.push(item) : null)));
await Promise.allSettled(
allCards.value.map((item) => (item.name.includes(search.value) ? res.push(item) : null)),
);
res.sort((a, b) => a.name.localeCompare(b.name));
console.log(res);
loading.value = false;

View File

@@ -3,9 +3,7 @@
<div v-for="item in cardsInfo" :key="item.id" class="card-box" @click="toOuter(item)">
<TibWikiWeapon size="128px" :model-value="item" />
</div>
<v-snackbar v-model="snackbar" timeout="1500" color="error">
该武器暂无详情
</v-snackbar>
<v-snackbar v-model="snackbar" timeout="1500" color="error"> 该武器暂无详情 </v-snackbar>
</div>
</template>
@@ -23,7 +21,7 @@ const snackbar = ref(false);
// data
const cardsInfo = computed(() => AppWeaponData);
function toOuter (item: TGApp.App.Weapon.WikiBriefInfo) {
function toOuter(item: TGApp.App.Weapon.WikiBriefInfo) {
if (item.contentId === 0) {
snackbar.value = true;
return;
@@ -31,7 +29,6 @@ function toOuter (item: TGApp.App.Weapon.WikiBriefInfo) {
const url = OBC_CONTENT_API.replace("{content_id}", item.contentId.toString());
createTGWindow(url, "武器详情", item.name, 1200, 800, true);
}
</script>
<style scoped>
.cards-grid {

View File

@@ -11,9 +11,11 @@ import {
AvatarUpRateApi,
AvatarUseRateApi,
DataUploadApi,
OverviewApi, TeamCombinationApi,
OverviewApi,
TeamCombinationApi,
UidCheckApi,
UidRankApi, WeaponCollocationApi,
UidRankApi,
WeaponCollocationApi,
} from "./abyss";
const HutaoApi = {

View File

@@ -15,13 +15,15 @@ import HutaoApi from "../api";
* @since Alpha v0.2.0
* @return {Promise<TGApp.Plugins.Hutao.AbyssAvatarCollocation[]>}
*/
async function getAvatarCollect (): Promise<TGApp.Plugins.Hutao.AbyssAvatarCollocation[]> {
async function getAvatarCollect(): Promise<TGApp.Plugins.Hutao.AbyssAvatarCollocation[]> {
const url = HutaoApi.Abyss.avatar.collect;
return await http.fetch<TGApp.Plugins.Hutao.AbyssAvatarCollocationResponse>(url, {
method: "GET",
}).then((res) => {
return res.data.data;
});
return await http
.fetch<TGApp.Plugins.Hutao.AbyssAvatarCollocationResponse>(url, {
method: "GET",
})
.then((res) => {
return res.data.data;
});
}
export default getAvatarCollect;

View File

@@ -15,13 +15,15 @@ import HutaoApi from "../api";
* @since Alpha v0.2.0
* @returns {Promise<TGApp.Plugins.Hutao.AbyssAvatarHoldRate[]>}
*/
async function getAvatarHoldRate (): Promise<TGApp.Plugins.Hutao.AbyssAvatarHoldRate[]> {
async function getAvatarHoldRate(): Promise<TGApp.Plugins.Hutao.AbyssAvatarHoldRate[]> {
const url = HutaoApi.Abyss.avatar.holdRate;
return await http.fetch<TGApp.Plugins.Hutao.AbyssAvatarHoldRateResponse>(url, {
method: "GET",
}).then((res) => {
return res.data.data;
});
return await http
.fetch<TGApp.Plugins.Hutao.AbyssAvatarHoldRateResponse>(url, {
method: "GET",
})
.then((res) => {
return res.data.data;
});
}
export default getAvatarHoldRate;

View File

@@ -15,13 +15,15 @@ import HutaoApi from "../api";
* @since Alpha v0.2.0
* @return {Promise<TGApp.Plugins.Hutao.AbyssAvatarUpRate[]>}
*/
async function getAvatarUpRate (): Promise<TGApp.Plugins.Hutao.AbyssAvatarUpRate[]> {
async function getAvatarUpRate(): Promise<TGApp.Plugins.Hutao.AbyssAvatarUpRate[]> {
const url = HutaoApi.Abyss.avatar.upRate;
return await http.fetch<TGApp.Plugins.Hutao.AbyssAvatarUpRateResponse>(url, {
method: "GET",
}).then((res) => {
return res.data.data;
});
return await http
.fetch<TGApp.Plugins.Hutao.AbyssAvatarUpRateResponse>(url, {
method: "GET",
})
.then((res) => {
return res.data.data;
});
}
export default getAvatarUpRate;

View File

@@ -15,13 +15,15 @@ import HutaoApi from "../api";
* @since Alpha v0.2.0
* @return {Promise<TGApp.Plugins.Hutao.AbyssAvatarUseRate[]>}
*/
async function getAvatarUseRate (): Promise<TGApp.Plugins.Hutao.AbyssAvatarUseRate[]> {
async function getAvatarUseRate(): Promise<TGApp.Plugins.Hutao.AbyssAvatarUseRate[]> {
const url = HutaoApi.Abyss.avatar.useRate;
return await http.fetch<TGApp.Plugins.Hutao.AbyssAvatarUseRateResponse>(url, {
method: "GET",
}).then((res) => {
return res.data.data;
});
return await http
.fetch<TGApp.Plugins.Hutao.AbyssAvatarUseRateResponse>(url, {
method: "GET",
})
.then((res) => {
return res.data.data;
});
}
export default getAvatarUseRate;

View File

@@ -15,13 +15,15 @@ import HutaoApi from "../api";
* @since Alpha v0.2.0
* @return {Promise<TGApp.Plugins.Hutao.AbyssOverview>}
*/
async function getOverview (): Promise<TGApp.Plugins.Hutao.AbyssOverview> {
async function getOverview(): Promise<TGApp.Plugins.Hutao.AbyssOverview> {
const url = HutaoApi.Abyss.overview;
return await http.fetch<TGApp.Plugins.Hutao.AbyssOverviewResponse>(url, {
method: "GET",
}).then((res) => {
return res.data.data;
});
return await http
.fetch<TGApp.Plugins.Hutao.AbyssOverviewResponse>(url, {
method: "GET",
})
.then((res) => {
return res.data.data;
});
}
export default getOverview;

View File

@@ -15,13 +15,15 @@ import HutaoApi from "../api";
* @since Alpha v0.2.0
* @return {Promise<TGApp.Plugins.Hutao.AbyssTeamCombination[]>}
*/
async function getTeamCollect (): Promise<TGApp.Plugins.Hutao.AbyssTeamCombination[]> {
async function getTeamCollect(): Promise<TGApp.Plugins.Hutao.AbyssTeamCombination[]> {
const url = HutaoApi.Abyss.team;
return await http.fetch<TGApp.Plugins.Hutao.AbyssTeamCombinationResponse>(url, {
method: "GET",
}).then((res) => {
return res.data.data;
});
return await http
.fetch<TGApp.Plugins.Hutao.AbyssTeamCombinationResponse>(url, {
method: "GET",
})
.then((res) => {
return res.data.data;
});
}
export default getTeamCollect;

View File

@@ -17,13 +17,15 @@ import HutaoApi from "../api";
* @param {string} uid
* @return {Promise<boolean>}
*/
export async function checkUid (uid: string): Promise<boolean> {
export async function checkUid(uid: string): Promise<boolean> {
const url = HutaoApi.Abyss.user.check.replace("{uid}", uid);
return await http.fetch<TGApp.Plugins.Hutao.AbyssRecordExistResponse>(url, {
method: "GET",
}).then((res) => {
return res.data.data;
});
return await http
.fetch<TGApp.Plugins.Hutao.AbyssRecordExistResponse>(url, {
method: "GET",
})
.then((res) => {
return res.data.data;
});
}
/**
@@ -33,11 +35,13 @@ export async function checkUid (uid: string): Promise<boolean> {
* @param {string} uid
* @return {Promise<TGApp.Plugins.Hutao.AbyssRecordRank>}
*/
export async function getUserData (uid: string): Promise<TGApp.Plugins.Hutao.AbyssRecordRank> {
export async function getUserData(uid: string): Promise<TGApp.Plugins.Hutao.AbyssRecordRank> {
const url = HutaoApi.Abyss.user.rank.replace("{uid}", uid);
return await http.fetch<TGApp.Plugins.Hutao.AbyssRecordRankResponse>(url, {
method: "GET",
}).then((res) => {
return res.data.data;
});
return await http
.fetch<TGApp.Plugins.Hutao.AbyssRecordRankResponse>(url, {
method: "GET",
})
.then((res) => {
return res.data.data;
});
}

View File

@@ -15,13 +15,15 @@ import HutaoApi from "../api";
* @since Alpha v0.2.0
* @return {Promise<TGApp.Plugins.Hutao.AbyssWeaponCollocation[]>}
*/
async function getWeaponCollect (): Promise<TGApp.Plugins.Hutao.AbyssWeaponCollocation[]> {
async function getWeaponCollect(): Promise<TGApp.Plugins.Hutao.AbyssWeaponCollocation[]> {
const url = HutaoApi.Abyss.weapon;
return await http.fetch<TGApp.Plugins.Hutao.AbyssWeaponCollocationResponse>(url, {
method: "GET",
}).then((res) => {
return res.data.data;
});
return await http
.fetch<TGApp.Plugins.Hutao.AbyssWeaponCollocationResponse>(url, {
method: "GET",
})
.then((res) => {
return res.data.data;
});
}
export default getWeaponCollect;

View File

@@ -17,12 +17,14 @@ import HutaoApi from "../api";
* @param {TGApp.Plugins.Hutao.AbyssRecordUpload} data 用户数据
* @returns {Promise<unknown>} 上传结果
*/
async function uploadData (data: TGApp.Plugins.Hutao.AbyssRecordUpload): Promise<unknown> {
async function uploadData(data: TGApp.Plugins.Hutao.AbyssRecordUpload): Promise<unknown> {
const url = HutaoApi.Abyss.upload;
return await http.fetch(url, {
method: "POST",
body: http.Body.json(data),
}).then(res => res.data);
return await http
.fetch(url, {
method: "POST",
body: http.Body.json(data),
})
.then((res) => res.data);
}
export default uploadData;

View File

@@ -15,9 +15,9 @@
* @returns {MysResponse}
*/
export interface MysResponse {
retcode: number
message: string
data: any
retcode: number;
message: string;
data: any;
}
/**
@@ -30,8 +30,8 @@ export interface MysResponse {
*/
export interface MysObcResponse extends MysResponse {
data: {
list: MysObc[]
}
list: MysObc[];
};
}
/**
@@ -48,13 +48,13 @@ export interface MysObcResponse extends MysResponse {
* @returns {MysObc}
*/
export interface MysObc {
id: number
name: string
parent_id: number
depth: number
ch_ext: string
children: MysObc[]
list: unknown[]
id: number;
name: string;
parent_id: number;
depth: number;
ch_ext: string;
children: MysObc[];
list: unknown[];
}
/**

View File

@@ -17,8 +17,8 @@ import { type MysResponse } from "./base";
*/
export interface GachaResponse extends MysResponse {
data: {
list: GachaData[]
}
list: GachaData[];
};
}
/**
@@ -39,16 +39,16 @@ export interface GachaResponse extends MysResponse {
* @returns {GachaData}
*/
export interface GachaData {
id: string
title: string
activity_url: string
content_before_act: string
pool: GachaPool[]
voice_icon: string
voice_url: string
voice_status: string
start_time: string
end_time: string
id: string;
title: string;
activity_url: string;
content_before_act: string;
pool: GachaPool[];
voice_icon: string;
voice_url: string;
voice_status: string;
start_time: string;
end_time: string;
}
/**
@@ -60,8 +60,8 @@ export interface GachaData {
* @returns {GachaPool}
*/
export interface GachaPool {
icon: string
url: string
icon: string;
url: string;
}
/**
@@ -82,16 +82,16 @@ export interface GachaPool {
* @returns {GachaCard}
*/
export interface GachaCard {
title: string
subtitle: string
cover: string
post_id: number
characters: GachaPool[]
voice: GachaPool
title: string;
subtitle: string;
cover: string;
post_id: number;
characters: GachaPool[];
voice: GachaPool;
time: {
start: string
start_stamp: number
end: string
end_stamp: number
}
start: string;
start_stamp: number;
end: string;
end_stamp: number;
};
}

View File

@@ -18,8 +18,8 @@ import { type UserInfoPost } from "./user";
*/
export interface LotteryResponse extends MysResponse {
data: {
show_lottery: LotteryData
}
show_lottery: LotteryData;
};
}
/**
@@ -44,21 +44,21 @@ export interface LotteryResponse extends MysResponse {
* @returns {LotteryData}
*/
export interface LotteryData {
id: string
creator: UserInfoPost
draw_time: string
participant_way: string
is_expect_unfocus_user: boolean
is_expect_non_real_name_user: boolean
user_rewards: LotteryReward[]
status: string
is_blocked: boolean
user_status: string
is_upload_address: boolean
lottery_entity_summary: string
entity_id: string
entity_type: string
now_time: string
id: string;
creator: UserInfoPost;
draw_time: string;
participant_way: string;
is_expect_unfocus_user: boolean;
is_expect_non_real_name_user: boolean;
user_rewards: LotteryReward[];
status: string;
is_blocked: boolean;
user_status: string;
is_upload_address: boolean;
lottery_entity_summary: string;
entity_id: string;
entity_type: string;
now_time: string;
}
/**
@@ -74,12 +74,12 @@ export interface LotteryData {
* @returns {LotteryReward}
*/
export interface LotteryReward {
reward_name: string
winner_number: number
scheduled_winner_number: number
is_send_by_post: boolean
users: UserInfoPost[]
id: string
reward_name: string;
winner_number: number;
scheduled_winner_number: number;
is_send_by_post: boolean;
users: UserInfoPost[];
id: string;
}
/**
@@ -95,12 +95,12 @@ export interface LotteryReward {
* @returns {LotteryCard}
*/
export interface LotteryCard {
id: string
participantWay: string
status: string
creator: UserInfoPost
drawTime: string
rewards: LotteryRewardCard[]
id: string;
participantWay: string;
status: string;
creator: UserInfoPost;
drawTime: string;
rewards: LotteryRewardCard[];
}
/**
@@ -114,8 +114,8 @@ export interface LotteryCard {
* @returns {LotteryRewardCard}
*/
export interface LotteryRewardCard {
rewardName: string
winnerNumber: number
scheduledWinnerNumber: number
users: UserInfoPost[]
rewardName: string;
winnerNumber: number;
scheduledWinnerNumber: number;
users: UserInfoPost[];
}

View File

@@ -19,7 +19,7 @@ import { type ImageData, type HelpSys } from "./utils";
* @returns {NewsResponse}
*/
export interface NewsResponse extends MysResponse {
data: NewsData
data: NewsData;
}
/**
@@ -32,9 +32,9 @@ export interface NewsResponse extends MysResponse {
* @returns {NewsData}
*/
export interface NewsData {
last_id: number
is_last: boolean
list: NewsItem[]
last_id: number;
is_last: boolean;
list: NewsItem[];
}
/**
@@ -65,27 +65,27 @@ export interface NewsData {
* @returns {NewsItem}
*/
export interface NewsItem {
post: Post
forum: Forum
topics: Topic[]
user: UserInfoPost
self_operation: SelfOperation
stat: PostStat
help_sys: HelpSys
cover: ImageData
image_list: ImageData[]
is_official_master: boolean
is_user_master: boolean
hot_reply_exist: boolean
vote_count: number
last_modify_time: number
recommend_type: string
collection: unknown
vod_list: unknown[]
is_block_on: boolean
forum_rank_info: unknown
link_card_list: unknown[]
news_meta: NewsMeta
post: Post;
forum: Forum;
topics: Topic[];
user: UserInfoPost;
self_operation: SelfOperation;
stat: PostStat;
help_sys: HelpSys;
cover: ImageData;
image_list: ImageData[];
is_official_master: boolean;
is_user_master: boolean;
hot_reply_exist: boolean;
vote_count: number;
last_modify_time: number;
recommend_type: string;
collection: unknown;
vod_list: unknown[];
is_block_on: boolean;
forum_rank_info: unknown;
link_card_list: unknown[];
news_meta: NewsMeta;
}
/**
@@ -98,9 +98,9 @@ export interface NewsItem {
* @returns {NewsMeta}
*/
export interface NewsMeta {
activity_status: number
start_at_sec: string
end_at_sec: string
activity_status: number;
start_at_sec: string;
end_at_sec: string;
}
/**
@@ -115,11 +115,11 @@ export interface NewsMeta {
* @returns {NewsCard}
*/
export interface NewsCard {
title: string
cover: string
post_id: number
subtitle: string
status?: ActivityStatus
title: string;
cover: string;
post_id: number;
subtitle: string;
status?: ActivityStatus;
}
/**
@@ -130,6 +130,6 @@ export interface NewsCard {
* @returns {ActivityStatus}
*/
export interface ActivityStatus {
status: string
colorCss: string
status: string;
colorCss: string;
}

View File

@@ -17,8 +17,8 @@ import { type MysObcResponse, type MysObc } from "./base";
*/
export interface PositionResponse extends MysObcResponse {
data: {
list: PositionObc[]
}
list: PositionObc[];
};
}
/**
@@ -30,7 +30,7 @@ export interface PositionResponse extends MysObcResponse {
* @returns {PositionObc}
*/
export interface PositionObc extends MysObc {
list: PositionData[]
list: PositionData[];
}
/**
@@ -53,19 +53,19 @@ export interface PositionObc extends MysObc {
* @returns {PositionData}
*/
export interface PositionData {
recommend_id: number
content_id: number
title: string
ext: string
type: number
url: string
icon: string
abstract: string
article_user_name: string
avatar_url: string
article_time: string
create_time: string
end_time: string
recommend_id: number;
content_id: number;
title: string;
ext: string;
type: number;
url: string;
icon: string;
abstract: string;
article_user_name: string;
avatar_url: string;
article_time: string;
create_time: string;
end_time: string;
}
/**
@@ -84,14 +84,14 @@ export interface PositionData {
* @returns {PositionCard}
*/
export interface PositionCard {
title: string
post_id: number
icon: string
abstract: string
title: string;
post_id: number;
icon: string;
abstract: string;
time: {
start: string
start_stamp: number
end: string
end_stamp: number
}
start: string;
start_stamp: number;
end: string;
end_stamp: number;
};
}

View File

@@ -20,8 +20,8 @@ import { type ImageData, type HelpSys } from "./utils";
*/
export interface PostResponse extends MysResponse {
data: {
post: PostData
}
post: PostData;
};
}
/**
@@ -52,27 +52,27 @@ export interface PostResponse extends MysResponse {
* @returns {PostData}
*/
export interface PostData {
post: Post
forum: Forum
topics: Topic[]
user: UserInfoPost
self_operation: SelfOperation
stat: PostStat
help_sys: HelpSys | null
cover: ImageData | null
image_list: ImageData[]
is_official_master: boolean
is_user_master: boolean
hot_reply_exist: boolean
vot_count: number
last_modify_time: number
recommend_type: string
collection: unknown | null
vod_list: unknown[]
is_block_on: boolean
forum_rank_info: unknown | null
link_card_list: unknown[]
news_meta: NewsMeta | null
post: Post;
forum: Forum;
topics: Topic[];
user: UserInfoPost;
self_operation: SelfOperation;
stat: PostStat;
help_sys: HelpSys | null;
cover: ImageData | null;
image_list: ImageData[];
is_official_master: boolean;
is_user_master: boolean;
hot_reply_exist: boolean;
vot_count: number;
last_modify_time: number;
recommend_type: string;
collection: unknown | null;
vod_list: unknown[];
is_block_on: boolean;
forum_rank_info: unknown | null;
link_card_list: unknown[];
news_meta: NewsMeta | null;
}
/**
@@ -121,46 +121,46 @@ export interface PostData {
* @returns {Post}
*/
export interface Post {
game_id: number
post_id: string
f_forum_id: number
uid: string
subject: string
content: string
cover: string
view_type: number
created_at: number
images: string[]
game_id: number;
post_id: string;
f_forum_id: number;
uid: string;
subject: string;
content: string;
cover: string;
view_type: number;
created_at: number;
images: string[];
post_status: {
is_top: boolean
is_good: boolean
is_official: boolean
}
topic_ids: number[]
view_status: number
max_floor: number
is_original: number
republish_authorization: number
reply_time: string
is_deleted: number
is_interactive: boolean
structured_content: string
structured_content_rows: string[]
review_id: number
is_profit: boolean
is_in_profit: boolean
updated_at: number
deleted_at: number
pre_pub_status: number
cate_id: number
profit_post_status: number
audit_status: number
meta_content: string
is_missing: boolean
block_reply_img: number
is_showing_missing: boolean
block_latest_reply_time: number
selected_comment: number
is_top: boolean;
is_good: boolean;
is_official: boolean;
};
topic_ids: number[];
view_status: number;
max_floor: number;
is_original: number;
republish_authorization: number;
reply_time: string;
is_deleted: number;
is_interactive: boolean;
structured_content: string;
structured_content_rows: string[];
review_id: number;
is_profit: boolean;
is_in_profit: boolean;
updated_at: number;
deleted_at: number;
pre_pub_status: number;
cate_id: number;
profit_post_status: number;
audit_status: number;
meta_content: string;
is_missing: boolean;
block_reply_img: number;
is_showing_missing: boolean;
block_latest_reply_time: number;
selected_comment: number;
}
/**
@@ -175,11 +175,11 @@ export interface Post {
* @returns {Forum}
*/
export interface Forum {
id: number
name: string
icon: string
game_id: number
forum_cate: unknown | null
id: number;
name: string;
icon: string;
game_id: number;
forum_cate: unknown | null;
}
/**
@@ -197,14 +197,14 @@ export interface Forum {
* @returns {Topic}
*/
export interface Topic {
id: number
name: string
cover: string
is_top: boolean
is_good: boolean
is_interactive: boolean
game_id: number
content_type: number
id: number;
name: string;
cover: string;
is_top: boolean;
is_good: boolean;
is_interactive: boolean;
game_id: number;
content_type: number;
}
/**
@@ -219,11 +219,11 @@ export interface Topic {
* @returns {PostStat}
*/
export interface PostStat {
view_num: number
reply_num: number
like_num: number
bookmark_num: number
forward_num: number
view_num: number;
reply_num: number;
like_num: number;
bookmark_num: number;
forward_num: number;
}
/**
@@ -236,8 +236,8 @@ export interface PostStat {
* @returns {PostContent}
*/
export interface PostContent {
describe: string
imgs?: string[]
describe: string;
imgs?: string[];
}
/**
@@ -271,38 +271,37 @@ export interface PostContent {
* @returns {PostStructuredContent}
*/
export interface PostStructuredContent {
insert:
| {
image?: string
video?: string
vod?: PostStructuredContentVod
backup_text?: string
lottery?: {
id: string
toast: string
}
fold?: {
title: string
content: string
}
link_card?: PostStructuredContentLinkCard
divider?: string
mention?: {
uid: string
nickname: string
}
}
| string
| {
image?: string;
video?: string;
vod?: PostStructuredContentVod;
backup_text?: string;
lottery?: {
id: string;
toast: string;
};
fold?: {
title: string;
content: string;
};
link_card?: PostStructuredContentLinkCard;
divider?: string;
mention?: {
uid: string;
nickname: string;
};
}
| string;
attributes?: {
height?: number
width?: number
size?: number
ext?: string
bold?: boolean
color?: string
link?: string
}
height?: number;
width?: number;
size?: number;
ext?: string;
bold?: boolean;
color?: string;
link?: string;
};
}
/**
@@ -327,22 +326,22 @@ export interface PostStructuredContent {
* @returns {PostStructuredContentVod}
*/
export interface PostStructuredContentVod {
id: number
duration: number
cover: string
id: number;
duration: number;
cover: string;
resolutions: Array<{
url: string
definition: string
height: number
width: number
bitrate: number
size: number
format: string
label: string
}>
view_num: number
transcoding_status: number
review_status: number
url: string;
definition: string;
height: number;
width: number;
bitrate: number;
size: number;
format: string;
label: string;
}>;
view_num: number;
transcoding_status: number;
review_status: number;
}
/**
@@ -363,15 +362,15 @@ export interface PostStructuredContentVod {
* @returns {PostStructuredContentLinkCard}
*/
export interface PostStructuredContentLinkCard {
link_type: number
origin_url: string
landing_url: string
cover: string
title: string
card_id: string
card_status: number
market_price: string
price: string
button_text: string
landing_url_type: number
link_type: number;
origin_url: string;
landing_url: string;
cover: string;
title: string;
card_id: string;
card_status: number;
market_price: string;
price: string;
button_text: string;
landing_url_type: number;
}

View File

@@ -16,7 +16,7 @@ import { type MysResponse } from "./base";
* @returns {UserResponse}
*/
export interface UserResponse extends MysResponse {
data: UserInfoFull
data: UserInfoFull;
}
/**
@@ -41,23 +41,23 @@ export interface UserResponse extends MysResponse {
* @returns {UserInfoFull}
*/
export interface UserInfoFull {
user_info: UserInfo
follow_relation: unknown
auth_relations: unknown[]
is_in_blacklist: boolean
is_has_collection: boolean
is_creator: boolean
user_info: UserInfo;
follow_relation: unknown;
auth_relations: unknown[];
is_in_blacklist: boolean;
is_has_collection: boolean;
is_creator: boolean;
custom_service: {
is_customer_service_staff: boolean
game_id: number
}
is_customer_service_staff: boolean;
game_id: number;
};
audit_info: {
is_nickname_in_audit: boolean
nickname: string
is_introduce_in_audit: boolean
introduce: string
nickname_status: number
}
is_nickname_in_audit: boolean;
nickname: string;
is_introduce_in_audit: boolean;
introduce: string;
nickname_status: number;
};
}
/**
@@ -82,21 +82,21 @@ export interface UserInfoFull {
* @returns {UserInfo}
*/
export interface UserInfo {
uid: string
nickname: string
introduce: string
avatar: string
gender: number
certification: UserCertification
level_exps: UserLevelExp[]
archive: UserArchive
community_info: UserCommunityInfo
avatar_url: string
certifications: UserCertification[]
level_exp: UserLevelExp
pendant: string
is_logoff: boolean
ip_region: string
uid: string;
nickname: string;
introduce: string;
avatar: string;
gender: number;
certification: UserCertification;
level_exps: UserLevelExp[];
archive: UserArchive;
community_info: UserCommunityInfo;
avatar_url: string;
certifications: UserCertification[];
level_exp: UserLevelExp;
pendant: string;
is_logoff: boolean;
ip_region: string;
}
/**
@@ -108,8 +108,8 @@ export interface UserInfo {
* @returns {UserCertification}
*/
export interface UserCertification {
type: number
label: string
type: number;
label: string;
}
/**
@@ -123,9 +123,9 @@ export interface UserCertification {
* @returns {UserLevelExp}
*/
export interface UserLevelExp {
level: number
exp: number
game_id: number
level: number;
exp: number;
game_id: number;
}
/**
@@ -144,15 +144,15 @@ export interface UserLevelExp {
* @returns {UserArchive}
*/
export interface UserArchive {
like_num: string
post_num: string
replypost_num: string
follow_cnt: string
followed_cnt: string
topic_cnt: string
new_follower_num: string
good_post_num: string
follow_collection_cnt: string
like_num: string;
post_num: string;
replypost_num: string;
follow_cnt: string;
followed_cnt: string;
topic_cnt: string;
new_follower_num: string;
good_post_num: string;
follow_collection_cnt: string;
}
/**
@@ -189,36 +189,36 @@ export interface UserArchive {
* @returns {UserCommunityInfo}
*/
export interface UserCommunityInfo {
is_realname: boolean
agree_status: boolean
silent_end_time: number
forbid_end_time: number
info_upd_time: number
is_realname: boolean;
agree_status: boolean;
silent_end_time: number;
forbid_end_time: number;
info_upd_time: number;
privacy_invisible: {
post: boolean
collect: boolean
watermark: boolean
reply: boolean
post_and_instant: boolean
}
post: boolean;
collect: boolean;
watermark: boolean;
reply: boolean;
post_and_instant: boolean;
};
notify_disable: {
reply: boolean
upvote: boolean
follow: boolean
system: boolean
chat: boolean
}
has_initialized: boolean
reply: boolean;
upvote: boolean;
follow: boolean;
system: boolean;
chat: boolean;
};
has_initialized: boolean;
user_func_status: {
enable_history_view: boolean
enable_recommend: boolean
enable_mention: boolean
user_center_view: number
}
forum_silent_info: unknown[]
last_login_ip: string
last_login_time: number
created_at: number
enable_history_view: boolean;
enable_recommend: boolean;
enable_mention: boolean;
user_center_view: number;
};
forum_silent_info: unknown[];
last_login_ip: string;
last_login_time: number;
created_at: number;
}
/**
@@ -241,20 +241,20 @@ export interface UserCommunityInfo {
* @returns {UserInfoPost}
*/
export interface UserInfoPost {
uid: string
nickname: string
introduce: string
avatar: string
gender: number
certification: UserCertification
uid: string;
nickname: string;
introduce: string;
avatar: string;
gender: number;
certification: UserCertification;
level_exp: {
level: number
exp: number
}
is_following: boolean
is_follower: boolean
avatar_url: string
pendant: string
level: number;
exp: number;
};
is_following: boolean;
is_follower: boolean;
avatar_url: string;
pendant: string;
}
/**
@@ -266,6 +266,6 @@ export interface UserInfoPost {
* @returns {SelfOperation}
*/
export interface SelfOperation {
attitude: number
is_collected: boolean
attitude: number;
is_collected: boolean;
}

View File

@@ -41,23 +41,23 @@ export const OBC_CONTENT_API =
* @returns {ImageData}
*/
export interface ImageData {
url: string
height: number
width: number
format: string
size: string
url: string;
height: number;
width: number;
format: string;
size: string;
crop: {
x: number
y: number
w: number
h: number
url: string
} | null
is_user_set_cover: boolean
image_id: string
entity_type: string
entity_id: string
is_deleted: boolean
x: number;
y: number;
w: number;
h: number;
url: string;
} | null;
is_user_set_cover: boolean;
image_id: string;
entity_type: string;
entity_id: string;
is_deleted: boolean;
}
/**
@@ -71,7 +71,7 @@ export interface ImageData {
* @returns {HelpSys}
*/
export interface HelpSys {
top_up: unknown | null
top_n: unknown[]
answer_num: number
top_up: unknown | null;
top_n: unknown[];
answer_num: number;
}

View File

@@ -9,21 +9,22 @@ import { http } from "@tauri-apps/api";
import { type GachaResponse, type GachaData } from "../interface/gacha";
// 卡池 API
const GACHA_POOL_API = "https://api-takumi.mihoyo.com/common/blackboard/ys_obc/v1/gacha_pool?app_sn=ys_obc";
const GACHA_POOL_API =
"https://api-takumi.mihoyo.com/common/blackboard/ys_obc/v1/gacha_pool?app_sn=ys_obc";
/**
* @description 获取卡池信息
* @since Alpha
* @return {Promise<GachaData[]>}
*/
export async function getGachaData (): Promise<GachaData[]> {
export async function getGachaData(): Promise<GachaData[]> {
return await http
.fetch<GachaResponse>(GACHA_POOL_API, {
method: "GET",
headers: {
"Content-Type": "application/json",
},
})
method: "GET",
headers: {
"Content-Type": "application/json",
},
})
.then((res) => {
return res.data.data.list;
});

View File

@@ -9,7 +9,8 @@ import { http } from "@tauri-apps/api";
import { type LotteryResponse, type LotteryData } from "../interface/lottery";
// 抽奖 API
const LOTTERY_API = "https://bbs-api.miyoushe.com/painter/wapi/lottery/user/show?gids=2&id={lottery_id}";
const LOTTERY_API =
"https://bbs-api.miyoushe.com/painter/wapi/lottery/user/show?gids=2&id={lottery_id}";
/**
* @description 获取抽奖信息
@@ -17,14 +18,14 @@ const LOTTERY_API = "https://bbs-api.miyoushe.com/painter/wapi/lottery/user/show
* @param {string} lotteryId 抽奖 ID
* @return {Promise<LotteryData>}
*/
export async function getLotteryData (lotteryId: string): Promise<LotteryData> {
export async function getLotteryData(lotteryId: string): Promise<LotteryData> {
return await http
.fetch<LotteryResponse>(LOTTERY_API.replace("{lottery_id}", lotteryId), {
method: "GET",
headers: {
"Content-Type": "application/json",
},
})
method: "GET",
headers: {
"Content-Type": "application/json",
},
})
.then((res) => {
return res.data.data.show_lottery;
});

View File

@@ -37,7 +37,11 @@ enum NewsType {
* @param {number} lastId 上一次请求的最后一条数据的 id
* @return {Promise<NewsData>}
*/
export async function getNoticeList (gid: string = "2", pageSize: number = 20, lastId: number = 0): Promise<NewsData> {
export async function getNoticeList(
gid: string = "2",
pageSize: number = 20,
lastId: number = 0,
): Promise<NewsData> {
const url = NEWS_LIST_API.replace("{page_size}", pageSize.toString())
.replace("{gid}", gid)
.replace("{news_type}", NewsType.NOTICE)
@@ -53,7 +57,11 @@ export async function getNoticeList (gid: string = "2", pageSize: number = 20, l
* @param {number} lastId 上一次请求的最后一条数据的 id
* @return {Promise<NewsData>}
*/
export async function getActivityList (gid: string = "2", pageSize: number = 20, lastId: number = 0): Promise<NewsData> {
export async function getActivityList(
gid: string = "2",
pageSize: number = 20,
lastId: number = 0,
): Promise<NewsData> {
const url = NEWS_LIST_API.replace("{page_size}", pageSize.toString())
.replace("{gid}", gid)
.replace("{news_type}", NewsType.ACTIVITY)
@@ -69,7 +77,11 @@ export async function getActivityList (gid: string = "2", pageSize: number = 20,
* @param {number} lastId 上一次请求的最后一条数据的 id
* @return {Promise<NewsData>}
*/
export async function getNewsList (gid: string = "2", pageSize: number = 20, lastId: number = 0): Promise<NewsData> {
export async function getNewsList(
gid: string = "2",
pageSize: number = 20,
lastId: number = 0,
): Promise<NewsData> {
const url = NEWS_LIST_API.replace("{page_size}", pageSize.toString())
.replace("{gid}", gid)
.replace("{news_type}", NewsType.NEWS)

View File

@@ -10,21 +10,22 @@ import { type PositionResponse, type PositionData } from "../interface/position"
import { dfs } from "../utils/position";
// 热点追踪 API
const POSITION_API = "https://api-static.mihoyo.com/common/blackboard/ys_obc/v1/home/position?app_sn=ys_obc";
const POSITION_API =
"https://api-static.mihoyo.com/common/blackboard/ys_obc/v1/home/position?app_sn=ys_obc";
/**
* @description 获取热点追踪信息
* @since Alpha v0.1.1
* @return {Promise<PositionData[]>}
*/
export async function getPositionData (): Promise<PositionData[]> {
export async function getPositionData(): Promise<PositionData[]> {
const res = await http
.fetch<PositionResponse>(POSITION_API, {
method: "GET",
headers: {
"Content-Type": "application/json",
},
})
method: "GET",
headers: {
"Content-Type": "application/json",
},
})
.then((res) => {
return res.data.data.list;
});

View File

@@ -18,15 +18,15 @@ const POST_REFERER = "https://bbs.mihoyo.com/";
* @param {number} postId 帖子 ID
* @return {Promise<PostData>}
*/
export async function getPostData (postId: number): Promise<PostData> {
export async function getPostData(postId: number): Promise<PostData> {
return await http
.fetch<PostResponse>(POST_API.replace("{post_id}", postId.toString()), {
method: "GET",
headers: {
"Content-Type": "application/json",
Referer: POST_REFERER,
},
})
method: "GET",
headers: {
"Content-Type": "application/json",
Referer: POST_REFERER,
},
})
.then((res) => {
return res.data.data.post;
});

View File

@@ -15,7 +15,7 @@ import { type GachaCard, type GachaData } from "../interface/gacha";
* @param {Map<string>} poolCover 卡池封面
* @returns {Promise<GachaCard[]>}
*/
export async function getGachaCard (
export async function getGachaCard(
gachaData: GachaData[],
poolCover: Record<number, string> | undefined = undefined,
): Promise<GachaCard[]> {

View File

@@ -5,7 +5,12 @@
* @since Alpha v0.1.1
*/
import { type LotteryData, type LotteryCard, type LotteryRewardCard, type LotteryReward } from "../interface/lottery";
import {
type LotteryData,
type LotteryCard,
type LotteryRewardCard,
type LotteryReward,
} from "../interface/lottery";
/**
* @description 根据抽奖信息转为渲染用的抽奖信息
@@ -13,7 +18,7 @@ import { type LotteryData, type LotteryCard, type LotteryRewardCard, type Lotter
* @param {LotteryData} lotteryData 抽奖信息
* @returns {LotteryCard}
*/
export function getLotteryCard (lotteryData: LotteryData): LotteryCard {
export function getLotteryCard(lotteryData: LotteryData): LotteryCard {
return {
id: lotteryData.id,
participantWay: lotteryData.participant_way,
@@ -32,7 +37,7 @@ export function getLotteryCard (lotteryData: LotteryData): LotteryCard {
* @param {LotteryReward} lotteryReward 抽奖奖励信息
* @returns {LotteryRewardCard}
*/
export function getLotteryRewardCard (lotteryReward: LotteryReward): LotteryRewardCard {
export function getLotteryRewardCard(lotteryReward: LotteryReward): LotteryRewardCard {
return {
rewardName: lotteryReward.reward_name,
winnerNumber: lotteryReward.winner_number,

View File

@@ -5,7 +5,12 @@
* @since Alpha v0.1.2
*/
import { type NewsData, type NewsItem, type NewsCard, type ActivityStatus } from "../interface/news";
import {
type NewsData,
type NewsItem,
type NewsCard,
type ActivityStatus,
} from "../interface/news";
// 默认封面图
const defaultCover = "/source/UI/defaultCover.webp";
@@ -44,7 +49,7 @@ const EnumStatus = {
* @param {number} status 活动状态码
* @returns {string}
*/
export function getActivityStatus (status: number): ActivityStatus {
export function getActivityStatus(status: number): ActivityStatus {
switch (status) {
case 1:
return EnumStatus.STARTED;
@@ -63,7 +68,7 @@ export function getActivityStatus (status: number): ActivityStatus {
* @param {NewsData} noticeData 公告数据
* @returns {NewsCard[]}
*/
export function getNoticeCard (noticeData: NewsData): NewsCard[] {
export function getNoticeCard(noticeData: NewsData): NewsCard[] {
const noticeCard: NewsCard[] = [];
noticeData.list.map((item: NewsItem) => {
return noticeCard.push({
@@ -82,7 +87,7 @@ export function getNoticeCard (noticeData: NewsData): NewsCard[] {
* @param {NewsData} activityData 活动数据
* @returns {NewsCard[]}
*/
export function getActivityCard (activityData: NewsData): NewsCard[] {
export function getActivityCard(activityData: NewsData): NewsCard[] {
const activityCard: NewsCard[] = [];
activityData.list.map((item: NewsItem) => {
const startTime = new Date(Number(item.news_meta.start_at_sec) * 1000).toLocaleDateString();
@@ -105,7 +110,7 @@ export function getActivityCard (activityData: NewsData): NewsCard[] {
* @param {NewsData} newsData 新闻数据
* @returns {NewsCard[]}
*/
export function getNewsCard (newsData: NewsData): NewsCard[] {
export function getNewsCard(newsData: NewsData): NewsCard[] {
const newsCard: NewsCard[] = [];
newsData.list.map((item: NewsItem) => {
return newsCard.push({

View File

@@ -12,7 +12,7 @@ import { type PostData, type PostStructuredContent } from "../interface/post";
* @param {string} hex 16进制颜色
* @returns {object} RGB 颜色
*/
function hexToRgb (hex: string): { r: number, g: number, b: number } {
function hexToRgb(hex: string): { r: number; g: number; b: number } {
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
if (result === null) {
throw new Error("无法解析颜色");
@@ -31,16 +31,20 @@ function hexToRgb (hex: string): { r: number, g: number, b: number } {
* @param {string} colorFg 前景颜色
* @returns {boolean} 是否相近
*/
function isColorSimilar (colorBg: string, colorFg: string): boolean {
function isColorSimilar(colorBg: string, colorFg: string): boolean {
const colorBgRGB = hexToRgb(colorBg);
const colorFgRGB = hexToRgb(colorFg);
const colorBgL = 0.2126 * colorBgRGB.r + 0.7152 * colorBgRGB.g + 0.0722 * colorBgRGB.b;
const colorFgL = 0.2126 * colorFgRGB.r + 0.7152 * colorFgRGB.g + 0.0722 * colorFgRGB.b;
const colorBgLum = colorBgL / 255;
const colorFgLum = colorFgL / 255;
const colorBgLumFinal = colorBgLum <= 0.03928 ? colorBgLum / 12.92 : Math.pow((colorBgLum + 0.055) / 1.055, 2.4);
const colorFgLumFinal = colorFgLum <= 0.03928 ? colorFgLum / 12.92 : Math.pow((colorFgLum + 0.055) / 1.055, 2.4);
const contrast = (Math.max(colorBgLumFinal, colorFgLumFinal) + 0.05) / (Math.min(colorBgLumFinal, colorFgLumFinal) + 0.05);
const colorBgLumFinal =
colorBgLum <= 0.03928 ? colorBgLum / 12.92 : Math.pow((colorBgLum + 0.055) / 1.055, 2.4);
const colorFgLumFinal =
colorFgLum <= 0.03928 ? colorFgLum / 12.92 : Math.pow((colorFgLum + 0.055) / 1.055, 2.4);
const contrast =
(Math.max(colorBgLumFinal, colorFgLumFinal) + 0.05) /
(Math.min(colorBgLumFinal, colorFgLumFinal) + 0.05);
return contrast <= 2.5;
}
@@ -50,7 +54,7 @@ function isColorSimilar (colorBg: string, colorFg: string): boolean {
* @param {string} url 链接
* @returns {boolean} 是否是米游社帖子
*/
export function IsMysPost (url: string): boolean {
export function IsMysPost(url: string): boolean {
const regBBS = /^https:\/\/bbs\.mihoyo\.com\/\w+\/article\/\d+$/;
const regMYS = /^https:\/\/www\.miyoushe\.com\/\w+\/article\/\d+$/;
return regBBS.test(url) || regMYS.test(url);
@@ -62,7 +66,7 @@ export function IsMysPost (url: string): boolean {
* @param {string} url 链接
* @returns {string} 帖子 id
*/
export function getPostId (url: string): string {
export function getPostId(url: string): string {
const postId: string | undefined = url.split("/").pop();
if (postId === undefined) {
throw new Error("无法获取帖子 id");
@@ -77,7 +81,7 @@ export function getPostId (url: string): string {
* @param {string} content 帖子内容
* @returns {string} 解析后的内容
*/
export function contentParser (content: string): string {
export function contentParser(content: string): string {
const data = JSON.parse(content);
const result: PostStructuredContent[] = [];
// 遍历 data 属性,值
@@ -114,7 +118,7 @@ export function contentParser (content: string): string {
* @description 为了安全考虑,不会解析所有的属性,只会解析几个常用的属性
* @returns {string} 解析后的HTML可作为 v-html 使用
*/
export function PostParser (post: PostData): string {
export function PostParser(post: PostData): string {
const postContent = post.post.content;
let parserData;
if (postContent.startsWith("<")) {
@@ -141,22 +145,22 @@ export function PostParser (post: PostData): string {
* @param {PostStructuredContent} data Mys数据
* @returns {HTMLDivElement | HTMLSpanElement} 解析后的中转
*/
function ParserTransfer (data: PostStructuredContent): HTMLDivElement | HTMLSpanElement {
function ParserTransfer(data: PostStructuredContent): HTMLDivElement | HTMLSpanElement {
if (typeof data.insert === "string") {
return TextParser(data);
} else if (data.insert.image) {
return ImageParser(data);
} else if (data.insert.vod) {
} else if (data.insert.vod != null) {
return VideoParser(data);
} else if (data.insert.video) {
return VideoParser(data);
} else if (data.insert.backup_text) {
return BackupTextParser(data);
} else if (data.insert.link_card) {
} else if (data.insert.link_card != null) {
return LinkCardParser(data);
} else if (data.insert.divider) {
return DividerParser(data);
} else if (data.insert.mention) {
} else if (data.insert.mention != null) {
return MentionParser(data);
} else {
return UnknownParser(data);
@@ -169,7 +173,7 @@ function ParserTransfer (data: PostStructuredContent): HTMLDivElement | HTMLSpan
* @param {PostStructuredContent} data Mys数据
* @returns {HTMLDivElement} 解析后的未知数据
*/
function UnknownParser (data: PostStructuredContent): HTMLDivElement {
function UnknownParser(data: PostStructuredContent): HTMLDivElement {
// 创建 div
const div = document.createElement("div");
div.classList.add("mys-post-unknown");
@@ -188,7 +192,7 @@ function UnknownParser (data: PostStructuredContent): HTMLDivElement {
* @param {PostStructuredContent} data Mys数据
* @returns {HTMLSpanElement} 解析后的文本
*/
function TextParser (data: PostStructuredContent): HTMLSpanElement {
function TextParser(data: PostStructuredContent): HTMLSpanElement {
// 检查数据
if (typeof data.insert !== "string") {
throw new Error("data.insert is not a string");
@@ -196,7 +200,7 @@ function TextParser (data: PostStructuredContent): HTMLSpanElement {
// 创建文本
const text = document.createElement("span");
// 设置文本属性
if (data.attributes) {
if (data.attributes != null) {
if (data.attributes.bold) text.style.fontWeight = "bold";
if (data.attributes.color) {
let colorGet = data.attributes.color;
@@ -224,12 +228,12 @@ function TextParser (data: PostStructuredContent): HTMLSpanElement {
* @param {PostStructuredContent} data Mys数据
* @returns {HTMLSpanElement} 解析后的链接
*/
function LinkTextParser (data: PostStructuredContent): HTMLSpanElement {
function LinkTextParser(data: PostStructuredContent): HTMLSpanElement {
// 检查数据
if (typeof data.insert !== "string") {
throw new Error("data.insert is not a string");
}
if (!data.attributes) {
if (data.attributes == null) {
throw new Error("data.attributes is not defined");
}
if (!data.attributes.link) {
@@ -263,7 +267,7 @@ function LinkTextParser (data: PostStructuredContent): HTMLSpanElement {
* @param {PostStructuredContent} data Mys数据
* @returns {HTMLDivElement} 解析后的分割线
*/
function DividerParser (data: PostStructuredContent): HTMLDivElement {
function DividerParser(data: PostStructuredContent): HTMLDivElement {
// 数据检查
if (typeof data.insert === "string") {
throw new Error("data.insert is a string");
@@ -286,7 +290,8 @@ function DividerParser (data: PostStructuredContent): HTMLDivElement {
img.src =
"https://mihoyo-community-web.oss-cn-shanghai.aliyuncs.com/upload/2021/01/05/e7047588e912d60ff87a975e037c7606.png";
} else if (data.insert.divider === "line_4") {
img.src = "https://mihoyo-community-web.oss-cn-shanghai.aliyuncs.com/upload/2022/07/13/line_4.png";
img.src =
"https://mihoyo-community-web.oss-cn-shanghai.aliyuncs.com/upload/2022/07/13/line_4.png";
} else {
console.error("Unknown divider type", data);
return UnknownParser(data);
@@ -303,7 +308,7 @@ function DividerParser (data: PostStructuredContent): HTMLDivElement {
* @param {PostStructuredContent} data Mys数据
* @returns {HTMLDivElement} 解析后的图片
*/
function ImageParser (data: PostStructuredContent): HTMLDivElement {
function ImageParser(data: PostStructuredContent): HTMLDivElement {
// 检查数据
if (typeof data.insert === "string") {
throw new Error("data.insert is a string");
@@ -340,18 +345,18 @@ function ImageParser (data: PostStructuredContent): HTMLDivElement {
* @param {PostStructuredContent} data Mys数据
* @returns {HTMLDivElement} 解析后的视频
*/
function VideoParser (data: PostStructuredContent): HTMLDivElement {
function VideoParser(data: PostStructuredContent): HTMLDivElement {
// 检查数据
if (typeof data.insert === "string") {
throw new Error("data.insert is a string");
}
if (!data.insert.vod && !data.insert.video) {
if (data.insert.vod == null && !data.insert.video) {
throw new Error("data.insert.vod is not defined");
}
// 创建 div
const div = document.createElement("div");
div.classList.add("mys-post-div");
if (data.insert.vod) {
if (data.insert.vod != null) {
// 创建视频
const video = document.createElement("video");
video.classList.add("mys-post-vod");
@@ -390,7 +395,7 @@ function VideoParser (data: PostStructuredContent): HTMLDivElement {
* @param {PostStructuredContent} data Mys数据
* @returns {HTMLDivElement} 解析后的折叠内容
*/
function BackupTextParser (data: PostStructuredContent): HTMLDivElement {
function BackupTextParser(data: PostStructuredContent): HTMLDivElement {
// 检查数据
if (typeof data.insert === "string") {
throw new Error("data.insert is a string");
@@ -398,7 +403,7 @@ function BackupTextParser (data: PostStructuredContent): HTMLDivElement {
if (data.insert.backup_text === "[抽奖]") {
return LotteryParser(data);
}
if (!data.insert.fold) {
if (data.insert.fold == null) {
throw new Error("data.insert.fold is not defined");
}
// 转换
@@ -436,7 +441,7 @@ function BackupTextParser (data: PostStructuredContent): HTMLDivElement {
* @param {PostStructuredContent} data Mys数据
* @returns {HTMLDivElement} 解析后的抽奖
*/
function LotteryParser (data: PostStructuredContent): HTMLDivElement {
function LotteryParser(data: PostStructuredContent): HTMLDivElement {
// 检查数据
if (typeof data.insert === "string") {
throw new Error("data.insert is a string");
@@ -447,7 +452,7 @@ function LotteryParser (data: PostStructuredContent): HTMLDivElement {
if (data.insert.backup_text !== "[抽奖]") {
throw new Error("data.insert.backup_text is not [抽奖]");
}
if (!data.insert.lottery) {
if (data.insert.lottery == null) {
throw new Error("data.insert.lottery is not defined");
}
// 创建 div
@@ -474,12 +479,12 @@ function LotteryParser (data: PostStructuredContent): HTMLDivElement {
* @param {PostStructuredContent} data Mys数据
* @returns {HTMLDivElement} 解析后的链接卡片
*/
function LinkCardParser (data: PostStructuredContent): HTMLDivElement {
function LinkCardParser(data: PostStructuredContent): HTMLDivElement {
// 检查数据
if (typeof data.insert === "string") {
throw new Error("data.insert is a string");
}
if (!data.insert.link_card) {
if (data.insert.link_card == null) {
throw new Error("data.insert.link_card is not defined");
}
// 创建 div
@@ -537,12 +542,12 @@ function LinkCardParser (data: PostStructuredContent): HTMLDivElement {
* @param {PostStructuredContent} data Mys数据
* @returns {HTMLAnchorElement} 解析后的 Mention
*/
function MentionParser (data: PostStructuredContent): HTMLAnchorElement {
function MentionParser(data: PostStructuredContent): HTMLAnchorElement {
// 检查数据
if (typeof data.insert === "string") {
throw new Error("data.insert is a string");
}
if (!data.insert.mention) {
if (data.insert.mention == null) {
throw new Error("data.insert.mention is not defined");
}
// 创建图标

View File

@@ -13,7 +13,7 @@ import { type PositionObc, type PositionData, type PositionCard } from "../inter
* @param {PositionObc[]} list 列表
* @returns {PositionData[]} 返回列表
*/
export function dfs (list: PositionObc[]): PositionData[] {
export function dfs(list: PositionObc[]): PositionData[] {
const res: PositionData[] = [];
for (const item of list) {
if (item.name === "近期活动") {
@@ -32,7 +32,7 @@ export function dfs (list: PositionObc[]): PositionData[] {
* @param {PositionData[]} positionData 列表
* @returns {PositionCard[]} 返回列表
*/
export function getPositionCard (positionData: PositionData[]): PositionCard[] {
export function getPositionCard(positionData: PositionData[]): PositionCard[] {
const res: PositionCard[] = [];
positionData.map((position) => {
let endStr: string;

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