mirror of
https://github.com/BTMuli/TeyvatGuide.git
synced 2025-12-13 09:28:14 +08:00
✨ UIGF4导入/导出浮窗
This commit is contained in:
340
src/components/userGacha/ugo-uid.vue
Normal file
340
src/components/userGacha/ugo-uid.vue
Normal file
@@ -0,0 +1,340 @@
|
|||||||
|
<template>
|
||||||
|
<TOverlay v-model="visible" blur-val="5px">
|
||||||
|
<div class="ugo-box">
|
||||||
|
<div class="ugo-top">
|
||||||
|
<div class="ugo-title">{{ title }}</div>
|
||||||
|
<div class="ugo-fp" title="点击选择文件路径" @click="selectFile()">文件路径:{{ fp }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="ugo-header" v-if="props.mode === 'import' && dataRaw">
|
||||||
|
<div class="ugo-header-item">
|
||||||
|
<div>应用信息:</div>
|
||||||
|
<div>{{ dataRaw.info.export_app }} {{ dataRaw.info.export_app_version }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="ugo-header-item">
|
||||||
|
<div>文件UIGF版本:</div>
|
||||||
|
<div>{{ dataRaw.info.version }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="ugo-header-item">
|
||||||
|
<div>导出时间:</div>
|
||||||
|
<div>{{ timestampToDate(Number(dataRaw.info.export_timestamp) * 1000) }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<v-item-group multiple v-model="selectedData" class="ugo-content">
|
||||||
|
<v-item
|
||||||
|
v-for="(item, index) in data"
|
||||||
|
:key="index"
|
||||||
|
v-slot="{ isSelected, toggle }"
|
||||||
|
:value="item"
|
||||||
|
>
|
||||||
|
<div class="ugo-item" @click="toggle">
|
||||||
|
<div class="ugo-item-left">
|
||||||
|
<div class="ugo-item-title">{{ item.uid }} - {{ item.length }}条记录</div>
|
||||||
|
<div class="ugo-item-sub">{{ item.time }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="ugo-item-right">
|
||||||
|
<v-btn :class="{ active: isSelected }" class="ugo-item-btn">
|
||||||
|
<v-icon>{{ isSelected ? "mdi-check" : "mdi-checkbox-blank-outline" }}</v-icon>
|
||||||
|
</v-btn>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</v-item>
|
||||||
|
</v-item-group>
|
||||||
|
<div class="ugo-bottom">
|
||||||
|
<v-btn class="ugo-item-btn" @click="visible = false" :rounded="true">取消</v-btn>
|
||||||
|
<v-btn class="ugo-item-btn" @click="handleSelected()" :rounded="true">确定</v-btn>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</TOverlay>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { path } from "@tauri-apps/api";
|
||||||
|
import { open } from "@tauri-apps/plugin-dialog";
|
||||||
|
import { writeTextFile } from "@tauri-apps/plugin-fs";
|
||||||
|
import { computed, onMounted, ref, shallowRef, watch } from "vue";
|
||||||
|
|
||||||
|
import TSUserGacha from "../../plugins/Sqlite/modules/userGacha.js";
|
||||||
|
import TGLogger from "../../utils/TGLogger.js";
|
||||||
|
import { timestampToDate } from "../../utils/toolFunc.js";
|
||||||
|
import { getUigf4Header, getUigf4Item, readUigf4Data, verifyUigfData } from "../../utils/UIGF.js";
|
||||||
|
import TOverlay from "../app/t-overlay.vue";
|
||||||
|
import showLoading from "../func/loading.js";
|
||||||
|
import showSnackbar from "../func/snackbar.js";
|
||||||
|
|
||||||
|
type UgoUidProps =
|
||||||
|
| { modelValue: boolean; mode: "export" }
|
||||||
|
| { modelValue: boolean; mode: "import" };
|
||||||
|
type UgoUidEmits = (e: "update:modelValue", v: boolean) => void;
|
||||||
|
type UgoUidItem = { uid: string; length: number; time: string };
|
||||||
|
|
||||||
|
const props = defineProps<UgoUidProps>();
|
||||||
|
const emits = defineEmits<UgoUidEmits>();
|
||||||
|
const dataRaw = shallowRef<TGApp.Plugins.UIGF.Schema4>();
|
||||||
|
const data = shallowRef<UgoUidItem[]>([]);
|
||||||
|
const selectedData = shallowRef<UgoUidItem[]>([]);
|
||||||
|
const title = computed<string>(() => (props.mode === "import" ? "导入" : "导出"));
|
||||||
|
const fp = ref<string>("未选择");
|
||||||
|
|
||||||
|
const visible = computed<boolean>({
|
||||||
|
get: () => props.modelValue,
|
||||||
|
set: (v) => emits("update:modelValue", v),
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
if (props.mode === "export") fp.value = await getDefaultSavePath();
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.modelValue,
|
||||||
|
async (v) => (v ? await refreshData() : undefined),
|
||||||
|
{ immediate: true },
|
||||||
|
);
|
||||||
|
|
||||||
|
async function getDefaultSavePath(): Promise<string> {
|
||||||
|
const tsNow = new Date().getTime();
|
||||||
|
return `${await path.downloadDir()}${path.sep()}UIGF4_${tsNow}.json`;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function refreshData(): Promise<void> {
|
||||||
|
selectedData.value = [];
|
||||||
|
data.value = [];
|
||||||
|
dataRaw.value = undefined;
|
||||||
|
if (props.mode === "import") {
|
||||||
|
fp.value = "未选择";
|
||||||
|
await handleImportData();
|
||||||
|
} else {
|
||||||
|
fp.value = await getDefaultSavePath();
|
||||||
|
await handleExportData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function selectFile(): Promise<void> {
|
||||||
|
const defaultPath =
|
||||||
|
props.mode === "import" ? await getDefaultSavePath() : await path.downloadDir();
|
||||||
|
const file = await open({
|
||||||
|
multiple: false,
|
||||||
|
title: "选择文件",
|
||||||
|
filters: [{ name: "UIGF JSON", extensions: ["json"] }],
|
||||||
|
defaultPath,
|
||||||
|
directory: false,
|
||||||
|
});
|
||||||
|
if (file === null) {
|
||||||
|
showSnackbar.cancel("已取消文件选择");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fp.value = file;
|
||||||
|
if (props.mode === "import") await handleImportData();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleImportData(): Promise<void> {
|
||||||
|
if (fp.value === "未选择") return;
|
||||||
|
try {
|
||||||
|
showLoading.start("正在导入数据...", "正在验证数据...");
|
||||||
|
const check = await verifyUigfData(fp.value, true);
|
||||||
|
if (!check) {
|
||||||
|
showLoading.end();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
showLoading.update("正在导入数据...", "正在读取数据...");
|
||||||
|
const uigfData = await readUigf4Data(fp.value);
|
||||||
|
dataRaw.value = uigfData;
|
||||||
|
data.value = uigfData.hk4e.map(parseData);
|
||||||
|
showLoading.end();
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof Error) {
|
||||||
|
showSnackbar.error(`[${e.name}] ${e.message}`);
|
||||||
|
await TGLogger.Error(`[UgoUid][handleImportData] ${e.name} ${e.message}`);
|
||||||
|
} else {
|
||||||
|
showSnackbar.error(`[${e}]`);
|
||||||
|
await TGLogger.Error(`[UgoUid][handleImportData] ${e}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseData(data: TGApp.Plugins.UIGF.GachaHk4e): UgoUidItem {
|
||||||
|
const timeList = data.list.map((item) => new Date(item.time).getTime());
|
||||||
|
return {
|
||||||
|
uid: data.uid.toString(),
|
||||||
|
length: data.list.length,
|
||||||
|
time: `${timestampToDate(Math.min(...timeList))} ~ ${timestampToDate(Math.max(...timeList))}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleExportData(): Promise<void> {
|
||||||
|
const uidList = await TSUserGacha.getUidList();
|
||||||
|
const tmpData: UgoUidItem[] = [];
|
||||||
|
for (const uid of uidList) {
|
||||||
|
const dataRaw = await TSUserGacha.getGachaRecords(uid);
|
||||||
|
tmpData.push(parseDataRaw(dataRaw));
|
||||||
|
}
|
||||||
|
data.value = tmpData;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseDataRaw(data: TGApp.Sqlite.GachaRecords.SingleTable[]): UgoUidItem {
|
||||||
|
const timeList = data.map((item) => new Date(item.time).getTime());
|
||||||
|
return {
|
||||||
|
uid: data[0].uid,
|
||||||
|
length: data.length,
|
||||||
|
time: `${timestampToDate(Math.min(...timeList))} ~ ${timestampToDate(Math.max(...timeList))}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleSelected(): Promise<void> {
|
||||||
|
if (props.mode === "import") {
|
||||||
|
if (!dataRaw.value) {
|
||||||
|
showSnackbar.error("未获取到数据!");
|
||||||
|
fp.value = "未选择";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (selectedData.value.length === 0) {
|
||||||
|
showSnackbar.warn("请至少选择一个!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (const item of selectedData.value) {
|
||||||
|
showLoading.start("正在导入数据...", `正在导入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;
|
||||||
|
}
|
||||||
|
await TSUserGacha.mergeUIGF4(dataFind);
|
||||||
|
}
|
||||||
|
showLoading.end();
|
||||||
|
showSnackbar.success("导入成功!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (selectedData.value.length === 0) {
|
||||||
|
showSnackbar.warn("请至少选择一个!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
showLoading.start("正在导出数据...", "正在生成文件头");
|
||||||
|
const header = await getUigf4Header();
|
||||||
|
const data: TGApp.Plugins.UIGF.GachaHk4e[] = [];
|
||||||
|
for (const item of selectedData.value) {
|
||||||
|
showLoading.update("正在导出数据...", `正在导出UID: ${item.uid}`);
|
||||||
|
const dataItem = await getUigf4Item(item.uid);
|
||||||
|
data.push(dataItem);
|
||||||
|
}
|
||||||
|
showLoading.update("正在导出数据...", "正在生成文件...");
|
||||||
|
await writeTextFile(fp.value, JSON.stringify({ info: header, hk4e: data }));
|
||||||
|
showLoading.end();
|
||||||
|
showSnackbar.success(`导出成功! 文件路径: ${fp.value}`);
|
||||||
|
fp.value = await getDefaultSavePath();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="css" scoped>
|
||||||
|
.ugo-box {
|
||||||
|
position: relative;
|
||||||
|
width: 600px;
|
||||||
|
padding: 10px;
|
||||||
|
border: 1px solid var(--common-shadow-2);
|
||||||
|
border-radius: 10px;
|
||||||
|
background: var(--app-page-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ugo-top {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
align-items: flex-end;
|
||||||
|
justify-content: space-between;
|
||||||
|
column-gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ugo-title {
|
||||||
|
color: var(--common-text-title);
|
||||||
|
font-family: var(--font-title);
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ugo-fp {
|
||||||
|
color: var(--tgc-od-white);
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ugo-header {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ugo-header-item {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
font-size: 16px;
|
||||||
|
gap: 5px;
|
||||||
|
|
||||||
|
:first-child {
|
||||||
|
color: var(--box-text-7);
|
||||||
|
font-family: var(--font-title);
|
||||||
|
}
|
||||||
|
|
||||||
|
:last-child {
|
||||||
|
color: var(--box-text-5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ugo-content {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
max-height: 300px;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
gap: 10px;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ugo-item {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 5px;
|
||||||
|
border: 1px solid var(--common-shadow-1);
|
||||||
|
border-radius: 5px;
|
||||||
|
background: var(--box-bg-1);
|
||||||
|
color: var(--box-text-1);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ugo-item-title {
|
||||||
|
font-family: var(--font-title);
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ugo-item-sub {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ugo-item-btn {
|
||||||
|
height: 40px;
|
||||||
|
border: 1px solid var(--common-shadow-2);
|
||||||
|
background: var(--btn-bg-1);
|
||||||
|
color: var(--btn-text-1);
|
||||||
|
font-family: var(--font-title);
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
color: var(--tgc-od-green);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ugo-bottom {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -113,11 +113,11 @@
|
|||||||
},
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "记录内部 ID,米哈游 API 返回"
|
"description": "记录内部 ID,米哈游 API 返回",
|
||||||
},
|
"maxLength": 19,
|
||||||
"maxLength": 19,
|
"minLength": 1,
|
||||||
"minLength": 1,
|
"pattern": "^[0-9]+$"
|
||||||
"pattern": "^[0-9]+$"
|
}
|
||||||
},
|
},
|
||||||
"required": ["uigf_gacha_type", "gacha_type", "item_id", "time", "id"]
|
"required": ["uigf_gacha_type", "gacha_type", "item_id", "time", "id"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,10 +12,8 @@
|
|||||||
<v-btn prepend-icon="mdi-refresh" class="gacha-top-btn" @click="confirmRefresh(true)">
|
<v-btn prepend-icon="mdi-refresh" class="gacha-top-btn" @click="confirmRefresh(true)">
|
||||||
全量刷新
|
全量刷新
|
||||||
</v-btn>
|
</v-btn>
|
||||||
<v-btn prepend-icon="mdi-import" class="gacha-top-btn" @click="handleImportBtn(false)">
|
<v-btn prepend-icon="mdi-import" class="gacha-top-btn" @click="importUigf()">导入</v-btn>
|
||||||
导入
|
<v-btn prepend-icon="mdi-import" class="gacha-top-btn" @click="importUigf4()">
|
||||||
</v-btn>
|
|
||||||
<v-btn prepend-icon="mdi-import" class="gacha-top-btn" @click="handleImportBtn(true)">
|
|
||||||
导入(v4)
|
导入(v4)
|
||||||
</v-btn>
|
</v-btn>
|
||||||
<v-btn prepend-icon="mdi-export" class="gacha-top-btn" @click="exportUigf()">导出</v-btn>
|
<v-btn prepend-icon="mdi-export" class="gacha-top-btn" @click="exportUigf()">导出</v-btn>
|
||||||
@@ -47,6 +45,7 @@
|
|||||||
</v-window-item>
|
</v-window-item>
|
||||||
</v-window>
|
</v-window>
|
||||||
</div>
|
</div>
|
||||||
|
<UgoUid v-model="ovShow" :mode="ovMode" />
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { path } from "@tauri-apps/api";
|
import { path } from "@tauri-apps/api";
|
||||||
@@ -61,17 +60,12 @@ import GroEcharts from "../../components/userGacha/gro-echarts.vue";
|
|||||||
import GroHistory from "../../components/userGacha/gro-history.vue";
|
import GroHistory from "../../components/userGacha/gro-history.vue";
|
||||||
import GroOverview from "../../components/userGacha/gro-overview.vue";
|
import GroOverview from "../../components/userGacha/gro-overview.vue";
|
||||||
import GroTable from "../../components/userGacha/gro-table.vue";
|
import GroTable from "../../components/userGacha/gro-table.vue";
|
||||||
|
import UgoUid from "../../components/userGacha/ugo-uid.vue";
|
||||||
import { AppCharacterData, AppWeaponData } from "../../data/index.js";
|
import { AppCharacterData, AppWeaponData } from "../../data/index.js";
|
||||||
import TSUserGacha from "../../plugins/Sqlite/modules/userGacha.js";
|
import TSUserGacha from "../../plugins/Sqlite/modules/userGacha.js";
|
||||||
import { useUserStore } from "../../store/modules/user.js";
|
import { useUserStore } from "../../store/modules/user.js";
|
||||||
import TGLogger from "../../utils/TGLogger.js";
|
import TGLogger from "../../utils/TGLogger.js";
|
||||||
import {
|
import { exportUigfData, readUigfData, verifyUigfData } from "../../utils/UIGF.js";
|
||||||
exportUigf4Data,
|
|
||||||
exportUigfData,
|
|
||||||
readUigf4Data,
|
|
||||||
readUigfData,
|
|
||||||
verifyUigfData,
|
|
||||||
} from "../../utils/UIGF.js";
|
|
||||||
import Hk4eApi from "../../web/request/hk4eReq.js";
|
import Hk4eApi from "../../web/request/hk4eReq.js";
|
||||||
import TakumiApi from "../../web/request/takumiReq.js";
|
import TakumiApi from "../../web/request/takumiReq.js";
|
||||||
|
|
||||||
@@ -86,6 +80,10 @@ const uidCur = ref<string>();
|
|||||||
const gachaListCur = ref<TGApp.Sqlite.GachaRecords.SingleTable[]>([]);
|
const gachaListCur = ref<TGApp.Sqlite.GachaRecords.SingleTable[]>([]);
|
||||||
const tab = ref<string>("overview");
|
const tab = ref<string>("overview");
|
||||||
|
|
||||||
|
// overlay
|
||||||
|
const ovShow = ref<boolean>(false);
|
||||||
|
const ovMode = ref<"export" | "import">("import");
|
||||||
|
|
||||||
// 监听 UID 变化
|
// 监听 UID 变化
|
||||||
watch(
|
watch(
|
||||||
() => uidCur.value,
|
() => uidCur.value,
|
||||||
@@ -243,9 +241,14 @@ async function refreshGachaPool(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 导入按钮点击事件
|
// 导入 v4 版本的祈愿数据
|
||||||
async function handleImportBtn(isV4: boolean): Promise<void> {
|
async function importUigf4(): Promise<void> {
|
||||||
await TGLogger.Info(`[UserGacha][handleImportBtn] 导入祈愿数据${isV4 ? "(v4)" : ""}`);
|
ovMode.value = "import";
|
||||||
|
ovShow.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function importUigf(): Promise<void> {
|
||||||
|
await TGLogger.Info(`[UserGacha][handleImportBtn] 导入祈愿数据`);
|
||||||
const selectedFile = await open({
|
const selectedFile = await open({
|
||||||
multiple: false,
|
multiple: false,
|
||||||
title: "导入UIGF文件",
|
title: "导入UIGF文件",
|
||||||
@@ -257,40 +260,9 @@ async function handleImportBtn(isV4: boolean): Promise<void> {
|
|||||||
showSnackbar.cancel("已取消文件选择");
|
showSnackbar.cancel("已取消文件选择");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const check = await verifyUigfData(selectedFile, isV4);
|
const check = await verifyUigfData(selectedFile, false);
|
||||||
if (!check) return;
|
if (!check) return;
|
||||||
if (isV4) await importUigf4(selectedFile);
|
const remoteData = await readUigfData(selectedFile);
|
||||||
else await importUigf(selectedFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 导入 v4 版本的祈愿数据
|
|
||||||
async function importUigf4(filePath: string): Promise<void> {
|
|
||||||
const remoteData = await readUigf4Data(filePath);
|
|
||||||
const uidCount = remoteData.hk4e.length;
|
|
||||||
const dataCount = remoteData.hk4e.reduce((acc, cur) => acc + cur.list.length, 0);
|
|
||||||
const importCheck = await showDialog.check(
|
|
||||||
"是否导入祈愿数据?",
|
|
||||||
`共 ${uidCount} 个 UID,${dataCount} 条数据`,
|
|
||||||
);
|
|
||||||
if (!importCheck) {
|
|
||||||
showSnackbar.cancel("已取消祈愿数据导入");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
showLoading.start("正在导入祈愿数据(v4)");
|
|
||||||
for (const account of remoteData.hk4e) {
|
|
||||||
showLoading.update("正在导入祈愿数据(v4)", `UID:${account.uid}`);
|
|
||||||
await TSUserGacha.mergeUIGF4(account);
|
|
||||||
}
|
|
||||||
showLoading.end();
|
|
||||||
showSnackbar.success(`成功导入 ${uidCount} 个 UID 的 ${dataCount} 条祈愿数据`);
|
|
||||||
await TGLogger.Info(
|
|
||||||
`[UserGacha][importUigf4] 成功导入 ${uidCount} 个 UID,${dataCount} 条祈愿数据`,
|
|
||||||
);
|
|
||||||
setTimeout(() => window.location.reload(), 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function importUigf(filePath: string): Promise<void> {
|
|
||||||
const remoteData = await readUigfData(filePath);
|
|
||||||
const importCheck = await showDialog.check(
|
const importCheck = await showDialog.check(
|
||||||
"是否导入祈愿数据?",
|
"是否导入祈愿数据?",
|
||||||
`UID:${remoteData.info.uid},共 ${remoteData.list.length} 条数据`,
|
`UID:${remoteData.info.uid},共 ${remoteData.list.length} 条数据`,
|
||||||
@@ -353,47 +325,13 @@ async function exportUigf(): Promise<void> {
|
|||||||
|
|
||||||
// 导出 UIGF v4 版本的祈愿数据
|
// 导出 UIGF v4 版本的祈愿数据
|
||||||
async function exportUigf4(): Promise<void> {
|
async function exportUigf4(): Promise<void> {
|
||||||
if (!uidCur.value) return;
|
if (!uidCur.value) {
|
||||||
const exportCheck = await showDialog.check("确定导出UIGFv4格式的祈愿数据?");
|
showSnackbar.error("未获取到 UID");
|
||||||
if (!exportCheck) {
|
|
||||||
showSnackbar.cancel("已取消 UIGF v4 格式导出");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await TGLogger.Info(`[UserGacha][${uidCur.value}][exportUigf4] 导出祈愿数据(v4)`);
|
await TGLogger.Info(`[UserGacha][${uidCur.value}][exportUigf4] 导出祈愿数据(v4)`);
|
||||||
// todo 单开一个overlay用于选取导出的UID
|
ovMode.value = "export";
|
||||||
const exportAllCheck = await showDialog.check(
|
ovShow.value = true;
|
||||||
"是否导出所有 UID 的祈愿数据?",
|
|
||||||
"取消则只导出当前 UID 的祈愿数据",
|
|
||||||
);
|
|
||||||
if (exportAllCheck === undefined) {
|
|
||||||
showSnackbar.cancel("已取消 UIGF v4 格式导出");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!exportAllCheck) {
|
|
||||||
const gachaList = await TSUserGacha.getGachaRecords(uidCur.value);
|
|
||||||
if (gachaList.length === 0) {
|
|
||||||
showSnackbar.error(`UID ${uidCur.value} 暂无祈愿数据`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const file = await save({
|
|
||||||
title: "选择导出祈愿数据的文件路径",
|
|
||||||
filters: [{ name: "UIGF JSON", extensions: ["json"] }],
|
|
||||||
defaultPath: `${await path.downloadDir()}${path.sep()}UIGF4.json`,
|
|
||||||
});
|
|
||||||
if (!file) {
|
|
||||||
showSnackbar.cancel("已取消文件保存");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
showLoading.start("正在导出祈愿数据", `路径:${file}`);
|
|
||||||
if (!exportAllCheck) {
|
|
||||||
await exportUigf4Data(file, uidCur.value);
|
|
||||||
} else {
|
|
||||||
await exportUigf4Data(file);
|
|
||||||
}
|
|
||||||
showLoading.end();
|
|
||||||
showSnackbar.success("祈愿数据已成功导出");
|
|
||||||
await TGLogger.Info(`[UserGacha][${uidCur.value}][exportUigf4] 导出祈愿数据完成`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除当前 UID 的祈愿数据
|
// 删除当前 UID 的祈愿数据
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
* @file utils/UIGF.ts
|
* @file utils/UIGF.ts
|
||||||
* @description UIGF工具类
|
* @description UIGF工具类
|
||||||
* @since Beta v0.5.5
|
* @since Beta v0.6.5
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { app, path } from "@tauri-apps/api";
|
import { app, path } from "@tauri-apps/api";
|
||||||
@@ -53,7 +53,7 @@ async function getUigfHeader(uid: string): Promise<TGApp.Plugins.UIGF.Info> {
|
|||||||
* @since Beta v0.5.1
|
* @since Beta v0.5.1
|
||||||
* @returns {TGApp.Plugins.UIGF.Info4} UIGF v4.0 头部信息
|
* @returns {TGApp.Plugins.UIGF.Info4} UIGF v4.0 头部信息
|
||||||
*/
|
*/
|
||||||
async function getUigf4Header(): Promise<TGApp.Plugins.UIGF.Info4> {
|
export async function getUigf4Header(): Promise<TGApp.Plugins.UIGF.Info4> {
|
||||||
const stamp = Date.now();
|
const stamp = Date.now();
|
||||||
return {
|
return {
|
||||||
export_timestamp: Math.floor(stamp / 1000).toString(),
|
export_timestamp: Math.floor(stamp / 1000).toString(),
|
||||||
@@ -90,14 +90,14 @@ function convertDataToUigf(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @description 检测是否存在 UIGF 数据,采用 ajv 验证 schema
|
* @description 检测是否存在 UIGF 数据,采用 ajv 验证 schema
|
||||||
* @since Beta v0.5.0
|
* @since Beta v0.6.5
|
||||||
* @param {string} path - UIGF 数据路径
|
* @param {string} path - UIGF 数据路径
|
||||||
* @param {boolean} isVersion4 - 是否为 UIGF v4.0
|
* @param {boolean} isVersion4 - 是否为 UIGF v4.0
|
||||||
* @returns {Promise<boolean>} 是否存在 UIGF 数据
|
* @returns {Promise<boolean>} 是否存在 UIGF 数据
|
||||||
*/
|
*/
|
||||||
export async function verifyUigfData(path: string, isVersion4: boolean = false): Promise<boolean> {
|
export async function verifyUigfData(path: string, isVersion4: boolean = false): Promise<boolean> {
|
||||||
const fileData: string = await readTextFile(path);
|
|
||||||
try {
|
try {
|
||||||
|
const fileData: string = await readTextFile(path);
|
||||||
const fileJson = JSON.parse(fileData);
|
const fileJson = JSON.parse(fileData);
|
||||||
if (isVersion4) return validateUigf4Data(fileJson);
|
if (isVersion4) return validateUigf4Data(fileJson);
|
||||||
return validateUigfData(fileJson);
|
return validateUigfData(fileJson);
|
||||||
@@ -194,31 +194,15 @@ export async function exportUigfData(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description 导出 UIGF v4.0 数据
|
* @description 获取单项UID的UIGF4.0数据
|
||||||
* @since Beta v0.5.0
|
* @param {string} uid - UID
|
||||||
* @param {string} filePath - 保存路径
|
* @returns {Promise<TGApp.Plugins.UIGF.GachaHk4e>}
|
||||||
* @param {string} uid - UID,如果为空表示导出所有数据
|
|
||||||
* @returns {Promise<void>}
|
|
||||||
*/
|
*/
|
||||||
export async function exportUigf4Data(filePath: string, uid?: string): Promise<void> {
|
export async function getUigf4Item(uid: string): Promise<TGApp.Plugins.UIGF.GachaHk4e> {
|
||||||
const UigfData: TGApp.Plugins.UIGF.Schema4 = {
|
const gachaList = await TSUserGacha.getGachaRecords(uid);
|
||||||
info: await getUigf4Header(),
|
return {
|
||||||
hk4e: [],
|
uid: uid,
|
||||||
|
timezone: getUigfTimeZone(uid),
|
||||||
|
list: convertDataToUigf(gachaList),
|
||||||
};
|
};
|
||||||
let uidList: string[] = [];
|
|
||||||
if (uid) {
|
|
||||||
uidList.push(uid);
|
|
||||||
} else {
|
|
||||||
uidList = await TSUserGacha.getUidList();
|
|
||||||
}
|
|
||||||
for (const uid of uidList) {
|
|
||||||
const gachaList = await TSUserGacha.getGachaRecords(uid);
|
|
||||||
const data: TGApp.Plugins.UIGF.GachaHk4e = {
|
|
||||||
uid: uid,
|
|
||||||
timezone: getUigfTimeZone(uid),
|
|
||||||
list: convertDataToUigf(gachaList),
|
|
||||||
};
|
|
||||||
UigfData.hk4e.push(data);
|
|
||||||
}
|
|
||||||
await writeTextFile(filePath, JSON.stringify(UigfData));
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user