♻️ 调整目录结构

This commit is contained in:
目棃
2024-11-19 14:45:29 +08:00
parent e1f85d1d92
commit 3fef8467f4
121 changed files with 532 additions and 554 deletions

View File

@@ -0,0 +1 @@
<!-- todo 角色云图 -->

View File

@@ -0,0 +1,176 @@
<template>
<div class="gro-dl-box">
<div class="gro-dl-progress" />
<div class="gro-dl-icon">
<img :alt="props.data.name" :src="getIcon()" />
</div>
<div class="gro-dl-line">
<div class="gro-dl-base">
<div class="gro-dl-name">{{ props.data.name }}</div>
<div class="gro-dl-time">{{ props.data.time }}</div>
</div>
<div class="gro-dl-info">
<div class="gro-dl-cnt">{{ props.count }}</div>
<div class="gro-dl-hint" v-if="hint !== ''">{{ hint }}</div>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { computed } from "vue";
import { AppGachaData } from "../../data/index.js";
import TSUserGacha from "../../plugins/Sqlite/modules/userGacha.js";
export interface GroDataLineProps {
data: TGApp.Sqlite.GachaRecords.SingleTable;
count: number;
}
const props = defineProps<GroDataLineProps>();
const hint = getEndHint();
function getIcon(): string {
const itemType = TSUserGacha.getGachaItemType(props.data.itemId);
if (itemType[0] === "角色") {
return `/WIKI/character/${props.data.itemId}.webp`;
} else if (itemType[0] === "武器") {
return `/WIKI/weapon/${props.data.itemId}.webp`;
} else {
return `/source/UI/paimon.webp`;
}
}
function getEndHint(): string {
if (props.data.gachaType === "100" || props.data.gachaType === "200") return "";
// if (props.data.rank !== "5") return "";
const itemTime = new Date(props.data.time).getTime();
const poolsFind = AppGachaData.filter((pool) => {
if (pool.type.toLocaleString() !== props.data.gachaType) return false;
const startTime = new Date(pool.from).getTime();
const endTime = new Date(pool.to).getTime();
return itemTime >= startTime && itemTime <= endTime;
});
if (poolsFind.length === 0) return "";
if (props.data.rank === "5") {
if (poolsFind.some((pool) => pool.up5List.includes(Number(props.data.itemId)))) return "UP";
return "歪";
} else if (props.data.rank === "4") {
if (poolsFind.some((pool) => pool.up4List.includes(Number(props.data.itemId)))) return "UP";
return "歪";
}
return "";
}
const progressColor = computed<string>(() => {
if (hint === "UP" && props.data.rank === "5") return "var(--tgc-od-orange)";
if (hint === "UP" && props.data.rank === "4") return "var(--tgc-od-purple)";
if (hint === "歪") return "var(--tgc-od-red)";
return "var(--tgc-od-blue)";
});
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="css" scoped>
.gro-dl-box {
position: relative;
display: flex;
width: calc(100% - 10px);
height: 40px;
align-items: center;
justify-content: flex-start;
padding: 5px;
border-radius: 5px;
margin: 0 5px;
background: var(--box-bg-3);
column-gap: 5px;
}
.gro-dl-progress {
position: absolute;
bottom: 0;
left: 0;
width: v-bind(progressWidth);
max-width: 100%;
height: 5px;
border-radius: 5px;
background: v-bind(progressColor);
transition: width 0.5s;
}
.gro-dl-icon {
display: flex;
width: 30px;
height: 30px;
align-items: center;
justify-content: center;
img {
width: 100%;
height: 100%;
}
}
.gro-dl-line {
display: flex;
width: 100%;
align-items: center;
justify-content: space-between;
}
.gro-dl-base {
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: center;
}
.gro-dl-name {
height: 16px;
color: var(--common-text-title);
font-family: var(--font-title);
font-size: 14px;
}
.gro-dl-time {
height: 14px;
color: var(--box-text-7);
font-size: 12px;
}
.gro-dl-info {
display: flex;
align-items: flex-end;
justify-content: center;
column-gap: 5px;
}
.gro-dl-cnt {
color: var(--common-text-title);
font-family: var(--font-title);
}
.gro-dl-hint {
display: flex;
width: 30px;
height: 30px;
align-items: center;
justify-content: center;
padding: 5px;
border-radius: 50%;
background: var(--box-bg-1);
color: v-bind(progressColor);
font-family: var(--font-title);
transform: rotate(25deg);
}
</style>

