Files
TeyvatGuide/src/pages/User/Gacha.vue
2023-12-29 23:05:26 +08:00

497 lines
14 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<ToLoading v-model="loading" :title="loadingTitle" :subtitle="loadingSub" />
<div class="gacha-top-bar">
<div class="gacha-top-title">祈愿记录</div>
<v-select v-model="uidCur" class="gacha-top-select" :items="selectItem" variant="outlined" />
<div class="gacha-top-btns">
<v-btn prepend-icon="mdi-refresh" class="gacha-top-btn" @click="confirmRefresh">刷新</v-btn>
<v-btn prepend-icon="mdi-import" class="gacha-top-btn" @click="handleImportBtn()">
导入
</v-btn>
<v-btn prepend-icon="mdi-export" class="gacha-top-btn" @click="handleExportBtn"> 导出</v-btn>
</div>
</div>
<div class="gacha-container">
<v-tabs v-model="tab" align-tabs="start" class="gacha-tab" direction="vertical">
<v-tab value="echarts">图表概览</v-tab>
<v-tab value="overview">数据概览</v-tab>
<div class="gacha-tab-bottom">
<v-btn class="gacha-tab-btn" @click="backupGacha">
<v-icon>mdi-cloud-download</v-icon>
<span>备份</span>
</v-btn>
<v-btn class="gacha-tab-btn" @click="deleteGacha">
<v-icon>mdi-delete</v-icon>
<span>删除</span>
</v-btn>
<v-btn class="gacha-tab-btn" @click="restoreGacha">
<v-icon>mdi-cloud-upload</v-icon>
<span>恢复</span>
</v-btn>
</div>
</v-tabs>
<v-window v-model="tab" class="gacha-window">
<v-window-item value="echarts" class="gacha-window-item">
<gro-echarts v-model="gachaListCur" />
</v-window-item>
<v-window-item value="overview" class="gacha-window-item">
<gro-overview v-model="gachaListCur" />
</v-window-item>
</v-window>
</div>
</template>
<script lang="ts" setup>
import { dialog, fs, path } from "@tauri-apps/api";
import { storeToRefs } from "pinia";
import { onMounted, ref, watch } from "vue";
import showConfirm from "../../components/func/confirm";
import showSnackbar from "../../components/func/snackbar";
import GroEcharts from "../../components/gachaRecord/gro-echarts.vue";
import GroOverview from "../../components/gachaRecord/gro-overview.vue";
import ToLoading from "../../components/overlay/to-loading.vue";
import { AppCharacterData, AppWeaponData } from "../../data";
import TGSqlite from "../../plugins/Sqlite";
import { useUserStore } from "../../store/modules/user";
import { backupUigfData, exportUigfData, readUigfData, verifyUigfData } from "../../utils/UIGF";
import TGRequest from "../../web/request/TGRequest";
// store
const userStore = storeToRefs(useUserStore());
const account = userStore.account.value;
const authkey = ref<string>("");
// loading
const loading = ref<boolean>(true);
const loadingTitle = ref<string>();
const loadingSub = ref<string>();
// data
const selectItem = ref<string[]>([]);
const uidCur = ref<string>("");
const gachaListCur = ref<TGApp.Sqlite.GachaRecords.SingleTable[]>([]);
const tab = ref<string>("");
onMounted(async () => {
loadingTitle.value = "正在获取祈愿 UID 列表";
selectItem.value = await TGSqlite.getUidList();
if (selectItem.value.length === 0) {
showSnackbar({
color: "error",
text: "暂无祈愿数据,请先导入祈愿数据",
});
loading.value = false;
return;
}
uidCur.value = selectItem.value[0];
loadingTitle.value = `正在获取祈愿数据,默认 UID${uidCur.value}`;
gachaListCur.value = await TGSqlite.getGachaRecords(uidCur.value);
loadingTitle.value = "正在渲染数据";
tab.value = "echarts";
loading.value = false;
showSnackbar({
text: `成功获取 ${gachaListCur.value.length} 条祈愿数据`,
});
});
// 刷新按钮点击事件
async function confirmRefresh(): Promise<void> {
const confirmRes = await showConfirm({
title: "是否刷新祈愿数据?",
text: `将刷新 UID${account.gameUid} 的祈愿数据`,
});
if (!confirmRes) {
showSnackbar({
color: "grey",
text: "已取消刷新祈愿数据",
});
return;
}
loadingTitle.value = "正在获取 authkey";
loading.value = true;
if (!userStore.cookie.value) {
showSnackbar({
color: "error",
text: "请先登录",
});
loading.value = false;
return;
}
const cookie = {
stoken: userStore.cookie.value.stoken,
mid: userStore.cookie.value.mid,
};
const gameUid = account.gameUid;
const authkeyRes = await TGRequest.User.getAuthkey(cookie, gameUid);
if (typeof authkeyRes === "string") {
authkey.value = authkeyRes;
} else {
showSnackbar({
color: "error",
text: "获取 authkey 失败",
});
return;
}
loadingTitle.value = "正在刷新新手祈愿数据";
await getGachaLogs("100");
loadingTitle.value = "正在刷新常驻祈愿数据";
await getGachaLogs("200");
loadingTitle.value = "正在刷新角色祈愿数据";
await getGachaLogs("301");
loadingTitle.value = "正在刷新角色祈愿2数据";
await getGachaLogs("400");
loadingTitle.value = "正在刷新武器祈愿数据";
await getGachaLogs("302");
loadingTitle.value = "数据获取完成,即将刷新页面";
loadingSub.value = "";
loading.value = false;
await new Promise((resolve) => {
setTimeout(() => {
resolve("");
}, 1000);
});
window.location.reload();
}
// 获取祈愿数据并写入数据库
async function getGachaLogs(pool: string, endId: string = "0"): Promise<void> {
const gachaRes = await TGRequest.User.getGachaLog(authkey.value, pool, endId);
console.log(pool, endId, gachaRes);
if (Array.isArray(gachaRes)) {
const uigfList: TGApp.Plugins.UIGF.GachaItem[] = [];
gachaRes.forEach((item) => {
loadingSub.value = `[${item.item_type}][${item.time}] ${item.name}`;
const tempItem: TGApp.Plugins.UIGF.GachaItem = {
gacha_type: item.gacha_type,
item_id: item.item_id,
count: item.count,
time: item.time,
name: item.name,
item_type: item.item_type,
rank_type: item.rank_type,
id: item.id,
uigf_gacha_type: item.gacha_type === "400" ? "301" : item.gacha_type,
};
if (item.item_type === "角色") {
const find = AppCharacterData.find((char) => char.name === item.name);
if (find) tempItem.item_id = find.id.toString();
} else if (item.item_type === "武器") {
const find = AppWeaponData.find((weapon) => weapon.name === item.name);
if (find) tempItem.item_id = find.id.toString();
}
uigfList.push(tempItem);
});
await TGSqlite.mergeUIGF(account.gameUid, uigfList);
if (gachaRes.length === 20) {
await new Promise((resolve) => {
setTimeout(() => {
resolve("");
}, 1000);
});
await getGachaLogs(pool, gachaRes[gachaRes.length - 1].id);
}
} else {
showSnackbar({
color: "error",
text: `[${pool}][${gachaRes.retcode}] ${gachaRes.message}`,
});
}
}
// 导入按钮点击事件
async function handleImportBtn(savePath?: string): Promise<void> {
let selectedFile;
if (savePath) {
selectedFile = await dialog.open({
multiple: false,
title: "选择要导入的祈愿数据文件",
filters: [
{
name: "UIGF JSON",
extensions: ["json"],
},
],
defaultPath: savePath,
directory: false,
});
} else {
selectedFile = await dialog.open({
multiple: false,
title: "选择要导入的祈愿数据文件",
filters: [
{
name: "UIGF JSON",
extensions: ["json"],
},
],
defaultPath: `${await path.downloadDir()}`,
directory: false,
});
}
if (selectedFile) {
const check = await verifyUigfData(<string>selectedFile);
if (!check) {
showSnackbar({
color: "error",
text: "读取 UIGF 文件失败,请检查文件是否符合规范",
});
return;
}
const remoteData = await readUigfData(<string>selectedFile);
const res = await showConfirm({
title: "是否导入祈愿数据?",
text: `UID${remoteData.info.uid}${remoteData.list.length} 条数据`,
});
if (!res) {
showSnackbar({
color: "grey",
text: "已取消祈愿数据导入",
});
return;
}
loadingTitle.value = "正在导入祈愿数据";
loading.value = true;
if (remoteData.list.length === 0) {
loading.value = false;
showSnackbar({
color: "error",
text: "导入的祈愿数据为空",
});
} else {
await TGSqlite.mergeUIGF(remoteData.info.uid, remoteData.list);
loading.value = false;
showSnackbar({
text: `成功导入 ${remoteData.list.length} 条祈愿数据`,
});
setTimeout(() => {
window.location.reload();
}, 1000);
}
} else {
showSnackbar({
color: "grey",
text: "已取消文件选择",
});
}
}
// 导出按钮点击事件
async function handleExportBtn(): Promise<void> {
const gachaList = await TGSqlite.getGachaRecords(uidCur.value);
if (gachaList.length === 0) {
showSnackbar({
color: "error",
text: `UID ${uidCur.value} 暂无祈愿数据`,
});
return;
}
const res = await showConfirm({
title: "是否导出祈愿数据?",
text: `UID${uidCur.value},共 ${gachaList.length} 条数据`,
});
if (!res) {
showSnackbar({
color: "grey",
text: "已取消祈愿数据导出",
});
return;
}
const file = await dialog.save({
title: "选择导出祈愿数据的文件路径",
filters: [
{
name: "UIGF JSON",
extensions: ["json"],
},
],
defaultPath: `${await path.downloadDir()}${path.sep}UIGF${uidCur.value}.json`,
});
if (!file) {
showSnackbar({
color: "grey",
text: "已取消文件保存",
});
return;
}
loadingTitle.value = "正在导出祈愿数据";
loading.value = true;
await exportUigfData(uidCur.value, gachaList, file);
loading.value = false;
showSnackbar({
text: "祈愿数据已成功导出",
});
}
// 恢复UID祈愿数据相当于导入祈愿数据不过目录固定
async function restoreGacha(): Promise<void> {
const backupPath = `${await path.appLocalDataDir()}userData`;
await handleImportBtn(backupPath);
}
// 备份当前 UID 的祈愿数据
async function backupGacha(): Promise<void> {
if (gachaListCur.value.length === 0) {
showSnackbar({
color: "error",
text: "暂无祈愿数据",
});
return;
}
const res = await showConfirm({
title: "是否备份祈愿数据?",
text: `UID${uidCur.value},共 ${gachaListCur.value.length} 条数据`,
});
if (!res) {
showSnackbar({
color: "grey",
text: "已取消祈愿数据备份",
});
return;
}
loadingTitle.value = "正在备份祈愿数据";
loading.value = true;
if (!(await fs.exists("userData", { dir: fs.BaseDirectory.AppLocalData }))) {
await fs.createDir("userData", { dir: fs.BaseDirectory.AppLocalData, recursive: true });
}
await backupUigfData(uidCur.value, gachaListCur.value);
loading.value = false;
showSnackbar({
text: `已成功备份 ${uidCur.value} 的祈愿数据`,
});
}
// 删除当前 UID 的祈愿数据
async function deleteGacha(): Promise<void> {
if (gachaListCur.value.length === 0) {
showSnackbar({
color: "error",
text: "暂无祈愿数据",
});
return;
}
const firstConfirm = await showConfirm({
title: "是否删除祈愿数据?",
text: `UID${uidCur.value},共 ${gachaListCur.value.length} 条数据`,
});
if (!firstConfirm) {
showSnackbar({
color: "grey",
text: "已取消祈愿数据删除",
});
return;
}
const uidList = await TGSqlite.getUidList();
let secondConfirm: string | boolean | undefined;
if (uidList.length <= 1) {
secondConfirm = await showConfirm({
title: "删除后数据库将为空,确定删除?",
text: `UID${uidCur.value},共 ${gachaListCur.value.length} 条数据`,
});
}
if (!secondConfirm) {
showSnackbar({
color: "grey",
text: "已取消祈愿数据删除",
});
return;
}
loadingTitle.value = `正在删除${uidCur.value}的祈愿数据`;
loading.value = true;
await TGSqlite.deleteGachaRecords(uidCur.value);
loading.value = false;
showSnackbar({
text: `已成功删除 ${uidCur.value} 的祈愿数据`,
});
setTimeout(() => {
window.location.reload();
}, 1000);
}
// 监听 UID 变化
watch(uidCur, async (newUid) => {
gachaListCur.value = await TGSqlite.getGachaRecords(newUid);
showSnackbar({
text: `成功获取 ${gachaListCur.value.length} 条祈愿数据`,
});
});
</script>
<style lang="css" scoped>
.gacha-top-bar {
display: flex;
width: 100%;
align-items: center;
justify-content: space-between;
padding: 10px;
border-radius: 5px;
margin-bottom: 10px;
background: var(--box-bg-1);
column-gap: 50px;
font-family: var(--font-title);
font-size: 20px;
}
.gacha-top-title {
color: var(--common-text-title);
}
.gacha-top-select {
height: 60px;
}
.gacha-top-btns {
display: flex;
column-gap: 10px;
}
.gacha-top-btn {
border-radius: 5px;
background: var(--tgc-btn-1);
color: var(--btn-text);
}
.gacha-container {
display: flex;
height: calc(100vh - 130px);
align-items: center;
justify-content: left;
border: 1px solid var(--common-shadow-1);
border-radius: 5px;
background: var(--box-bg-1);
}
.gacha-tab {
width: 100px;
height: 100%;
color: var(--box-text-4);
font-family: var(--font-title);
}
.gacha-tab-bottom {
position: absolute;
bottom: 0;
display: flex;
width: 100%;
flex-direction: column;
padding: 10px;
gap: 10px;
}
.gacha-tab-btn {
border-radius: 5px;
background: var(--tgc-btn-1);
color: var(--btn-text);
}
.gacha-window {
width: 100%;
height: 100%;
padding: 10px;
}
.gacha-window-item {
height: 100%;
border-radius: 5px;
overflow-y: auto;
}
</style>