diff --git a/src/pages/User/Challenge.vue b/src/pages/User/Challenge.vue
index 7114841d..571e222f 100644
--- a/src/pages/User/Challenge.vue
+++ b/src/pages/User/Challenge.vue
@@ -37,7 +37,7 @@
label="服务器"
width="200px"
density="compact"
- :disabled="isReq"
+ :disabled="reqPop"
/>
@@ -72,14 +72,52 @@
+
+
+
+
+ {{ item.name }}
+ {{ item.startTime.slice(0, 10) }} ~ {{ item.endTime.slice(0, 10) }}
+
+
+
+
+
+
+ {{ JSON.stringify(item, null, 2) }}
+
+
+
+
+

+
暂无数据,请尝试刷新
+
+
diff --git a/src/pages/User/Combat.vue b/src/pages/User/Combat.vue
index c3abe3d8..9e9748c8 100644
--- a/src/pages/User/Combat.vue
+++ b/src/pages/User/Combat.vue
@@ -135,14 +135,15 @@ onMounted(async () => {
await TGLogger.Info("[UserCombat][onMounted] 打开真境剧诗页面");
await showLoading.update("正在加载UID列表");
uidList.value = await TSUserCombat.getAllUid();
- if (uidList.value.includes(account.value.gameUid)) uidCur.value = account.value.gameUid;
- else if (uidList.value.length > 0) {
- uidCur.value = uidList.value[0];
+ if (uidList.value.length === 0) {
+ uidCur.value = "";
+ } else {
+ if (uidList.value.includes(account.value.gameUid)) uidCur.value = account.value.gameUid;
+ else uidCur.value = uidList.value[0];
await showLoading.update(`正在加载UID${uidCur.value}的剧诗数据`);
- } else uidCur.value = "";
+ }
await loadCombat();
await showLoading.end();
- console.log("UserCombat", localCombat.value);
});
watch(() => uidCur.value, loadCombat);
@@ -150,6 +151,7 @@ watch(() => uidCur.value, loadCombat);
async function toAbyss(): Promise {
await router.push({ name: "深境螺旋" });
}
+
async function toChallenge(): Promise {
await router.push({ name: "幽境危战" });
}
@@ -174,7 +176,7 @@ async function loadWiki(): Promise {
async function refreshCombat(): Promise {
if (!cookie.value) {
showSnackbar.error("未登录");
- await TGLogger.Warn("[UserCombat][getAbyssData] 未登录");
+ await TGLogger.Warn("[UserCombat][refreshCombat] 未登录");
return;
}
if (uidCur.value && uidCur.value !== account.value.gameUid) {
@@ -196,7 +198,7 @@ async function refreshCombat(): Promise {
return;
}
}
- await TGLogger.Info("[UserCombat][getCombatData] 更新剧诗数据");
+ await TGLogger.Info("[UserCombat][refreshCombat] 更新剧诗数据");
await showLoading.start(`正在获取${account.value.gameUid}的剧诗数据`);
const res = await recordReq.roleCombat(cookie.value, account.value);
if (res === false) {
@@ -207,8 +209,8 @@ async function refreshCombat(): Promise {
if ("retcode" in res) {
await showLoading.end();
showSnackbar.error(`[${res.retcode}]${res.message}`);
- await TGLogger.Error(`[UserCombat][getCombatData] 获取${account.value.gameUid}的剧诗数据失败`);
- await TGLogger.Error(`[UserCombat][getCombatData] ${res.retcode} ${res.message}`);
+ await TGLogger.Error(`[UserCombat][refreshCombat] 获取${account.value.gameUid}的剧诗数据失败`);
+ await TGLogger.Error(`[UserCombat][refreshCombat] ${res.retcode} ${res.message}`);
return;
}
await showLoading.update("正在保存剧诗数据");
@@ -225,7 +227,7 @@ async function refreshCombat(): Promise {
async function shareCombat(): Promise {
await TGLogger.Info(`[UserCombat][shareCombat][${userTab.value}] 生成剧诗数据分享图片`);
- const fileName = `【剧诗数据】${userTab.value}-${account.value.gameUid}.png`;
+ const fileName = `【真境剧诗】${userTab.value}-${uidCur.value}.png`;
const shareDom = document.querySelector(`#user-combat-${userTab.value}`);
if (shareDom === null) {
showSnackbar.error("未找到分享数据");
diff --git a/src/plugins/Sqlite/index.ts b/src/plugins/Sqlite/index.ts
index f67eda23..90a819b2 100644
--- a/src/plugins/Sqlite/index.ts
+++ b/src/plugins/Sqlite/index.ts
@@ -1,7 +1,7 @@
/**
* @file plugins/Sqlite/index.ts
* @description Sqlite 数据库操作类
- * @since Beta v0.6.1
+ * @since Beta v0.8.0
*/
import { app } from "@tauri-apps/api";
@@ -19,6 +19,8 @@ class Sqlite {
"GachaRecords",
"GameAccount",
"SpiralAbyss",
+ "RoleCombat",
+ "HardChallenge",
"UFCollection",
"UFMap",
"UFPost",
diff --git a/src/plugins/Sqlite/modules/userChallenge.ts b/src/plugins/Sqlite/modules/userChallenge.ts
new file mode 100644
index 00000000..b2db0b86
--- /dev/null
+++ b/src/plugins/Sqlite/modules/userChallenge.ts
@@ -0,0 +1,177 @@
+/**
+ * @file sqlite/modules/userChallenge.ts
+ * @description 幽境危战模块
+ * @since Beta v0.8.0
+ */
+
+import { path } from "@tauri-apps/api";
+import { exists, mkdir, readTextFile, writeTextFile } from "@tauri-apps/plugin-fs";
+import TGLogger from "@utils/TGLogger.js";
+import { timestampToDate } from "@utils/toolFunc.js";
+
+import TGSqlite from "../index.js";
+
+/**
+ * @description 将通过 api 获取到的数据转换为数据库中的数据
+ * @since Beta v0.8.0
+ * @param {TGApp.Game.Challenge.ChallengeItem} data - 挑战数据
+ * @returns {TGApp.Sqlite.Challenge.SingleTable} 转换后的数据
+ */
+function transUserChallenge(
+ data: TGApp.Game.Challenge.ChallengeItem,
+): TGApp.Sqlite.Challenge.SingleTable {
+ return {
+ uid: "",
+ id: Number(data.schedule.schedule_id),
+ startTime: timestampToDate(Number(data.schedule.start_time) * 1000),
+ endTime: timestampToDate(Number(data.schedule.end_time) * 1000),
+ name: data.schedule.name,
+ single: data.single,
+ mp: data.mp,
+ updated: timestampToDate(new Date().getTime()),
+ };
+}
+
+/**
+ * @description 直接插入数据
+ * @since Beta v0.8.0
+ * @param {TGApp.Sqlite.Challenge.SingleTable} data - 挑战数据
+ * @param {string} [uid] - 用户UID
+ * @returns {string} - 插入 SQL 语句
+ */
+function getInsertSql(data: TGApp.Sqlite.Challenge.SingleTable, uid?: string): string {
+ const timeNow = timestampToDate(new Date().getTime());
+ return `
+ INSERT INTO HardChallenge(uid, id, startTime, endTime, name, single, mp, updated)
+ VALUES ('${uid ?? data.uid}', ${data.id}, '${data.startTime}', '${data.endTime}', '${data.name}',
+ '${JSON.stringify(data.single)}', '${JSON.stringify(data.mp)}', '${timeNow}') ON CONFLICT(uid,id) DO
+ UPDATE
+ SET startTime = '${data.startTime}',
+ endTime = '${data.endTime}',
+ name = '${data.name}',
+ single = '${JSON.stringify(data.single)}',
+ mp = '${JSON.stringify(data.mp)}',
+ updated = '${timeNow}'
+ `;
+}
+
+/**
+ * @description 获取所有数据的UID
+ * @since Beta v0.8.0
+ * @return {Promise} - 所有数据的UID
+ */
+async function getAllUid(): Promise> {
+ const db = await TGSqlite.getDB();
+ type resType = Array<{ uid: string }>;
+ const res = await db.select("SELECT DISTINCT uid FROM HardChallenge;");
+ return res.map((i) => i.uid);
+}
+
+/**
+ * @description 获取挑战数据
+ * @since Beta v0.8.0
+ * @param {string} [uid] - 游戏UID
+ * @return {Promise>} - 挑战数据
+ */
+async function getChallenge(uid?: string): Promise> {
+ const db = await TGSqlite.getDB();
+ let resR: Array;
+ if (uid === undefined) {
+ resR = await db.select>(
+ "SELECT * FROM HardChallenge ORDER BY id DESC;",
+ );
+ } else {
+ resR = await db.select>(
+ `SELECT *
+ FROM HardChallenge
+ WHERE uid = ?;`,
+ [uid],
+ );
+ }
+ const res: Array = [];
+ for (const raw of resR) {
+ res.push({
+ uid: raw.uid,
+ id: raw.id,
+ startTime: raw.startTime,
+ endTime: raw.endTime,
+ name: raw.name,
+ single: JSON.parse(raw.single),
+ mp: JSON.parse(raw.mp),
+ updated: raw.updated,
+ });
+ }
+ return res;
+}
+
+/**
+ * @description 保存挑战数据
+ * @since Beta v0.8.0
+ * @param {string} uid - 游戏UID
+ * @param {TGApp.Game.Challenge.ChallengeItem} data - 挑战数据
+ * @return {Promise}
+ */
+async function saveChallenge(uid: string, data: TGApp.Game.Challenge.ChallengeItem): Promise {
+ const db = await TGSqlite.getDB();
+ await db.execute(getInsertSql(transUserChallenge(data), uid));
+}
+
+/**
+ * @description 删除指定UID的挑战数据
+ * @since Beta v0.8.0
+ * @param {string} uid - 游戏UID
+ * @return {Promise}
+ */
+async function delChallenge(uid: string): Promise {
+ const db = await TGSqlite.getDB();
+ await db.execute("DELETE FROM HardChallenge WHERE uid = ?;", [uid]);
+}
+
+/**
+ * @description 备份挑战数据
+ * @since Beta v0.8.0
+ * @param {string} dir - 备份目录
+ * @return {Promise}
+ */
+async function backupChallenge(dir: string): Promise {
+ if (!(await exists(dir))) {
+ await mkdir(dir, { recursive: true });
+ await TGLogger.Warn(`[TSUserChallenge][Backup] 未检测到备份目录,已创建`);
+ }
+ const data = await getChallenge();
+ await writeTextFile(`${dir}${path.sep()}challenge.json`, JSON.stringify(data));
+}
+
+/**
+ * @description 恢复挑战数据
+ * @since Beta v0.8.0
+ * @param {string} dir - 备份目录
+ * @return {Promise}
+ */
+async function restoreChallenge(dir: string): Promise {
+ const filePath = `${dir}${path.sep()}challenge.json`;
+ if (!(await exists(filePath))) return false;
+ try {
+ const data: Array = JSON.parse(
+ await readTextFile(filePath),
+ );
+ const db = await TGSqlite.getDB();
+ for (const challenge of data) await db.execute(getInsertSql(challenge));
+ return true;
+ } catch (e) {
+ await TGLogger.Error(`[TSUserChallenge][Restore] 恢复挑战数据失败: `);
+ await TGLogger.Error(`${e}`);
+ return false;
+ }
+}
+
+const TSUserChallenge = {
+ getAllUid,
+ getChallenge,
+ saveChallenge,
+ delChallenge,
+ backupChallenge,
+ restoreChallenge,
+};
+
+export default TSUserChallenge;
diff --git a/src/plugins/Sqlite/modules/userCombat.ts b/src/plugins/Sqlite/modules/userCombat.ts
index 2296d92c..254071eb 100644
--- a/src/plugins/Sqlite/modules/userCombat.ts
+++ b/src/plugins/Sqlite/modules/userCombat.ts
@@ -1,7 +1,7 @@
/**
* @file plugins/Sqlite/modules/userCombat.ts
* @description Sqlite-幻想真境剧诗模块
- * @since Beta v0.6.3
+ * @since Beta v0.8.0
*/
import { path } from "@tauri-apps/api";
@@ -10,13 +10,32 @@ import TGLogger from "@utils/TGLogger.js";
import { timestampToDate } from "@utils/toolFunc.js";
import TGSqlite from "../index.js";
-import { transUserCombat } from "../utils/transUserCombat.js";
+
+/**
+ * @description 将通过 api 获取到的数据转换为数据库中的数据
+ * @since Beta v0.8.0
+ * @param {TGApp.Game.Combat.Combat} data - 剧诗数据
+ * @returns {TGApp.Sqlite.Combat.SingleTable} 转换后端数据
+ */
+function transUserCombat(data: TGApp.Game.Combat.Combat): TGApp.Sqlite.Combat.SingleTable {
+ return {
+ uid: "",
+ detail: data.detail,
+ endTime: timestampToDate(Number(data.schedule.end_time) * 1000),
+ hasData: data.has_data,
+ hasDetailData: data.has_detail_data,
+ id: data.schedule.schedule_id,
+ startTime: timestampToDate(Number(data.schedule.start_time) * 1000),
+ stat: data.stat,
+ updated: timestampToDate(new Date().getTime()),
+ };
+}
/**
* @description 直接插入数据
* @since Beta v0.6.3
- * @param {string} uid 用户UID
* @param {TGApp.Sqlite.Combat.SingleTable} data 剧诗数据
+ * @param {string} [uid] 用户UID
* @returns {string}
*/
function getInsertSql(data: TGApp.Sqlite.Combat.SingleTable, uid?: string): string {
@@ -41,7 +60,7 @@ function getInsertSql(data: TGApp.Sqlite.Combat.SingleTable, uid?: string): stri
/**
* @description 获取所有有数据的UID
* @since Beta v0.6.3
- * @returns {Promise}
+ * @returns {Promise>}
*/
async function getAllUid(): Promise> {
const db = await TGSqlite.getDB();
@@ -52,20 +71,20 @@ async function getAllUid(): Promise> {
/**
* @description 获取剧诗数据
- * @since Beta v0.6.3
- * @param {string} uid - 游戏UID
+ * @since Beta v0.8.0
+ * @param {string} [uid] - 游戏UID
* @returns {Promise}
*/
async function getCombat(uid?: string): Promise {
const db = await TGSqlite.getDB();
- let resR: TGApp.Sqlite.Combat.RawTable[];
+ let resR: Array;
if (uid === undefined) {
- resR = await db.select(
- "SELECT * FROM RoleCombat order by id DESC;",
+ resR = await db.select>(
+ "SELECT * FROM RoleCombat ORDER BY id DESC;",
);
} else {
- resR = await db.select(
- "SELECT * FROM RoleCombat WHERE uid = ? order by id DESC;",
+ resR = await db.select>(
+ "SELECT * FROM RoleCombat WHERE uid = ?;",
[uid],
);
}
@@ -118,7 +137,7 @@ async function delCombat(uid: string): Promise {
async function backupCombat(dir: string): Promise {
if (!(await exists(dir))) {
await mkdir(dir, { recursive: true });
- await TGLogger.Warn(`未检测到备份目录,已创建`);
+ await TGLogger.Warn(`[TSUserCombat][Backup] 未检测到备份目录,已创建`);
}
const data = await getCombat();
await writeTextFile(`${dir}${path.sep()}combat.json`, JSON.stringify(data));
@@ -126,7 +145,7 @@ async function backupCombat(dir: string): Promise {
/**
* @description 恢复剧诗数据
- * @since Beta v0.6.3
+ * @since Beta v0.8.0
* @param {string} dir - 备份文件目录
* @returns {Promise}
*/
@@ -134,12 +153,12 @@ async function restoreCombat(dir: string): Promise {
const filePath = `${dir}${path.sep()}combat.json`;
if (!(await exists(filePath))) return false;
try {
- const data: TGApp.Sqlite.Combat.SingleTable[] = JSON.parse(await readTextFile(filePath));
+ const data: Array = JSON.parse(await readTextFile(filePath));
const db = await TGSqlite.getDB();
for (const abyss of data) await db.execute(getInsertSql(abyss));
return true;
} catch (e) {
- await TGLogger.Error(`恢复剧诗数据失败${filePath}`);
+ await TGLogger.Error(`[TSUserCombat][Restore] 恢复剧诗数据失败${filePath}`);
await TGLogger.Error(`${e}`);
return false;
}
diff --git a/src/plugins/Sqlite/sql/createTable.sql b/src/plugins/Sqlite/sql/createTable.sql
index a19d7b81..fadf548b 100644
--- a/src/plugins/Sqlite/sql/createTable.sql
+++ b/src/plugins/Sqlite/sql/createTable.sql
@@ -1,6 +1,6 @@
-- @file plugins/Sqlite/sql/createTable.sql
-- @brief sqlite数据库创建表语句
--- @since Beta v0.7.2
+-- @since Beta v0.8.0
-- @brief 创建成就数据表
create table if not exists Achievements
@@ -47,27 +47,6 @@ create table if not exists GameAccount
primary key (uid, gameBiz, gameUid)
);
--- @brief 创建临时游戏账号数据表用于迁移数据
-create table GameAccountTemp
-(
- uid text,
- gameBiz text,
- gameUid text,
- isChosen boolean,
- isOfficial boolean,
- level integer,
- nickname text,
- region text,
- regionName text,
- updated text,
- primary key (uid, gameBiz, gameUid)
-);
-
---- @brief 迁移数据
-insert into GameAccountTemp select * from GameAccount;
-drop table GameAccount;
-alter table GameAccountTemp rename to GameAccount;
-
-- @brief 创建深渊数据表
create table if not exists SpiralAbyss
(
@@ -107,6 +86,20 @@ create table if not exists RoleCombat
primary key (uid, id)
);
+-- @brief 创建幽境危战数据表
+create table if not exists HardChallenge
+(
+ uid text,
+ id integer,
+ startTime text,
+ endTime text,
+ name text,
+ single text,
+ mp text,
+ updated text,
+ primary key (uid, id)
+);
+
-- @brief 创建战绩数据表
create table if not exists UserRecord
(
diff --git a/src/plugins/Sqlite/utils/transUserCombat.ts b/src/plugins/Sqlite/utils/transUserCombat.ts
deleted file mode 100644
index 12053d6d..00000000
--- a/src/plugins/Sqlite/utils/transUserCombat.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-/**
- * @file plugins/Sqlite/utils/transUserCombat.ts
- * @description Sqlite 数据转换-幻想真境剧诗
- * @since Beta v0.6.3
- */
-
-import { timestampToDate } from "@utils/toolFunc.js";
-
-/**
- * @description 将通过 api 获取到的数据转换为数据库中的数据
- * @since Beta v0.6.3
- * @param {TGApp.Game.Combat.Combat} data - 剧诗数据
- * @returns {TGApp.Sqlite.Combat.SingleTable} 转换后端数据
- */
-export function transUserCombat(data: TGApp.Game.Combat.Combat): TGApp.Sqlite.Combat.SingleTable {
- return {
- uid: "",
- detail: data.detail,
- endTime: timestampToDate(Number(data.schedule.end_time) * 1000),
- hasData: data.has_data,
- hasDetailData: data.has_detail_data,
- id: data.schedule.schedule_id,
- startTime: timestampToDate(Number(data.schedule.start_time) * 1000),
- stat: data.stat,
- updated: timestampToDate(new Date().getTime()),
- };
-}
diff --git a/src/request/recordReq.ts b/src/request/recordReq.ts
index 326b7a65..4bfd5e3a 100644
--- a/src/request/recordReq.ts
+++ b/src/request/recordReq.ts
@@ -166,12 +166,34 @@ async function hardChallengePopularity(
return resp;
}
+/**
+ * @description 获取挑战数据
+ * @since Beta v0.8.0
+ * @param {TGApp.App.Account.Cookie} cookie Cookie
+ * @param {TGApp.Sqlite.Account.Game} user 用户
+ * @returns {Promise}
+ */
+async function hardChallengeDetail(
+ cookie: TGApp.App.Account.Cookie,
+ user: TGApp.Sqlite.Account.Game,
+): Promise {
+ const ck = { account_id: cookie.account_id, cookie_token: cookie.cookie_token };
+ const params = { need_detail: true, role_id: user.gameUid, server: user.region };
+ const resp = await TGHttp(
+ `${trgAbu}hard_challenge`,
+ { method: "GET", headers: getRequestHeader(ck, "GET", params), query: params },
+ );
+ if (resp.retcode !== 0) return resp;
+ return resp.data;
+}
+
const recordReq = {
index: index,
character: { list: characterList, detail: characterDetail },
roleCombat: roleCombat,
spiralAbyss: spiralAbyss,
challenge: {
+ detail: hardChallengeDetail,
pop: hardChallengePopularity,
},
};
diff --git a/src/types/Game/Challenge.d.ts b/src/types/Game/Challenge.d.ts
index 42acaf14..98b098b1 100644
--- a/src/types/Game/Challenge.d.ts
+++ b/src/types/Game/Challenge.d.ts
@@ -38,4 +38,197 @@ declare namespace TGApp.Game.Challenge {
image: string;
rarity: number;
};
+
+ /**
+ * @description 挑战数据返回响应(详细)
+ * @since Beta v0.8.0
+ * @interface ChallengeResp
+ * @extends TGApp.BBS.Response.BaseWithData
+ */
+ type ChallengeResp = TGApp.BBS.Response.BaseWithData;
+
+ /**
+ * @description 挑战数据返回(详细)
+ * @since Beta v0.8.0
+ * @interface ChallengeRes
+ * @property {Array} data - 挑战数据列表
+ * @property {boolean} is_unlock - 是否解锁挑战
+ * @property {ChallengeLink} link - 挑战链接信息
+ */
+ type ChallengeRes = { data: Array; is_unlock: boolean; link: ChallengeLink };
+
+ /**
+ * @description 挑战链接信息
+ * @since Beta v0.8.0
+ * @interface ChallengeLink
+ * @property {string} lineup_link - 队伍配置链接
+ * @property {string} play_link - 挑战链接
+ */
+ type ChallengeLink = { lineup_link: string; play_link: string };
+
+ /**
+ * @description 挑战数据项
+ * @since Beta v0.8.0
+ * @interface ChallengeItem
+ * @property {ChallengeSchedule} schedule - 挑战周期信息
+ * @property {ChallengeSingle} single - 单次挑战数据
+ * @property {ChallengeMp} mp - 多人挑战数据
+ * @property {Array} blings - 挑战相关的其他数据(如成就等)
+ */
+ type ChallengeItem = {
+ schedule: ChallengeSchedule;
+ single: ChallengeSingle;
+ mp: ChallengeMp;
+ blings: Array;
+ };
+
+ /**
+ * @description 多人挑战数据
+ * @since Beta v0.8.0
+ * @interface ChallengeMp
+ * @todo 待测试
+ */
+ type ChallengeMp = ChallengeSingle;
+
+ /**
+ * @description 时间对象
+ * @since Beta v0.8.0
+ * @interface DateTime
+ * @property {number} year - 年份
+ * @property {number} month - 月份(1-12)
+ * @property {number} day - 日(1-31)
+ * @property {number} hour - 小时(0-23)
+ * @property {number} minute - 分钟(0-59)
+ * @property {number} second - 秒(0-59)
+ */
+ type DateTime = {
+ year: number;
+ month: number;
+ day: number;
+ hour: number;
+ minute: number;
+ second: number;
+ };
+
+ /**
+ * @description 挑战周期信息
+ * @since Beta v0.8.0
+ * @interface ChallengeSchedule
+ * @property {string} schedule_id - 挑战周期 ID
+ * @property {string} start_time - 挑战开始时间(秒级时间戳)
+ * @property {string} end_time - 挑战结束时间(秒级时间戳)
+ * @property {DateTime} start_date_time - 挑战开始时间(DateTime 对象)
+ * @property {DateTime} end_date_time - 挑战结束时间(DateTime 对象)
+ * @property {boolean} is_valid - 是否有效
+ * @property {string} name - 挑战名称
+ */
+ type ChallengeSchedule = {
+ schedule_id: string;
+ start_time: string;
+ end_time: string;
+ start_date_time: DateTime;
+ end_date_time: DateTime;
+ is_valid: boolean;
+ name: string;
+ };
+
+ /**
+ * @description 单次挑战数据
+ * @since Beta v0.8.0
+ * @interface ChallengeSingle
+ * @property {SingleBest} best - 最佳挑战数据
+ * @property {Array} challenge - 挑战列表
+ * @property {boolean} has_data - 是否有数据
+ */
+ type ChallengeSingle = { best: SingleBest; challenge: Array; has_data: boolean };
+
+ /**
+ * @description 单次挑战最佳数据
+ * @since Beta v0.8.0
+ * @interface SingleBest
+ * @property {number} difficulty - 挑战难度
+ * @property {number} second - 挑战用时(秒)
+ * @property {string} icon - 挑战图标名称
+ */
+ type SingleBest = { difficulty: number; second: number; icon: string };
+
+ /**
+ * @description 单次挑战数据项
+ * @since Beta v0.8.0
+ * @interface SingleChallenge
+ * @property {string} name - 怪物名称
+ * @property {number} seconds - 挑战用时(秒)
+ * @property {Array} teams - 挑战队伍列表
+ * @property {Array} best_avatar - 最佳角色列表
+ * @property {SingleMonster} monster - 挑战怪物数据
+ */
+ type SingleChallenge = {
+ name: string;
+ seconds: number;
+ teams: Array;
+ best_avatar: Array;
+ monster: SingleMonster;
+ };
+
+ /**
+ * @description 单次挑战队伍数据
+ * @since Beta v0.8.0
+ * @interface SingleTeam
+ * @property {number} avatar_id - 角色 ID
+ * @property {string} name - 角色名称
+ * @property {string} element - 角色元素
+ * @property {string} image - 角色头像图片 URL
+ * @property {number} level - 角色等级
+ * @property {number} rarity - 角色稀有度
+ * @property {number} rank - 角色命座
+ */
+ type SingleTeam = {
+ avatar_id: number;
+ name: string;
+ element: string;
+ image: string;
+ level: number;
+ rarity: number;
+ rank: number;
+ };
+
+ /**
+ * @description 单次挑战最佳角色数据
+ * @since Beta v0.8.0
+ * @interface SingleAvatar
+ * @property {number} avatar_id - 角色 ID
+ * @property {string} side_icon - 角色侧边图标 URL
+ * @property {string} dps - 角色 DPS 数据
+ * @property {string} type - 1-最强一击,2-最高总伤害
+ */
+ type SingleAvatar = { avatar_id: number; side_icon: string; dps: string; type: string };
+
+ /**
+ * @description 单次挑战怪物数据
+ * @since Beta v0.8.0
+ * @interface SingleMonster
+ * @property {string} name - 怪物名称
+ * @property {number} level - 怪物等级
+ * @property {string} icon - 怪物图标 URL
+ * @property {Array} desc - 怪物描述列表
+ * @property {Array} tags - 怪物标签列表
+ * @property {string} monster_id - 怪物 ID
+ */
+ type SingleMonster = {
+ name: string;
+ level: number;
+ icon: string;
+ desc: Array;
+ tags: Array;
+ monster_id: string;
+ };
+
+ /**
+ * @description 怪物标签
+ * @since Beta v0.8.0
+ * @interface MonsterTag
+ * @property {string} type - 标签类型
+ * @property {string} desc - 标签描述
+ */
+ type MonsterTag = { type: string; desc: string };
}
diff --git a/src/types/Sqlite/Challenge.d.ts b/src/types/Sqlite/Challenge.d.ts
new file mode 100644
index 00000000..099c03f4
--- /dev/null
+++ b/src/types/Sqlite/Challenge.d.ts
@@ -0,0 +1,55 @@
+/**
+ * @file types/Sqlite/Challenge.d.ts
+ * @description 幽境危战类型定义文件
+ * @since Beta v0.8.0
+ */
+
+declare namespace TGApp.Sqlite.Challenge {
+ /**
+ * @description 数据库-幽境危战表(原始数据)
+ * @since Beta v0.8.0
+ * @interface RawTable
+ * @property {string} uid - 用户 UID
+ * @property {number} id - 挑战 ID
+ * @property {string} startTime - 开始时间
+ * @property {string} endTime - 结束时间
+ * @property {string} name - 挑战名称
+ * @property {string} single - 挑战单个数据(JSON 字符串)
+ * @property {string} mp - 挑战多人数据(JSON 字符串)
+ * @property {string} updated - 更新时间
+ */
+ type RawTable = {
+ uid: string;
+ id: number;
+ startTime: string;
+ endTime: string;
+ name: string;
+ single: string; // JSON 字符串
+ mp: string; // JSON 字符串
+ updated: string;
+ };
+
+ /**
+ * @description 数据库-幽境危战表
+ * @since Beta v0.8.0
+ * @interface SingleTable
+ * @property {string} uid - 用户 UID
+ * @property {number} id - 挑战 ID
+ * @property {string} startTime - 开始时间
+ * @property {string} endTime - 结束时间
+ * @property {string} name - 挑战名称
+ * @property {TGApp.Game.Challenge.ChallengeSingle} single - 挑战单个数据
+ * @property {TGApp.Game.Challenge.ChallengeMp} mp - 挑战多人数据
+ * @property {string} updated - 更新时间
+ */
+ type SingleTable = {
+ uid: string;
+ id: number;
+ startTime: string;
+ endTime: string;
+ name: string;
+ single: TGApp.Game.Challenge.ChallengeSingle;
+ mp: TGApp.Game.Challenge.ChallengeMp;
+ updated: string;
+ };
+}
diff --git a/src/utils/dataBS.ts b/src/utils/dataBS.ts
index 5283a6fa..0bd51b66 100644
--- a/src/utils/dataBS.ts
+++ b/src/utils/dataBS.ts
@@ -1,13 +1,14 @@
/**
* @file utils/dataBS.ts
* @description 用户数据的备份、恢复、迁移
- * @since Beta v0.6.7
+ * @since Beta v0.8.0
*/
import showLoading from "@comp/func/loading.js";
import showSnackbar from "@comp/func/snackbar.js";
import TSUserAbyss from "@Sqlm/userAbyss.js";
import TSUserAccount from "@Sqlm/userAccount.js";
import TSUserAchi from "@Sqlm/userAchi.js";
+import TSUserChallenge from "@Sqlm/userChallenge.js";
import TSUserCombat from "@Sqlm/userCombat.js";
import TSUserGacha from "@Sqlm/userGacha.js";
import { exists, mkdir } from "@tauri-apps/plugin-fs";
@@ -16,7 +17,7 @@ import TGLogger from "./TGLogger.js";
/**
* @description 备份用户数据
- * @since Beta v0.6.7
+ * @since Beta v0.8.0
* @param {string} dir 备份目录路径
* @returns {Promise}
*/
@@ -33,13 +34,15 @@ export async function backUpUserData(dir: string): Promise {
await TSUserAbyss.backupAbyss(dir);
await showLoading.update("正在备份真境剧诗数据");
await TSUserCombat.backupCombat(dir);
+ await showLoading.update("正在备份幽境危战数据");
+ await TSUserChallenge.backupChallenge(dir);
await showLoading.update("正在备份UIGF祈愿数据");
await TSUserGacha.backUpUigf(dir);
}
/**
* @description 恢复用户数据
- * @since Beta v0.6.7
+ * @since Beta v0.8.0
* @param {string} dir 备份目录路径
* @returns {Promise}
*/
@@ -73,6 +76,12 @@ export async function restoreUserData(dir: string): Promise {
showSnackbar.error("真境剧诗数据恢复失败");
errNum++;
}
+ await showLoading.update("正在恢复幽境危战数据");
+ const restoreChallenge = await TSUserChallenge.restoreChallenge(dir);
+ if (!restoreChallenge) {
+ showSnackbar.error("幽境危战数据恢复失败");
+ errNum++;
+ }
await showLoading.update("正在恢复祈愿数据");
const restoreGacha = await TSUserGacha.restoreUigf(dir);
if (!restoreGacha) {