View File

@@ -0,0 +1,239 @@
<template>
<div class="gro-dv-container">
<div class="gro-dvt-title">
<span>{{ title }}</span>
<span>{{ props.dataVal.length }}</span>
</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-ml-item">
<span>4已垫</span>
<span>{{ reset4count - 1 }}</span>
</div>
<div class="gro-ml-item">
<span>5已垫</span>
<span>{{ reset5count - 1 }}</span>
</div>
<div class="gro-ml-item">
<span>5平均</span>
<span>{{ star5avg }}</span>
</div>
</div>
<div class="gro-mid-list">
<div class="gro-ml-item">
<span>5统计</span>
<span>{{ getTitle("5") }}</span>
</div>
<div class="gro-ml-item">
<span>4统计</span>
<span>{{ getTitle("4") }}</span>
</div>
</div>
<!-- 这边放具体物品的列表 -->
<div class="gro-bottom">
<v-tabs v-model="tab">
<v-tab value="5">5</v-tab>
<v-tab value="4">4</v-tab>
</v-tabs>
<v-window v-model="tab" class="gro-bottom-window">
<v-window-item value="5" class="gro-b-window-item">
<GroDataLine
v-for="(item, index) in star5List"
:key="index"
:data="item.data"
:count="item.count"
/>
</v-window-item>
<v-window-item value="4" class="gro-b-window-item">
<GroDataLine
v-for="(item, index) in star4List"
:key="index"
:data="item.data"
:count="item.count"
/>
</v-window-item>
</v-window>
</div>
</div>
</template>
<script lang="ts" setup>
import { onMounted, ref, watch } from "vue";
import GroDataLine, { GroDataLineProps } from "./gro-data-line.vue";
interface GachaDataViewProps {
dataType: "new" | "avatar" | "weapon" | "normal" | "mix";
dataVal: TGApp.Sqlite.GachaRecords.SingleTable[];
}
const props = defineProps<GachaDataViewProps>();
// data
const loading = ref<boolean>(true); // 是否加载完
const title = ref<string>(""); // 卡片标题
const startDate = ref<string>(""); // 最早的时间
const endDate = ref<string>(""); // 最晚的时间
const star5List = ref<GroDataLineProps[]>([]); // 5星物品数据
const star4List = ref<GroDataLineProps[]>([]); // 4星物品数据
const reset5count = ref<number>(1); // 5星垫抽数量
const reset4count = ref<number>(1); // 4星垫抽数量
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;
// 按照 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 === "3") {
reset4count.value++;
reset5count.value++;
star3count.value++;
} else if (item.rank === "4") {
reset5count.value++;
star4List.value.push({
data: item,
count: reset4count.value,
});
reset4count.value = 1;
} else if (item.rank === "5") {
reset4count.value++;
star5List.value.push({
data: item,
count: reset5count.value,
});
reset5count.value = 1;
}
});
star5avg.value = getStar5Avg();
// 两个列表反序
star5List.value.reverse();
star4List.value.reverse();
}
// 获取标题
function getTitle(type: "top" | "5" | "4" | "3"): string {
if (type === "top") {
if (props.dataType === "new") return "新手祈愿";
if (props.dataType === "avatar") return "角色祈愿";
if (props.dataType === "weapon") return "武器祈愿";
if (props.dataType === "normal") return "常驻祈愿";
if (props.dataType === "mix") return "集录祈愿";
return "";
} else if (props.dataVal.length === 0) {
return "暂无数据";
} else if (type === "5") {
// 5星物品统计 00.00%
return `${star5List.value.length} [${((star5List.value.length * 100) / props.dataVal.length)
.toFixed(2)
.padStart(5, "0")}%]`;
} else if (type === "4") {
// 4星物品统计
return `${star4List.value.length} [${((star4List.value.length * 100) / props.dataVal.length)
.toFixed(2)
.padStart(5, "0")}%]`;
} else {
// 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>
.gro-dv-container {
height: 100%;
padding: 10px;
border-radius: 5px;
background: var(--box-bg-2);
}
.gro-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: 20px;
}
.gro-dvt-subtitle {
width: 100%;
font-family: var(--font-text);
font-size: 12px;
opacity: 0.6;
}
.gro-mid-list {
padding-top: 5px;
padding-bottom: 5px;
border-top: 1px solid var(--common-shadow-4);
color: var(--box-text-7);
}
.gro-ml-item {
display: flex;
width: 100%;
align-items: center;
justify-content: space-between;
font-family: var(--font-title);
font-size: 14px;
}
.gro-bottom {
width: 100%;
}
.gro-bottom-window {
height: calc(100vh - 440px);
}
.gro-b-window-item {
display: flex;
flex-direction: column;
margin-top: 10px;
gap: 5px;
overflow-y: auto;
}
</style>

View File

@@ -0,0 +1,236 @@
<template>
<div class="gro-echart">
<v-chart :option="getPoolData()" autoresize />
</div>
</template>
<script lang="ts" setup>
// about import err,see:https://github.com/apache/echarts/issues/19992
import { PieChart } from "echarts/charts.js";
import {
LegendComponent,
TitleComponent,
TooltipComponent,
ToolboxComponent,
} from "echarts/components.js";
import { use } from "echarts/core.js";
import { LabelLayout } from "echarts/features.js";
import { CanvasRenderer } from "echarts/renderers.js";
import type { EChartsOption } from "echarts/types/dist/shared.js";
import { provide } from "vue";
import VChart, { THEME_KEY } from "vue-echarts";
// echarts
use([
TitleComponent,
TooltipComponent,
ToolboxComponent,
LegendComponent,
PieChart,
CanvasRenderer,
LabelLayout,
]);
// 获取当前主题
provide(THEME_KEY, "dark");
interface GachaOverviewEchartsProps {
modelValue: TGApp.Sqlite.GachaRecords.SingleTable[];
}
const props = defineProps<GachaOverviewEchartsProps>();
// data
const defaultOptions = <EChartsOption>{
title: [
{
text: ">> 祈愿系统大数据分析 <<",
left: "center",
top: "5%",
},
{
text: "卡池分布",
left: "17%",
top: "45%",
},
{
text: "星级分布",
left: "17%",
top: "90%",
},
{
text: "角色池分布",
left: "45%",
bottom: "10%",
},
{
text: "武器池分布",
right: "5%",
bottom: "10%",
},
],
tooltip: { trigger: "item" },
legend: {
type: "scroll",
orient: "vertical",
left: 10,
top: 20,
bottom: 20,
},
toolbox: {
show: true,
feature: {
restore: {},
saveAsImage: {},
},
},
series: [
{
name: "卡池分布",
type: "pie",
radius: "50%",
data: [],
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: "rgba(0, 0, 0, 0.5)",
},
},
right: "60%",
top: 0,
bottom: "50%",
},
{
name: "星级分布",
type: "pie",
radius: "50%",
data: [],
right: "60%",
top: "50%",
bottom: "0",
},
{
name: "角色池分布",
type: "pie",
radius: [30, 150],
center: ["50%", "50%"],
roseType: "area",
itemStyle: {
borderRadius: [2, 10],
},
data: [],
datasetIndex: 3,
},
{
name: "武器池分布",
type: "pie",
radius: [30, 100],
itemStyle: {
borderRadius: [3, 10],
},
data: [],
left: "70%",
},
],
};
// 获取卡池分布的数据
// todo 重构以完善类型
function getPoolData(): EChartsOption {
const data: EChartsOption = JSON.parse(JSON.stringify(defaultOptions));
if (data.title !== undefined && Array.isArray(data.title)) {
data.title[0].subtext = `${props.modelValue.length} 条数据`;
}
if (data.series !== undefined && Array.isArray(data.series)) {
data.series[0].data = [
{
value: props.modelValue.filter((item) => item.uigfType === "100").length,
name: "新手祈愿",
},
{
value: props.modelValue.filter((item) => item.uigfType === "200").length,
name: "常驻祈愿",
},
{
value: props.modelValue.filter((item) => item.uigfType === "301").length,
name: "角色活动祈愿",
},
{
value: props.modelValue.filter((item) => item.uigfType === "302").length,
name: "武器活动祈愿",
},
{
value: props.modelValue.filter((item) => item.uigfType === "500").length,
name: "集录祈愿",
},
];
data.series[1].data = [
{ value: props.modelValue.filter((item) => item.rank === "3").length, name: "3星" },
{ value: props.modelValue.filter((item) => item.rank === "4").length, name: "4星" },
{ value: props.modelValue.filter((item) => item.rank === "5").length, name: "5星" },
];
}
const tempSet = new Set<string>();
const tempRecord = new Map<string, number>();
// 角色池分析
let tempList = props.modelValue.filter((item) => item.uigfType === "301");
let star3 = tempList.filter((item) => item.rank === "3").length;
if (data.title !== undefined && Array.isArray(data.title)) {
data.title[3].subtext = `${tempList.length} 条数据, 其中三星武器 ${star3}`;
}
tempList
.filter((item) => item.rank !== "3")
.forEach((item) => {
if (tempSet.has(item.name)) {
tempRecord.set(item.name, (tempRecord.get(item.name) ?? 0) + 1);
} else {
tempSet.add(item.name);
tempRecord.set(item.name, 1);
}
});
if (data.series !== undefined && Array.isArray(data.series)) {
data.series[2].data = Array.from(tempRecord).map((item) => {
return {
value: item[1],
name: item[0],
};
});
}
tempSet.clear();
tempRecord.clear();
// 武器池分析
tempList = props.modelValue.filter((item) => item.uigfType === "302");
star3 = tempList.filter((item) => item.rank === "3").length;
if (data.title !== undefined && Array.isArray(data.title)) {
data.title[4].subtext = `${tempList.length} 条数据,其中三星武器 ${star3}`;
}
tempList
.filter((item) => item.rank !== "3")
.forEach((item) => {
if (tempSet.has(item.name)) {
tempRecord.set(item.name, (tempRecord.get(item.name) ?? 0) + 1);
} else {
tempSet.add(item.name);
tempRecord.set(item.name, 1);
}
});
if (data.series !== undefined && Array.isArray(data.series)) {
data.series[3].data = Array.from(tempRecord).map((item) => {
return {
value: item[1],
name: item[0],
};
});
}
return data;
}
</script>
<style lang="css" scoped>
.gro-echart {
display: flex;
width: 100%;
height: 100%;
align-items: center;
justify-content: center;
}
</style>

