🌱 初步完成UIGF 导入导出备份恢复,并参照优化UIAF相关代码

This commit is contained in:
BTMuli
2023-08-29 20:04:37 +08:00
parent 0033565c58
commit f961e607b0
6 changed files with 318 additions and 98 deletions

View File

@@ -1,5 +1,146 @@
<template>
<ToLoading v-model="loading" :title="loadingTitle" />
<h1>祈愿数据获取展示统计</h1>
<v-btn @click="handleImportBtn">导入</v-btn>
<v-btn @click="handleExportBtn">导出</v-btn>
</template>
<script lang="ts" setup></script>
<style lang="css" scoped></style>
<script lang="ts" setup>
// vue
import { onMounted, ref } from "vue";
import showSnackbar from "../../components/func/snackbar";
import showConfirm from "../../components/func/confirm";
import ToLoading from "../../components/overlay/to-loading.vue";
// tauri
import { dialog } from "@tauri-apps/api";
// store
import { useUserStore } from "../../store/modules/user";
// utils
import { exportUigfData, readUigfData, verifyUigfData } from "../../utils/UIGF";
import TGSqlite from "../../plugins/Sqlite";
// store
const userStore = useUserStore();
// loading
const loading = ref<boolean>(true);
const loadingTitle = ref<string>();
// data
const user = userStore.getCurAccount();
onMounted(() => {
loading.value = false;
});
// 导入按钮点击事件
async function handleImportBtn(): Promise<void> {
const selectedFile = await dialog.open({
multiple: false,
filters: [
{
name: "UIGF",
extensions: ["json"],
},
],
});
if (selectedFile) {
const check = await verifyUigfData(<string>selectedFile);
if (!check) {
showSnackbar({
color: "error",
text: `读取 UIGF 文件失败,请检查文件是否符合规范`,
});
return;
}
const remoteData = await readUigfData(<string>selectedFile);
if (remoteData.info.uid !== user.gameUid) {
await showConfirm({
title: "UID 不匹配,是否继续导入?",
text: `当前 UID${user.gameUid},导入 UID${remoteData.info.uid}`,
});
} else {
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;
await importRemoteData(remoteData);
loading.value = false;
showSnackbar({
text: `成功导入 ${remoteData.list.length} 条祈愿数据`,
});
}
} else {
showSnackbar({
color: "grey",
text: `已取消文件选择`,
});
}
}
// 导入
async function importRemoteData(data: TGApp.Plugins.UIGF.FullData): Promise<void> {
if (data.list.length === 0) {
showSnackbar({
color: "error",
text: `导入的祈愿数据为空`,
});
return;
}
await TGSqlite.mergeUIGF(user.gameUid, data.list);
}
// 导出按钮点击事件
async function handleExportBtn(): Promise<void> {
const gachaList = await TGSqlite.getGachaRecords(user.gameUid);
if (gachaList.length === 0) {
showSnackbar({
color: "error",
text: `用户 ${user.gameUid} 暂无祈愿数据`,
});
return;
}
const res = await showConfirm({
title: `是否导出祈愿数据?`,
text: `UID${user.gameUid},共 ${gachaList.length} 条数据`,
});
if (!res) {
showSnackbar({
color: "grey",
text: `已取消祈愿数据导出`,
});
return;
}
const file = await dialog.save({
defaultPath: `UIGF_${user.gameUid}.json`,
filters: [
{
name: `UIGF`,
extensions: ["json"],
},
],
});
if (!file) {
showSnackbar({
color: "grey",
text: `已取消文件保存`,
});
return;
}
loadingTitle.value = "正在导出祈愿数据";
loading.value = true;
await exportUigfData(user.gameUid, gachaList);
loading.value = false;
showSnackbar({
text: `祈愿数据已成功导出`,
});
}
</script>

View File

@@ -266,24 +266,29 @@ async function importJson(): Promise<void> {
},
],
});
if (selectedFile && (await verifyUiafData(<string>selectedFile))) {
const remoteRaw: string | false = await readUiafData(<string>selectedFile);
if (remoteRaw === false) {
showSnackbar({
color: "error",
text: "读取 UIAF 数据失败,请检查文件是否符合规范",
});
return;
}
loadingTitle.value = "正在解析数据";
loading.value = true;
loadingTitle.value = "正在合并成就数据";
await TGSqlite.mergeUIAF(JSON.parse(remoteRaw).list);
loadingTitle.value = "即将刷新页面";
setTimeout(() => {
window.location.reload();
}, 1000);
if (!selectedFile) {
showSnackbar({
color: "grey",
text: "已取消文件选择",
});
return;
}
if (!(await verifyUiafData(<string>selectedFile))) {
showSnackbar({
color: "error",
text: "读取 UIAF 数据失败,请检查文件是否符合规范",
});
return;
}
const remoteRaw = await readUiafData(<string>selectedFile);
loadingTitle.value = "正在解析数据";
loading.value = true;
loadingTitle.value = "正在合并成就数据";
await TGSqlite.mergeUIAF(remoteRaw.list);
loadingTitle.value = "即将刷新页面";
setTimeout(() => {
window.location.reload();
}, 1000);
}
// 导出

