mirror of
https://github.com/BTMuli/TeyvatGuide.git
synced 2025-12-06 08:32:51 +08:00
✨ 千星奇域页面
This commit is contained in:
144
src/components/userGacha/gbr-data-line.vue
Normal file
144
src/components/userGacha/gbr-data-line.vue
Normal file
@@ -0,0 +1,144 @@
|
||||
<!-- 千星奇域概览单项组件 -->
|
||||
<template>
|
||||
<div class="gbr-dl-box">
|
||||
<div class="gbr-dl-progress" />
|
||||
<div class="gbr-dl-icon">
|
||||
<img :alt="props.data.name" :src="getIcon()" />
|
||||
</div>
|
||||
<div class="gbr-dl-base">
|
||||
<div class="gbr-dl-name">{{ props.data.name }}</div>
|
||||
<div class="gbr-dl-time">{{ props.data.time }}</div>
|
||||
</div>
|
||||
<div class="gbr-dl-info">
|
||||
<div class="gbr-dl-cnt">{{ props.count }}</div>
|
||||
<div class="gbr-dl-hint" v-if="hint !== ''">{{ hint }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { computed } from "vue";
|
||||
|
||||
export type GbrDataLineProps = { data: TGApp.Sqlite.GachaRecords.TableGachaB; count: number };
|
||||
|
||||
const props = defineProps<GbrDataLineProps>();
|
||||
const hint = getEndHint();
|
||||
|
||||
function getIcon(): string {
|
||||
console.log(props.data);
|
||||
// const find = AppGachaBData.find((i) => i.id.toString() === props.data.itemId);
|
||||
// if (!find) return `/source/UI/paimon.webp`;
|
||||
// return `https://api.hakush.in/gi/UI/${find.icon}.webp`;
|
||||
// TODO: 缺失元数据
|
||||
return `/source/UI/paimon.webp`;
|
||||
}
|
||||
|
||||
function getEndHint(): string {
|
||||
if (props.data.gachaType === "1000") return "";
|
||||
if (!props.data.isUp) return "歪";
|
||||
return "";
|
||||
}
|
||||
|
||||
const progressColor = computed<string>(() => {
|
||||
if (hint === "UP" && props.data.rank === "5") return "#d19a66";
|
||||
if (hint === "UP" && props.data.rank === "4") return "#c678dd";
|
||||
if (hint === "歪") return "#e06c75";
|
||||
return "#61afef";
|
||||
});
|
||||
const progressWidth = computed<string>(() => {
|
||||
let final = 10;
|
||||
if (props.data.rank === "5") {
|
||||
if (props.data.gachaType === "302") final = 80;
|
||||
else final = 90;
|
||||
} else if (props.data.rank === "4") final = 10;
|
||||
else return "0%";
|
||||
return ((props.count / final) * 100).toFixed(2) + "%";
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.gbr-dl-box {
|
||||
position: relative;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 48px;
|
||||
box-sizing: border-box;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
padding: 8px;
|
||||
border: 1px solid var(--common-shadow-1);
|
||||
border-radius: 4px;
|
||||
background: var(--box-bg-2);
|
||||
column-gap: 4px;
|
||||
}
|
||||
|
||||
.gbr-dl-progress {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: v-bind(progressWidth); /* stylelint-disable-line value-keyword-case */
|
||||
max-width: 100%;
|
||||
height: 4px;
|
||||
border-radius: 4px;
|
||||
background: v-bind(progressColor); /* stylelint-disable-line value-keyword-case */
|
||||
}
|
||||
|
||||
.gbr-dl-icon {
|
||||
display: flex;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
flex-shrink: 0;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.gbr-dl-base {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.gbr-dl-name {
|
||||
color: var(--common-text-title);
|
||||
font-family: var(--font-title);
|
||||
font-size: 14px;
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
.gbr-dl-time {
|
||||
color: var(--box-text-7);
|
||||
font-size: 12px;
|
||||
line-height: 14px;
|
||||
}
|
||||
|
||||
.gbr-dl-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-left: auto;
|
||||
column-gap: 4px;
|
||||
}
|
||||
|
||||
.gbr-dl-cnt {
|
||||
color: var(--common-text-title);
|
||||
font-family: var(--font-title);
|
||||
}
|
||||
|
||||
.gbr-dl-hint {
|
||||
display: flex;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 4px;
|
||||
border-radius: 50%;
|
||||
background: var(--box-bg-3);
|
||||
color: v-bind(progressColor); /* stylelint-disable-line value-keyword-case */
|
||||
font-family: var(--font-title);
|
||||
transform: rotate(25deg);
|
||||
}
|
||||
</style>
|
||||
263
src/components/userGacha/gbr-data-view.vue
Normal file
263
src/components/userGacha/gbr-data-view.vue
Normal file
@@ -0,0 +1,263 @@
|
||||
<!-- 千星奇域概览数据视图组件 -->
|
||||
<template>
|
||||
<div class="gbr-dv-container">
|
||||
<div class="gbr-dvt-title">
|
||||
<span>{{ title }}</span>
|
||||
<span>{{ props.dataVal.length }}</span>
|
||||
</div>
|
||||
<div class="gbr-dvt-subtitle">
|
||||
<span v-show="props.dataVal.length === 0">暂无数据</span>
|
||||
<span v-show="props.dataVal.length !== 0">{{ startDate }} ~ {{ endDate }}</span>
|
||||
</div>
|
||||
<div class="gbr-mid-list">
|
||||
<div class="gbr-ml-item">
|
||||
<span>4☆已垫</span>
|
||||
<span>{{ reset4count - 1 }}</span>
|
||||
</div>
|
||||
<div class="gbr-ml-item">
|
||||
<span>5☆已垫</span>
|
||||
<span>{{ reset5count - 1 }}</span>
|
||||
</div>
|
||||
<div class="gbr-ml-item">
|
||||
<span>5☆平均</span>
|
||||
<span>{{ star5avg }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="gbr-mid-list">
|
||||
<div class="gbr-ml-item">
|
||||
<span>5☆统计</span>
|
||||
<span>{{ getTitle("5") }}</span>
|
||||
</div>
|
||||
<div class="gbr-ml-item">
|
||||
<span>4☆统计</span>
|
||||
<span>{{ getTitle("4") }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 这边放具体物品的列表 -->
|
||||
<div class="gbr-bottom">
|
||||
<v-tabs v-model="tab" density="compact">
|
||||
<v-tab value="5">5☆</v-tab>
|
||||
<v-tab value="4">4☆</v-tab>
|
||||
<v-tab value="3">3☆</v-tab>
|
||||
</v-tabs>
|
||||
<v-window v-model="tab" class="gbr-bottom-window">
|
||||
<v-window-item value="5" class="gbr-b-window-item">
|
||||
<v-virtual-scroll :items="star5List" :item-height="48">
|
||||
<template #default="{ item }">
|
||||
<GbrDataLine :data="item.data" :count="item.count" />
|
||||
</template>
|
||||
</v-virtual-scroll>
|
||||
</v-window-item>
|
||||
<v-window-item value="4" class="gbr-b-window-item">
|
||||
<v-virtual-scroll :items="star4List" :item-height="48">
|
||||
<template #default="{ item }">
|
||||
<GbrDataLine :data="item.data" :count="item.count" />
|
||||
</template>
|
||||
</v-virtual-scroll>
|
||||
</v-window-item>
|
||||
<v-window-item value="3" class="gbr-b-window-item">
|
||||
<v-virtual-scroll :items="star3List" :item-height="48">
|
||||
<template #default="{ item }">
|
||||
<GbrDataLine :data="item.data" :count="item.count" />
|
||||
</template>
|
||||
</v-virtual-scroll>
|
||||
</v-window-item>
|
||||
</v-window>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, ref, shallowRef, watch } from "vue";
|
||||
|
||||
import GbrDataLine, { type GbrDataLineProps } from "./gbr-data-line.vue";
|
||||
|
||||
type GachaDataViewProps = {
|
||||
dataType: "normal" | "boy" | "girl";
|
||||
dataVal: Array<TGApp.Sqlite.GachaRecords.TableGachaB>;
|
||||
};
|
||||
|
||||
const props = defineProps<GachaDataViewProps>();
|
||||
|
||||
// data
|
||||
const loading = ref<boolean>(true); // 是否加载完
|
||||
const title = ref<string>(""); // 卡片标题
|
||||
const startDate = ref<string>(""); // 最早的时间
|
||||
const endDate = ref<string>(""); // 最晚的时间
|
||||
const star5List = shallowRef<Array<GbrDataLineProps>>([]); // 5星物品数据
|
||||
const star4List = shallowRef<Array<GbrDataLineProps>>([]); // 4星物品数据
|
||||
const star3List = shallowRef<Array<GbrDataLineProps>>([]);
|
||||
const reset5count = ref<number>(1); // 5星垫抽数量
|
||||
const reset4count = ref<number>(1); // 4星垫抽数量
|
||||
const reset3count = ref<number>(1); // 3星垫抽数量
|
||||
const star3count = ref<number>(0); // 3星物品数量
|
||||
const star5avg = ref<string>(""); // 5星平均抽数
|
||||
const tab = ref<string>("5"); // tab
|
||||
|
||||
onMounted(() => {
|
||||
loadData();
|
||||
loading.value = false;
|
||||
});
|
||||
|
||||
function loadData(): void {
|
||||
title.value = getTitle("top");
|
||||
const tempData = props.dataVal;
|
||||
const temp5Data: Array<GbrDataLineProps> = [];
|
||||
const temp4Data: Array<GbrDataLineProps> = [];
|
||||
const temp3Data: Array<GbrDataLineProps> = [];
|
||||
// 按照 id 升序
|
||||
tempData
|
||||
.sort((a, b) => a.id.localeCompare(b.id))
|
||||
.forEach((item) => {
|
||||
// 处理时间
|
||||
if (startDate.value === "" || item.time < startDate.value) startDate.value = item.time;
|
||||
if (endDate.value === "" || item.time > endDate.value) endDate.value = item.time;
|
||||
if (item.rank === "2") {
|
||||
reset3count.value++;
|
||||
reset4count.value++;
|
||||
reset5count.value++;
|
||||
} else if (item.rank === "3") {
|
||||
reset4count.value++;
|
||||
reset5count.value++;
|
||||
temp3Data.push({ data: item, count: reset3count.value });
|
||||
reset3count.value = 1;
|
||||
} else if (item.rank === "4") {
|
||||
reset5count.value++;
|
||||
temp4Data.push({ data: item, count: reset4count.value });
|
||||
reset4count.value = 1;
|
||||
} else if (item.rank === "5") {
|
||||
reset4count.value++;
|
||||
temp5Data.push({ data: item, count: reset5count.value });
|
||||
reset5count.value = 1;
|
||||
}
|
||||
});
|
||||
star5List.value = temp5Data.reverse();
|
||||
star4List.value = temp4Data.reverse();
|
||||
star3List.value = temp3Data.reverse();
|
||||
star5avg.value = getStar5Avg();
|
||||
}
|
||||
|
||||
// 获取标题
|
||||
function getTitle(type: "top" | "5" | "4" | "3"): string {
|
||||
if (type === "top") {
|
||||
if (props.dataType === "normal") return "常驻颂愿";
|
||||
if (props.dataType === "boy") return "活动颂愿(男)";
|
||||
if (props.dataType === "girl") return "活动颂愿(女)";
|
||||
return "";
|
||||
}
|
||||
if (props.dataVal.length === 0) return "暂无数据";
|
||||
if (type === "5") {
|
||||
// 5星物品统计 00.00%
|
||||
return `${star5List.value.length} [${((star5List.value.length * 100) / props.dataVal.length)
|
||||
.toFixed(2)
|
||||
.padStart(5, "0")}%]`;
|
||||
}
|
||||
if (type === "4") {
|
||||
// 4星物品统计
|
||||
return `${star4List.value.length} [${((star4List.value.length * 100) / props.dataVal.length)
|
||||
.toFixed(2)
|
||||
.padStart(5, "0")}%]`;
|
||||
}
|
||||
// 3星物品统计
|
||||
return `${star3count.value} [${((star3count.value * 100) / props.dataVal.length)
|
||||
.toFixed(2)
|
||||
.padStart(5, "0")}%]`;
|
||||
}
|
||||
|
||||
// 获取5星平均抽数
|
||||
function getStar5Avg(): string {
|
||||
const resetList = star5List.value.map((item) => item.count);
|
||||
if (resetList.length === 0) return "0";
|
||||
const total = resetList.reduce((a, b) => a + b);
|
||||
return (total / star5List.value.length).toFixed(2);
|
||||
}
|
||||
|
||||
// 监听数据变化
|
||||
watch(
|
||||
() => props.dataVal,
|
||||
() => {
|
||||
star5List.value = [];
|
||||
star4List.value = [];
|
||||
reset5count.value = 1;
|
||||
reset4count.value = 1;
|
||||
star3count.value = 1;
|
||||
startDate.value = "";
|
||||
endDate.value = "";
|
||||
star5avg.value = "";
|
||||
tab.value = "5";
|
||||
loadData();
|
||||
},
|
||||
);
|
||||
</script>
|
||||
<style lang="css" scoped>
|
||||
.gbr-dv-container {
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 8px;
|
||||
border-radius: 4px;
|
||||
background: var(--box-bg-1);
|
||||
}
|
||||
|
||||
.gbr-dvt-title {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
color: var(--common-text-title);
|
||||
font-family: var(--font-title);
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.gbr-dvt-subtitle {
|
||||
width: 100%;
|
||||
font-family: var(--font-text);
|
||||
font-size: 12px;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.gbr-mid-list {
|
||||
padding-top: 4px;
|
||||
padding-bottom: 4px;
|
||||
border-top: 1px solid var(--common-shadow-4);
|
||||
color: var(--box-text-7);
|
||||
}
|
||||
|
||||
.gbr-ml-item {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-family: var(--font-title);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.gbr-bottom {
|
||||
position: relative;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: calc(100% - 150px);
|
||||
box-sizing: border-box;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.gbr-bottom-window {
|
||||
position: relative;
|
||||
height: calc(100vh - 428px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.gbr-b-window-item {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
padding-right: 4px;
|
||||
}
|
||||
|
||||
/* stylelint-disable selector-class-pattern */
|
||||
|
||||
:deep(.v-virtual-scroll__item + .v-virtual-scroll__item) {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
/* stylelint-enable selector-class-pattern */
|
||||
</style>
|
||||
34
src/components/userGacha/gbr-overview.vue
Normal file
34
src/components/userGacha/gbr-overview.vue
Normal file
@@ -0,0 +1,34 @@
|
||||
<!-- 千星奇域祈愿概览组件 -->
|
||||
<template>
|
||||
<div class="gro-o-container">
|
||||
<GbrDataView :data-val="normalData" data-type="normal" />
|
||||
<GbrDataView :data-val="boyData" data-type="boy" />
|
||||
<GbrDataView :data-val="girlData" data-type="girl" />
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { computed } from "vue";
|
||||
|
||||
import GbrDataView from "./gbr-data-view.vue";
|
||||
|
||||
type GachaOverviewProps = { modelValue: Array<TGApp.Sqlite.GachaRecords.TableGachaB> };
|
||||
|
||||
const props = defineProps<GachaOverviewProps>();
|
||||
const normalData = computed<Array<TGApp.Sqlite.GachaRecords.TableGachaB>>(() =>
|
||||
props.modelValue.filter((item) => item.opGachaType === "1000"),
|
||||
);
|
||||
const girlData = computed<Array<TGApp.Sqlite.GachaRecords.TableGachaB>>(() =>
|
||||
props.modelValue.filter((item) => item.opGachaType === "20011" || item.opGachaType === "20012"),
|
||||
);
|
||||
const boyData = computed<Array<TGApp.Sqlite.GachaRecords.TableGachaB>>(() =>
|
||||
props.modelValue.filter((item) => item.opGachaType === "20021" || item.opGachaType === "20022"),
|
||||
);
|
||||
</script>
|
||||
<style lang="css" scoped>
|
||||
.gro-o-container {
|
||||
display: grid;
|
||||
height: 100%;
|
||||
column-gap: 8px;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
</style>
|
||||
62
src/components/userGacha/gbr-table.vue
Normal file
62
src/components/userGacha/gbr-table.vue
Normal file
@@ -0,0 +1,62 @@
|
||||
<!-- 千星奇域数据表格 -->
|
||||
<template>
|
||||
<v-data-table
|
||||
:headers="headers"
|
||||
:items="props.modelValue"
|
||||
fixed-header
|
||||
fixed-footer
|
||||
class="gbr-t-box"
|
||||
>
|
||||
<template v-slot:item="{ item }">
|
||||
<tr class="gbr-t-tr">
|
||||
<td>{{ item.time }}</td>
|
||||
<td>{{ getPool(item.opGachaType) }}</td>
|
||||
<td>{{ item.type }}</td>
|
||||
<td>{{ item.name }}</td>
|
||||
<td>{{ item.rank }}</td>
|
||||
</tr>
|
||||
</template>
|
||||
</v-data-table>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
type GroTableProps = { modelValue: Array<TGApp.Sqlite.GachaRecords.TableGachaB> };
|
||||
|
||||
const props = defineProps<GroTableProps>();
|
||||
|
||||
const headers = <const>[
|
||||
{ title: "时间", align: "center", key: "time" },
|
||||
{ title: "卡池", align: "center", key: "opGachaType" },
|
||||
{ title: "类型", align: "center", key: "type" },
|
||||
{ title: "名称", align: "center", key: "name" },
|
||||
{ title: "星级", align: "center", key: "rank" },
|
||||
];
|
||||
|
||||
function getPool(type: string) {
|
||||
switch (type) {
|
||||
case "1000":
|
||||
return "常驻颂愿";
|
||||
case "2000":
|
||||
return "活动颂愿";
|
||||
case "20011":
|
||||
case "20012":
|
||||
return "活动颂愿-男";
|
||||
case "20021":
|
||||
case "20022":
|
||||
return "活动颂愿-女";
|
||||
default:
|
||||
return "未知";
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="css" scoped>
|
||||
.gbr-t-box {
|
||||
height: calc(100vh - 200px);
|
||||
padding-right: 5px;
|
||||
border-radius: 5px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.gbr-t-tr {
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
@@ -35,22 +35,28 @@ const GachaIdMap: Record<string, string> = {
|
||||
"20021": "57016dec6b768231ba1342c01935417a799b", // 千星奇域角色活动-女
|
||||
};
|
||||
|
||||
const tabNormal: ReadonlyArray<GroTab> = [
|
||||
{ label: "常驻祈愿", value: "200" },
|
||||
{ label: "角色活动祈愿", value: "301" },
|
||||
{ label: "武器活动祈愿", value: "302" },
|
||||
{ label: "角色活动祈愿-2", value: "400" },
|
||||
];
|
||||
const tabBeyond: ReadonlyArray<GroTab> = [
|
||||
{ label: "常驻颂愿", value: "1000", beyond: true },
|
||||
{ label: "活动颂愿-男", value: "20011", beyond: true },
|
||||
{ label: "活动颂愿-女", value: "20021", beyond: true },
|
||||
];
|
||||
|
||||
type GroTabKey = keyof typeof GachaIdMap;
|
||||
type GroTab = { label: string; value: string; beyond?: boolean };
|
||||
type GroIframeProps = { mode: "normal" | "beyond" };
|
||||
const props = defineProps<GroIframeProps>();
|
||||
|
||||
const { cookie, account } = storeToRefs(useUserStore());
|
||||
const authkey = ref<string>("");
|
||||
const link = ref<string>("");
|
||||
const poolTab = ref<GroTabKey>("200");
|
||||
const tabList = shallowRef<ReadonlyArray<GroTab>>([
|
||||
{ label: "常驻祈愿", value: "200" },
|
||||
{ label: "角色活动祈愿", value: "301" },
|
||||
{ label: "武器活动祈愿", value: "302" },
|
||||
{ label: "角色活动祈愿-2", value: "400" },
|
||||
{ label: "常驻颂愿", value: "1000", beyond: true },
|
||||
{ label: "活动颂愿-男", value: "20011", beyond: true },
|
||||
{ label: "活动颂愿-女", value: "20021", beyond: true },
|
||||
]);
|
||||
const tabList = shallowRef<ReadonlyArray<GroTab>>(props.mode === "beyond" ? tabBeyond : tabNormal);
|
||||
|
||||
onMounted(async () => {
|
||||
link.value = await getUrl();
|
||||
@@ -107,7 +113,7 @@ async function refreshAuthkey(): Promise<void> {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
align-items: center;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<!-- 祈愿记录页面 -->
|
||||
<template>
|
||||
<v-app-bar>
|
||||
<template #prepend>
|
||||
@@ -12,6 +13,7 @@
|
||||
variant="outlined"
|
||||
label="游戏UID"
|
||||
/>
|
||||
<img src="/icon/nation/千星奇域.webp" alt="byd" @click="toBeyond()" title="千星奇域" />
|
||||
</div>
|
||||
</template>
|
||||
<template #extension>
|
||||
@@ -56,7 +58,7 @@
|
||||
<gro-history />
|
||||
</v-window-item>
|
||||
<v-window-item value="iframe" class="gacha-window-item">
|
||||
<gro-iframe />
|
||||
<gro-iframe mode="normal" />
|
||||
</v-window-item>
|
||||
</v-window>
|
||||
</div>
|
||||
@@ -83,9 +85,12 @@ import TGLogger from "@utils/TGLogger.js";
|
||||
import { exportUigfData, readUigfData, verifyUigfData } from "@utils/UIGF.js";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { onMounted, ref, shallowRef, watch } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
|
||||
import { AppCharacterData, AppWeaponData } from "@/data/index.js";
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const { isLogin } = storeToRefs(useAppStore());
|
||||
const { account, cookie } = storeToRefs(useUserStore());
|
||||
|
||||
@@ -130,6 +135,10 @@ watch(
|
||||
},
|
||||
);
|
||||
|
||||
async function toBeyond(): Promise<void> {
|
||||
await router.push({ name: "千星奇域祈愿记录" });
|
||||
}
|
||||
|
||||
// 刷新按钮点击事件
|
||||
async function confirmRefresh(force: boolean): Promise<void> {
|
||||
await TGLogger.Info(`[UserGacha][${account.value.gameUid}][confirmRefresh] 刷新祈愿数据`);
|
||||
|
||||
341
src/pages/User/GachaB.vue
Normal file
341
src/pages/User/GachaB.vue
Normal file
@@ -0,0 +1,341 @@
|
||||
<!-- 千星奇域祈愿记录页面 -->
|
||||
<template>
|
||||
<v-app-bar>
|
||||
<template #prepend>
|
||||
<div class="gb-top-title">
|
||||
<img src="/icon/nation/千星奇域.webp" alt="gacha" />
|
||||
<span>祈愿记录</span>
|
||||
<v-select
|
||||
:hide-details="true"
|
||||
density="compact"
|
||||
v-model="uidCur"
|
||||
:items="selectItem"
|
||||
variant="outlined"
|
||||
label="游戏UID"
|
||||
/>
|
||||
<img src="/source/UI/userGacha.webp" alt="byd" @click="toGacha()" title="祈愿" />
|
||||
</div>
|
||||
</template>
|
||||
<template #extension>
|
||||
<div class="gb-top-btns">
|
||||
<v-btn prepend-icon="mdi-refresh" class="gb-top-btn" @click="confirmRefresh(false)">
|
||||
增量刷新
|
||||
</v-btn>
|
||||
<v-btn prepend-icon="mdi-refresh" class="gb-top-btn" @click="confirmRefresh(true)">
|
||||
全量刷新
|
||||
</v-btn>
|
||||
<v-btn prepend-icon="mdi-delete" class="gb-top-btn" @click="deleteGacha()">删除</v-btn>
|
||||
</div>
|
||||
</template>
|
||||
</v-app-bar>
|
||||
<div class="gb-container">
|
||||
<v-tabs v-model="tab" align-tabs="start" class="gb-tab" density="compact">
|
||||
<v-tab value="overview">数据概览</v-tab>
|
||||
<v-tab value="table">数据表格</v-tab>
|
||||
<v-tab value="iframe" v-if="isLogin">祈愿详情</v-tab>
|
||||
</v-tabs>
|
||||
<v-window v-model="tab" class="gb-window">
|
||||
<v-window-item value="overview" class="gb-window-item">
|
||||
<gbr-overview v-model="gachaListCur" />
|
||||
</v-window-item>
|
||||
<v-window-item value="table" class="gb-window-item">
|
||||
<gbr-table v-model="gachaListCur" />
|
||||
</v-window-item>
|
||||
<v-window-item value="iframe" class="gb-window-item">
|
||||
<gro-iframe mode="beyond" />
|
||||
</v-window-item>
|
||||
</v-window>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import showDialog from "@comp/func/dialog.js";
|
||||
import showLoading from "@comp/func/loading.js";
|
||||
import showSnackbar from "@comp/func/snackbar.js";
|
||||
import GbrOverview from "@comp/userGacha/gbr-overview.vue";
|
||||
import GbrTable from "@comp/userGacha/gbr-table.vue";
|
||||
import GroIframe from "@comp/userGacha/gro-iframe.vue";
|
||||
import hk4eReq from "@req/hk4eReq.js";
|
||||
import takumiReq from "@req/takumiReq.js";
|
||||
import TSUserGachaB from "@Sqlm/userGachaB.js";
|
||||
import useAppStore from "@store/app.js";
|
||||
import useUserStore from "@store/user.js";
|
||||
import TGLogger from "@utils/TGLogger.js";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { onMounted, ref, shallowRef, watch } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
|
||||
const router = useRouter();
|
||||
const { isLogin } = storeToRefs(useAppStore());
|
||||
const { account, cookie } = storeToRefs(useUserStore());
|
||||
|
||||
const authkey = ref<string>("");
|
||||
const uidCur = ref<string>();
|
||||
const tab = ref<string>("overview");
|
||||
const selectItem = shallowRef<Array<string>>([]);
|
||||
const gachaListCur = shallowRef<Array<TGApp.Sqlite.GachaRecords.TableGachaB>>([]);
|
||||
|
||||
onMounted(async () => {
|
||||
await TGLogger.Info("[UserGachaB][onMounted] 进入千星奇域页面");
|
||||
await showLoading.start("正在加载千星奇域祈愿数据", "正在获取UID列表...");
|
||||
selectItem.value = await TSUserGachaB.getUidList();
|
||||
await TGLogger.Info(`[UserGachaB][onMounted] 总UID数:${selectItem.value.length}`);
|
||||
if (selectItem.value.length === 0) {
|
||||
await showLoading.end();
|
||||
await TGLogger.Info("[UserGachaB][onMounted] UID列表为空");
|
||||
return;
|
||||
}
|
||||
uidCur.value = selectItem.value[0];
|
||||
await TGLogger.Info(`[UserGachaB][onMounted] 当前UID:${uidCur.value}`);
|
||||
await showLoading.update(`UID: ${uidCur.value}`);
|
||||
gachaListCur.value = await TSUserGachaB.getGachaRecords(uidCur.value);
|
||||
await TGLogger.Info(`[UserGachaB][onMounted] 祈愿记录数: ${gachaListCur.value.length}`);
|
||||
await showLoading.end();
|
||||
showSnackbar.success(`加载完成,共 ${gachaListCur.value.length} 条祈愿记录`);
|
||||
});
|
||||
|
||||
watch(
|
||||
() => uidCur.value,
|
||||
async (newUid) => {
|
||||
if (!newUid) return;
|
||||
gachaListCur.value = await TSUserGachaB.getGachaRecords(newUid);
|
||||
showSnackbar.success(`成功获取 ${gachaListCur.value.length} 条祈愿数据`);
|
||||
await TGLogger.Info(
|
||||
`[UserGachaB][${newUid}][watch] 成功获取 ${gachaListCur.value.length} 条祈愿数据`,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* 跳转至祈愿页面
|
||||
*/
|
||||
async function toGacha(): Promise<void> {
|
||||
await router.push({ name: "祈愿记录" });
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新祈愿数据
|
||||
* @param {boolean} force 是否强制刷新
|
||||
* @return {Promise<void>} void
|
||||
*/
|
||||
async function confirmRefresh(force: boolean): Promise<void> {
|
||||
if (!isLogin.value || !cookie.value) {
|
||||
showSnackbar.warn("请先登录账号");
|
||||
return;
|
||||
}
|
||||
await TGLogger.Info(`[UserGachaB][${account.value.gameUid}] 开始刷新千星奇域祈愿数据`);
|
||||
if (uidCur.value && uidCur.value !== account.value.gameUid) {
|
||||
const switchCheck = await showDialog.check(
|
||||
"是否切换游戏账户",
|
||||
`确认则尝试切换至 ${uidCur.value}`,
|
||||
);
|
||||
if (switchCheck) {
|
||||
await useUserStore().switchGameAccount(uidCur.value);
|
||||
await confirmRefresh(force);
|
||||
return;
|
||||
}
|
||||
const freshCheck = await showDialog.check(
|
||||
"确定刷新?",
|
||||
`用户${account.value.gameUid}与当前UID${uidCur.value}不一致`,
|
||||
);
|
||||
if (!freshCheck) {
|
||||
showSnackbar.cancel("已取消祈愿数据刷新");
|
||||
return;
|
||||
}
|
||||
}
|
||||
await showLoading.start(`正在刷新祈愿数据`, `UID:${account.value.gameUid},正在获取 authkey`);
|
||||
const authkeyRes = await takumiReq.bind.authKey(cookie.value, account.value);
|
||||
if (typeof authkeyRes === "string") {
|
||||
authkey.value = authkeyRes;
|
||||
await TGLogger.Info(`[UserGacha][${account.value.gameUid}] 成功获取 authkey`);
|
||||
} else {
|
||||
showSnackbar.error("获取 authkey 失败");
|
||||
await TGLogger.Error(`[UserGacha][${account.value.gameUid}] 获取 authkey 失败`);
|
||||
await TGLogger.Error(
|
||||
`[UserGachaB][${account.value.gameUid}] ${authkeyRes.retcode} ${authkeyRes.message}`,
|
||||
);
|
||||
await showLoading.end();
|
||||
return;
|
||||
}
|
||||
await refreshGachaPool("1000", "常驻颂愿", force);
|
||||
await refreshGachaPool("20011", "活动颂愿·男", force);
|
||||
await refreshGachaPool("20012", "活动颂愿·男2", force);
|
||||
await refreshGachaPool("20021", "活动颂愿·女", force);
|
||||
await refreshGachaPool("20022", "活动颂愿·女2", force);
|
||||
await showLoading.end();
|
||||
await TGLogger.Info(`[UserGacha][${account.value.gameUid}] 刷新祈愿数据完成`);
|
||||
showSnackbar.success("祈愿数据刷新完成,即将刷新页面");
|
||||
await new Promise<void>((resolve) => setTimeout(resolve, 1500));
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新指定祈愿池数据
|
||||
* @param {string} gachaType 祈愿池类型
|
||||
* @param {string} gachaName 祈愿池名称
|
||||
* @param {boolean} force 是否强制刷新
|
||||
* @return {Promise<void>} void
|
||||
*/
|
||||
async function refreshGachaPool(
|
||||
gachaType: string,
|
||||
gachaName: string,
|
||||
force: boolean,
|
||||
): Promise<void> {
|
||||
let endId = "0";
|
||||
let reqId = "0";
|
||||
let page = 0;
|
||||
await showLoading.start(`正在刷新${gachaName}数据`, "");
|
||||
if (!force) {
|
||||
endId = (await TSUserGachaB.getGachaCheck(account.value.gameUid, gachaType)) ?? "0";
|
||||
}
|
||||
while (true) {
|
||||
page++;
|
||||
const gachaRes = await hk4eReq.gachaB(authkey.value, gachaType, reqId);
|
||||
if (!Array.isArray(gachaRes)) {
|
||||
showSnackbar.error(`[${gachaType}][${gachaRes.retcode}] ${gachaRes.message}`);
|
||||
await TGLogger.Error(
|
||||
`[UserGachaB][${account.value.gameUid}][refreshGachaPool] 获取祈愿数据失败`,
|
||||
);
|
||||
await TGLogger.Error(
|
||||
`[UserGachaB][${account.value.gameUid}][refreshGachaPool] ${gachaRes.retcode} ${gachaRes.message}`,
|
||||
);
|
||||
await new Promise<void>((resolve) => setTimeout(resolve, 1000));
|
||||
break;
|
||||
}
|
||||
if (gachaRes.length === 0) {
|
||||
// if (force) {
|
||||
// await showLoading.update(`正在清理${label}数据`);
|
||||
// if (gachaDataMap) {
|
||||
// await TSUserGacha.cleanGachaRecords(account.value.gameUid, type, gachaDataMap);
|
||||
// }
|
||||
// }
|
||||
break;
|
||||
}
|
||||
if (force) await showLoading.update(`[${gachaName}] 第${page}页,${gachaRes.length}条`);
|
||||
for (const item of gachaRes) {
|
||||
if (!force) {
|
||||
await showLoading.update(`[${item.item_type}][${item.time}] ${item.item_name}`);
|
||||
}
|
||||
if (force) {
|
||||
// if (!gachaDataMap) gachaDataMap = {};
|
||||
// if (!gachaDataMap[item.time]) gachaDataMap[item.time] = [];
|
||||
// gachaDataMap[item.time].push(item.id.toString());
|
||||
}
|
||||
}
|
||||
await TSUserGachaB.insertGachaList(gachaRes);
|
||||
if (!force && gachaRes.some((i) => i.id.toString() === endId.toString())) break;
|
||||
reqId = gachaRes[gachaRes.length - 1].id.toString();
|
||||
if (force) await new Promise<void>((resolve) => setTimeout(resolve, 1000));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除当前UID的祈愿数据
|
||||
* @return {Promise<void>} void
|
||||
*/
|
||||
async function deleteGacha(): Promise<void> {
|
||||
if (gachaListCur.value.length === 0 || !uidCur.value) {
|
||||
showSnackbar.error("暂无祈愿数据");
|
||||
return;
|
||||
}
|
||||
await TGLogger.Info(`[UserGachaB][${uidCur.value}][deleteGacha] 删除祈愿数据`);
|
||||
const delCheck = await showDialog.check(
|
||||
"确定删除祈愿数据?",
|
||||
`UID:${uidCur.value},共 ${gachaListCur.value.length} 条数据`,
|
||||
);
|
||||
if (!delCheck) {
|
||||
showSnackbar.cancel("已取消祈愿数据删除");
|
||||
await TGLogger.Info(`[UserGachaB][${uidCur.value}][deleteGacha] 已取消祈愿数据删除`);
|
||||
return;
|
||||
}
|
||||
const uidList = await TSUserGachaB.getUidList();
|
||||
if (uidList.length <= 1) {
|
||||
const forceCheck = await showDialog.check("删除后数据库将为空,确定删除?");
|
||||
if (!forceCheck) {
|
||||
showSnackbar.cancel("已取消祈愿数据删除");
|
||||
return;
|
||||
}
|
||||
}
|
||||
await showLoading.start("正在删除祈愿数据", `UID:${uidCur.value}`);
|
||||
await TSUserGachaB.deleteRecords(uidCur.value);
|
||||
await showLoading.end();
|
||||
showSnackbar.success(`已成功删除 ${uidCur.value} 的祈愿数据,即将刷新页面`);
|
||||
await TGLogger.Info(
|
||||
`[UserGachaB][${uidCur.value}][deleteGacha] 成功删除 ${gachaListCur.value.length} 条祈愿数据`,
|
||||
);
|
||||
await new Promise<void>((resolve) => setTimeout(resolve, 1500));
|
||||
window.location.reload();
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.gb-top-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-left: 12px;
|
||||
column-gap: 8px;
|
||||
|
||||
img {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
|
||||
&:last-child {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
span {
|
||||
color: var(--common-text-title);
|
||||
font-family: var(--font-title);
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.gb-top-btns {
|
||||
display: flex;
|
||||
margin-left: 16px;
|
||||
column-gap: 8px;
|
||||
}
|
||||
|
||||
.gb-top-btn {
|
||||
border-radius: 4px;
|
||||
background: var(--tgc-btn-1);
|
||||
color: var(--btn-text);
|
||||
}
|
||||
|
||||
.dark .gb-top-btn {
|
||||
border: 1px solid var(--common-shadow-2);
|
||||
}
|
||||
|
||||
.gb-container {
|
||||
display: flex;
|
||||
height: calc(100vh - 144px);
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
border: 1px solid var(--common-shadow-2);
|
||||
border-radius: 4px;
|
||||
background: var(--app-page-bg);
|
||||
}
|
||||
|
||||
.gb-tab {
|
||||
height: 40px;
|
||||
color: var(--box-text-4);
|
||||
font-family: var(--font-title);
|
||||
|
||||
img {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.gb-window {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.gb-window-item {
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
@@ -1,7 +1,6 @@
|
||||
/**
|
||||
* @file plugins/Sqlite/index.ts
|
||||
* @description Sqlite 数据库操作类
|
||||
* @since Beta v0.8.0
|
||||
* Sqlite 数据库操作类
|
||||
* @since Beta v0.8.4
|
||||
*/
|
||||
|
||||
import { app } from "@tauri-apps/api";
|
||||
@@ -17,10 +16,11 @@ class Sqlite {
|
||||
"Achievements",
|
||||
"AppData",
|
||||
"GachaRecords",
|
||||
"GachaBRecords",
|
||||
"GameAccount",
|
||||
"SpiralAbyss",
|
||||
"RoleCombat",
|
||||
"HardChallenge",
|
||||
"RoleCombat",
|
||||
"SpiralAbyss",
|
||||
"UFCollection",
|
||||
"UFMap",
|
||||
"UFPost",
|
||||
@@ -39,7 +39,7 @@ class Sqlite {
|
||||
private constructor() {}
|
||||
|
||||
/**
|
||||
* @description 获取数据库实例
|
||||
* 获取数据库实例
|
||||
* @since Beta v0.3.3
|
||||
* @returns {Promise<Database>}
|
||||
*/
|
||||
@@ -49,7 +49,7 @@ class Sqlite {
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 检测是否需要创建数据库
|
||||
* 检测是否需要创建数据库
|
||||
* @since Beta v0.6.1
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
@@ -70,7 +70,7 @@ class Sqlite {
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 初始化数据库
|
||||
* 初始化数据库
|
||||
* @since Beta v0.4.5
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
@@ -81,7 +81,7 @@ class Sqlite {
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取数据库信息
|
||||
* 获取数据库信息
|
||||
* @since Beta v0.3.3
|
||||
* @returns {Promise<TGApp.Sqlite.AppData.Item[]>}
|
||||
*/
|
||||
@@ -92,7 +92,7 @@ class Sqlite {
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 对比数据判断是否需要更新
|
||||
* 对比数据判断是否需要更新
|
||||
* @since Beta v0.3.3
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
@@ -105,7 +105,7 @@ class Sqlite {
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 保存 appData
|
||||
* 保存 appData
|
||||
* @since Beta v0.3.3
|
||||
* @param {string} key
|
||||
* @param {string} value
|
||||
@@ -118,7 +118,7 @@ class Sqlite {
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 已有数据表跟触发器不变的情况下,更新数据库数据
|
||||
* 已有数据表跟触发器不变的情况下,更新数据库数据
|
||||
* @since Beta v0.3.3
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
@@ -131,7 +131,7 @@ class Sqlite {
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 更新 SpiralAbyss 表
|
||||
* 更新 SpiralAbyss 表
|
||||
* @since Beta v0.6.1
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
@@ -147,7 +147,7 @@ class Sqlite {
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 重置数据库
|
||||
* 重置数据库
|
||||
* @since Beta v0.4.0
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
|
||||
161
src/plugins/Sqlite/modules/userGachaB.ts
Normal file
161
src/plugins/Sqlite/modules/userGachaB.ts
Normal file
@@ -0,0 +1,161 @@
|
||||
/**
|
||||
* 千星奇域祈愿模块
|
||||
* @since Beta v0.8.4
|
||||
*/
|
||||
|
||||
import showSnackbar from "@comp/func/snackbar.js";
|
||||
import TGSqlite from "@Sql/index.js";
|
||||
import { exists, mkdir } from "@tauri-apps/plugin-fs";
|
||||
import TGLogger from "@utils/TGLogger.js";
|
||||
|
||||
/**
|
||||
* 获取导入 Sql
|
||||
* @since Beta v0.8.4
|
||||
* @param {TGApp.Game.Gacha.GachaBItem} gacha - 抽卡记录数据
|
||||
* @returns {string}
|
||||
*/
|
||||
function getInsertSql(gacha: TGApp.Game.Gacha.GachaBItem): string {
|
||||
return `
|
||||
INSERT INTO GachaBRecords(id, uid, region, scheduleId, gachaType,
|
||||
opGachaType, time, itemId, name, type,
|
||||
rank, isUp, updated)
|
||||
VALUES ('${gacha.id}', '${gacha.uid}', '${gacha.region}', '${gacha.schedule_id}',
|
||||
'${gacha.op_gacha_type === "1000" ? "1000" : "2000"}', '${gacha.op_gacha_type}', '${gacha.time}',
|
||||
'${gacha.item_id}', '${gacha.item_name}', '${gacha.item_type}',
|
||||
'${gacha.rank_type}', '${gacha.is_up}', datetime('now', 'localtime'))
|
||||
ON CONFLICT (id)
|
||||
DO UPDATE
|
||||
SET uid = '${gacha.uid}',
|
||||
region = '${gacha.region}',
|
||||
scheduleId = '${gacha.schedule_id}',
|
||||
gachaType = '${gacha.op_gacha_type === "1000" ? "1000" : "2000"}',
|
||||
opGachaType = '${gacha.op_gacha_type}',
|
||||
time = '${gacha.time}',
|
||||
itemId = '${gacha.item_id}',
|
||||
name = '${gacha.item_name}',
|
||||
type = '${gacha.item_type}',
|
||||
rank = '${gacha.rank_type}',
|
||||
isUp = '${gacha.is_up}',
|
||||
updated = datetime('now', 'localtime');
|
||||
`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 插入列表数据
|
||||
* @since Beta v0.8.4
|
||||
* @param {Array<TGApp.Game.Gacha.GachaBItem>} list - 抽卡记录列表
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function insertGachaList(list: Array<TGApp.Game.Gacha.GachaBItem>): Promise<void> {
|
||||
const db = await TGSqlite.getDB();
|
||||
for (const gacha of list) {
|
||||
const sql = getInsertSql(gacha);
|
||||
await db.execute(sql);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数据库UID列表
|
||||
* @since Beta v0.8.4
|
||||
* @returns {Promise<Array<string>>}
|
||||
*/
|
||||
async function getUidList(): Promise<Array<string>> {
|
||||
const db = await TGSqlite.getDB();
|
||||
type resType = Array<{ uid: string }>;
|
||||
const res = await db.select<resType>("SELECT DISTINCT uid FROM GachaBRecords;");
|
||||
return res.map((i) => i.uid);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取增量更新的记录 ID
|
||||
* @since Beta v0.8.4
|
||||
* @param {string} uid - UID
|
||||
* @param {string} type - 类型
|
||||
* @returns {Promise<string|undefined>}
|
||||
*/
|
||||
async function getGachaCheck(uid: string, type: string): Promise<string | undefined> {
|
||||
const db = await TGSqlite.getDB();
|
||||
type resType = Array<{ id: string }>;
|
||||
const res = await db.select<resType>(
|
||||
"SELECT id FROM GachaBRecords WHERE uid = ? AND opGachaType = ? ORDER BY id DESC LIMIT 1;",
|
||||
[uid, type],
|
||||
);
|
||||
if (res.length === 0) return undefined;
|
||||
return res[0].id;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户祈愿记录
|
||||
* @since Beta v0.8.4
|
||||
* @param {string} uid - UID
|
||||
* @param {string} [type] - 类型
|
||||
* @return {Promise<Array<TGApp.Sqlite.GachaRecords.TableGachaB>>}
|
||||
*/
|
||||
async function getGachaRecords(
|
||||
uid: string,
|
||||
type?: string,
|
||||
): Promise<Array<TGApp.Sqlite.GachaRecords.TableGachaB>> {
|
||||
const db = await TGSqlite.getDB();
|
||||
if (type) {
|
||||
return await db.select("SELECT * FROM GachaBRecords WHERE uid = ? AND opGachaType = ?;", [
|
||||
uid,
|
||||
type,
|
||||
]);
|
||||
}
|
||||
return await db.select("SELECT * FROM GachaBRecords WHERE uid = ?;", [uid]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 备份祈愿数据
|
||||
* @since Beta v0.8.4
|
||||
* @param {string} dir - 备份目录
|
||||
* @remarks 等UIGF标准最终确定后与TSUserGacha合并
|
||||
*/
|
||||
async function backUpUigf(dir: string): Promise<void> {
|
||||
if (!(await exists(dir))) {
|
||||
await TGLogger.Warn("不存在指定的祈愿备份目录,即将创建");
|
||||
await mkdir(dir, { recursive: true });
|
||||
}
|
||||
showSnackbar.error(`千星奇域祈愿数据备份功能尚未实现,请耐心等待后续版本更新。`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 恢复祈愿数据
|
||||
* @since Beta v0.8.4
|
||||
* @param {string} dir - 恢复目录
|
||||
* @remarks 等UIGF标准最终确定后与TSUserGacha合并
|
||||
*/
|
||||
async function restoreUigf(dir: string): Promise<boolean> {
|
||||
if (!(await exists(dir))) {
|
||||
await TGLogger.Warn("不存在指定的祈愿备份目录");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除用户祈愿数据
|
||||
* @since Beta v0.8.4
|
||||
* @param {string} uid - UID
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function deleteRecords(uid: string): Promise<void> {
|
||||
const db = await TGSqlite.getDB();
|
||||
await db.execute("DELETE FROM GachaBRecords WHERE uid = ?;", [uid]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 千星奇域祈愿模块
|
||||
* @since Beta v0.8.4
|
||||
*/
|
||||
const TSUserGachaB = {
|
||||
getUidList,
|
||||
getGachaCheck,
|
||||
getGachaRecords,
|
||||
insertGachaList,
|
||||
backUpUigf,
|
||||
restoreUigf,
|
||||
deleteRecords,
|
||||
};
|
||||
|
||||
export default TSUserGachaB;
|
||||
@@ -1,6 +1,5 @@
|
||||
-- @file plugins/Sqlite/sql/createTable.sql
|
||||
-- @brief sqlite数据库创建表语句
|
||||
-- @since Beta v0.8.0
|
||||
-- @since Beta v0.8.4
|
||||
|
||||
-- @brief 创建成就数据表
|
||||
create table if not exists Achievements
|
||||
@@ -148,6 +147,24 @@ create table if not exists GachaRecords
|
||||
updated text
|
||||
);
|
||||
|
||||
-- @brief 创建千星奇域祈愿数据表
|
||||
create table if not exists GachaBRecords
|
||||
(
|
||||
id text primary key not null,
|
||||
uid text,
|
||||
region text,
|
||||
scheduleId text,
|
||||
gachaType text,
|
||||
opGachaType text,
|
||||
time text,
|
||||
itemId text,
|
||||
name text,
|
||||
type text,
|
||||
rank text,
|
||||
isUp text,
|
||||
updated text
|
||||
);
|
||||
|
||||
-- @brief 创建用户帖子收藏
|
||||
create table if not exists UFPost
|
||||
(
|
||||
|
||||
@@ -150,7 +150,7 @@ async function getBeyondGachaLog(
|
||||
authkey_ver: "1",
|
||||
sign_type: "2",
|
||||
gacha_type: gachaType,
|
||||
size: "20",
|
||||
size: "5",
|
||||
end_id: endId,
|
||||
};
|
||||
const resp = await TGHttp<TGApp.Game.Gacha.GachaBLogResp | TGApp.BBS.Response.Base>(
|
||||
|
||||
@@ -31,6 +31,11 @@ const userRoutes = (<const>[
|
||||
name: "祈愿记录",
|
||||
component: async () => await import("@/pages/User/Gacha.vue"),
|
||||
},
|
||||
{
|
||||
path: "/user/gachaB",
|
||||
name: "千星奇域祈愿记录",
|
||||
component: async () => await import("@/pages/User/GachaB.vue"),
|
||||
},
|
||||
{
|
||||
path: "/user/record",
|
||||
name: "原神战绩",
|
||||
|
||||
300
src/types/Plugins/UIGF.d.ts
vendored
300
src/types/Plugins/UIGF.d.ts
vendored
@@ -1,161 +1,165 @@
|
||||
/**
|
||||
* @file types/Plugins/UIGF.d.ts
|
||||
* @description UIGF 插件类型定义文件
|
||||
* @since Beta v0.5.1
|
||||
* @version UIGF v3.0 | UIGF v4.0
|
||||
* UIGF 标准类型定义文件
|
||||
* @since Beta v0.8.4
|
||||
* @version UIGF v3.0 | UIGF v4.1
|
||||
*/
|
||||
|
||||
declare namespace TGApp.Plugins.UIGF {
|
||||
/**
|
||||
* @description UIGF 数据
|
||||
* UIGF 数据
|
||||
* @since Beta v0.5.0
|
||||
* @interface Schema
|
||||
* @property {Info} info - UIGF 头部信息
|
||||
* @property {GachaItem[]} list - UIGF 祈愿列表
|
||||
* @return Schema
|
||||
*/
|
||||
interface Schema {
|
||||
info: Info;
|
||||
list: GachaItem[];
|
||||
}
|
||||
|
||||
/**
|
||||
* @description UIGF 数据, v4.0
|
||||
* @since Beta v0.5.0
|
||||
* @interface Schema4
|
||||
* @property {Info4} info - UIGF 头部信息
|
||||
* @property {GachaHk4e[]} hk4e - UIGF 祈愿列表,原神数据
|
||||
* @return Schema4
|
||||
*/
|
||||
interface Schema4 {
|
||||
info: Info4;
|
||||
hk4e: GachaHk4e[];
|
||||
}
|
||||
|
||||
/**
|
||||
* @description UIGF 头部信息
|
||||
* @since Beta v0.5.0
|
||||
* @interface Info
|
||||
* @see docs\UIGF.md
|
||||
* @property {string} uid - UID
|
||||
* @property {string} lang - 语言
|
||||
* @property {string} uigf_version - UIGF 版本,应用使用的是 2.3.0
|
||||
* @property {number} export_timestamp - 导出时间戳(秒)
|
||||
* @property {string} export_time - 导出时间 yyyy-MM-dd HH:mm:ss
|
||||
* @property {string} export_app - 导出应用
|
||||
* @property {string} export_app_version - 导出应用版本
|
||||
* @property {number} region_time_zone - 时区
|
||||
* @return Info
|
||||
*/
|
||||
interface Info {
|
||||
uid: string;
|
||||
lang: string;
|
||||
uigf_version: string;
|
||||
export_timestamp?: number;
|
||||
export_time?: string;
|
||||
export_app?: string;
|
||||
export_app_version?: string;
|
||||
region_time_zone?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description UIGF 头部信息, v4.0
|
||||
* @since Beta v0.5.1
|
||||
* @interface Info4
|
||||
* @see docs\UIGF4.md
|
||||
* @property {string} export_timestamp - 导出时间戳(秒)
|
||||
* @property {string} export_app - 导出应用
|
||||
* @property {string} export_app_version - 导出应用版本
|
||||
* @property {string} version - UIGF 版本
|
||||
* @property {string} lang - 语言
|
||||
* @return Info4
|
||||
*/
|
||||
interface Info4 {
|
||||
export_timestamp: string;
|
||||
export_app: string;
|
||||
export_app_version: string;
|
||||
version: string;
|
||||
lang?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 祈愿类型
|
||||
* @since Alpha v0.2.3
|
||||
* @enum EnumGachaType
|
||||
* @property {string} CharacterUp - 角色活动祈愿
|
||||
* @property {string} CharacterUp2 - 角色活动祈愿2
|
||||
* @property {string} WeaponUp - 武器活动祈愿
|
||||
* @property {string} Normal - 普通祈愿
|
||||
* @property {string} Newbie - 新手祈愿
|
||||
* @return EnumGachaType
|
||||
*/
|
||||
enum EnumGachaType {
|
||||
CharacterUp = "301",
|
||||
CharacterUp2 = "400",
|
||||
WeaponUp = "302",
|
||||
Normal = "200",
|
||||
Newbie = "100",
|
||||
}
|
||||
|
||||
/**
|
||||
* @description UIGF 祈愿类型
|
||||
* @since Alpha v0.2.3
|
||||
* @enum EnumUigfGachaType
|
||||
* @property {string} CharacterUp - 角色活动祈愿&角色活动祈愿2
|
||||
* @property {string} WeaponUp - 武器活动祈愿
|
||||
* @property {string} Normal - 普通祈愿
|
||||
* @property {string} Newbie - 新手祈愿
|
||||
* @return EnumUigfGachaType
|
||||
*/
|
||||
enum EnumUigfGachaType {
|
||||
CharacterUp = "301",
|
||||
WeaponUp = "302",
|
||||
Normal = "200",
|
||||
Newbie = "100",
|
||||
}
|
||||
|
||||
/**
|
||||
* @description UIGF 祈愿列表
|
||||
* @Beta v0.5.1
|
||||
* @version UIGF v3.0
|
||||
* @interface GachaItem
|
||||
* @property {EnumGachaType} gacha_type - 祈愿类型
|
||||
* @property {string} item_id - 物品ID
|
||||
* @property {string} count - 数量
|
||||
* @property {string} time - 时间 yyyy-MM-dd HH:mm:ss
|
||||
* @property {string} name - 名称
|
||||
* @property {string} item_type - 物品类型
|
||||
* @property {string} rank_type - 稀有度
|
||||
* @property {string} id - ID
|
||||
* @property {EnumUigfGachaType} uigf_gacha_type - UIGF 祈愿类型
|
||||
* @return GachaItem
|
||||
*/
|
||||
interface GachaItem {
|
||||
uigf_gacha_type: string;
|
||||
gacha_type: string;
|
||||
item_id: string;
|
||||
count?: string;
|
||||
time: string;
|
||||
name: string;
|
||||
item_type?: string;
|
||||
rank_type?: string;
|
||||
id: string;
|
||||
}
|
||||
type Schema = {
|
||||
/** 头部信息 */
|
||||
info: Info;
|
||||
/** 祈愿列表 */
|
||||
list: Array<GachaItem>;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description UIGF 祈愿列表, v4.0,原神数据
|
||||
* @since Beta v0.5.0
|
||||
* @interface GachaHk4e
|
||||
* @property {string|number} uid - UID
|
||||
* @property {number} timezone - 时区
|
||||
* @property {string} lang - 语言
|
||||
* @property {GachaItem[]} list - 祈愿列表
|
||||
* @return GachaHk4e
|
||||
* UIGF 数据, v4.0
|
||||
* @since Beta v0.8.4
|
||||
*/
|
||||
interface GachaHk4e {
|
||||
uid: string | number;
|
||||
timezone: number;
|
||||
type Schema4 = {
|
||||
/** 头部信息 */
|
||||
info: Info4;
|
||||
/** 祈愿列表,原神数据 */
|
||||
hk4e: Array<GachaHk4e>;
|
||||
/** 祈愿列表,千星奇域数据 */
|
||||
hk4e_ugc?: Array<GachaUgc>;
|
||||
};
|
||||
|
||||
/**
|
||||
* UIGF 头部信息
|
||||
* @since Beta v0.5.0
|
||||
* @version UIGF v3.0
|
||||
*/
|
||||
type Info = {
|
||||
/** UID */
|
||||
uid: string;
|
||||
/** 语言 */
|
||||
lang: string;
|
||||
/** UIGF 版本,应用使用的是 3.0 */
|
||||
uigf_version: string;
|
||||
/** 导出时间戳(秒) */
|
||||
export_timestamp?: number;
|
||||
/** 导出时间 yyyy-MM-dd HH:mm:ss */
|
||||
export_time?: string;
|
||||
/** 导出应用 */
|
||||
export_app?: string;
|
||||
/** 导出应用版本 */
|
||||
export_app_version?: string;
|
||||
/** 时区 */
|
||||
region_time_zone?: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* UIGF 头部信息
|
||||
* @since Beta v0.5.1
|
||||
* @version v4.0+
|
||||
*/
|
||||
type Info4 = {
|
||||
/** 导出时间戳(秒) */
|
||||
export_timestamp: string;
|
||||
/** 导出应用 */
|
||||
export_app: string;
|
||||
/** 导出应用版本 */
|
||||
export_app_version: string;
|
||||
/** UIGF 版本 */
|
||||
version: string;
|
||||
/** 语言 */
|
||||
lang?: string;
|
||||
list: GachaItem[];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* UIGF4 祈愿项,原神
|
||||
* @since Beta v0.5.0
|
||||
*/
|
||||
type GachaHk4e = {
|
||||
/** UID */
|
||||
uid: string | number;
|
||||
/** 时区 */
|
||||
timezone: number;
|
||||
/** 语言 */
|
||||
lang?: string;
|
||||
list: Array<GachaItem>;
|
||||
};
|
||||
|
||||
/**
|
||||
* UIGF4 祈愿项,千星奇域
|
||||
* @since Beta v0.8.4
|
||||
* @remarks 该标准尚未最终确定
|
||||
*/
|
||||
type GachaUgc = {
|
||||
/** UID */
|
||||
uid: string | number;
|
||||
/** 时区 */
|
||||
timezone: number;
|
||||
/** 语言 */
|
||||
lang?: string;
|
||||
/** 服务器区域 */
|
||||
region: string;
|
||||
/** 祈愿列表 */
|
||||
list: Array<GachaItemB>;
|
||||
};
|
||||
|
||||
/**
|
||||
* UIGF 祈愿项-原神
|
||||
* @since Beta v0.5.1
|
||||
* @version UIGF v3.0
|
||||
*/
|
||||
type GachaItem = {
|
||||
/** UIGF 祈愿类型 */
|
||||
uigf_gacha_type: string;
|
||||
/** 祈愿类型 */
|
||||
gacha_type: string;
|
||||
/** 物品ID */
|
||||
item_id: string;
|
||||
/** 数量 */
|
||||
count?: string;
|
||||
/** 时间 yyyy-MM-dd HH:mm:ss */
|
||||
time: string;
|
||||
/** 名称 */
|
||||
name: string;
|
||||
/** 物品类型 */
|
||||
item_type?: string;
|
||||
/** 稀有度 */
|
||||
rank_type?: string;
|
||||
/** ID */
|
||||
id: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* UIGF 祈愿项-千星奇域
|
||||
* @since Beta v0.8.4
|
||||
* @remarks 该标准尚未最终确定
|
||||
*/
|
||||
type GachaItemB = {
|
||||
/** id */
|
||||
id: string;
|
||||
/** 排期id */
|
||||
schedule_id: string;
|
||||
/** 物品类型 */
|
||||
item_type: string;
|
||||
/** 物品id */
|
||||
item_id: string;
|
||||
/** 名称 */
|
||||
item_name: string;
|
||||
/** 稀有度 */
|
||||
rank_type: string;
|
||||
/** 是否限定 */
|
||||
is_up: string;
|
||||
/** 时间 yyyy-MM-dd HH:mm:ss */
|
||||
time: string;
|
||||
/**
|
||||
* 祈愿类型
|
||||
* @remarks
|
||||
* 1000-常驻池
|
||||
* 2000-活动池
|
||||
*/
|
||||
gacha_type: string;
|
||||
/** 祈愿类型,用于接口请求 */
|
||||
op_gacha_type: string;
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user