View File

@@ -0,0 +1,299 @@
<template>
<div class="gro-container">
<v-tabs class="gro-tabs" v-model="historyTab" align-tabs="start" direction="vertical">
<v-tab v-for="(item, index) in tabList" :key="index" :value="item.tab">
v{{ item.tab }}
</v-tab>
</v-tabs>
<v-window v-model="historyTab" class="gro-window">
<v-window-item
v-for="(item, index) in tabList"
:key="index"
:value="item.tab"
class="gro-pools"
>
<div v-for="pool in item.value" :key="pool.order" class="gro-pool">
<img
:src="pool.banner"
class="gro-banner"
alt="banner"
@click="createPost(pool.postId)"
/>
<div class="gro-pool-info">
<div class="gro-pi-title">
<span>{{ pool.name }}</span>
<span class="gro-pi-tag">{{ pool.order === 1 ? "上半" : "下半" }}</span>
<span class="gro-pi-tag">{{ getType(pool.type) }}</span>
</div>
<div class="gro-pi-time">{{ getTimeStr(pool) }}</div>
<div class="gro-pi-sub">Up 五星</div>
<div class="gro-pool-up lv5">
<TItembox
v-for="i in pool.up5List"
:key="i"
:model-value="getBox(i)"
@click="toWiki(i)"
/>
</div>
<div class="gro-pi-sub">Up 四星</div>
<div class="gro-pool-up lv4">
<TItembox
v-for="i in pool.up4List"
:key="i"
:model-value="getBox(i)"
@click="toWiki(i)"
/>
</div>
</div>
</div>
</v-window-item>
</v-window>
</div>
</template>
<script lang="ts" setup>
import { onMounted, ref } from "vue";
import { useRouter } from "vue-router";
import { AppGachaData, AppCharacterData, AppWeaponData } from "../../data/index.js";
import { createPost } from "../../utils/TGWindow.js";
import { timestampToDate } from "../../utils/toolFunc.js";
import TItembox, { TItemBoxData } from "../app/t-item-box.vue";
import showDialog from "../func/dialog.js";
import showSnackbar from "../func/snackbar.js";
interface GroHistoryMap {
tab: string;
value: TGApp.App.Gacha.PoolItem[];
}
const historyTab = ref<string>("");
const tabList = ref<GroHistoryMap[]>([]);
const router = useRouter();
onMounted(() => {
const res: GroHistoryMap[] = [];
AppGachaData.forEach((pool) => {
const index = res.findIndex((item) => item.tab === pool.version);
if (index === -1) {
res.push({
tab: pool.version,
value: [pool],
});
} else {
res[index].value.push(pool);
}
});
tabList.value = res.reverse();
historyTab.value = res[0].tab;
});
async function toWiki(id: number): Promise<void> {
const cFind = AppCharacterData.find((item) => item.id === id);
const wFind = AppWeaponData.find((item) => item.id === id);
const jumpCheck = await showDialog.check("是否跳转到对应图鉴界面?");
if (!jumpCheck) {
showSnackbar.cancel("已取消");
return;
}
if (cFind) {
await router.push({ name: "角色图鉴", params: { id: id.toString() } });
return;
}
if (wFind) {
await router.push({ name: "武器图鉴", params: { id: id.toString() } });
return;
}
showSnackbar.warn("未找到对应角色或武器");
}
function getType(type: TGApp.App.Gacha.WishType): string {
switch (type) {
case 301:
return "角色活动祈愿";
case 400:
return "角色活动祈愿2";
case 302:
return "武器活动祈愿";
case 500:
return "集录祈愿";
default:
return `未知类型 ${type}`;
}
}
function getTimeStr(pool: TGApp.App.Gacha.PoolItem): string {
const startTime = timestampToDate(Date.parse(pool.from));
const endTime = timestampToDate(Date.parse(pool.to));
return `${startTime} ~ ${endTime}`;
}
function getBox(id: number): TItemBoxData {
const cFind = AppCharacterData.find((item) => item.id === id);
const wFind = AppWeaponData.find((item) => item.id === id);
if (cFind) {
return {
bg: `/icon/bg/${cFind.star}-Star.webp`,
icon: `/WIKI/character/${cFind.id}.webp`,
size: "80px",
height: "80px",
display: "inner",
clickable: true,
lt: `/icon/element/${cFind.element}元素.webp`,
ltSize: "20px",
innerHeight: 20,
innerIcon: `/icon/weapon/${cFind.weapon}.webp`,
innerText: cFind.name,
};
}
if (wFind) {
return {
bg: `/icon/bg/${wFind.star}-Star.webp`,
icon: `/WIKI/weapon/${wFind.id}.webp`,
size: "80px",
height: "80px",
display: "inner",
clickable: true,
lt: wFind.weaponIcon,
ltSize: "20px",
innerHeight: 20,
innerText: wFind.name,
};
}
return {
bg: "/icon/bg/0-Star.webp",
icon: "/source/UI/empty/webp",
size: "80px",
height: "80px",
display: "inner",
clickable: false,
lt: "",
ltSize: "0",
innerHeight: 20,
innerText: "未找到对应角色或武器",
};
}
</script>
<style lang="css" scoped>
.gro-container {
display: flex;
width: 100%;
height: 100%;
align-items: center;
justify-content: space-between;
column-gap: 10px;
}
.gro-tabs {
width: 100px;
height: 100%;
}
/* stylelint-disable-next-line selector-class-pattern */
.gro-container :deep(.v-tabs.v-slide-group--vertical) {
max-height: 100%;
}
.gro-window {
position: relative;
display: flex;
width: 100%;
height: 100%;
flex-direction: row;
align-items: center;
justify-content: space-between;
padding-right: 10px;
overflow-y: scroll;
}
/* stylelint-disable-next-line selector-class-pattern */
.gro-window :deep(.v-window__container) {
width: 100%;
}
.gro-pools {
position: relative;
display: flex;
width: 100%;
flex-direction: column;
align-items: center;
justify-content: center;
row-gap: 10px;
}
.gro-pool {
position: relative;
display: flex;
width: 100%;
align-items: flex-start;
justify-content: flex-start;
padding: 10px;
border-radius: 10px;
background: var(--box-bg-2);
column-gap: 10px;
}
.gro-banner {
width: 50vw;
border-radius: 10px;
cursor: pointer;
transition: 0.5s ease-in-out;
}
.gro-banner:hover {
box-shadow: 0 0 10px 5px var(--box-bg-2);
scale: 0.95;
transition: 0.5s ease-in-out;
}
.gro-pool-info {
display: flex;
height: 100%;
flex-direction: column;
align-items: flex-start;
justify-content: flex-start;
}
.gro-pi-title {
display: flex;
column-gap: 10px;
}
.gro-pi-title :first-child {
color: var(--common-text-title);
font-family: var(--font-title);
font-size: 20px;
}
.gro-pi-tag {
display: flex;
align-items: center;
justify-content: center;
padding: 0 5px;
border-radius: 5px;
background: var(--box-bg-1);
color: var(--box-text-5);
font-size: 16px;
}
.gro-pi-sub {
font-family: var(--font-title);
font-size: 18px;
}
.gro-pool-up {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: flex-start;
gap: 10px;
overflow-y: auto;
}
.gro-pool-up.lv5 {
max-height: 80px;
}
.gro-pool-up.lv4 {
max-height: 170px;
}
</style>