View File

@@ -1,7 +1,7 @@
<template>
<ToLoading v-model="loading" :title="loadingTitle" />
<ToLoading v-model="loading" :title="loadingTitle" :subtitle="loadingSub" />
<v-list class="config-list">
<v-list-subheader :inset="true" class="config-header"> 应用信息 </v-list-subheader>
<v-list-subheader :inset="true" class="config-header"> 应用信息</v-list-subheader>
<v-divider :inset="true" class="border-opacity-75" />
<v-list-item title="Tauri 版本" @click="toOuter('https://next--tauri.netlify.app/')">
<template #prepend>
@@ -56,7 +56,7 @@
</v-btn>
</template>
</v-list-item>
<v-list-subheader :inset="true" class="config-header"> 系统信息 </v-list-subheader>
<v-list-subheader :inset="true" class="config-header"> 系统信息</v-list-subheader>
<v-divider :inset="true" class="border-opacity-75" />
<v-list-item title="系统平台">
<template #prepend>
@@ -76,27 +76,27 @@
</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
>
{{ 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
>
{{ dbInfo.find((item) => item.key === "appVersion")?.updated }}
</v-list-item-subtitle>
</v-list-item>
<v-list-subheader :inset="true" class="config-header"> 设置 </v-list-subheader>
<v-list-subheader :inset="true" class="config-header"> 设置</v-list-subheader>
<v-divider :inset="true" class="border-opacity-75" />
<v-list-item>
<template #prepend>
@@ -124,7 +124,7 @@
<v-list-item prepend-icon="mdi-delete" title="清除用户缓存" @click="confirmDelUC" />
<v-list-item prepend-icon="mdi-delete" title="清除临时数据" @click="confirmDelTemp" />
<v-list-item prepend-icon="mdi-cog" title="恢复默认设置" @click="confirmResetApp" />
<v-list-subheader :inset="true" class="config-header"> 调试 </v-list-subheader>
<v-list-subheader :inset="true" class="config-header"> 调试</v-list-subheader>
<v-divider :inset="true" class="border-opacity-75" />
<v-list-item v-if="appStore.devEnv" title="调试模式" subtitle="开启后将显示调试信息">
<template #prepend>
@@ -162,7 +162,7 @@
prepend-icon="mdi-database-check"
@click="confirmCheckDB"
/>
<v-list-subheader :inset="true" class="config-header"> 路径 </v-list-subheader>
<v-list-subheader :inset="true" class="config-header"> 路径</v-list-subheader>
<v-divider :inset="true" class="border-opacity-75" />
<v-list-item prepend-icon="mdi-database">
<v-list-item-title>本地数据库路径</v-list-item-title>
@@ -194,6 +194,7 @@ import { useAchievementsStore } from "../../store/modules/achievements";
import { useUserStore } from "../../store/modules/user";
// utils
import { backupUiafData, restoreUiafData } from "../../utils/UIAF";
import { backupUigfData, restoreUigfData } from "../../utils/UIGF";
import { backupAbyssData, backupCookieData } from "../../web/utils/backupData";
import { restoreAbyssData, restoreCookieData } from "../../web/utils/restoreData";
import TGSqlite from "../../plugins/Sqlite";
@@ -218,6 +219,7 @@ const dbInfo = ref<Array<{ key: string; value: string; updated: string }>>([]);
// loading
const loading = ref<boolean>(true);
const loadingTitle = ref<string>("正在加载...");
const loadingSub = ref<string>("");
// data
const showHome = ref<string[]>(homeStore.getShowValue());
@@ -233,10 +235,15 @@ const userInfo = computed(() => {
// load version
onMounted(async () => {
loadingSub.value = "正在获取应用版本";
versionApp.value = await app.getVersion();
loadingSub.value = "正在获取 Tauri 版本";
versionTauri.value = await app.getTauriVersion();
loadingSub.value = "正在获取系统信息";
osPlatform.value = `${await os.platform()}`;
loadingSub.value = "正在获取系统版本";
osVersion.value = await os.version();
loadingSub.value = "正在获取数据库信息";
try {
dbInfo.value = await TGSqlite.getAppData();
const ck = dbInfo.value.find((v) => v.key === "cookie");
@@ -249,6 +256,7 @@ onMounted(async () => {
text: "读取数据库失败!",
});
}
loadingSub.value = "";
loading.value = false;
});
@@ -264,7 +272,7 @@ async function confirmRefreshUser(): Promise<void> {
text: "将会重新获取用户信息",
});
if (!res) {
snackbar({
showSnackbar({
color: "grey",
text: "已取消刷新",
});
@@ -347,7 +355,7 @@ async function confirmBackup(): Promise<void> {
text: "若已备份将会被覆盖",
});
if (!res) {
snackbar({
showSnackbar({
color: "grey",
text: "已取消备份",
});
@@ -355,12 +363,28 @@ async function confirmBackup(): Promise<void> {
}
loadingTitle.value = "正在备份数据...";
loading.value = true;
loadingSub.value = "正在获取成就数据";
const achievements = await TGSqlite.getUIAF();
loadingSub.value = "正在备份成就数据";
await backupUiafData(achievements);
loadingSub.value = "正在获取 Cookie";
const cookie = await TGSqlite.getCookie();
loadingSub.value = "正在备份 Cookie";
await backupCookieData(cookie);
loadingSub.value = "正在获取深渊数据";
const abyss = await TGSqlite.getAbyss();
loadingSub.value = "正在备份深渊数据";
await backupAbyssData(abyss);
if (userInfo.value.uid === "-1") {
loadingSub.value = "用户未登录,跳过祈愿数据备份";
} else {
loadingSub.value = "正在获取祈愿数据";
const gameUid = userStore.getCurAccount().gameUid;
const gacha = await TGSqlite.getGachaRecords(gameUid);
loadingSub.value = "正在备份祈愿数据";
await backupUigfData(gameUid, gacha);
}
loadingSub.value = "";
loading.value = false;
showSnackbar({ text: "数据已备份!" });
}
@@ -372,7 +396,7 @@ async function confirmRestore(): Promise<void> {
text: "请确保存在备份数据",
});
if (!resConfirm) {
snackbar({
showSnackbar({
color: "grey",
text: "已取消恢复",
});
@@ -380,19 +404,39 @@ async function confirmRestore(): Promise<void> {
}
loadingTitle.value = "正在恢复数据...";
loading.value = true;
const fail = [];
let res = await restoreUiafData();
const fail: string[] = [];
let res: boolean;
loadingSub.value = "正在恢复成就数据";
res = await restoreUiafData();
if (!res) {
fail.push("成就数据");
}
loadingSub.value = "正在恢复祈愿数据";
res = await restoreCookieData();
if (!res) {
fail.push("Cookie");
}
loadingSub.value = "正在恢复深渊数据";
res = await restoreAbyssData();
if (!res) {
fail.push("深渊数据");
}
if (userInfo.value.uid === "-1") {
showSnackbar({
color: "grey",
text: "用户未登录,跳过祈愿数据恢复",
});
await new Promise(() => {
setTimeout(() => {}, 1500);
});
} else {
loadingSub.value = "正在恢复祈愿数据";
const gameUid = userStore.getCurAccount().gameUid;
res = await restoreUigfData(gameUid);
if (!res) {
fail.push("祈愿数据");
}
}
fail.length > 0
? showSnackbar({ text: `${fail.join("、")} 恢复失败!`, color: "error" })
: showSnackbar({ text: "数据已恢复!" });
@@ -408,7 +452,7 @@ async function confirmUpdate(title?: string): Promise<void> {
text: "请确保存在备份数据",
});
if (!res) {
snackbar({
showSnackbar({
color: "grey",
text: "已取消更新数据库",
});
@@ -433,9 +477,9 @@ async function confirmDelUC(): Promise<void> {
text: "备份数据也将被清除",
});
if (!res) {
snackbar({
showSnackbar({
color: "grey",
text: "已取消清除",
text: "已取消清除用户缓存",
});
return;
}
@@ -454,9 +498,9 @@ async function confirmDelTemp(): Promise<void> {
title: "确认清除临时数据吗?",
});
if (!res) {
snackbar({
showSnackbar({
color: "grey",
text: "已取消清除",
text: "已取消清除临时数据",
});
return;
}
@@ -474,9 +518,9 @@ async function confirmResetApp(): Promise<void> {
title: "确认恢复默认设置吗?",
});
if (!res) {
snackbar({
showSnackbar({
color: "grey",
text: "已取消恢复",
text: "已取消恢复默认设置",
});
return;
}
@@ -495,16 +539,16 @@ async function confirmInputCK(): Promise<void> {
title: "确认手动输入 Cookie 吗?",
});
if (!res) {
snackbar({
showSnackbar({
color: "grey",
text: "已取消输入",
text: "已取消输入Cookie",
});
return;
}
if (typeof res !== "string") {
snackbar({
showSnackbar({
color: "error",
text: "参数错误!",
text: "Confirm组件类型错误!",
});
return;
}
@@ -566,7 +610,7 @@ async function confirmResetDB(title?: string): Promise<void> {
text: "请确认已经备份关键数据",
});
if (!res) {
snackbar({
showSnackbar({
color: "grey",
text: "已取消重置数据库",
});
@@ -590,7 +634,7 @@ async function confirmCheckDB(): Promise<void> {
title: "确认检测数据库完整性吗?",
});
if (!resConfirm) {
snackbar({
showSnackbar({
color: "grey",
text: "已取消检测",
});