从Hakushi获取动态字典

#183
This commit is contained in:
BTMuli
2025-12-14 02:34:21 +08:00
parent 53174ffdd7
commit 1b2399822c
4 changed files with 185 additions and 22 deletions

View File

@@ -52,7 +52,8 @@
{ "url": "https://*.miyoushe.com/*" },
{ "url": "https://*.mihoyo.com/*" },
{ "url": "https://homa.snapgenshin.com/*" },
{ "url": "https://*.hoyoverse.com/*" }
{ "url": "https://*.hoyoverse.com/*" },
{ "url": "https://api.hakush.in/*" }
]
},
{

View File

@@ -3,42 +3,43 @@
<v-app-bar>
<template #prepend>
<div class="gacha-top-title">
<img src="/source/UI/userGacha.webp" alt="gacha" />
<img alt="gacha" src="/source/UI/userGacha.webp" />
<span>祈愿记录</span>
<v-select
:hide-details="true"
density="compact"
v-model="uidCur"
:hide-details="true"
:items="selectItem"
variant="outlined"
density="compact"
label="游戏UID"
variant="outlined"
/>
<img
alt="byd"
class="gacha-top-byd"
src="/icon/nation/千星奇域.webp"
alt="byd"
@click="toBeyond()"
title="千星奇域"
@click="toBeyond()"
/>
</div>
</template>
<template #extension>
<div class="gacha-top-btns">
<v-btn prepend-icon="mdi-refresh" class="gacha-top-btn" @click="confirmRefresh(false)">
<v-btn class="gacha-top-btn" prepend-icon="mdi-refresh" @click="confirmRefresh(false)">
增量刷新
</v-btn>
<v-btn prepend-icon="mdi-refresh" class="gacha-top-btn" @click="confirmRefresh(true)">
<v-btn class="gacha-top-btn" prepend-icon="mdi-refresh" @click="confirmRefresh(true)">
全量刷新
</v-btn>
<v-btn prepend-icon="mdi-import" class="gacha-top-btn" @click="importUigf()">导入</v-btn>
<v-btn prepend-icon="mdi-import" class="gacha-top-btn" @click="importUigf4()">
<v-btn class="gacha-top-btn" prepend-icon="mdi-import" @click="importUigf()">导入</v-btn>
<v-btn class="gacha-top-btn" prepend-icon="mdi-import" @click="importUigf4()">
导入(v4)
</v-btn>
<v-btn prepend-icon="mdi-export" class="gacha-top-btn" @click="exportUigf()">导出</v-btn>
<v-btn prepend-icon="mdi-export" class="gacha-top-btn" @click="exportUigf4()">
<v-btn class="gacha-top-btn" prepend-icon="mdi-export" @click="exportUigf()">导出</v-btn>
<v-btn class="gacha-top-btn" prepend-icon="mdi-export" @click="exportUigf4()">
导出(v4)
</v-btn>
<v-btn prepend-icon="mdi-delete" class="gacha-top-btn" @click="deleteGacha()">删除</v-btn>
<v-btn class="gacha-top-btn" prepend-icon="mdi-delete" @click="deleteGacha()">删除</v-btn>
<v-btn class="gacha-top-btn" prepend-icon="mdi-delete" @click="checkData()">检测数据</v-btn>
</div>
</template>
</v-app-bar>
@@ -49,22 +50,22 @@
<v-tab value="table">数据表格</v-tab>
<v-tab value="history">过往祈愿</v-tab>
<!-- TODO: 暂时隐藏内置祈愿链接 -->
<v-tab value="iframe" v-if="false">祈愿详情</v-tab>
<v-tab v-if="false" value="iframe">祈愿详情</v-tab>
</v-tabs>
<v-window v-model="tab" class="gacha-window">
<v-window-item value="overview" class="gacha-window-item">
<v-window-item class="gacha-window-item" value="overview">
<gro-overview v-model="gachaListCur" />
</v-window-item>
<v-window-item value="echarts" class="gacha-window-item">
<gro-echarts :uid="uidCur" v-if="uidCur" />
<v-window-item class="gacha-window-item" value="echarts">
<gro-echarts v-if="uidCur" :uid="uidCur" />
</v-window-item>
<v-window-item value="table" class="gacha-window-item">
<v-window-item class="gacha-window-item" value="table">
<gro-table v-model="gachaListCur" />
</v-window-item>
<v-window-item value="history" class="gacha-window-item">
<v-window-item class="gacha-window-item" value="history">
<gro-history />
</v-window-item>
<v-window-item value="iframe" class="gacha-window-item">
<v-window-item class="gacha-window-item" value="iframe">
<gro-iframe mode="normal" />
</v-window-item>
</v-window>
@@ -88,6 +89,7 @@ import TSUserGacha from "@Sqlm/userGacha.js";
import useUserStore from "@store/user.js";
import { path } from "@tauri-apps/api";
import { open, save } from "@tauri-apps/plugin-dialog";
import Hakushi from "@utils/Hakushi.js";
import TGLogger from "@utils/TGLogger.js";
import { exportUigfData, readUigfData, verifyUigfData } from "@utils/UIGF.js";
import { storeToRefs } from "pinia";
@@ -108,9 +110,12 @@ const ovShow = ref<boolean>(false);
const ovMode = ref<"export" | "import">("import");
const selectItem = shallowRef<Array<string>>([]);
const gachaListCur = shallowRef<Array<TGApp.Sqlite.GachaRecords.TableGacha>>([]);
const hakushiData = shallowRef<Array<TGApp.Plugins.Hakushi.ConvertData>>([]);
onMounted(async () => {
await showLoading.start("正在加载祈愿数据", "正在获取祈愿 UID 列表");
await showLoading.start("正在加载祈愿数据", "正在获取Hakushi元数据");
hakushiData.value = await Hakushi.fetch();
await showLoading.update("正在获取祈愿 UID 列表");
await TGLogger.Info("[UserGacha][onMounted] 进入角色祈愿页面");
selectItem.value = await TSUserGacha.getUidList();
if (selectItem.value.length === 0) {
@@ -259,6 +264,16 @@ async function refreshGachaPool(
const find = AppWeaponData.find((weapon) => weapon.name === item.name);
if (find) tempItem.item_id = find.id.toString();
}
if (tempItem.item_id === "") {
const find = hakushiData.value.find(
(i) => i.type === item.item_type && i.name === item.name,
);
if (find) tempItem.item_id = find.id.toString();
else {
showSnackbar.warn(`无法搜索到 ${item.item_type} ${item.name} 的ID请等待元数据更新`);
continue;
}
}
uigfList.push(tempItem);
if (force) {
if (!gachaDataMap) gachaDataMap = {};
@@ -388,6 +403,11 @@ async function deleteGacha(): Promise<void> {
await new Promise<void>((resolve) => setTimeout(resolve, 1500));
window.location.reload();
}
async function checkData(): Promise<void> {
// TODO: 读取当前UID数据并补充itemId
showSnackbar.warn("尚未实现");
}
</script>
<style lang="css" scoped>
.gacha-top-title {

90
src/types/Plugins/Hakushi.d.ts vendored Normal file
View File

@@ -0,0 +1,90 @@
/**
* Hakushi插件相关类型
* @since Beta v0.9.0
*/
declare namespace TGApp.Plugins.Hakushi {
/**
* 武器JSON返回
* @since Beta v0.9.0
* @remarks https://api.hakush.in/gi/data/weapon.json
*/
type WeaponResp = Record<string, WeaponBrief>;
/**
* 角色JSON返回
* @since Beta v0.9.0
* @remarks https://api.hakush.in/gi/data/character.json
*/
type AvatarResp = Record<string, AvatarBrief>;
/**
* 武器信息
* @since Beta v0.9.0
*/
type WeaponBrief = {
/** 图标 */
icon: string;
/** 稀有度 */
rank: number;
/**
* 类型
* @remarks 枚举
* - WEAPON_BOW 弓
*/
type: string;
/** 英文译名 */
EN: string;
/** 描述(英文) */
desc: string;
/** 韩文译名 */
KR: string;
/** 中文译名 */
CHS: string;
/** 日文译名 */
JP: string;
};
/**
* 角色信息
* @since Beta v0.9.0
*/
type AvatarBrief = {
/** 生日 */
birth: Array<number> & { length: 2 };
/** 图标 */
icon: string;
/** 稀有度 */
rank: number;
/**
* 武器类型
* @remarks 枚举
* - WEAPON_BOW 弓
*/
weapon: string;
/** 上线时间 */
release: string;
/** 元素 */
element: string;
/** 英文译名 */
EN: string;
/** 描述(英文) */
desc: string;
/** 韩文译名 */
KR: string;
/** 中文译名 */
CHS: string;
/** 日文译名 */
JP: string;
};
/**
* 转换后的数据
* @since Beta v0.9.0
*/
type ConvertData = {
id: string;
name: string;
type: "武器" | "角色";
};
}

52
src/utils/Hakushi.ts Normal file
View File

@@ -0,0 +1,52 @@
/**
* Hakushi 插件入口
* @since Beta v0.9.0
*/
import TGHttp from "@utils/TGHttp.js";
const HAKUSHI_API = "https://api.hakush.in/gi/data/";
/**
* 请求角色数据
* @since Beta v0.9.0
* @returns {Promise<TGApp.Plugins.Hakushi.AvatarResp>}
*/
async function fetchAvatar(): Promise<TGApp.Plugins.Hakushi.AvatarResp> {
return await TGHttp<TGApp.Plugins.Hakushi.AvatarResp>(`${HAKUSHI_API}character.json`, {
method: "GET",
});
}
/**
* 请求武器数据
* @since Beta v0.9.0
* @returns {Promise<TGApp.Plugins.Hakushi.WeaponResp>}
*/
async function fetchWeapon(): Promise<TGApp.Plugins.Hakushi.WeaponResp> {
return await TGHttp<TGApp.Plugins.Hakushi.WeaponResp>(`${HAKUSHI_API}weapon.json`, {
method: "GET",
});
}
/**
* 转换数据
* @since Beta v0.9.0
*/
async function fetchJson(): Promise<Array<TGApp.Plugins.Hakushi.ConvertData>> {
const jsonW = await fetchWeapon();
const jsonA = await fetchAvatar();
const res: Array<TGApp.Plugins.Hakushi.ConvertData> = [];
for (const [id, data] of Object.entries(jsonW)) {
res.push({ id: id.toString(), name: data.CHS, type: "武器" });
}
for (const [id, data] of Object.entries(jsonA)) {
res.push({ id: id.toString(), name: data.CHS, type: "角色" });
}
return res;
}
const Hakushi = {
fetch: fetchJson,
};
export default Hakushi;