View File

@@ -0,0 +1,71 @@
<template>
<div class="gro-o-container">
<gro-dataview v-if="newData.length !== 0" :data-val="newData" data-type="new" />
<gro-dataview :data-val="normalData" data-type="normal" />
<gro-dataview :data-val="avatarData" data-type="avatar" />
<gro-dataview :data-val="weaponData" data-type="weapon" />
<gro-dataview v-if="mixData.length !== 0" :data-val="mixData" data-type="mix" />
</div>
</template>
<script lang="ts" setup>
import { computed, ref, watch } from "vue";
import GroDataview from "./gro-dataview.vue";
interface GachaOverviewProps {
modelValue: TGApp.Sqlite.GachaRecords.SingleTable[];
}
const props = defineProps<GachaOverviewProps>();
// data
const newData = computed<TGApp.Sqlite.GachaRecords.SingleTable[]>(() =>
props.modelValue.filter((item) => item.uigfType === "100"),
);
const normalData = computed<TGApp.Sqlite.GachaRecords.SingleTable[]>(() =>
props.modelValue.filter((item) => item.uigfType === "200"),
);
const avatarData = computed<TGApp.Sqlite.GachaRecords.SingleTable[]>(() =>
props.modelValue.filter((item) => item.uigfType === "301"),
);
const weaponData = computed<TGApp.Sqlite.GachaRecords.SingleTable[]>(() =>
props.modelValue.filter((item) => item.uigfType === "302"),
);
const mixData = computed<TGApp.Sqlite.GachaRecords.SingleTable[]>(() =>
props.modelValue.filter((item) => item.uigfType === "500"),
);
const cnCols = ref<string>(getCnCols());
function getCnCols(): string {
let total = 5;
if (newData.value.length === 0) {
total -= 1;
}
if (mixData.value.length === 0) {
total -= 1;
}
if (total === 5) {
return "repeat(5, 0.2fr)";
} else if (total === 4) {
return "repeat(4, 0.25fr)";
} else {
return "repeat(3, 0.33fr)";
}
}
// 监听数据变化
watch(
() => props.modelValue,
() => {
cnCols.value = getCnCols();
},
);
</script>
<style lang="css" scoped>
.gro-o-container {
display: grid;
height: 100%;
grid-column-gap: 10px;
grid-template-columns: v-bind(cnCols);
}
</style>

