mirror of
https://github.com/BTMuli/TeyvatGuide.git
synced 2025-12-12 09:18:14 +08:00
✨ feat(backup): 成就数据备份
This commit is contained in:
34
src/App.vue
34
src/App.vue
@@ -33,11 +33,13 @@ import TBackTop from "./components/t-backTop.vue";
|
|||||||
import { fs, window, app, event } from "@tauri-apps/api";
|
import { fs, window, app, event } from "@tauri-apps/api";
|
||||||
// store
|
// store
|
||||||
import { useAppStore } from "./store/modules/app";
|
import { useAppStore } from "./store/modules/app";
|
||||||
|
import { useAchievementsStore } from "./store/modules/achievements";
|
||||||
// utils
|
// utils
|
||||||
import { InitTGData, DeleteTGData, WriteTGData } from "./utils/TGIndex";
|
import { InitTGData, DeleteTGData, WriteTGData } from "./utils/TGIndex";
|
||||||
import { getBuildTime } from "./utils/TGBuild";
|
import { getBuildTime } from "./utils/TGBuild";
|
||||||
// data
|
// data
|
||||||
import { TGAppDataList, TGGetDataList } from "./data";
|
import { TGGetDataList } from "./data";
|
||||||
|
import { restoreUiafData } from "./utils/UIAF";
|
||||||
|
|
||||||
const appStore = useAppStore();
|
const appStore = useAppStore();
|
||||||
const isMain = ref(true as boolean);
|
const isMain = ref(true as boolean);
|
||||||
@@ -84,27 +86,19 @@ async function checkLoad () {
|
|||||||
}
|
}
|
||||||
DeleteTGData();
|
DeleteTGData();
|
||||||
await createDataDir();
|
await createDataDir();
|
||||||
await writeData();
|
|
||||||
await writeIndex();
|
await writeIndex();
|
||||||
|
await writeData();
|
||||||
appStore.loading = true;
|
appStore.loading = true;
|
||||||
console.info("数据加载完成!");
|
console.info("数据加载完成!");
|
||||||
}
|
}
|
||||||
// 创建数据文件夹
|
// 创建数据文件夹
|
||||||
async function createDataDir () {
|
async function createDataDir () {
|
||||||
console.info("开始创建数据文件夹...");
|
console.info("开始创建数据文件夹...");
|
||||||
await fs.createDir("appData", { dir: fs.BaseDirectory.AppLocalData, recursive: true });
|
// 如果不存在则创建
|
||||||
await fs.createDir("userData", { dir: fs.BaseDirectory.AppLocalData, recursive: true });
|
if (!await fs.exists("userData", { dir: fs.BaseDirectory.AppLocalData })) { await fs.createDir("userData", { dir: fs.BaseDirectory.AppLocalData, recursive: true }); }
|
||||||
await fs.createDir("tempData", { dir: fs.BaseDirectory.AppLocalData, recursive: true });
|
if (!await fs.exists("tempData", { dir: fs.BaseDirectory.AppLocalData })) { await fs.createDir("tempData", { dir: fs.BaseDirectory.AppLocalData, recursive: true }); }
|
||||||
console.info("数据文件夹创建完成!");
|
console.info("数据文件夹创建完成!");
|
||||||
}
|
}
|
||||||
// 将数据写入文件夹
|
|
||||||
async function writeData () {
|
|
||||||
console.info("开始写入数据...");
|
|
||||||
TGAppDataList.map(async (item) => {
|
|
||||||
await fs.writeFile(`${appStore.dataPath.appDataDir}\\${item.name}`, JSON.stringify(item.data));
|
|
||||||
});
|
|
||||||
console.info("数据写入完成!");
|
|
||||||
}
|
|
||||||
// 写入 IndexedDB
|
// 写入 IndexedDB
|
||||||
async function writeIndex () {
|
async function writeIndex () {
|
||||||
console.info("开始写入 IndexedDB...");
|
console.info("开始写入 IndexedDB...");
|
||||||
@@ -114,6 +108,20 @@ async function writeIndex () {
|
|||||||
});
|
});
|
||||||
console.info("IndexedDB 写入完成!");
|
console.info("IndexedDB 写入完成!");
|
||||||
}
|
}
|
||||||
|
// 恢复数据
|
||||||
|
async function writeData () {
|
||||||
|
console.info("开始恢复数据...");
|
||||||
|
const res = await restoreUiafData();
|
||||||
|
if (res !== false) {
|
||||||
|
const { total, completed } = res;
|
||||||
|
console.info("开始恢复成就数据...");
|
||||||
|
const achievementsStore = useAchievementsStore();
|
||||||
|
achievementsStore.flushData(total, completed);
|
||||||
|
console.info("成就数据恢复完成!");
|
||||||
|
} else {
|
||||||
|
console.info("未找到成就数据!");
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
<style lang="css">
|
<style lang="css">
|
||||||
.app-main {
|
.app-main {
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ import { useAchievementsStore } from "../store/modules/achievements";
|
|||||||
import { TGAppData } from "../data";
|
import { TGAppData } from "../data";
|
||||||
import { createTGWindow } from "../utils/TGWindow";
|
import { createTGWindow } from "../utils/TGWindow";
|
||||||
import { ReadAllTGData, ReadTGDataByIndex, ReadTGDataByKey, UpdateTGDataByKey } from "../utils/TGIndex";
|
import { ReadAllTGData, ReadTGDataByIndex, ReadTGDataByKey, UpdateTGDataByKey } from "../utils/TGIndex";
|
||||||
import { getUiafHeader, readUiafData, verifyUiafData } from "../utils/UIAF";
|
import { getUiafHeader, getUiafStatus, readUiafData, verifyUiafData, backupUiafData, timestampToDate } from "../utils/UIAF";
|
||||||
|
|
||||||
// Store
|
// Store
|
||||||
const achievementsStore = useAchievementsStore();
|
const achievementsStore = useAchievementsStore();
|
||||||
@@ -266,14 +266,7 @@ async function importJson () {
|
|||||||
const localTime = localData.completed_time;
|
const localTime = localData.completed_time;
|
||||||
// 如果本地数据不存在,或者本地数据的 timeStamp 小于远程数据的 timeStamp,更新数据
|
// 如果本地数据不存在,或者本地数据的 timeStamp 小于远程数据的 timeStamp,更新数据
|
||||||
if (data.timestamp !== 0) {
|
if (data.timestamp !== 0) {
|
||||||
const finishTime = new Date(data.timestamp * 1000).toLocaleString("zh", {
|
const finishTime = timestampToDate(data.timestamp);
|
||||||
year: "numeric",
|
|
||||||
month: "2-digit",
|
|
||||||
day: "2-digit",
|
|
||||||
hour: "2-digit",
|
|
||||||
minute: "2-digit",
|
|
||||||
second: "2-digit",
|
|
||||||
});
|
|
||||||
if (finishTime !== localTime || localData.progress !== data.current) {
|
if (finishTime !== localTime || localData.progress !== data.current) {
|
||||||
// eslint-disable-next-line camelcase
|
// eslint-disable-next-line camelcase
|
||||||
localData.completed_time = finishTime;
|
localData.completed_time = finishTime;
|
||||||
@@ -307,6 +300,8 @@ async function importJson () {
|
|||||||
await UpdateTGDataByKey("AchievementSeries", data);
|
await UpdateTGDataByKey("AchievementSeries", data);
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
loadingTitle.value = "正在备份数据";
|
||||||
|
await backupAchievementData();
|
||||||
loadingTitle.value = "正在刷新数据";
|
loadingTitle.value = "正在刷新数据";
|
||||||
seriesDB = await ReadAllTGData("AchievementSeries");
|
seriesDB = await ReadAllTGData("AchievementSeries");
|
||||||
const finishAchievments = seriesDB.reduce((a, b) => {
|
const finishAchievments = seriesDB.reduce((a, b) => {
|
||||||
@@ -321,6 +316,13 @@ async function importJson () {
|
|||||||
selectedIndex.value = -1;
|
selectedIndex.value = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 备份成就数据
|
||||||
|
async function backupAchievementData () {
|
||||||
|
const achievements = await ReadAllTGData("Achievements");
|
||||||
|
await backupUiafData(achievements);
|
||||||
|
}
|
||||||
|
|
||||||
// 导出
|
// 导出
|
||||||
async function exportJson () {
|
async function exportJson () {
|
||||||
// 判断是否有数据
|
// 判断是否有数据
|
||||||
@@ -340,24 +342,11 @@ async function exportJson () {
|
|||||||
};
|
};
|
||||||
// 转换数据
|
// 转换数据
|
||||||
UiafData.list = achievements.map((data) => {
|
UiafData.list = achievements.map((data) => {
|
||||||
let status;
|
|
||||||
// 计算点数但是没有完成
|
|
||||||
if (data.progress !== 0 && data.completed === false) {
|
|
||||||
status = 1;
|
|
||||||
// 已完成且未计算点数
|
|
||||||
} else if (data.progress === 0 && data.completed === true) {
|
|
||||||
status = 2;
|
|
||||||
// 已完成且已计算点数
|
|
||||||
} else if (data.progress !== 0 && data.completed === true) {
|
|
||||||
status = 3;
|
|
||||||
} else {
|
|
||||||
status = 0;
|
|
||||||
}
|
|
||||||
return {
|
return {
|
||||||
id: data.id,
|
id: data.id,
|
||||||
timestamp: data.completed ? Math.round(new Date(data.completed_time).getTime() / 1000) : 0,
|
timestamp: data.completed && data.completed_time ? Math.round(new Date(data.completed_time).getTime() / 1000) : 0,
|
||||||
current: data.progress,
|
current: data.progress,
|
||||||
status,
|
status: getUiafStatus(data.completed, data.progress),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
const isSave = await dialog.save({
|
const isSave = await dialog.save({
|
||||||
|
|||||||
@@ -65,7 +65,8 @@
|
|||||||
设置
|
设置
|
||||||
</v-list-subheader>
|
</v-list-subheader>
|
||||||
<v-divider inset class="border-opacity-75" />
|
<v-divider inset class="border-opacity-75" />
|
||||||
<v-list-item prepend-icon="mdi-folder" title="打开用户数据目录" @click="openMergeData" />
|
<v-list-item prepend-icon="mdi-content-save" title="数据备份" @click="tryConfirm('backup')" />
|
||||||
|
<v-list-item prepend-icon="mdi-content-save" title="数据恢复" @click="tryConfirm('restore')" />
|
||||||
<v-list-item prepend-icon="mdi-delete" title="清除用户缓存" @click="tryConfirm('delUser')" />
|
<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-delete" title="清除临时数据" @click="tryConfirm('delTemp')" />
|
||||||
<v-list-item prepend-icon="mdi-cog" title="恢复默认设置" @click="tryConfirm('delApp')" />
|
<v-list-item prepend-icon="mdi-cog" title="恢复默认设置" @click="tryConfirm('delApp')" />
|
||||||
@@ -120,8 +121,8 @@
|
|||||||
</v-list-subheader>
|
</v-list-subheader>
|
||||||
<v-divider inset class="border-opacity-75" />
|
<v-divider inset class="border-opacity-75" />
|
||||||
<v-list-item prepend-icon="mdi-folder">
|
<v-list-item prepend-icon="mdi-folder">
|
||||||
<v-list-item-title>本地应用数据路径</v-list-item-title>
|
<v-list-item-title>本地临时数据路径</v-list-item-title>
|
||||||
<v-list-item-subtitle>{{ appStore.dataPath.appDataDir }}</v-list-item-subtitle>
|
<v-list-item-subtitle>{{ appStore.dataPath.tempDataDir }}</v-list-item-subtitle>
|
||||||
</v-list-item>
|
</v-list-item>
|
||||||
<v-list-item prepend-icon="mdi-folder">
|
<v-list-item prepend-icon="mdi-folder">
|
||||||
<v-list-item-title>本地用户数据路径</v-list-item-title>
|
<v-list-item-title>本地用户数据路径</v-list-item-title>
|
||||||
@@ -145,14 +146,15 @@ import { getBuildTime } from "../utils/TGBuild";
|
|||||||
import TLoading from "../components/t-loading.vue";
|
import TLoading from "../components/t-loading.vue";
|
||||||
import TConfirm from "../components/t-confirm.vue";
|
import TConfirm from "../components/t-confirm.vue";
|
||||||
// tauri
|
// tauri
|
||||||
import { dialog, fs, app, os, tauri } from "@tauri-apps/api";
|
import { fs, app, os, tauri } from "@tauri-apps/api";
|
||||||
// store
|
// store
|
||||||
import { useAppStore } from "../store/modules/app";
|
import { useAppStore } from "../store/modules/app";
|
||||||
import { useHomeStore } from "../store/modules/home";
|
import { useHomeStore } from "../store/modules/home";
|
||||||
import { useHk4eStore } from "../store/modules/hk4e";
|
import { useHk4eStore } from "../store/modules/hk4e";
|
||||||
import { useAchievementsStore } from "../store/modules/achievements";
|
import { useAchievementsStore } from "../store/modules/achievements";
|
||||||
// utils
|
// utils
|
||||||
import { WriteTGData, DeleteTGData } from "../utils/TGIndex";
|
import { WriteTGData, DeleteTGData, ReadAllTGData } from "../utils/TGIndex";
|
||||||
|
import { backupUiafData, restoreUiafData } from "../utils/UIAF";
|
||||||
// data
|
// data
|
||||||
import { getDataList } from "../data/init";
|
import { getDataList } from "../data/init";
|
||||||
|
|
||||||
@@ -203,17 +205,19 @@ function toOuter (url: string) {
|
|||||||
window.open(url);
|
window.open(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 打开用户数据目录
|
|
||||||
async function openMergeData () {
|
|
||||||
await dialog.open({
|
|
||||||
defaultPath: appStore.dataPath.userDataDir,
|
|
||||||
filters: [],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// open confirm
|
// open confirm
|
||||||
function tryConfirm (oper: string) {
|
function tryConfirm (oper: string) {
|
||||||
switch (oper) {
|
switch (oper) {
|
||||||
|
case "backup":
|
||||||
|
confirmText.value = "确认备份数据吗?";
|
||||||
|
confirmOper.value = "backup";
|
||||||
|
confirmShow.value = true;
|
||||||
|
break;
|
||||||
|
case "restore":
|
||||||
|
confirmText.value = "确认恢复数据吗?";
|
||||||
|
confirmOper.value = "restore";
|
||||||
|
confirmShow.value = true;
|
||||||
|
break;
|
||||||
case "delTemp":
|
case "delTemp":
|
||||||
confirmText.value = "确认清除临时数据吗?";
|
confirmText.value = "确认清除临时数据吗?";
|
||||||
confirmOper.value = "delTemp";
|
confirmOper.value = "delTemp";
|
||||||
@@ -250,6 +254,12 @@ function tryConfirm (oper: string) {
|
|||||||
// transfer confirm oper
|
// transfer confirm oper
|
||||||
async function doConfirm (oper: string) {
|
async function doConfirm (oper: string) {
|
||||||
switch (oper) {
|
switch (oper) {
|
||||||
|
case "backup":
|
||||||
|
await backupData();
|
||||||
|
break;
|
||||||
|
case "restore":
|
||||||
|
await restoreData();
|
||||||
|
break;
|
||||||
case "delTemp":
|
case "delTemp":
|
||||||
await delTempData();
|
await delTempData();
|
||||||
break;
|
break;
|
||||||
@@ -275,6 +285,28 @@ async function doConfirm (oper: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// confirmOper
|
// confirmOper
|
||||||
|
async function backupData () {
|
||||||
|
const achievements = await ReadAllTGData("achievements");
|
||||||
|
await backupUiafData(achievements);
|
||||||
|
snackbarText.value = "数据已备份!";
|
||||||
|
snackbarColor.value = "success";
|
||||||
|
snackbar.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function restoreData () {
|
||||||
|
const res = await restoreUiafData();
|
||||||
|
if (res !== false) {
|
||||||
|
achievementsStore.flushData(res.total, res.completed);
|
||||||
|
snackbarText.value = "数据已恢复!";
|
||||||
|
snackbarColor.value = "success";
|
||||||
|
snackbar.value = true;
|
||||||
|
} else {
|
||||||
|
snackbarText.value = "未检测到备份数据!";
|
||||||
|
snackbarColor.value = "error";
|
||||||
|
snackbar.value = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function delTempData () {
|
async function delTempData () {
|
||||||
await fs.removeDir("tempData", {
|
await fs.removeDir("tempData", {
|
||||||
dir: fs.BaseDirectory.AppLocalData,
|
dir: fs.BaseDirectory.AppLocalData,
|
||||||
@@ -290,10 +322,6 @@ async function delUserData () {
|
|||||||
dir: fs.BaseDirectory.AppLocalData,
|
dir: fs.BaseDirectory.AppLocalData,
|
||||||
recursive: true,
|
recursive: true,
|
||||||
});
|
});
|
||||||
await fs.removeDir("tempData", {
|
|
||||||
dir: fs.BaseDirectory.AppLocalData,
|
|
||||||
recursive: true,
|
|
||||||
});
|
|
||||||
getDataList.map(async (item) => {
|
getDataList.map(async (item) => {
|
||||||
await WriteTGData(item.name, item.data);
|
await WriteTGData(item.name, item.data);
|
||||||
});
|
});
|
||||||
@@ -301,7 +329,6 @@ async function delUserData () {
|
|||||||
snackbar.value = true;
|
snackbar.value = true;
|
||||||
achievementsStore.init();
|
achievementsStore.init();
|
||||||
await fs.createDir("userData", { dir: fs.BaseDirectory.AppLocalData });
|
await fs.createDir("userData", { dir: fs.BaseDirectory.AppLocalData });
|
||||||
await fs.createDir("tempData", { dir: fs.BaseDirectory.AppLocalData });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 恢复默认配置
|
// 恢复默认配置
|
||||||
@@ -309,7 +336,7 @@ function initAppData () {
|
|||||||
appStore.init();
|
appStore.init();
|
||||||
homeStore.init();
|
homeStore.init();
|
||||||
achievementsStore.init();
|
achievementsStore.init();
|
||||||
snackbarText.value = "已恢复默认配置!";
|
snackbarText.value = "已恢复默认配置!即将刷新页面...";
|
||||||
snackbar.value = true;
|
snackbar.value = true;
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
|
|||||||
@@ -12,8 +12,6 @@ import { defineStore } from "pinia";
|
|||||||
// tauri
|
// tauri
|
||||||
import { path } from "@tauri-apps/api";
|
import { path } from "@tauri-apps/api";
|
||||||
|
|
||||||
// 用于存储原生数据的路径
|
|
||||||
const appDataDir = `${await path.appLocalDataDir()}appData`;
|
|
||||||
// 用于存储用户数据的路径
|
// 用于存储用户数据的路径
|
||||||
const userDataDir = `${await path.appLocalDataDir()}userData`;
|
const userDataDir = `${await path.appLocalDataDir()}userData`;
|
||||||
// 用于各种临时数据的路径
|
// 用于各种临时数据的路径
|
||||||
@@ -44,20 +42,12 @@ export const useAppStore = defineStore(
|
|||||||
const theme = ref("default");
|
const theme = ref("default");
|
||||||
|
|
||||||
const dataPath = reactive({
|
const dataPath = reactive({
|
||||||
appDataDir,
|
|
||||||
userDataDir,
|
userDataDir,
|
||||||
tempDataDir,
|
tempDataDir,
|
||||||
});
|
});
|
||||||
|
// 用户数据路径
|
||||||
// 应用数据路径
|
|
||||||
const appPath = ref({
|
|
||||||
achievements: `${dataPath.appDataDir}/achievements.json`,
|
|
||||||
achievementSeries: `${dataPath.appDataDir}/achievementSeries.json`,
|
|
||||||
nameCards: `${dataPath.appDataDir}/nameCards.json`,
|
|
||||||
});
|
|
||||||
// 用户数据路径
|
|
||||||
const userPath = ref({
|
const userPath = ref({
|
||||||
achievements: `${dataPath.userDataDir}/achievements.json`,
|
UIAF: `${dataPath.userDataDir}/UIAF.json`,
|
||||||
});
|
});
|
||||||
|
|
||||||
// 初始化
|
// 初始化
|
||||||
@@ -88,7 +78,6 @@ export const useAppStore = defineStore(
|
|||||||
sidebar,
|
sidebar,
|
||||||
devMode,
|
devMode,
|
||||||
dataPath,
|
dataPath,
|
||||||
appPath,
|
|
||||||
userPath,
|
userPath,
|
||||||
init,
|
init,
|
||||||
getSubmenu,
|
getSubmenu,
|
||||||
@@ -100,7 +89,7 @@ export const useAppStore = defineStore(
|
|||||||
{
|
{
|
||||||
key: "appPath",
|
key: "appPath",
|
||||||
storage: window.localStorage,
|
storage: window.localStorage,
|
||||||
paths: ["dataPath", "appPath", "userPath"],
|
paths: ["dataPath", "userPath"],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "app",
|
key: "app",
|
||||||
|
|||||||
@@ -2,10 +2,51 @@
|
|||||||
* @file utils UIAF.ts
|
* @file utils UIAF.ts
|
||||||
* @description UIAF工具类
|
* @description UIAF工具类
|
||||||
* @author BTMuli<bt-muli@outlook.com>
|
* @author BTMuli<bt-muli@outlook.com>
|
||||||
* @since Alpha v0.1.3
|
* @since Alpha v0.1.4
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { app, fs } from "@tauri-apps/api";
|
// tauri
|
||||||
|
import { app, fs, path } from "@tauri-apps/api";
|
||||||
|
// data
|
||||||
|
import { TGAppData } from "../data";
|
||||||
|
// utils
|
||||||
|
import { UpdateTGDataByKey } from "./TGIndex";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 时间戳转换为日期
|
||||||
|
* @since Alpha v0.1.4
|
||||||
|
* @param {number} timestamp - 时间戳
|
||||||
|
* @returns {string} 日期
|
||||||
|
*/
|
||||||
|
export function timestampToDate (timestamp: number): string {
|
||||||
|
return new Date(timestamp * 1000).toLocaleString("zh", {
|
||||||
|
year: "numeric",
|
||||||
|
month: "2-digit",
|
||||||
|
day: "2-digit",
|
||||||
|
hour: "2-digit",
|
||||||
|
minute: "2-digit",
|
||||||
|
second: "2-digit",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 根据 completed 跟 progress 获取 status
|
||||||
|
* @since Alpha v0.1.4
|
||||||
|
* @param {boolean} completed - 是否完成
|
||||||
|
* @param {number} progress - 进度
|
||||||
|
* @returns {number} status
|
||||||
|
*/
|
||||||
|
export function getUiafStatus (completed: boolean, progress: number): number {
|
||||||
|
if (progress !== 0 && !completed) {
|
||||||
|
return 1;
|
||||||
|
} else if (progress === 0 && completed) {
|
||||||
|
return 2;
|
||||||
|
} else if (progress !== 0 && completed) {
|
||||||
|
return 3;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description 获取 UIAF 头部信息
|
* @description 获取 UIAF 头部信息
|
||||||
@@ -56,3 +97,70 @@ export async function readUiafData (userPath: string): Promise<string | false> {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 根据成就数据导出 UIAF 数据
|
||||||
|
* @since Alpha v0.1.4
|
||||||
|
* @param {BTMuli.Genshin.Achievement[]} achievementData - 成就数据
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
export async function backupUiafData (achievementData: BTMuli.Genshin.Achievement[]): Promise<void> {
|
||||||
|
const res = [] as TGPlugin.UIAF.Achievement[];
|
||||||
|
const savePath = `${await path.appLocalDataDir()}\\userData\\UIAF.json`;
|
||||||
|
achievementData.forEach((achievement) => {
|
||||||
|
if (achievement.completed || achievement.progress !== 0) {
|
||||||
|
return res.push({
|
||||||
|
id: achievement.id,
|
||||||
|
timestamp: achievement.completed && achievement.completed_time ? Math.round(new Date(achievement.completed_time).getTime() / 1000) : 0,
|
||||||
|
current: achievement.progress,
|
||||||
|
status: getUiafStatus(achievement.completed, achievement.progress),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
await fs.writeTextFile(savePath, JSON.stringify(res, null, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 根据 UIAF 数据恢复成就数据
|
||||||
|
* @since Alpha v0.1.4
|
||||||
|
* @returns {Promise<{total: number, completed: number}> | false} 恢复的成就数量
|
||||||
|
*/
|
||||||
|
export async function restoreUiafData (): Promise<{ total: number, completed: number } | false> {
|
||||||
|
const uiafPath = `${await path.appLocalDataDir()}\\userData\\UIAF.json`;
|
||||||
|
// 检测是否存在 UIAF 数据
|
||||||
|
if (!await fs.exists(uiafPath)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const uiafData = JSON.parse(await fs.readTextFile(uiafPath)) as TGPlugin.UIAF.Achievement[];
|
||||||
|
const achievementData = TGAppData.achievements;
|
||||||
|
const seriesData = TGAppData.achievementSeries;
|
||||||
|
uiafData.forEach((uiafAchievement) => {
|
||||||
|
// 更新成就数据
|
||||||
|
const localAchievement = achievementData[uiafAchievement.id];
|
||||||
|
localAchievement.completed = uiafAchievement.status === 2 || uiafAchievement.status === 3;
|
||||||
|
localAchievement.progress = uiafAchievement.current;
|
||||||
|
// eslint-disable-next-line camelcase
|
||||||
|
localAchievement.completed_time = uiafAchievement.timestamp !== 0 ? timestampToDate(uiafAchievement.timestamp) : "";
|
||||||
|
UpdateTGDataByKey("Achievements", localAchievement);
|
||||||
|
// 更新成就系列数据
|
||||||
|
if (localAchievement.completed) {
|
||||||
|
const localSeries = seriesData[localAchievement.series];
|
||||||
|
// eslint-disable-next-line camelcase
|
||||||
|
localSeries.completed_count += 1;
|
||||||
|
seriesData[localAchievement.series] = localSeries;
|
||||||
|
UpdateTGDataByKey("AchievementSeries", localSeries);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// 获取 total 和 completed
|
||||||
|
let total = 0;
|
||||||
|
let completed = 0;
|
||||||
|
Object.values(seriesData).forEach((series) => {
|
||||||
|
total += series.total_count;
|
||||||
|
completed += series.completed_count;
|
||||||
|
});
|
||||||
|
// 返回
|
||||||
|
return {
|
||||||
|
total,
|
||||||
|
completed,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user