mirror of
https://github.com/BTMuli/TeyvatGuide.git
synced 2025-12-12 09:18:14 +08:00
✨ feat(db): 数据源更改
This commit is contained in:
@@ -6,12 +6,26 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { initDatabase, resetDatabase, checkDatabase, sqlitePath } from "./init";
|
import { initDatabase, resetDatabase, checkDatabase, sqlitePath } from "./init";
|
||||||
|
import { getAllSeries, getAchievementsBySeries, searchAchievement, getAchievementOverview } from "./select";
|
||||||
|
import { importUIAFData, exportUIAFData } from "./UIAF";
|
||||||
import { checkAchievement, checkAchievementSeries } from "./update";
|
import { checkAchievement, checkAchievementSeries } from "./update";
|
||||||
|
|
||||||
const TGSqlite = {
|
const TGSqlite = {
|
||||||
initDB: initDatabase,
|
initDB: initDatabase,
|
||||||
resetDB: resetDatabase,
|
resetDB: resetDatabase,
|
||||||
checkDB: checkDatabase,
|
checkDB: checkDatabase,
|
||||||
|
search: {
|
||||||
|
achievement: {
|
||||||
|
bySeries: getAchievementsBySeries,
|
||||||
|
bySearch: searchAchievement,
|
||||||
|
},
|
||||||
|
achievementSeries: getAllSeries,
|
||||||
|
overview: getAchievementOverview,
|
||||||
|
},
|
||||||
|
UIAF: {
|
||||||
|
import: importUIAFData,
|
||||||
|
export: exportUIAFData,
|
||||||
|
},
|
||||||
update: {
|
update: {
|
||||||
achievement: checkAchievement,
|
achievement: checkAchievement,
|
||||||
achievementSeries: checkAchievementSeries,
|
achievementSeries: checkAchievementSeries,
|
||||||
|
|||||||
86
src/core/database/UIAF.ts
Normal file
86
src/core/database/UIAF.ts
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
/**
|
||||||
|
* @file core database UIAF.ts
|
||||||
|
* @description UIAF 数据导入导出
|
||||||
|
* @author BTMuli<bt-muli@outlook.com>
|
||||||
|
* @since Alpha v0.1.4
|
||||||
|
*/
|
||||||
|
|
||||||
|
// tauri
|
||||||
|
import Database from "tauri-plugin-sql-api";
|
||||||
|
// local
|
||||||
|
import { sqlitePath } from "./init";
|
||||||
|
// utils
|
||||||
|
import { timestampToDate, getUiafStatus } from "../../utils/UIAF";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 根据本地数据跟导入数据获取更新 SQL
|
||||||
|
* @since Alpha v0.1.4
|
||||||
|
* @param {TGPlugin.UIAF.Achievement} importData - 导入数据
|
||||||
|
* @param {BTMuli.SQLite.Achievements} localData - 本地数据
|
||||||
|
* @returns {string} SQL
|
||||||
|
*/
|
||||||
|
function getUpdateSql (importData: TGPlugin.UIAF.Achievement, localData: BTMuli.SQLite.Achievements): string {
|
||||||
|
// 如果本地为未完成状态,直接更新
|
||||||
|
if (localData.isCompleted === 0) {
|
||||||
|
// 已完成
|
||||||
|
if (importData.status === 2 || importData.status === 3) {
|
||||||
|
const completedTime = timestampToDate(importData.timestamp);
|
||||||
|
return `UPDATE Achievements SET isCompleted = 1, progress = ${importData.current}, completedTime = '${completedTime}' WHERE id = ${importData.id}`;
|
||||||
|
} else if (importData.current > localData.progress) {
|
||||||
|
return `UPDATE Achievements SET progress = ${importData.current} WHERE id = ${importData.id}`;
|
||||||
|
} else {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 本地为已完成状态,判断进度
|
||||||
|
if (importData.current > localData.progress) {
|
||||||
|
// 进度大于本地进度,更新
|
||||||
|
return `UPDATE Achievements SET progress = ${importData.current} WHERE id = ${importData.id}`;
|
||||||
|
} else {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 导入 UIAF 数据,更新数据库
|
||||||
|
* @since Alpha v0.1.4
|
||||||
|
* @param {TGPlugin.UIAF.Achievement[]} achievements - 成就列表
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
export async function importUIAFData (achievements: TGPlugin.UIAF.Achievement[]): Promise<void> {
|
||||||
|
const db = await Database.load(sqlitePath);
|
||||||
|
for (const achievement of achievements) {
|
||||||
|
const id = achievement.id;
|
||||||
|
const selects: BTMuli.SQLite.Achievements[] = await db.select(`SELECT * FROM Achievements WHERE id = ${id}`);
|
||||||
|
if (selects.length === 1) {
|
||||||
|
const sql = getUpdateSql(achievement, selects[0]);
|
||||||
|
if (sql !== "") await db.execute(sql);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await db.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 导出 UIAF 数据
|
||||||
|
* @since Alpha v0.1.4
|
||||||
|
* @returns {Promise<TGPlugin.UIAF.Achievement[]>}
|
||||||
|
*/
|
||||||
|
export async function exportUIAFData (): Promise<TGPlugin.UIAF.Achievement[]> {
|
||||||
|
const db = await Database.load(sqlitePath);
|
||||||
|
const sql = "SELECT * FROM Achievements WHERE isCompleted = 1 OR progress > 0";
|
||||||
|
const selects: BTMuli.SQLite.Achievements[] = await db.select(sql);
|
||||||
|
await db.close();
|
||||||
|
const achievements: TGPlugin.UIAF.Achievement[] = [];
|
||||||
|
for (const select of selects) {
|
||||||
|
const completed = select.isCompleted === 1;
|
||||||
|
const status = getUiafStatus(completed, select.progress);
|
||||||
|
achievements.push({
|
||||||
|
id: select.id,
|
||||||
|
status,
|
||||||
|
timestamp: completed && select.completedTime ? Math.round(new Date(select.completedTime).getTime() / 1000) : 0,
|
||||||
|
current: select.progress,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return achievements;
|
||||||
|
}
|
||||||
@@ -29,7 +29,7 @@ CREATE TABLE IF NOT EXISTS AchievementSeries
|
|||||||
version TEXT DEFAULT NULL,
|
version TEXT DEFAULT NULL,
|
||||||
totalCount INTEGER DEFAULT 0,
|
totalCount INTEGER DEFAULT 0,
|
||||||
finCount INTEGER DEFAULT 0,
|
finCount INTEGER DEFAULT 0,
|
||||||
icon TEXT DEFAULT NULL,
|
icon TEXT NOT NULL,
|
||||||
nameCard TEXT DEFAULT NULL,
|
nameCard TEXT DEFAULT NULL,
|
||||||
updated TEXT DEFAULT NULL
|
updated TEXT DEFAULT NULL
|
||||||
);
|
);
|
||||||
|
|||||||
71
src/core/database/select.ts
Normal file
71
src/core/database/select.ts
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
/**
|
||||||
|
* @file core database select.ts
|
||||||
|
* @description SQLite 数据库查询操作封装模块
|
||||||
|
* @author BTMuli<bt-muli@outlook.com>
|
||||||
|
* @since Alpha v0.1.4
|
||||||
|
*/
|
||||||
|
|
||||||
|
// tauri
|
||||||
|
import Database from "tauri-plugin-sql-api";
|
||||||
|
// local
|
||||||
|
import { sqlitePath } from "./init";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 返回所有成就系列数据
|
||||||
|
* @since Alpha v0.1.4
|
||||||
|
* @returns {Promise<BTMuli.SQLite.AchievementSeries[]>}
|
||||||
|
*/
|
||||||
|
export async function getAllSeries (): Promise<BTMuli.SQLite.AchievementSeries[]> {
|
||||||
|
const db = await Database.load(sqlitePath);
|
||||||
|
const sql = "SELECT * FROM AchievementSeries ORDER BY `order` ASC";
|
||||||
|
const result = await db.select(sql);
|
||||||
|
await db.close();
|
||||||
|
return result as BTMuli.SQLite.AchievementSeries[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 查询成就数据,无参默认查询所有成就
|
||||||
|
* @since Alpha v0.1.4
|
||||||
|
* @param {number} series
|
||||||
|
* @returns {Promise<BTMuli.SQLite.Achievements[]>}
|
||||||
|
*/
|
||||||
|
export async function getAchievementsBySeries (series?: number): Promise<BTMuli.SQLite.Achievements[]> {
|
||||||
|
const db = await Database.load(sqlitePath);
|
||||||
|
let sql;
|
||||||
|
// 无参默认查询所有成就
|
||||||
|
if (!series) {
|
||||||
|
sql = "SELECT * FROM Achievements";
|
||||||
|
} else {
|
||||||
|
sql = `SELECT * FROM Achievements WHERE series = ${series}`;
|
||||||
|
}
|
||||||
|
const result = await db.select(sql);
|
||||||
|
await db.close();
|
||||||
|
return result as BTMuli.SQLite.Achievements[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 条件查询
|
||||||
|
* @since Alpha v0.1.4
|
||||||
|
* @param {string} search
|
||||||
|
* @returns {Promise<BTMuli.SQLite.Achievements[]>}
|
||||||
|
*/
|
||||||
|
export async function searchAchievement (search: string): Promise<BTMuli.SQLite.Achievements[]> {
|
||||||
|
const db = await Database.load(sqlitePath);
|
||||||
|
const sql = `SELECT * FROM Achievements WHERE name LIKE '%${search}%' AND description LIKE '%${search}%'`;
|
||||||
|
const result = await db.select(sql);
|
||||||
|
await db.close();
|
||||||
|
return result as BTMuli.SQLite.Achievements[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 返回成就概况
|
||||||
|
* @since Alpha v0.1.4
|
||||||
|
* @returns {Promise<{total: number, fin:number}>}
|
||||||
|
*/
|
||||||
|
export async function getAchievementOverview (): Promise<{ total: number, fin: number }> {
|
||||||
|
const db = await Database.load(sqlitePath);
|
||||||
|
const sql = "SELECT COUNT(*) AS total, SUM(isCompleted) AS fin FROM Achievements";
|
||||||
|
const res: Array<{ total: number, fin: number }> = await db.select(sql);
|
||||||
|
await db.close();
|
||||||
|
return res[0];
|
||||||
|
}
|
||||||
@@ -39,7 +39,7 @@
|
|||||||
<v-list-item-title>
|
<v-list-item-title>
|
||||||
{{ series.name }}
|
{{ series.name }}
|
||||||
</v-list-item-title>
|
</v-list-item-title>
|
||||||
<v-list-item-subtitle> {{ series.completed_count }} / {{ series.total_count }} </v-list-item-subtitle>
|
<v-list-item-subtitle> {{ series.finCount }} / {{ series.totalCount }} </v-list-item-subtitle>
|
||||||
</v-list-item>
|
</v-list-item>
|
||||||
</v-list>
|
</v-list>
|
||||||
</div>
|
</div>
|
||||||
@@ -72,9 +72,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<v-list-item>
|
<v-list-item>
|
||||||
<template #prepend>
|
<template #prepend>
|
||||||
<v-icon :color="achievement.completed ? '#fec90b' : '#485466'">
|
<v-icon :color="achievement.isCompleted ? '#fec90b' : '#485466'">
|
||||||
<!-- todo 图标替换 -->
|
<!-- todo 图标替换 -->
|
||||||
{{ achievement.completed ? "mdi-check-circle" : "mdi-circle" }}
|
{{ achievement.isCompleted ? "mdi-check-circle" : "mdi-circle" }}
|
||||||
</v-icon>
|
</v-icon>
|
||||||
</template>
|
</template>
|
||||||
<v-list-item-title>
|
<v-list-item-title>
|
||||||
@@ -83,7 +83,7 @@
|
|||||||
</v-list-item-title>
|
</v-list-item-title>
|
||||||
<v-list-item-subtitle>{{ achievement.description }}</v-list-item-subtitle>
|
<v-list-item-subtitle>{{ achievement.description }}</v-list-item-subtitle>
|
||||||
<template #append>
|
<template #append>
|
||||||
<span v-show="achievement.completed" class="right-time">{{ achievement.completed_time }}</span>
|
<span v-show="achievement.isCompleted" class="right-time">{{ achievement.completedTime }}</span>
|
||||||
<v-card class="reward-card" @click="showMaterial('/source/material/原石.webp')">
|
<v-card class="reward-card" @click="showMaterial('/source/material/原石.webp')">
|
||||||
<v-img src="/source/material/原石.webp" sizes="32" />
|
<v-img src="/source/material/原石.webp" sizes="32" />
|
||||||
<div class="reward-num">
|
<div class="reward-num">
|
||||||
@@ -112,8 +112,8 @@ import { useAchievementsStore } from "../store/modules/achievements";
|
|||||||
// Utils
|
// Utils
|
||||||
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 { getUiafHeader, readUiafData, verifyUiafData, backupUiafData } from "../utils/UIAF";
|
||||||
import { getUiafHeader, getUiafStatus, readUiafData, verifyUiafData, backupUiafData, timestampToDate } from "../utils/UIAF";
|
import TGSqlite from "../core/database/TGSqlite";
|
||||||
|
|
||||||
// Store
|
// Store
|
||||||
const achievementsStore = useAchievementsStore();
|
const achievementsStore = useAchievementsStore();
|
||||||
@@ -127,10 +127,10 @@ const title = ref(achievementsStore.title as string);
|
|||||||
const CardsInfo = ref([] as BTMuli.Genshin.NameCard[]);
|
const CardsInfo = ref([] as BTMuli.Genshin.NameCard[]);
|
||||||
const getCardInfo = ref({} as BTMuli.Genshin.NameCard);
|
const getCardInfo = ref({} as BTMuli.Genshin.NameCard);
|
||||||
// series
|
// series
|
||||||
const seriesList = ref([] as BTMuli.Genshin.AchievementSeries[]);
|
const seriesList = ref([] as BTMuli.SQLite.AchievementSeries[]);
|
||||||
const selectedIndex = ref(-1 as number);
|
const selectedIndex = ref(-1 as number);
|
||||||
const selectedSeries = ref(-1 as number);
|
const selectedSeries = ref(-1 as number);
|
||||||
const selectedAchievement = ref([] as BTMuli.Genshin.Achievement[]);
|
const selectedAchievement = ref([] as BTMuli.SQLite.Achievements[]);
|
||||||
|
|
||||||
// render
|
// render
|
||||||
const search = ref("" as string);
|
const search = ref("" as string);
|
||||||
@@ -144,17 +144,18 @@ onMounted(async () => {
|
|||||||
|
|
||||||
// 加载数据,数据源:合并后的本地数据
|
// 加载数据,数据源:合并后的本地数据
|
||||||
async function loadData () {
|
async function loadData () {
|
||||||
|
const { total, fin } = await TGSqlite.search.overview();
|
||||||
|
achievementsStore.flushData(total, fin);
|
||||||
loadingTitle.value = "正在获取成就系列数据";
|
loadingTitle.value = "正在获取成就系列数据";
|
||||||
const seriesDB: BTMuli.Genshin.AchievementSeries[] = await ReadAllTGData("AchievementSeries");
|
|
||||||
CardsInfo.value = TGAppData.nameCards[1];
|
CardsInfo.value = TGAppData.nameCards[1];
|
||||||
seriesList.value = seriesDB.sort((a, b) => a.order - b.order);
|
seriesList.value = await TGSqlite.search.achievementSeries();
|
||||||
loadingTitle.value = "正在获取成就数据";
|
loadingTitle.value = "正在获取成就数据";
|
||||||
const getAchievements = await ReadAllTGData("Achievements");
|
const getAchievements = await TGSqlite.search.achievement.bySeries();
|
||||||
getAchievements.sort((a, b) => {
|
getAchievements.sort((a, b) => {
|
||||||
if (a.completed === b.completed) {
|
if (a.isCompleted === b.isCompleted) {
|
||||||
return a.id - b.id;
|
return a.id - b.id;
|
||||||
} else {
|
} else {
|
||||||
return a.completed ? 1 : -1;
|
return a.isCompleted ? 1 : -1;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
selectedAchievement.value = getAchievements;
|
selectedAchievement.value = getAchievements;
|
||||||
@@ -171,21 +172,21 @@ async function selectSeries (index: number) {
|
|||||||
}
|
}
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
loadingTitle.value = "正在获取对应的成就数据";
|
loadingTitle.value = "正在获取对应的成就数据";
|
||||||
const getAchievements = await ReadTGDataByIndex("Achievements", "series", seriesList.value[index].id);
|
|
||||||
selectedIndex.value = index;
|
selectedIndex.value = index;
|
||||||
selectedSeries.value = seriesList.value[index].id;
|
selectedSeries.value = seriesList.value[index].id;
|
||||||
|
const getAchievements = await TGSqlite.search.achievement.bySeries(selectedSeries.value);
|
||||||
loadingTitle.value = "正在查找对应的成就名片";
|
loadingTitle.value = "正在查找对应的成就名片";
|
||||||
let getCard: BTMuli.Genshin.NameCard;
|
let getCard: BTMuli.Genshin.NameCard;
|
||||||
if (selectedSeries.value !== 0 && selectedSeries.value !== 17) {
|
if (selectedSeries.value !== 0 && selectedSeries.value !== 17) {
|
||||||
getCard = CardsInfo.value.find((card) => card.name === seriesList.value[index].card)!;
|
getCard = CardsInfo.value.find((card) => card.name === seriesList.value[index].nameCard)!;
|
||||||
} else {
|
} else {
|
||||||
getCard = {} as BTMuli.Genshin.NameCard;
|
getCard = {} as BTMuli.Genshin.NameCard;
|
||||||
}
|
}
|
||||||
getAchievements.sort((a, b) => {
|
getAchievements.sort((a, b) => {
|
||||||
if (a.completed === b.completed) {
|
if (a.isCompleted === b.isCompleted) {
|
||||||
return a.id - b.id;
|
return a.id - b.id;
|
||||||
} else {
|
} else {
|
||||||
return a.completed ? 1 : -1;
|
return a.isCompleted ? 1 : -1;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
selectedAchievement.value = getAchievements;
|
selectedAchievement.value = getAchievements;
|
||||||
@@ -208,14 +209,7 @@ async function searchCard () {
|
|||||||
}
|
}
|
||||||
loadingTitle.value = "正在搜索";
|
loadingTitle.value = "正在搜索";
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
const res: BTMuli.Genshin.Achievement[] = [];
|
const res = await TGSqlite.search.achievement.bySearch(search.value);
|
||||||
const allAchievements = await ReadAllTGData("Achievements");
|
|
||||||
allAchievements.map((achievement) => {
|
|
||||||
if (achievement.name.includes(search.value) || achievement.description.includes(search.value)) {
|
|
||||||
return res.push(achievement);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
selectedIndex.value = -1;
|
selectedIndex.value = -1;
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
@@ -224,13 +218,13 @@ async function searchCard () {
|
|||||||
snackbarColor.value = "#F5810A";
|
snackbarColor.value = "#F5810A";
|
||||||
snackbarText.value = "没有找到对应的成就";
|
snackbarText.value = "没有找到对应的成就";
|
||||||
snackbar.value = true;
|
snackbar.value = true;
|
||||||
selectedAchievement.value = allAchievements;
|
selectedAchievement.value = await TGSqlite.search.achievement.bySeries();
|
||||||
} else {
|
} else {
|
||||||
res.sort((a, b) => {
|
res.sort((a, b) => {
|
||||||
if (a.completed === b.completed) {
|
if (a.isCompleted === b.isCompleted) {
|
||||||
return a.id - b.id;
|
return a.id - b.id;
|
||||||
} else {
|
} else {
|
||||||
return a.completed ? 1 : -1;
|
return a.isCompleted ? 1 : -1;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
selectedAchievement.value = res;
|
selectedAchievement.value = res;
|
||||||
@@ -258,59 +252,12 @@ async function importJson () {
|
|||||||
loading.value = true;
|
loading.value = true;
|
||||||
const remoteData: TGPlugin.UIAF.BaseData = JSON.parse(remoteRaw);
|
const remoteData: TGPlugin.UIAF.BaseData = JSON.parse(remoteRaw);
|
||||||
loadingTitle.value = "正在合并成就数据";
|
loadingTitle.value = "正在合并成就数据";
|
||||||
await Promise.allSettled(
|
await TGSqlite.UIAF.import(remoteData.list);
|
||||||
remoteData.list.map(async (data) => {
|
|
||||||
const id = data.id;
|
|
||||||
const localData: BTMuli.Genshin.Achievement = (await ReadTGDataByKey("Achievements", [id]))[0];
|
|
||||||
// 获取 timeStamp 2023-03-15 00:00:00
|
|
||||||
const localTime = localData.completed_time;
|
|
||||||
// 如果本地数据不存在,或者本地数据的 timeStamp 小于远程数据的 timeStamp,更新数据
|
|
||||||
if (data.timestamp !== 0) {
|
|
||||||
const finishTime = timestampToDate(data.timestamp);
|
|
||||||
if (finishTime !== localTime || localData.progress !== data.current) {
|
|
||||||
// eslint-disable-next-line camelcase
|
|
||||||
localData.completed_time = finishTime;
|
|
||||||
localData.progress = data.current;
|
|
||||||
localData.completed = true;
|
|
||||||
// 更新数据
|
|
||||||
await UpdateTGDataByKey("Achievements", localData);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (localData.progress !== data.current) {
|
|
||||||
// eslint-disable-next-line camelcase
|
|
||||||
localData.completed_time = "";
|
|
||||||
localData.progress = data.current;
|
|
||||||
localData.completed = false;
|
|
||||||
// 更新数据
|
|
||||||
await UpdateTGDataByKey("Achievements", localData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
loadingTitle.value = "正在更新成就系列数据";
|
|
||||||
let seriesDB = await ReadAllTGData("AchievementSeries");
|
|
||||||
await Promise.allSettled(
|
|
||||||
seriesDB.map(async (data) => {
|
|
||||||
const seriesId = data.id;
|
|
||||||
const achievementsDB = await ReadTGDataByIndex("Achievements", "series", seriesId);
|
|
||||||
// eslint-disable-next-line camelcase
|
|
||||||
data.completed_count = achievementsDB.filter((data) => {
|
|
||||||
return data.completed === true;
|
|
||||||
}).length;
|
|
||||||
await UpdateTGDataByKey("AchievementSeries", data);
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
loadingTitle.value = "正在备份数据";
|
loadingTitle.value = "正在备份数据";
|
||||||
await backupAchievementData();
|
await backupAchievementData();
|
||||||
loadingTitle.value = "正在刷新数据";
|
loadingTitle.value = "正在刷新数据";
|
||||||
seriesDB = await ReadAllTGData("AchievementSeries");
|
const overview = await TGSqlite.search.overview();
|
||||||
const finishAchievments = seriesDB.reduce((a, b) => {
|
achievementsStore.flushData(overview.total, overview.fin);
|
||||||
return a + b.completed_count;
|
|
||||||
}, 0);
|
|
||||||
const totalAchievements = seriesDB.reduce((a, b) => {
|
|
||||||
return a + b.total_count;
|
|
||||||
}, 0);
|
|
||||||
achievementsStore.flushData(totalAchievements, finishAchievments);
|
|
||||||
// 刷新数据
|
// 刷新数据
|
||||||
await loadData();
|
await loadData();
|
||||||
selectedIndex.value = -1;
|
selectedIndex.value = -1;
|
||||||
@@ -319,7 +266,7 @@ async function importJson () {
|
|||||||
|
|
||||||
// 备份成就数据
|
// 备份成就数据
|
||||||
async function backupAchievementData () {
|
async function backupAchievementData () {
|
||||||
const achievements = await ReadAllTGData("Achievements");
|
const achievements = await TGSqlite.UIAF.export();
|
||||||
await backupUiafData(achievements);
|
await backupUiafData(achievements);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -333,22 +280,10 @@ async function exportJson () {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 获取本地数据
|
// 获取本地数据
|
||||||
const achievements = (await ReadAllTGData("Achievements")).filter((data) => {
|
|
||||||
return data.progress !== 0 || data.completed === true;
|
|
||||||
});
|
|
||||||
const UiafData = {
|
const UiafData = {
|
||||||
info: await getUiafHeader(),
|
info: await getUiafHeader(),
|
||||||
list: [] as TGPlugin.UIAF.Achievement[],
|
list: await TGSqlite.UIAF.export(),
|
||||||
};
|
};
|
||||||
// 转换数据
|
|
||||||
UiafData.list = achievements.map((data) => {
|
|
||||||
return {
|
|
||||||
id: data.id,
|
|
||||||
timestamp: data.completed && data.completed_time ? Math.round(new Date(data.completed_time).getTime() / 1000) : 0,
|
|
||||||
current: data.progress,
|
|
||||||
status: getUiafStatus(data.completed, data.progress),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
const isSave = await dialog.save({
|
const isSave = await dialog.save({
|
||||||
// TODO: 设置保存文件名
|
// TODO: 设置保存文件名
|
||||||
filters: [
|
filters: [
|
||||||
|
|||||||
@@ -117,6 +117,7 @@
|
|||||||
</v-list-item>
|
</v-list-item>
|
||||||
<v-list-item title="删除 IndexedDB" prepend-icon="mdi-delete" @click="tryConfirm('delDB')" />
|
<v-list-item title="删除 IndexedDB" prepend-icon="mdi-delete" @click="tryConfirm('delDB')" />
|
||||||
<v-list-item title="检测 SQLite 数据库完整性" prepend-icon="mdi-database-check" @click="tryConfirm('checkDB')" />
|
<v-list-item title="检测 SQLite 数据库完整性" prepend-icon="mdi-database-check" @click="tryConfirm('checkDB')" />
|
||||||
|
<v-list-item title="重置 SQLite 数据库" prepend-icon="mdi-database-remove" @click="tryConfirm('resetDB')" />
|
||||||
<v-list-subheader inset class="config-header">
|
<v-list-subheader inset class="config-header">
|
||||||
路径
|
路径
|
||||||
</v-list-subheader>
|
</v-list-subheader>
|
||||||
@@ -261,6 +262,11 @@ function tryConfirm (oper: string) {
|
|||||||
confirmOper.value = "checkDB";
|
confirmOper.value = "checkDB";
|
||||||
confirmShow.value = true;
|
confirmShow.value = true;
|
||||||
break;
|
break;
|
||||||
|
case "resetDB":
|
||||||
|
confirmText.value = "确认重置 SQlite 数据库吗?";
|
||||||
|
confirmOper.value = "resetDB";
|
||||||
|
confirmShow.value = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -295,6 +301,9 @@ async function doConfirm (oper: string) {
|
|||||||
case "checkDB":
|
case "checkDB":
|
||||||
await checkDB();
|
await checkDB();
|
||||||
break;
|
break;
|
||||||
|
case "resetDB":
|
||||||
|
await resetDB();
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -418,12 +427,10 @@ async function checkDB () {
|
|||||||
loadingTitle.value = "正在检查数据库表单完整性...";
|
loadingTitle.value = "正在检查数据库表单完整性...";
|
||||||
const res = await TGSqlite.checkDB();
|
const res = await TGSqlite.checkDB();
|
||||||
if (!res) {
|
if (!res) {
|
||||||
loadingTitle.value = "检测到表单不完整,正在重置数据库...";
|
confirmOper.value = "resetDB";
|
||||||
await TGSqlite.resetDB();
|
confirmText.value = "数据库表单不完整,是否重置数据库?";
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
snackbarText.value = "数据库已重置!请载入备份数据。";
|
confirmShow.value = true;
|
||||||
snackbarColor.value = "success";
|
|
||||||
snackbar.value = true;
|
|
||||||
} else {
|
} else {
|
||||||
loadingTitle.value = "正在检查数据库数据完整性...";
|
loadingTitle.value = "正在检查数据库数据完整性...";
|
||||||
await TGSqlite.update.achievement();
|
await TGSqlite.update.achievement();
|
||||||
@@ -434,6 +441,14 @@ async function checkDB () {
|
|||||||
snackbar.value = true;
|
snackbar.value = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 重置 SQLite 数据库
|
||||||
|
async function resetDB () {
|
||||||
|
await TGSqlite.resetDB();
|
||||||
|
snackbarText.value = "数据库已重置!请载入备份数据。";
|
||||||
|
snackbarColor.value = "success";
|
||||||
|
snackbar.value = true;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="css" scoped>
|
<style lang="css" scoped>
|
||||||
|
|||||||
2
src/types/Achievement.d.ts
vendored
2
src/types/Achievement.d.ts
vendored
@@ -164,7 +164,7 @@ declare namespace BTMuli.SQLite {
|
|||||||
version: string | null
|
version: string | null
|
||||||
totalCount: number
|
totalCount: number
|
||||||
finCount: number
|
finCount: number
|
||||||
icon: string | null
|
icon: string
|
||||||
nameCard: string | null
|
nameCard: string | null
|
||||||
updated: string | null
|
updated: string | null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,16 +7,14 @@
|
|||||||
|
|
||||||
// tauri
|
// tauri
|
||||||
import { app, fs, path } from "@tauri-apps/api";
|
import { app, fs, path } from "@tauri-apps/api";
|
||||||
// data
|
|
||||||
import { TGAppData } from "../data";
|
|
||||||
// utils
|
// utils
|
||||||
import { UpdateTGDataByKey } from "./TGIndex";
|
import TGSqlite from "../core/database/TGSqlite";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description 时间戳转换为日期
|
* @description 时间戳转换为日期
|
||||||
* @since Alpha v0.1.4
|
* @since Alpha v0.1.4
|
||||||
* @param {number} timestamp - 时间戳
|
* @param {number} timestamp - 时间戳
|
||||||
* @returns {string} 日期
|
* @returns {string} 日期 2021-01-01 00:00:00
|
||||||
*/
|
*/
|
||||||
export function timestampToDate (timestamp: number): string {
|
export function timestampToDate (timestamp: number): string {
|
||||||
return new Date(timestamp * 1000).toLocaleString("zh", {
|
return new Date(timestamp * 1000).toLocaleString("zh", {
|
||||||
@@ -101,66 +99,27 @@ export async function readUiafData (userPath: string): Promise<string | false> {
|
|||||||
/**
|
/**
|
||||||
* @description 根据成就数据导出 UIAF 数据
|
* @description 根据成就数据导出 UIAF 数据
|
||||||
* @since Alpha v0.1.4
|
* @since Alpha v0.1.4
|
||||||
* @param {BTMuli.Genshin.Achievement[]} achievementData - 成就数据
|
* @param {TGPlugin.UIAF.Achievement[]} achievementData - 成就数据
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
export async function backupUiafData (achievementData: BTMuli.Genshin.Achievement[]): Promise<void> {
|
export async function backupUiafData (achievementData: TGPlugin.UIAF.Achievement[]): Promise<void> {
|
||||||
const res = [] as TGPlugin.UIAF.Achievement[];
|
|
||||||
const savePath = `${await path.appLocalDataDir()}\\userData\\UIAF.json`;
|
const savePath = `${await path.appLocalDataDir()}\\userData\\UIAF.json`;
|
||||||
achievementData.forEach((achievement) => {
|
await fs.writeTextFile(savePath, JSON.stringify(achievementData, null, 2));
|
||||||
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 数据恢复成就数据
|
* @description 根据 UIAF 数据恢复成就数据
|
||||||
* @since Alpha v0.1.4
|
* @since Alpha v0.1.4
|
||||||
* @returns {Promise<{total: number, completed: number}> | false} 恢复的成就数量
|
* @returns {Promise<{total: number, fin: number}> | false} 恢复的成就数量
|
||||||
*/
|
*/
|
||||||
export async function restoreUiafData (): Promise<{ total: number, completed: number } | false> {
|
export async function restoreUiafData (): Promise<{ total: number, fin: number } | false> {
|
||||||
const uiafPath = `${await path.appLocalDataDir()}\\userData\\UIAF.json`;
|
const uiafPath = `${await path.appLocalDataDir()}\\userData\\UIAF.json`;
|
||||||
// 检测是否存在 UIAF 数据
|
// 检测是否存在 UIAF 数据
|
||||||
if (!await fs.exists(uiafPath)) {
|
if (!await fs.exists(uiafPath)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const uiafData = JSON.parse(await fs.readTextFile(uiafPath)) as TGPlugin.UIAF.Achievement[];
|
const uiafData = JSON.parse(await fs.readTextFile(uiafPath)) as TGPlugin.UIAF.Achievement[];
|
||||||
const achievementData = TGAppData.achievements;
|
await TGSqlite.UIAF.import(uiafData);
|
||||||
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 {
|
return await TGSqlite.search.overview();
|
||||||
total,
|
|
||||||
completed,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user