mirror of
https://github.com/BTMuli/TeyvatGuide.git
synced 2025-12-07 08:42:49 +08:00
🎨 代码格式化
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -113,7 +113,6 @@ FullCommits: [`v0.1.2 ~ v0.1.3`](https://github.com/BTMuli/Tauri.Genshin/compare
|
||||
- 咨讯:支持多种游戏咨讯查看
|
||||
- 米游社解析:添加 `mention` 类型解析
|
||||
|
||||
|
||||
### Fix
|
||||
|
||||
- 首页:近期活动、限时祈愿结束处理
|
||||
|
||||
73
docs/UIAF.md
73
docs/UIAF.md
@@ -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`
|
||||
|
||||
185
docs/UIGF.md
185
docs/UIGF.md
@@ -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 |
|
||||
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
|
||||
|
||||
@@ -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`,256x256,32位深度
|
||||
- 成就系列图标:`source/achievementSeries/${series_id}.webp`,256x256,32位深度
|
||||
- 原石图标:`source/material/原石.webp`,256x256,32 位深度
|
||||
- 成就系列图标:`source/achievementSeries/${series_id}.webp`,256x256,32 位深度
|
||||
- 成就系列对应名片图标:见 [名片相关](#名片相关)
|
||||
|
||||
## 名片相关
|
||||
@@ -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`,250x165,32位深度
|
||||
- 名片bg:`source/nameCard/bg/${card.name}.webp`,880x140,32位深度
|
||||
- 名片profile:`source/nameCard/profile/${card.name}.webp`,840x400,32位深度
|
||||
- 名片图标:`source/nameCard/icon/${card.name}.webp`,250x165,32 位深度
|
||||
- 名片 bg:`source/nameCard/bg/${card.name}.webp`,880x140,32 位深度
|
||||
- 名片 profile:`source/nameCard/profile/${card.name}.webp`,840x400,32 位深度
|
||||
|
||||
### 格式说明
|
||||
|
||||
- 卡牌图标:`source/gcg/card/${cardType}/${cardName}.webp`,420x720,32位深度
|
||||
- 卡牌图标:`source/gcg/card/${cardType}/${cardName}.webp`,420x720,32 位深度
|
||||
|
||||
## 素材日历相关
|
||||
|
||||
@@ -101,9 +101,9 @@ Update: 2023-05-19
|
||||
|
||||
> 角色&武器资源见 [Wiki](#wiki)。
|
||||
|
||||
- 素材图标:`source/calendar/material/${sourceName}.webp`,128x128,32位深度
|
||||
- 星级图标:`icon/star/${star}.webp`,133x29,32位深度
|
||||
- 地区图标: `icon/nation/${nation}.webp`,256x256,32位深度
|
||||
- 素材图标:`source/calendar/material/${sourceName}.webp`,128x128,32 位深度
|
||||
- 星级图标:`icon/star/${star}.webp`,133x29,32 位深度
|
||||
- 地区图标: `icon/nation/${nation}.webp`,256x256,32 位深度
|
||||
|
||||
## Wiki
|
||||
|
||||
@@ -131,9 +131,8 @@ Update: 2023-05-19
|
||||
>
|
||||
> iconType 为 `normal` 或 `special`,目前只有 `normal`。
|
||||
|
||||
- 角色图鉴:`WIKI/character/icon/${avatarId}.webp`,256x256,32位深度
|
||||
- 武器图鉴:`WIKI/weapon/icon/${weaponId}.webp`,256x256,32位深度
|
||||
- 角色图鉴:`WIKI/character/icon/${avatarId}.webp`,256x256,32 位深度
|
||||
- 武器图鉴:`WIKI/weapon/icon/${weaponId}.webp`,256x256,32 位深度
|
||||
- 卡牌图鉴:
|
||||
- 卡牌背景:`WIKI/GCG/${iconType}/${cardName}.webp`,420x720,32位深度
|
||||
- 卡牌图标:`WIKI/GCG/bg.${bgType}.webp`,420x720,32位深度
|
||||
|
||||
- 卡牌背景:`WIKI/GCG/${iconType}/${cardName}.webp`,420x720,32 位深度
|
||||
- 卡牌图标:`WIKI/GCG/bg.${bgType}.webp`,420x720,32 位深度
|
||||
|
||||
20
index.html
20
index.html
@@ -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>
|
||||
|
||||
18
latest.json
18
latest.json
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
10
src/App.vue
10
src/App.vue
@@ -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("数据文件夹创建完成!");
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ const visible = computed({
|
||||
set: (value) => emits("update:modelValue", value),
|
||||
});
|
||||
|
||||
function onCancel () {
|
||||
function onCancel() {
|
||||
visible.value = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -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>();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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>();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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(() => {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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>();
|
||||
|
||||
@@ -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>(), {
|
||||
|
||||
@@ -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>();
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
interface TAOProps {
|
||||
title: string,
|
||||
text: string | number,
|
||||
title: string;
|
||||
text: string | number;
|
||||
}
|
||||
|
||||
defineProps<TAOProps>();
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
@@ -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
@@ -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`]();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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, "-");
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -11,9 +11,11 @@ import {
|
||||
AvatarUpRateApi,
|
||||
AvatarUseRateApi,
|
||||
DataUploadApi,
|
||||
OverviewApi, TeamCombinationApi,
|
||||
OverviewApi,
|
||||
TeamCombinationApi,
|
||||
UidCheckApi,
|
||||
UidRankApi, WeaponCollocationApi,
|
||||
UidRankApi,
|
||||
WeaponCollocationApi,
|
||||
} from "./abyss";
|
||||
|
||||
const HutaoApi = {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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[];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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[];
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
|
||||
@@ -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[]> {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
// 创建图标
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user