mirror of
https://github.com/BTMuli/TeyvatGuide.git
synced 2026-03-21 04:49:46 +08:00
♻️ 合并导入
This commit is contained in:
@@ -5,18 +5,22 @@
|
||||
<div class="ugo-title">{{ title }}</div>
|
||||
<div class="ugo-fp" title="点击选择文件路径" @click="selectFile()">文件路径:{{ fp }}</div>
|
||||
</div>
|
||||
<div v-if="props.mode === 'import' && dataRaw" class="ugo-header">
|
||||
<div v-if="props.mode === 'import' && importRaw" class="ugo-header">
|
||||
<div class="ugo-header-item">
|
||||
<div>应用信息:</div>
|
||||
<div>{{ dataRaw.info.export_app }} {{ dataRaw.info.export_app_version }}</div>
|
||||
<div>
|
||||
{{ importRaw.data.info.export_app }} {{ importRaw.data.info.export_app_version }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="ugo-header-item">
|
||||
<div>文件UIGF版本:</div>
|
||||
<div>{{ dataRaw.info.version }}</div>
|
||||
<div>
|
||||
{{ importRaw.isV4 ? importRaw.data.info.version : importRaw.data.info.uigf_version }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="ugo-header-item">
|
||||
<div>导出时间:</div>
|
||||
<div>{{ timestampToDate(Number(dataRaw.info.export_timestamp) * 1000) }}</div>
|
||||
<div>{{ timestampToDate(Number(importRaw.data.info.export_timestamp) * 1000) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<v-item-group v-model="selectedData" class="ugo-content" multiple>
|
||||
@@ -55,17 +59,27 @@ import { path } from "@tauri-apps/api";
|
||||
import { open } from "@tauri-apps/plugin-dialog";
|
||||
import TGLogger from "@utils/TGLogger.js";
|
||||
import { timestampToDate } from "@utils/toolFunc.js";
|
||||
import { exportUigf4Data, readUigf4Data, verifyUigfData } from "@utils/UIGF.js";
|
||||
import { checkUigfData, exportUigf4Data, readUigf4Data, readUigfData } from "@utils/UIGF.js";
|
||||
import { computed, onMounted, ref, shallowRef, watch } from "vue";
|
||||
|
||||
type UgoUidProps = { mode: "import" | "export" };
|
||||
type UgoUidProps = {
|
||||
/** 导入/导出 */
|
||||
mode: "import" | "export";
|
||||
/** filePathImport,导出路径 */
|
||||
fpi?: string;
|
||||
};
|
||||
type UgoUidItem = { uid: string; length: number; time: string };
|
||||
/** 兼容不同版本的导入 */
|
||||
type UgoUidImportRaw =
|
||||
| { isV4: true; data: TGApp.Plugins.UIGF.Schema4 }
|
||||
| { isV4: false; data: TGApp.Plugins.UIGF.Schema };
|
||||
|
||||
const fpEmptyText = "点击选择文件路径";
|
||||
|
||||
const props = defineProps<UgoUidProps>();
|
||||
const visible = defineModel<boolean>();
|
||||
const fp = ref<string>(fpEmptyText);
|
||||
const importRaw = shallowRef<UgoUidImportRaw>();
|
||||
const dataRaw = shallowRef<TGApp.Plugins.UIGF.Schema4>();
|
||||
const data = shallowRef<Array<UgoUidItem>>([]);
|
||||
const selectedData = shallowRef<Array<UgoUidItem>>([]);
|
||||
@@ -76,7 +90,7 @@ onMounted(async () => {
|
||||
});
|
||||
|
||||
watch(
|
||||
() => [visible.value, props.mode],
|
||||
() => [visible.value, props.mode, props.fpi],
|
||||
async () => {
|
||||
if (visible.value) await refreshData();
|
||||
},
|
||||
@@ -91,12 +105,13 @@ async function refreshData(): Promise<void> {
|
||||
selectedData.value = [];
|
||||
data.value = [];
|
||||
dataRaw.value = undefined;
|
||||
importRaw.value = undefined;
|
||||
if (props.mode === "import") {
|
||||
fp.value = fpEmptyText;
|
||||
await handleImportData();
|
||||
fp.value = props.fpi ?? fpEmptyText;
|
||||
await refreshImport();
|
||||
} else {
|
||||
fp.value = await getDefaultSavePath();
|
||||
await handleExportData();
|
||||
await refreshExport();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,22 +130,30 @@ async function selectFile(): Promise<void> {
|
||||
return;
|
||||
}
|
||||
fp.value = file;
|
||||
if (props.mode === "import") await handleImportData();
|
||||
if (props.mode === "import") await refreshImport();
|
||||
}
|
||||
|
||||
async function handleImportData(): Promise<void> {
|
||||
async function refreshImport(): Promise<void> {
|
||||
if (fp.value === fpEmptyText) return;
|
||||
try {
|
||||
await showLoading.start("正在导入数据...", "正在验证数据...");
|
||||
const check = await verifyUigfData(fp.value, true);
|
||||
if (!check) {
|
||||
const isV4 = await checkUigfData(fp.value);
|
||||
console.info(isV4);
|
||||
if (isV4 === null) {
|
||||
await showLoading.end();
|
||||
return;
|
||||
}
|
||||
await showLoading.update("数据验证成功,正在读取数据...");
|
||||
const uigfData = await readUigf4Data(fp.value);
|
||||
dataRaw.value = uigfData;
|
||||
data.value = uigfData.hk4e.map(parseData);
|
||||
if (isV4) {
|
||||
const read = await readUigf4Data(fp.value);
|
||||
importRaw.value = { isV4: true, data: read };
|
||||
data.value = read.hk4e.map(parseData4);
|
||||
} else {
|
||||
const read = await readUigfData(fp.value);
|
||||
console.log(read.list.length);
|
||||
importRaw.value = { isV4: false, data: read };
|
||||
data.value = [parseData(read)];
|
||||
}
|
||||
await showLoading.end();
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
@@ -143,7 +166,16 @@ async function handleImportData(): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
function parseData(data: TGApp.Plugins.UIGF.GachaHk4e): UgoUidItem {
|
||||
function parseData(data: TGApp.Plugins.UIGF.Schema): UgoUidItem {
|
||||
const timeList = data.list.map((item) => new Date(item.time).getTime());
|
||||
return {
|
||||
uid: data.info.uid,
|
||||
length: data.list.length,
|
||||
time: `${timestampToDate(Math.min(...timeList))} ~ ${timestampToDate(Math.max(...timeList))}`,
|
||||
};
|
||||
}
|
||||
|
||||
function parseData4(data: TGApp.Plugins.UIGF.GachaHk4e): UgoUidItem {
|
||||
const timeList = data.list.map((item) => new Date(item.time).getTime());
|
||||
return {
|
||||
uid: data.uid.toString(),
|
||||
@@ -152,7 +184,7 @@ function parseData(data: TGApp.Plugins.UIGF.GachaHk4e): UgoUidItem {
|
||||
};
|
||||
}
|
||||
|
||||
async function handleExportData(): Promise<void> {
|
||||
async function refreshExport(): Promise<void> {
|
||||
const uidList = await TSUserGacha.getUidList();
|
||||
const tmpData: Array<UgoUidItem> = [];
|
||||
for (const uid of uidList) {
|
||||
@@ -177,7 +209,7 @@ async function handleSelected(): Promise<void> {
|
||||
}
|
||||
|
||||
async function handleImport(): Promise<void> {
|
||||
if (!dataRaw.value) {
|
||||
if (!importRaw.value) {
|
||||
showSnackbar.error("未获取到数据!");
|
||||
fp.value = fpEmptyText;
|
||||
return;
|
||||
@@ -187,15 +219,21 @@ async function handleImport(): Promise<void> {
|
||||
return;
|
||||
}
|
||||
await showLoading.start("正在导入数据...");
|
||||
for (const item of selectedData.value) {
|
||||
await showLoading.update(`正在导入UID: ${item.uid}`);
|
||||
const dataFind = dataRaw.value.hk4e.find((i) => i.uid.toString() === item.uid);
|
||||
if (!dataFind) {
|
||||
showSnackbar.error(`未找到UID: ${item.uid}`);
|
||||
await new Promise<void>((resolve) => setTimeout(resolve, 1000));
|
||||
continue;
|
||||
if (importRaw.value.isV4) {
|
||||
for (const item of selectedData.value) {
|
||||
await showLoading.update(`正在导入UID: ${item.uid}`);
|
||||
const dataFind = importRaw.value.data.hk4e.find((i) => i.uid.toString() === item.uid);
|
||||
if (!dataFind) {
|
||||
showSnackbar.error(`未找到UID: ${item.uid}`);
|
||||
await new Promise<void>((resolve) => setTimeout(resolve, 1000));
|
||||
continue;
|
||||
}
|
||||
await TSUserGacha.mergeUIGF4(dataFind, true);
|
||||
}
|
||||
await TSUserGacha.mergeUIGF4(dataFind, true);
|
||||
} else {
|
||||
const iUid = selectedData.value[0].uid;
|
||||
await showLoading.update(`正在导入UID:${iUid}`);
|
||||
await TSUserGacha.mergeUIGF(iUid, importRaw.value.data.list, true);
|
||||
}
|
||||
await showLoading.end();
|
||||
showSnackbar.success("导入成功!即将刷新页面...");
|
||||
|
||||
@@ -82,14 +82,6 @@
|
||||
>
|
||||
导入
|
||||
</v-btn>
|
||||
<v-btn
|
||||
class="gacha-top-btn"
|
||||
prepend-icon="mdi-import"
|
||||
variant="elevated"
|
||||
@click="importUigf4()"
|
||||
>
|
||||
导入(v4)
|
||||
</v-btn>
|
||||
<v-btn
|
||||
class="gacha-top-btn"
|
||||
prepend-icon="mdi-export"
|
||||
@@ -153,7 +145,7 @@
|
||||
</v-window-item>
|
||||
</v-window>
|
||||
</div>
|
||||
<UgoUid v-model="ovShow" :mode="ovMode" />
|
||||
<UgoUid v-model="ovShow" :fpi="ovFpi" :mode="ovMode" />
|
||||
<UgoHutaoDu v-model="hutaoShow" :mode="htMode" @selected="handleHutaoDu" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
@@ -179,7 +171,7 @@ import { open, save } from "@tauri-apps/plugin-dialog";
|
||||
import Hakushi from "@utils/Hakushi.js";
|
||||
import TGLogger from "@utils/TGLogger.js";
|
||||
import { str2timeStr, timeStr2str } from "@utils/toolFunc.js";
|
||||
import { exportUigfData, readUigfData, verifyUigfData } from "@utils/UIGF.js";
|
||||
import { exportUigfData } from "@utils/UIGF.js";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { onMounted, ref, shallowRef, watch } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
@@ -193,12 +185,14 @@ const { isLogin } = storeToRefs(useAppStore());
|
||||
const { account, cookie } = storeToRefs(useUserStore());
|
||||
const { isLogin: isLoginHutao, accessToken, userName, userInfo } = storeToRefs(hutaoStore);
|
||||
|
||||
const ovMode = ref<"export" | "import">("import");
|
||||
const ovShow = ref<boolean>(false);
|
||||
const ovFpi = ref<string>();
|
||||
|
||||
const authkey = ref<string>("");
|
||||
const uidCur = ref<string>();
|
||||
const tab = ref<string>("overview");
|
||||
const ovShow = ref<boolean>(false);
|
||||
const hutaoShow = ref<boolean>(false);
|
||||
const ovMode = ref<"export" | "import">("import");
|
||||
const htMode = ref<UgoHutaoMode>("download");
|
||||
const selectItem = shallowRef<Array<string>>([]);
|
||||
const gachaListCur = shallowRef<Array<TGApp.Sqlite.Gacha.Gacha>>([]);
|
||||
@@ -605,11 +599,6 @@ async function refreshGachaPool(
|
||||
}
|
||||
}
|
||||
|
||||
function importUigf4(): void {
|
||||
ovMode.value = "import";
|
||||
ovShow.value = true;
|
||||
}
|
||||
|
||||
async function loadHakushi(): Promise<void> {
|
||||
try {
|
||||
hakushiData.value = await Hakushi.fetch();
|
||||
@@ -634,28 +623,9 @@ async function importUigf(): Promise<void> {
|
||||
showSnackbar.cancel("已取消文件选择");
|
||||
return;
|
||||
}
|
||||
await showLoading.start("正在导入祈愿数据", "正在验证祈愿数据");
|
||||
const check = await verifyUigfData(selectedFile, false);
|
||||
if (!check) {
|
||||
await showLoading.end();
|
||||
return;
|
||||
}
|
||||
await showLoading.update("正在读取祈愿数据");
|
||||
const remoteData = await readUigfData(selectedFile);
|
||||
await showLoading.update(`UID:${remoteData.info.uid},共 ${remoteData.list.length} 条数据`);
|
||||
if (remoteData.list.length === 0) {
|
||||
await showLoading.end();
|
||||
showSnackbar.error("导入的祈愿数据为空");
|
||||
return;
|
||||
}
|
||||
await TSUserGacha.mergeUIGF(remoteData.info.uid, remoteData.list, true);
|
||||
await showLoading.end();
|
||||
showSnackbar.success(`成功导入 ${remoteData.list.length} 条祈愿数据,即将刷新页面`);
|
||||
await TGLogger.Info(
|
||||
`[UserGacha][importUigf] 成功导入 ${remoteData.info.uid} 的 ${remoteData.list.length} 条祈愿数据`,
|
||||
);
|
||||
await new Promise<void>((resolve) => setTimeout(resolve, 1500));
|
||||
window.location.reload();
|
||||
ovFpi.value = selectedFile;
|
||||
ovMode.value = "import";
|
||||
ovShow.value = true;
|
||||
}
|
||||
|
||||
// 导出当前UID的祈愿数据
|
||||
|
||||
@@ -62,7 +62,7 @@ export async function isRunInAdmin(): Promise<boolean> {
|
||||
|
||||
/**
|
||||
* 尝试调用Yae
|
||||
* @since Beta v0.9.1
|
||||
* @since Beta v0.9.2
|
||||
* @param gameDir - 游戏目录
|
||||
* @param uid - 启动UID
|
||||
* @returns void
|
||||
@@ -92,7 +92,6 @@ export async function tryCallYae(gameDir: string, uid?: string): Promise<void> {
|
||||
}
|
||||
const adminCheck = await isRunInAdmin();
|
||||
if (!adminCheck) {
|
||||
showSnackbar.warn("未检测到管理员权限");
|
||||
const check = await showDialog.check("是否以管理员模式重启?", "该功能需要管理员权限才能使用");
|
||||
if (!check) {
|
||||
showSnackbar.cancel("已取消以管理员模式重启");
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* UIGF工具类
|
||||
* @since Beta v0.9.0
|
||||
* @since Beta v0.9.2
|
||||
*/
|
||||
|
||||
import showLoading from "@comp/func/loading.js";
|
||||
@@ -105,6 +105,36 @@ function convertDataToUigf(
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测是否是v4版本的UIGF
|
||||
* @since Beta v0.9.2
|
||||
* @param path - UIGF 文件路径
|
||||
* @returns 是否是v4,null表示数据异常
|
||||
*/
|
||||
export async function checkUigfData(path: string): Promise<boolean | null> {
|
||||
try {
|
||||
const fileData: string = await readTextFile(path);
|
||||
const fileJson = JSON.parse(fileData);
|
||||
if (!("info" in fileJson) || typeof fileJson.info !== "object") {
|
||||
validateUigf4Data(fileJson);
|
||||
return null;
|
||||
}
|
||||
if ("uigf_version" in fileJson.info) {
|
||||
const check = validateUigfData(fileJson);
|
||||
if (!check) return null;
|
||||
return false;
|
||||
}
|
||||
const check = validateUigf4Data(fileJson);
|
||||
if (!check) return null;
|
||||
return true;
|
||||
} catch (e) {
|
||||
showSnackbar.error(`UIGF校验异常:${e}`);
|
||||
await TGLogger.Error(`[checkUigfData]路径:${path}`);
|
||||
await TGLogger.Error(`[checkUigfData]异常:${e}`);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测是否存在 UIGF 数据,采用 ajv 验证 schema
|
||||
* @since Beta v0.6.5
|
||||
|
||||
Reference in New Issue
Block a user