♻️ 合并导入

This commit is contained in:
BTMuli
2026-01-15 20:58:30 +08:00
parent bd54e86f5b
commit 7dcbd8204a
4 changed files with 107 additions and 70 deletions

View File

@@ -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("导入成功!即将刷新页面...");

View File

@@ -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的祈愿数据

View File

@@ -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("已取消以管理员模式重启");

View File

@@ -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 是否是v4null表示数据异常
*/
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