♻️ uid 直接读取数据库,优化渲染

This commit is contained in:
BTMuli
2023-08-31 20:08:04 +08:00
parent f842da01fa
commit 1bdc614940
4 changed files with 143 additions and 78 deletions

View File

@@ -1,10 +1,13 @@
<template> <template>
<div v-if="!loading" class="gro-dv-container"> <div class="gro-dv-container">
<div class="gro-dvt-title"> <div class="gro-dvt-title">
<span>{{ title }}</span> <span>{{ title }}</span>
<span>{{ props.dataVal.length }}</span> <span>{{ props.dataVal.length }}</span>
</div> </div>
<div class="gro-dvt-subtitle">{{ startDate }} ~ {{ endDate }}</div> <div class="gro-dvt-subtitle">
<span v-show="props.dataVal.length === 0">暂无数据</span>
<span v-show="props.dataVal.length !== 0">{{ startDate }} ~ {{ endDate }}</span>
</div>
<div class="gro-mid-list"> <div class="gro-mid-list">
<div class="gro-ml-item"> <div class="gro-ml-item">
<span>4已垫</span> <span>4已垫</span>
@@ -64,7 +67,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
// vue // vue
import { onMounted, ref } from "vue"; import { onMounted, ref, watch } from "vue";
interface GachaDataViewProps { interface GachaDataViewProps {
dataType: "new" | "avatar" | "weapon" | "normal"; dataType: "new" | "avatar" | "weapon" | "normal";
@@ -147,24 +150,27 @@ function getTitle(type: "top" | "5" | "4" | "3"): string {
if (props.dataType === "normal") return "常驻祈愿"; if (props.dataType === "normal") return "常驻祈愿";
return ""; return "";
} else if (type === "5") { } else if (type === "5") {
// 5星物品统计 // 5星物品统计 00.00%
return `${star5List.value.length} [${(star5List.value.length / props.dataVal.length).toFixed( return `${star5List.value.length} [${((star5List.value.length * 100) / props.dataVal.length)
2, .toFixed(2)
)}%]`; .padStart(5, "0")}%]`;
} else if (type === "4") { } else if (type === "4") {
// 4星物品统计 // 4星物品统计
return `${star4List.value.length} [${(star4List.value.length / props.dataVal.length).toFixed( return `${star4List.value.length} [${((star4List.value.length * 100) / props.dataVal.length)
2, .toFixed(2)
)}%]`; .padStart(5, "0")}%]`;
} else { } else {
// 3星物品统计 // 3星物品统计
return `${star3count.value} [${(star3count.value / props.dataVal.length).toFixed(2)}%]`; return `${star3count.value} [${((star3count.value * 100) / props.dataVal.length)
.toFixed(2)
.padStart(5, "0")}%]`;
} }
} }
// 获取5星平均抽数 // 获取5星平均抽数
function getStar5Avg(): string { function getStar5Avg(): string {
const resetList = star5List.value.map((item) => item.gachaCount); const resetList = star5List.value.map((item) => item.gachaCount);
if (resetList.length === 0) return "0";
const total = resetList.reduce((a, b) => a + b); const total = resetList.reduce((a, b) => a + b);
return (total / star5List.value.length).toFixed(2); return (total / star5List.value.length).toFixed(2);
} }
@@ -177,6 +183,23 @@ function getIcon(itemId: string, type: string): string {
return "/WIKI/weapon/icon/" + itemId + ".webp"; return "/WIKI/weapon/icon/" + itemId + ".webp";
} }
} }
// 监听数据变化
watch(
() => props.dataVal,
(newVal) => {
star5List.value = [];
star4List.value = [];
reset5count.value = 0;
reset4count.value = 0;
star3count.value = 0;
startDate.value = "";
endDate.value = "";
star5avg.value = "";
tab.value = "5";
loadData();
},
);
</script> </script>
<style lang="css" scoped> <style lang="css" scoped>
.gro-dv-container { .gro-dv-container {

View File

@@ -1,13 +1,19 @@
<template> <template>
<div class="gro-o-container"> <div
<gro-dataview v-if="newData.length > 0" :data-val="newData" data-type="new" /> class="gro-o-container"
<gro-dataview :data-val="normalData" data-type="normal" /> :style="{
<gro-dataview :data-val="avatarData" data-type="avatar" /> gridTemplateColumns: newData.length !== 0 ? 'repeat(4, 1fr)' : 'repeat(3, 1fr)',
<gro-dataview :data-val="weaponData" data-type="weapon" /> }"
>
<gro-dataview v-if="newData.length !== 0" v-model:data-val="newData" data-type="new" />
<gro-dataview v-model:data-val="normalData" data-type="normal" />
<gro-dataview v-model:data-val="avatarData" data-type="avatar" />
<gro-dataview v-model:data-val="weaponData" data-type="weapon" />
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
// vue // vue
import { watch } from "vue";
import GroDataview from "./gro-dataview.vue"; import GroDataview from "./gro-dataview.vue";
interface GachaOverviewProps { interface GachaOverviewProps {
@@ -15,13 +21,22 @@ interface GachaOverviewProps {
} }
const props = defineProps<GachaOverviewProps>(); const props = defineProps<GachaOverviewProps>();
// data // data
const newData = props.modelValue.filter((item) => item.uigfType === "100"); let newData = props.modelValue.filter((item) => item.uigfType === "100");
const normalData = props.modelValue.filter((item) => item.uigfType === "200"); let normalData = props.modelValue.filter((item) => item.uigfType === "200");
const avatarData = props.modelValue.filter((item) => item.uigfType === "301"); let avatarData = props.modelValue.filter((item) => item.uigfType === "301");
const weaponData = props.modelValue.filter((item) => item.uigfType === "302"); let weaponData = props.modelValue.filter((item) => item.uigfType === "302");
const getColNum = newData.length === 0 ? 3 : 4;
// 监听数据变化
watch(
() => props.modelValue,
(newVal) => {
newData = newVal.filter((item) => item.uigfType === "100");
normalData = newVal.filter((item) => item.uigfType === "200");
avatarData = newVal.filter((item) => item.uigfType === "301");
weaponData = newVal.filter((item) => item.uigfType === "302");
},
);
</script> </script>
<style lang="css" scoped> <style lang="css" scoped>
.gro-o-container { .gro-o-container {
@@ -29,6 +44,6 @@ const getColNum = newData.length === 0 ? 3 : 4;
width: 100%; width: 100%;
height: 100%; height: 100%;
grid-gap: 10px; grid-gap: 10px;
grid-template-columns: repeat(v-bind(getColNum), 1fr); transition: all 0.3s ease-in-out;
} }
</style> </style>

View File

@@ -2,12 +2,17 @@
<ToLoading v-model="loading" :title="loadingTitle" /> <ToLoading v-model="loading" :title="loadingTitle" />
<v-app-bar class="gacha-top-bar"> <v-app-bar class="gacha-top-bar">
<template #prepend> <template #prepend>
<v-app-bar-title> <v-app-bar-title> 祈愿记录 </v-app-bar-title>
<span>祈愿记录</span> </template>
<span v-if="isLogin()"> - {{ user.nickname }} UID{{ user.gameUid }}</span> <template #default>
</v-app-bar-title> <v-select
v-model="uidCur"
class="gacha-top-select"
:items="selectItem"
variant="underlined"
/>
<v-spacer />
</template> </template>
<v-spacer />
<template #append> <template #append>
<v-btn prepend-icon="mdi-import" class="gacha-top-btn" @click="handleImportBtn"> 导入</v-btn> <v-btn prepend-icon="mdi-import" class="gacha-top-btn" @click="handleImportBtn"> 导入</v-btn>
<v-btn prepend-icon="mdi-export" class="gacha-top-btn" @click="handleExportBtn"> 导出</v-btn> <v-btn prepend-icon="mdi-export" class="gacha-top-btn" @click="handleExportBtn"> 导出</v-btn>
@@ -30,7 +35,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
// vue // vue
import { onMounted, ref } from "vue"; import { onMounted, ref, watch } from "vue";
import showSnackbar from "../../components/func/snackbar"; import showSnackbar from "../../components/func/snackbar";
import showConfirm from "../../components/func/confirm"; import showConfirm from "../../components/func/confirm";
import ToLoading from "../../components/overlay/to-loading.vue"; import ToLoading from "../../components/overlay/to-loading.vue";
@@ -38,29 +43,34 @@ import GroEcharts from "../../components/gachaRecord/gro-echarts.vue";
import GroOverview from "../../components/gachaRecord/gro-overview.vue"; import GroOverview from "../../components/gachaRecord/gro-overview.vue";
// tauri // tauri
import { dialog } from "@tauri-apps/api"; import { dialog } from "@tauri-apps/api";
// store
import { useUserStore } from "../../store/modules/user";
// utils // utils
import { exportUigfData, readUigfData, verifyUigfData } from "../../utils/UIGF"; import { exportUigfData, readUigfData, verifyUigfData } from "../../utils/UIGF";
import TGSqlite from "../../plugins/Sqlite"; import TGSqlite from "../../plugins/Sqlite";
// todo: 不读取用户数据,直接读取数据库,获取 uid 列表,然后根据 uid 获取祈愿数据
// store
const userStore = useUserStore();
// loading // loading
const loading = ref<boolean>(true); const loading = ref<boolean>(true);
const loadingTitle = ref<string>(); const loadingTitle = ref<string>();
// data // data
const user = userStore.getCurAccount(); const selectItem = ref<string[]>([]);
const uidCur = ref<string>("");
const gachaListCur = ref<TGApp.Sqlite.GachaRecords.SingleTable[]>([]); const gachaListCur = ref<TGApp.Sqlite.GachaRecords.SingleTable[]>([]);
const tab = ref<string>(""); const tab = ref<string>("");
onMounted(async () => { onMounted(async () => {
loadingTitle.value = `正在获取用户 ${user.gameUid} 的祈愿数据`; loadingTitle.value = `正在获取祈愿 UID 列表`;
gachaListCur.value = await TGSqlite.getGachaRecords(user.gameUid); selectItem.value = await TGSqlite.getUidList();
if (selectItem.value.length === 0) {
showSnackbar({
color: "error",
text: `暂无祈愿数据,请先导入祈愿数据`,
});
loading.value = false;
return;
}
uidCur.value = selectItem.value[0];
loadingTitle.value = `正在获取祈愿数据,默认 UID${uidCur.value}`;
gachaListCur.value = await TGSqlite.getGachaRecords(uidCur.value);
loadingTitle.value = `正在渲染数据`; loadingTitle.value = `正在渲染数据`;
tab.value = "echarts"; tab.value = "echarts";
loading.value = false; loading.value = false;
@@ -69,11 +79,6 @@ onMounted(async () => {
}); });
}); });
// 判断用户是否登录
function isLogin(): boolean {
return user?.gameUid !== undefined;
}
// 导入按钮点击事件 // 导入按钮点击事件
async function handleImportBtn(): Promise<void> { async function handleImportBtn(): Promise<void> {
const selectedFile = await dialog.open({ const selectedFile = await dialog.open({
@@ -95,38 +100,31 @@ async function handleImportBtn(): Promise<void> {
return; return;
} }
const remoteData = await readUigfData(<string>selectedFile); const remoteData = await readUigfData(<string>selectedFile);
if (remoteData.info.uid !== user.gameUid) { const res = await showConfirm({
await showConfirm({ title: "是否导入祈愿数据?",
title: "UID 不匹配,是否继续导入?", text: `UID${remoteData.info.uid}${remoteData.list.length} 条数据`,
text: `当前 UID${user.gameUid},导入 UID${remoteData.info.uid}`, });
if (!res) {
showSnackbar({
color: "grey",
text: `已取消祈愿数据导入`,
});
return;
}
loadingTitle.value = "正在导入祈愿数据";
loading.value = true;
if (remoteData.list.length === 0) {
loading.value = false;
showSnackbar({
color: "error",
text: `导入的祈愿数据为空`,
}); });
} else { } else {
const res = await showConfirm({ await TGSqlite.mergeUIGF(remoteData.info.uid, remoteData.list);
title: "是否导入祈愿数据?", loading.value = false;
text: `UID${remoteData.info.uid}${remoteData.list.length} 条数据`, showSnackbar({
text: `成功导入 ${remoteData.list.length} 条祈愿数据`,
}); });
if (!res) {
showSnackbar({
color: "grey",
text: `已取消祈愿数据导入`,
});
return;
}
loadingTitle.value = "正在导入祈愿数据";
loading.value = true;
if (remoteData.list.length === 0) {
loading.value = false;
showSnackbar({
color: "error",
text: `导入的祈愿数据为空`,
});
} else {
await TGSqlite.mergeUIGF(user.gameUid, remoteData.list);
loading.value = false;
showSnackbar({
text: `成功导入 ${remoteData.list.length} 条祈愿数据`,
});
}
} }
} else { } else {
showSnackbar({ showSnackbar({
@@ -138,17 +136,17 @@ async function handleImportBtn(): Promise<void> {
// 导出按钮点击事件 // 导出按钮点击事件
async function handleExportBtn(): Promise<void> { async function handleExportBtn(): Promise<void> {
const gachaList = await TGSqlite.getGachaRecords(user.gameUid); const gachaList = await TGSqlite.getGachaRecords(uidCur.value);
if (gachaList.length === 0) { if (gachaList.length === 0) {
showSnackbar({ showSnackbar({
color: "error", color: "error",
text: `用户 ${user.gameUid} 暂无祈愿数据`, text: `UID ${uidCur.value} 暂无祈愿数据`,
}); });
return; return;
} }
const res = await showConfirm({ const res = await showConfirm({
title: `是否导出祈愿数据?`, title: `是否导出祈愿数据?`,
text: `UID${user.gameUid},共 ${gachaList.length} 条数据`, text: `UID${uidCur.value},共 ${gachaList.length} 条数据`,
}); });
if (!res) { if (!res) {
showSnackbar({ showSnackbar({
@@ -158,7 +156,7 @@ async function handleExportBtn(): Promise<void> {
return; return;
} }
const file = await dialog.save({ const file = await dialog.save({
defaultPath: `UIGF_${user.gameUid}.json`, defaultPath: `UIGF_${uidCur.value}.json`,
filters: [ filters: [
{ {
name: `UIGF`, name: `UIGF`,
@@ -175,20 +173,36 @@ async function handleExportBtn(): Promise<void> {
} }
loadingTitle.value = "正在导出祈愿数据"; loadingTitle.value = "正在导出祈愿数据";
loading.value = true; loading.value = true;
await exportUigfData(user.gameUid, gachaList, file); await exportUigfData(uidCur.value, gachaList, file);
loading.value = false; loading.value = false;
showSnackbar({ showSnackbar({
text: `祈愿数据已成功导出`, text: `祈愿数据已成功导出`,
}); });
} }
// 监听 UID 变化
watch(uidCur, async (newUid) => {
gachaListCur.value = await TGSqlite.getGachaRecords(newUid);
showSnackbar({
text: `成功获取 ${gachaListCur.value.length} 条祈愿数据`,
});
});
</script> </script>
<style lang="css" scoped> <style lang="css" scoped>
.gacha-top-bar { .gacha-top-bar {
display: flex;
align-items: center;
justify-content: space-between;
background: rgb(0 0 0/50%); background: rgb(0 0 0/50%);
color: #f4d8a8; color: #f4d8a8;
font-family: var(--font-title); font-family: var(--font-title);
} }
.gacha-top-select {
height: 60px;
margin-left: 20px;
}
.gacha-top-btn { .gacha-top-btn {
margin: 0 10px; margin: 0 10px;
background: #393b40; background: #393b40;

View File

@@ -470,6 +470,19 @@ class Sqlite {
return res; return res;
} }
/**
* @description 获取已有 uid 列表
* @since Alpha v0.2.0
* @returns {Promise<string[]>}
*/
public async getUidList(): Promise<string[]> {
const db = await Database.load(this.dbPath);
const sql = "SELECT DISTINCT uid FROM GachaRecords";
const res: Array<{ uid: string }> = await db.select(sql);
await db.close();
return res.map((item) => item.uid);
}
/** /**
* @description 获取指定 uid 的用户角色数据 * @description 获取指定 uid 的用户角色数据
* @since Alpha v0.2。3 * @since Alpha v0.2。3