View File

@@ -0,0 +1,67 @@
<template>
<!-- todo 优化增加筛选功能 -->
<div class="ua-gt-box">
<v-data-table
:headers="headers"
:items="props.modelValue"
height="500px"
fixed-header
fixed-footer
>
<template v-slot:item="{ item }">
<tr class="ua-gt-tr">
<td>{{ item.time }}</td>
<td>{{ getPool(item.uigfType) }}</td>
<td>{{ item.type }}</td>
<td>{{ item.name }}</td>
<td>{{ item.rank }}</td>
</tr>
</template>
</v-data-table>
</div>
</template>
<script lang="ts" setup>
interface GroTableProps {
modelValue: TGApp.Sqlite.GachaRecords.SingleTable[];
}
const props = defineProps<GroTableProps>();
const headers = [
{ title: "时间", align: "center", key: "time" },
{ title: "卡池", align: "center", key: "uigfType" },
{ title: "类型", align: "center", key: "type" },
{ title: "名称", align: "center", key: "name" },
{ title: "星级", align: "center", key: "rank" },
];
function getPool(type: string) {
switch (type) {
case "100":
return "新手祈愿";
case "200":
return "常驻祈愿";
case "301":
return "角色活动祈愿";
case "302":
return "武器活动祈愿";
case "500":
return "集录祈愿";
default:
return "未知";
}
}
</script>
<style lang="css" scoped>
.ua-gt-box {
height: 100%;
max-height: calc(100vh - 120px);
padding-right: 5px;
border-radius: 5px;
overflow-y: auto;
}
.ua-gt-tr {
text-align: center;
}
</style>

View File

@@ -0,0 +1 @@
<!-- todo 时间轴 -->