mirror of
https://github.com/BTMuli/TeyvatGuide.git
synced 2025-12-18 10:18:14 +08:00
174
src/components/userAchi/tua-achi-list.vue
Normal file
174
src/components/userAchi/tua-achi-list.vue
Normal file
@@ -0,0 +1,174 @@
|
||||
<template>
|
||||
<div class="tua-al-container">
|
||||
<div v-if="ncData !== undefined">
|
||||
<TopNameCard :data="ncData" @selected="showNc = true" />
|
||||
</div>
|
||||
<!-- todo 虚拟列表优化 -->
|
||||
<div v-for="(item, index) in renderAchi" :key="index">
|
||||
<TuaAchi :modelValue="item" @select-achi="selectAchi" />
|
||||
</div>
|
||||
<ToNameCard v-model="showNc" :data="ncData" v-if="ncData" />
|
||||
<ToAchiInfo
|
||||
v-if="selectedAchi"
|
||||
v-model="showOverlay"
|
||||
:data="selectedAchi"
|
||||
@select-series="selectSeries"
|
||||
>
|
||||
<template #left>
|
||||
<div class="card-arrow left" @click="switchAchiInfo(false)">
|
||||
<img src="../../assets/icons/arrow-right.svg" alt="right" />
|
||||
</div>
|
||||
</template>
|
||||
<template #right>
|
||||
<div class="card-arrow" @click="switchAchiInfo(true)">
|
||||
<img src="../../assets/icons/arrow-right.svg" alt="right" />
|
||||
</div>
|
||||
</template>
|
||||
</ToAchiInfo>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, watch, onMounted } from "vue";
|
||||
|
||||
import { AppAchievementSeriesData, AppNameCardsData } from "../../data/index.js";
|
||||
import TSUserAchi from "../../plugins/Sqlite/modules/userAchi.js";
|
||||
import showSnackbar from "../func/snackbar.js";
|
||||
import ToNameCard from "../overlay/to-namecard.vue";
|
||||
import TopNameCard from "../overlay/top-namecard.vue";
|
||||
|
||||
import ToAchiInfo from "./tua-achi-overlay.vue";
|
||||
import TuaAchi from "./tua-achi.vue";
|
||||
|
||||
interface TuaAchiListProps {
|
||||
uid: number;
|
||||
hideFin: boolean;
|
||||
series?: number;
|
||||
search?: string;
|
||||
}
|
||||
|
||||
interface TuaAchiListEmits {
|
||||
(e: "update:series", v: number): void;
|
||||
}
|
||||
|
||||
const props = defineProps<TuaAchiListProps>();
|
||||
const emits = defineEmits<TuaAchiListEmits>();
|
||||
|
||||
const achievements = ref<TGApp.Sqlite.Achievement.RenderAchi[]>([]);
|
||||
const selectedAchi = ref<TGApp.Sqlite.Achievement.RenderAchi | undefined>(undefined);
|
||||
|
||||
const nameCard = ref<string>();
|
||||
const ncData = ref<TGApp.App.NameCard.Item | undefined>(undefined);
|
||||
const showNc = ref<boolean>(false);
|
||||
|
||||
const showOverlay = ref<boolean>(false);
|
||||
|
||||
const renderAchi = computed<Array<TGApp.Sqlite.Achievement.RenderAchi>>(() => {
|
||||
if (props.hideFin) return achievements.value.filter((a) => a.isCompleted);
|
||||
return achievements.value;
|
||||
});
|
||||
|
||||
onMounted(async () => await loadAchi());
|
||||
|
||||
watch(
|
||||
() => [props.series, props.search, props.uid],
|
||||
async () => await loadAchi(),
|
||||
);
|
||||
|
||||
async function loadAchi(): Promise<void> {
|
||||
if (props.search && props.search !== "") {
|
||||
nameCard.value = undefined;
|
||||
ncData.value = undefined;
|
||||
achievements.value = await TSUserAchi.searchAchi(props.uid, props.search);
|
||||
return;
|
||||
}
|
||||
achievements.value = await TSUserAchi.getAchievements(props.uid, props.series);
|
||||
if (!selectedAchi.value && achievements.value.length > 0) {
|
||||
selectedAchi.value = achievements.value[0];
|
||||
} else if (selectedAchi.value !== undefined && achievements.value.length > 0) {
|
||||
const index = achievements.value.findIndex((a) => a.id === selectedAchi.value!.id);
|
||||
if (index === -1) selectedAchi.value = achievements.value[0];
|
||||
}
|
||||
const seriesFind = AppAchievementSeriesData.find((s) => s.id === props.series);
|
||||
if (!seriesFind || seriesFind.card === "") {
|
||||
nameCard.value = undefined;
|
||||
ncData.value = undefined;
|
||||
} else nameCard.value = seriesFind.card;
|
||||
if (nameCard.value && nameCard.value !== "") {
|
||||
const ncFind = AppNameCardsData.find((c) => c.name === nameCard.value);
|
||||
if (ncFind) ncData.value = ncFind;
|
||||
else ncData.value = undefined;
|
||||
}
|
||||
showSnackbar({ text: `已获取 ${renderAchi.value.length} 条成就数据`, color: "success" });
|
||||
}
|
||||
|
||||
function selectAchi(data: TGApp.Sqlite.Achievement.RenderAchi): void {
|
||||
selectedAchi.value = data;
|
||||
showOverlay.value = true;
|
||||
}
|
||||
|
||||
function selectSeries(data: number): void {
|
||||
emits("update:series", data);
|
||||
}
|
||||
|
||||
function switchAchiInfo(next: boolean): void {
|
||||
if (selectedAchi.value === undefined) {
|
||||
showSnackbar({ text: "当前未选中成就!", color: "warn" });
|
||||
return;
|
||||
}
|
||||
const index = renderAchi.value.findIndex((i) => i === selectedAchi.value);
|
||||
if (index === -1) {
|
||||
showSnackbar({
|
||||
text: `未找到选中成就 ${selectedAchi.value.name}(${selectedAchi.value.id}) 的索引!`,
|
||||
color: "error",
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (next) {
|
||||
if (index === renderAchi.value.length - 1) {
|
||||
showSnackbar({ text: "已经是最后一个了", color: "warn" });
|
||||
return;
|
||||
}
|
||||
selectedAchi.value = renderAchi.value[index + 1];
|
||||
return;
|
||||
}
|
||||
if (index === 0) {
|
||||
showSnackbar({ text: "已经是第一个了", color: "warn" });
|
||||
return;
|
||||
}
|
||||
selectedAchi.value = renderAchi.value[index - 1];
|
||||
}
|
||||
</script>
|
||||
<style lang="css" scoped>
|
||||
.tua-al-container {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
flex-direction: column;
|
||||
padding-right: 10px;
|
||||
overflow-y: scroll;
|
||||
row-gap: 10px;
|
||||
}
|
||||
|
||||
.card-arrow {
|
||||
position: relative;
|
||||
display: flex;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.dark .card-arrow {
|
||||
filter: invert(11%) sepia(73%) saturate(11%) hue-rotate(139deg) brightness(97%) contrast(81%);
|
||||
}
|
||||
|
||||
.card-arrow img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.card-arrow.left img {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
</style>
|
||||
175
src/components/userAchi/tua-achi-overlay.vue
Normal file
175
src/components/userAchi/tua-achi-overlay.vue
Normal file
@@ -0,0 +1,175 @@
|
||||
<template>
|
||||
<TOverlay v-model="visible" hide :to-click="onCancel" blur-val="0">
|
||||
<div class="tua-ao-container" v-if="props.data">
|
||||
<slot name="left"></slot>
|
||||
<div class="tua-ao-box">
|
||||
<div class="tua-ao-top">
|
||||
<span class="tua-ao-click" @click="searchDirect(props.data.name)">
|
||||
{{ props.data.name }}
|
||||
</span>
|
||||
<span>{{ props.data.description }}</span>
|
||||
</div>
|
||||
<div class="tua-ao-mid-title">
|
||||
<span>所属系列:</span>
|
||||
<span class="tua-ao-click" @click="emits('select-series', props.data.series)">
|
||||
{{ AppAchievementSeriesData.find((s) => s.id === props.data?.series)?.name ?? "未知" }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="tua-ao-mid-title">
|
||||
<span>原石奖励:</span>
|
||||
<span>{{ props.data.reward }}</span>
|
||||
</div>
|
||||
<div class="tua-ao-mid-title">
|
||||
<span>触发方式:</span>
|
||||
<span>{{ props.data.trigger.task ? "完成以下所有任务" : props.data.trigger.type }}</span>
|
||||
</div>
|
||||
<div class="tua-ao-mid-item" v-for="item in props.data.trigger.task" :key="item.questId">
|
||||
<v-icon>mdi-alert-decagram</v-icon>
|
||||
<span class="tua-ao-click" @click="searchDirect(item.name)">{{ item.name }}</span>
|
||||
<span>({{ item.type }})</span>
|
||||
</div>
|
||||
<div>
|
||||
<div class="tua-ao-bottom-title">
|
||||
<span>是否完成:</span>
|
||||
<span>{{ props.data.isCompleted ? "是" : "否" }}</span>
|
||||
</div>
|
||||
<div class="tua-ao-bottom-title">
|
||||
<span>完成时间:</span>
|
||||
<span>{{ props.data.completedTime }}</span>
|
||||
</div>
|
||||
<div class="tua-ao-bottom-title">
|
||||
<span>当前进度:</span>
|
||||
<span>{{ props.data.progress }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tua-ao-extra">
|
||||
<span>ID:{{ props.data.id }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<slot name="right"></slot>
|
||||
</div>
|
||||
</TOverlay>
|
||||
<ToPostSearch gid="2" v-model="showSearch" :keyword="search" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref } from "vue";
|
||||
|
||||
import { AppAchievementSeriesData } from "../../data/index.js";
|
||||
import TGLogger from "../../utils/TGLogger.js";
|
||||
import TOverlay from "../main/t-overlay.vue";
|
||||
import ToPostSearch from "../post/to-postSearch.vue";
|
||||
|
||||
interface ToAchiInfoProps {
|
||||
modelValue: boolean;
|
||||
data: TGApp.Sqlite.Achievement.RenderAchi;
|
||||
}
|
||||
|
||||
interface ToAchiInfoEmits {
|
||||
(e: "update:modelValue", v: boolean): void;
|
||||
|
||||
(e: "select-series", v: number): void;
|
||||
}
|
||||
|
||||
const props = defineProps<ToAchiInfoProps>();
|
||||
const emits = defineEmits<ToAchiInfoEmits>();
|
||||
const showSearch = ref(false);
|
||||
const search = ref("");
|
||||
|
||||
const visible = computed({
|
||||
get: () => props.modelValue,
|
||||
set: (value) => {
|
||||
emits("update:modelValue", value);
|
||||
},
|
||||
});
|
||||
|
||||
function onCancel() {
|
||||
visible.value = false;
|
||||
}
|
||||
|
||||
async function searchDirect(word: string): Promise<void> {
|
||||
await TGLogger.Info(`[ToAchiInfo][${props.data.id}][Search] 查询 ${word}`);
|
||||
search.value = word;
|
||||
showSearch.value = true;
|
||||
}
|
||||
</script>
|
||||
<style lang="css" scoped>
|
||||
.tua-ao-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
column-gap: 10px;
|
||||
}
|
||||
|
||||
.tua-ao-box {
|
||||
position: relative;
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
width: 600px;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
padding: 10px;
|
||||
border-radius: 10px;
|
||||
background: var(--box-bg-1);
|
||||
row-gap: 10px;
|
||||
}
|
||||
|
||||
.tua-ao-top {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.tua-ao-top :first-child {
|
||||
color: var(--common-text-title);
|
||||
font-family: var(--font-title);
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.tua-ao-top-main :last-child {
|
||||
padding: 0 5px;
|
||||
border-radius: 5px;
|
||||
background: var(--box-bg-2);
|
||||
color: var(--box-text-5);
|
||||
font-family: var(--font-title);
|
||||
}
|
||||
|
||||
.tua-ao-mid-title,
|
||||
.tua-ao-bottom-title {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tua-ao-mid-title :first-child,
|
||||
.tua-ao-bottom-title :first-child {
|
||||
font-family: var(--font-title);
|
||||
}
|
||||
|
||||
.tua-ao-mid-title :last-child {
|
||||
color: var(--box-text-3);
|
||||
}
|
||||
|
||||
.tua-ao-mid-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
padding-left: 15px;
|
||||
margin-top: 5px;
|
||||
column-gap: 5px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tua-ao-mid-item :first-child {
|
||||
color: var(--box-text-5);
|
||||
}
|
||||
|
||||
.tua-ao-extra {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
bottom: 10px;
|
||||
}
|
||||
|
||||
.tua-ao-click {
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
233
src/components/userAchi/tua-achi.vue
Normal file
233
src/components/userAchi/tua-achi.vue
Normal file
@@ -0,0 +1,233 @@
|
||||
<template>
|
||||
<div class="achi-container" :title="getAchiTitle()">
|
||||
<div class="achi-version">v{{ data.version }}</div>
|
||||
<div class="achi-pre">
|
||||
<div class="achi-pre-icon">
|
||||
<v-icon v-if="!data.isCompleted" color="var(--tgc-blue-3)" @click="setAchiStat(true)">
|
||||
mdi-circle
|
||||
</v-icon>
|
||||
<v-icon v-else class="achi-finish" @click="setAchiStat(false)">
|
||||
<img alt="finish" src="/source/UI/finish.webp" />
|
||||
</v-icon>
|
||||
</div>
|
||||
<div class="achi-pre-info" @click="selectAchi()">
|
||||
<span>
|
||||
<span>{{ data.name }}</span>
|
||||
<span v-if="data.progress !== 0">
|
||||
{{ data.progress }}
|
||||
</span>
|
||||
</span>
|
||||
<span>{{ data.description }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="achi-append">
|
||||
<span v-show="data.isCompleted">{{ data.completedTime }}</span>
|
||||
<div class="achi-append-icon">
|
||||
<img alt="icon" src="/icon/material/201.webp" />
|
||||
<span>{{ data.reward }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { event } from "@tauri-apps/api";
|
||||
import { toRaw, ref, watch } from "vue";
|
||||
|
||||
import { AppAchievementSeriesData } from "../../data/index.js";
|
||||
import TSUserAchi from "../../plugins/Sqlite/modules/userAchi.js";
|
||||
import { timestampToDate } from "../../utils/toolFunc.js";
|
||||
import showConfirm from "../func/confirm.js";
|
||||
import showSnackbar from "../func/snackbar.js";
|
||||
|
||||
interface TuaAchiProps {
|
||||
modelValue: TGApp.Sqlite.Achievement.RenderAchi;
|
||||
}
|
||||
|
||||
interface TuaAchiEmits {
|
||||
(e: "update:modelValue", data: TGApp.Sqlite.Achievement.RenderAchi): void;
|
||||
|
||||
(e: "update:update", data: boolean): void;
|
||||
|
||||
(e: "select-achi", data: TGApp.Sqlite.Achievement.RenderAchi): void;
|
||||
}
|
||||
|
||||
const props = defineProps<TuaAchiProps>();
|
||||
const emits = defineEmits<TuaAchiEmits>();
|
||||
const data = ref<TGApp.Sqlite.Achievement.RenderAchi>(toRaw(props.modelValue));
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(newVal: TGApp.Sqlite.Achievement.RenderAchi) => {
|
||||
data.value = toRaw(newVal);
|
||||
},
|
||||
);
|
||||
|
||||
function getAchiTitle(): string {
|
||||
const seriesFind = AppAchievementSeriesData.find((s) => s.id === data.value.series);
|
||||
if (!seriesFind) return "未知";
|
||||
return seriesFind.name;
|
||||
}
|
||||
|
||||
function selectAchi(): void {
|
||||
emits("select-achi", props.modelValue);
|
||||
}
|
||||
|
||||
async function setAchiStat(stat: boolean): Promise<void> {
|
||||
if (!stat) {
|
||||
data.value.isCompleted = false;
|
||||
await TSUserAchi.updateAchi(data.value);
|
||||
emits("update:modelValue", data.value);
|
||||
await event.emit("updateAchi", data.value.series);
|
||||
showSnackbar({
|
||||
text: `已将成就 ${data.value.name}(${data.value.id}) 状态设为未完成`,
|
||||
color: "success",
|
||||
});
|
||||
return;
|
||||
}
|
||||
let progress: false | undefined | string = await showConfirm({
|
||||
mode: "input",
|
||||
title: "请输入成就进度",
|
||||
text: "进度",
|
||||
input: data.value.progress,
|
||||
});
|
||||
if (progress === false) {
|
||||
showSnackbar({ text: "已取消成就编辑", color: "cancel" });
|
||||
return;
|
||||
}
|
||||
if (progress === undefined) progress = data.value.progress.toString();
|
||||
if (isNaN(Number(progress)) || progress === "0") {
|
||||
showSnackbar({ text: "请输入有效数字!", color: "warn" });
|
||||
return;
|
||||
}
|
||||
data.value.progress = Number(progress);
|
||||
data.value.completedTime = timestampToDate(new Date().getTime());
|
||||
data.value.isCompleted = true;
|
||||
await TSUserAchi.updateAchi(data.value);
|
||||
await event.emit("updateAchi", data.value.series);
|
||||
showSnackbar({
|
||||
text: `已将成就 ${data.value.name}(${data.value.id}) 状态设为已完成`,
|
||||
color: "success",
|
||||
});
|
||||
emits("update:modelValue", data.value);
|
||||
}
|
||||
</script>
|
||||
<style lang="css" scoped>
|
||||
.achi-container {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 10px;
|
||||
border-radius: 10px;
|
||||
background: var(--box-bg-1);
|
||||
color: var(--box-text-7);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.achi-version {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 50px;
|
||||
border-right: 1px solid var(--common-shadow-1);
|
||||
border-bottom: 1px solid var(--common-shadow-1);
|
||||
background: var(--box-bg-2);
|
||||
border-bottom-right-radius: 20px;
|
||||
border-top-left-radius: 10px;
|
||||
color: var(--tgc-pink-1);
|
||||
font-family: var(--font-title);
|
||||
font-size: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.achi-pre {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
column-gap: 10px;
|
||||
}
|
||||
|
||||
.achi-pre-icon {
|
||||
display: flex;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.achi-finish img {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
filter: invert(51%) sepia(100%) saturate(353%) hue-rotate(42deg) brightness(107%) contrast(91%);
|
||||
}
|
||||
|
||||
.achi-pre-info {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
flex-flow: column wrap;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.achi-pre-info :nth-child(1) {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
column-gap: 5px;
|
||||
font-family: var(--font-title);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.achi-pre-info :nth-child(1) :nth-child(2) {
|
||||
color: var(--tgc-blue-2);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.achi-pre-info :nth-child(2) {
|
||||
font-size: 12px;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.achi-append-icon span {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 10px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: rgb(0 0 0 / 50%);
|
||||
border-bottom-left-radius: 5px;
|
||||
border-bottom-right-radius: 5px;
|
||||
color: var(--tgc-white-1);
|
||||
font-size: 8px;
|
||||
}
|
||||
|
||||
.achi-append {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
column-gap: 10px;
|
||||
}
|
||||
|
||||
.achi-append :nth-last-child(2) {
|
||||
margin-right: 10px;
|
||||
color: var(--box-text-4);
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
.achi-append-icon {
|
||||
position: relative;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 5px;
|
||||
background-image: url("/icon/bg/5-Star.webp");
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.achi-append-icon img {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
</style>
|
||||
0
src/components/userAchi/tua-nc-overlay.vue
Normal file
0
src/components/userAchi/tua-nc-overlay.vue
Normal file
135
src/components/userAchi/tua-series.vue
Normal file
135
src/components/userAchi/tua-series.vue
Normal file
@@ -0,0 +1,135 @@
|
||||
<template>
|
||||
<div class="tuas-card" @click="selectSeries" v-if="data">
|
||||
<div class="tuas-version">v{{ data.version }}</div>
|
||||
<img alt="icon" class="tuas-icon" :src="data.icon" />
|
||||
<div class="tuas-content">
|
||||
<span :title="data.name">{{ data.name }}</span>
|
||||
<span>{{ overview.fin }}/{{ overview.total }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { listen, UnlistenFn } from "@tauri-apps/api/event";
|
||||
import { ref, onMounted, watch, onUnmounted } from "vue";
|
||||
|
||||
import { AppAchievementSeriesData } from "../../data/index.js";
|
||||
import TSUserAchi from "../../plugins/Sqlite/modules/userAchi.js";
|
||||
import showSnackbar from "../func/snackbar.js";
|
||||
|
||||
interface TuaSeriesProps {
|
||||
uid: number;
|
||||
series: number;
|
||||
cur: number;
|
||||
}
|
||||
|
||||
interface TuaSeriesEmits {
|
||||
(e: "selectSeries", v: number): void;
|
||||
}
|
||||
|
||||
const props = defineProps<TuaSeriesProps>();
|
||||
const emits = defineEmits<TuaSeriesEmits>();
|
||||
|
||||
const overview = ref<TGApp.Sqlite.Achievement.Overview>({ fin: 0, total: 0 });
|
||||
const data = ref<TGApp.App.Achievement.Series | undefined>(
|
||||
AppAchievementSeriesData.find((s) => s.id === props.series),
|
||||
);
|
||||
let achiListener: UnlistenFn | null = null;
|
||||
|
||||
onMounted(async () => {
|
||||
await refreshOverview();
|
||||
achiListener = await listenAchi();
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.cur,
|
||||
async () => await refreshOverview(),
|
||||
);
|
||||
|
||||
async function refreshOverview(): Promise<void> {
|
||||
overview.value = await TSUserAchi.getOverview(props.uid, props.series);
|
||||
console.log(overview.value);
|
||||
}
|
||||
|
||||
async function listenAchi(): Promise<UnlistenFn> {
|
||||
return await listen<number>("updateAchi", async (e) => {
|
||||
if (e.payload === props.series) await refreshOverview();
|
||||
});
|
||||
}
|
||||
|
||||
onUnmounted(async () => {
|
||||
if (achiListener !== null) {
|
||||
achiListener();
|
||||
achiListener = null;
|
||||
}
|
||||
});
|
||||
|
||||
async function selectSeries(): Promise<void> {
|
||||
if (props.cur === props.series) {
|
||||
showSnackbar({
|
||||
text: "已选中当前系列!",
|
||||
color: "warn",
|
||||
});
|
||||
return;
|
||||
}
|
||||
emits("selectSeries", props.series);
|
||||
}
|
||||
</script>
|
||||
<style lang="css" scoped>
|
||||
.tuas-card {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 10px;
|
||||
border-radius: 10px;
|
||||
background: var(--box-bg-1);
|
||||
color: var(--box-text-1);
|
||||
column-gap: 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.tuas-version {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 80px;
|
||||
border-top: 1px solid var(--common-shadow-1);
|
||||
border-left: 1px solid var(--common-shadow-1);
|
||||
background: var(--box-bg-2);
|
||||
border-bottom-right-radius: 10px;
|
||||
border-top-left-radius: 20px;
|
||||
color: var(--tgc-yellow-1);
|
||||
font-family: var(--font-title);
|
||||
font-size: 10px;
|
||||
text-align: center;
|
||||
text-shadow: 1px 1px 1px var(--common-shadow-1);
|
||||
}
|
||||
|
||||
.tuas-icon {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
padding: 5px;
|
||||
border-radius: 50%;
|
||||
background: var(--tgc-dark-7);
|
||||
}
|
||||
|
||||
.tuas-content {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
flex-flow: column wrap;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
color: var(--box-text-1);
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.tuas-content :first-child {
|
||||
font-family: var(--font-title);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.tuas-content :last-child {
|
||||
font-size: 12px;
|
||||
opacity: 0.8;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user