🚸 隐藏完成成就支持隐藏成就系列

close #205
This commit is contained in:
BTMuli
2026-01-15 12:51:56 +08:00
parent 7b5a57fd5c
commit 859ddc3d8d
4 changed files with 52 additions and 40 deletions

View File

@@ -17,8 +17,8 @@
{{ data.subtitle }}
</div>
<div class="loading-img">
<img v-if="data.empty" alt="loading" src="/source/UI/empty.webp" />
<img v-else :src="iconUrl" alt="empty" />
<img v-if="data.empty" alt="empty" src="/source/UI/empty.webp" />
<img v-else :src="iconUrl" alt="loading" />
</div>
</div>
</div>

View File

@@ -56,7 +56,7 @@
<slot name="right"></slot>
</div>
</TOverlay>
<VpOverlaySearch v-model="showSearch" :keyword="search" gid="2" />
<VpOverlaySearch v-model="showSearch" :keyword="search" :gid="2" />
</template>
<script lang="ts" setup>
import TOverlay from "@comp/app/t-overlay.vue";

View File

@@ -1,25 +1,26 @@
<!-- 成就系列 -->
<template>
<div
v-if="data"
v-if="series"
v-show="!(hideFin && progress === 100)"
:class="{
'tuas-selected': props.cur === props.series,
'tuas-selected': props.cur === props.series.id,
'tuas-radius': showCard,
}"
:title="data.name"
:title="series.name"
class="tuas-card"
@click="selectSeries"
>
<div class="tuas-version">v{{ data.version }}</div>
<div class="tuas-version">v{{ series.version }}</div>
<div v-if="showCard" class="tuas-reward">
<img
:class="{ finish: progress === 100 }"
:src="`/WIKI/nameCard/bg/${data.card}.webp`"
:class="progress === 100 ? 'finish' : ''"
:src="`/WIKI/nameCard/bg/${series.card}.webp`"
alt="card"
/>
</div>
<div class="tuas-icon">
<img :src="`/icon/achievement/${data.icon}.webp`" alt="icon" />
<img :src="`/icon/achievement/${series.icon}.webp`" alt="icon" />
<v-progress-circular
:model-value="progress"
bg-color="var(--tgc-od-white)"
@@ -28,37 +29,39 @@
/>
</div>
<div class="tuas-content">
<span :title="data.name">{{ data.name }}</span>
<span :title="series.name">{{ series.name }}</span>
<span>{{ overview.fin }}/{{ overview.total }}</span>
</div>
</div>
</template>
<script lang="ts" setup>
import showSnackbar from "@comp/func/snackbar.js";
import TSUserAchi from "@Sqlm/userAchi.js";
import { type Event, listen, type UnlistenFn } from "@tauri-apps/api/event";
import { computed, onMounted, onUnmounted, shallowRef, watch } from "vue";
import { AppAchievementSeriesData } from "@/data/index.js";
type TuaSeriesProps = { uid: number; series: number; cur: number };
type TuaSeriesEmits = (e: "selectSeries", v: number) => void;
type TuaSeriesProps = {
/** 存档 UID */
uid: number;
/** 成就系列数据 */
series: TGApp.App.Achievement.Series;
/** 当前选中系列 ID-1表示未选择 */
cur: number;
/** 是否隐藏已完成 */
hideFin: boolean;
};
type TuaSeriesEmits = (e: "selected", v: number) => void;
let achiListener: UnlistenFn | null = null;
const props = defineProps<TuaSeriesProps>();
const emits = defineEmits<TuaSeriesEmits>();
const overview = shallowRef<TGApp.App.Achievement.Overview>({ fin: 0, total: 0 });
const data = computed<TGApp.App.Achievement.Series | undefined>(() =>
AppAchievementSeriesData.find((s) => s.id === props.series),
);
const progress = computed<number>(() => {
if (overview.value.total === 0) return 0;
return Math.round((overview.value.fin / overview.value.total) * 100);
});
const showCard = computed<boolean>(() => {
if (data.value === undefined) return false;
return data.value.card !== "";
return props.series.card !== "";
});
onMounted(async () => {
@@ -72,12 +75,12 @@ watch(
);
async function refreshOverview(): Promise<void> {
overview.value = await TSUserAchi.getOverview(props.uid, props.series);
overview.value = await TSUserAchi.getOverview(props.uid, props.series.id);
}
async function listenAchi(): Promise<UnlistenFn> {
return await listen<number>("updateAchi", async (e: Event<number>) => {
if (e.payload === props.series) await refreshOverview();
if (e.payload === props.series.id) await refreshOverview();
});
}
@@ -89,11 +92,7 @@ onUnmounted(async () => {
});
function selectSeries(): void {
if (props.cur === props.series) {
showSnackbar.warn("已选中当前系列!");
return;
}
emits("selectSeries", props.series);
emits("selected", props.series.id);
}
</script>
<style lang="scss" scoped>
@@ -106,6 +105,7 @@ function selectSeries(): void {
display: flex;
overflow: hidden;
height: 60px;
flex-shrink: 0;
align-items: center;
justify-content: flex-start;
padding: 8px;

View File

@@ -64,21 +64,22 @@
</template>
</v-app-bar>
<div class="wrap">
<v-virtual-scroll :items="seriesList" class="left-wrap" item-height="60">
<template #default="{ item }">
<TuaSeries
v-model:cur="selectedSeries"
:series="item"
:uid="uidCur"
@click="selectedSeries = item"
/>
</template>
</v-virtual-scroll>
<div class="left-wrap">
<TuaSeries
v-for="item in seriesList"
:key="item.id"
v-model:cur="selectedSeries"
:hideFin
:series="item"
:uid="uidCur"
@selected="handleSeriesSelect"
/>
</div>
<TuaAchiList
v-model:isSearch="isSearch"
v-model:search="search"
v-model:series="selectedSeries"
:hideFin="hideFin"
:hideFin
:uid="uidCur"
/>
</div>
@@ -110,7 +111,7 @@ import { useRoute, useRouter } from "vue-router";
import { AppAchievementSeriesData } from "@/data/index.js";
const seriesList = AppAchievementSeriesData.sort((a, b) => a.order - b.order).map((s) => s.id);
const seriesList = AppAchievementSeriesData.sort((a, b) => a.order - b.order);
const route = useRoute();
const router = useRouter();
@@ -171,6 +172,14 @@ function switchHideFin(): void {
showSnackbar.success(`${text}`);
}
function handleSeriesSelect(id: number): void {
if (selectedSeries.value === id) {
showSnackbar.warn("已经选中当前系列");
return;
}
selectedSeries.value = id;
}
async function refreshOverview(): Promise<void> {
overview.value = await TSUserAchi.getOverview(uidCur.value);
}
@@ -388,12 +397,15 @@ async function toYae(): Promise<void> {
.left-wrap {
position: relative;
display: flex;
width: 332px;
height: 100%;
box-sizing: border-box;
flex-direction: column;
flex-shrink: 0;
padding-right: 8px;
overflow-y: auto;
row-gap: 8px;
}
:deep(.v-virtual-scroll__item + .v-virtual-scroll__item) {