Files
TeyvatGuide/src/pages/common/PostNews.vue
2024-12-16 11:13:30 +08:00

210 lines
6.6 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<v-app-bar density="compact">
<template #prepend>
<v-tabs v-model="tab" align-tabs="start" class="news-tab">
<v-tab
v-for="(value, index) in tabValues"
:key="index"
:value="value"
@click="firstLoad(value)"
>
{{ rawData[value].name }}
</v-tab>
</v-tabs>
</template>
<template #title>
<v-text-field
v-model="search"
class="news-search"
append-icon="mdi-magnify"
label="请输入帖子 ID 或搜索词"
:single-line="true"
:hide-details="true"
@keydown.enter="searchPost()"
@click:append="searchPost()"
/>
</template>
<template #append>
<v-btn class="post-news-btn" @click="firstLoad(tab, true)" icon="mdi-refresh" />
<v-btn class="post-news-btn" @click="showList = true" icon="mdi-view-list" />
<v-btn
class="post-news-btn"
@click="switchAnno"
v-if="gid === '2'"
prepend-icon="mdi-bullhorn"
>
切换游戏内公告
</v-btn>
</template>
</v-app-bar>
<v-window v-model="tab">
<v-window-item v-for="(value, index) in tabValues" :key="index" :value="value">
<div class="news-grid">
<div v-for="item in postData[value]" :key="item.post.post_id">
<TPostCard :model-value="item" />
</div>
</div>
<div class="load-news">
<v-btn class="post-news-btn" :rounded="true" :loading="loading" @click="loadMore(value)">
已加载{{ rawData[value].lastId }}加载更多
</v-btn>
</div>
</v-window-item>
</v-window>
<ToChannel v-model="showList" :gid="gid" />
<VpOverlaySearch :gid="gid" v-model="showSearch" :keyword="search" />
</template>
<script lang="ts" setup>
import TPostCard from "@comp/app/t-postcard.vue";
import showLoading from "@comp/func/loading.js";
import showSnackbar from "@comp/func/snackbar.js";
import ToChannel from "@comp/pageNews/to-channel.vue";
import VpOverlaySearch from "@comp/viewPost/vp-overlay-search.vue";
import Mys from "@Mys/index.js";
import { storeToRefs } from "pinia";
import { computed, onMounted, ref, shallowRef, triggerRef } from "vue";
import { useRoute, useRouter } from "vue-router";
import { type NewsType, NewsTypeEnum, useAppStore } from "@/store/modules/app.js";
import TGLogger from "@/utils/TGLogger.js";
import { createPost } from "@/utils/TGWindow.js";
import { getGameName } from "@/web/utils/tools.js";
type PostData = { [key in NewsType]: Array<TGApp.Plugins.Mys.Post.FullData> };
type RawData = { [key in NewsType]: { isLast: boolean; name: string; lastId: number } };
const router = useRouter();
const { recentNewsType } = storeToRefs(useAppStore());
const tabValues: Readonly<Array<NewsType>> = ["notice", "activity", "news"];
const { gid } = <{ gid: string }>useRoute().params;
const gameName = getGameName(Number(gid));
const loading = ref<boolean>(false);
const showList = ref<boolean>(false);
const showSearch = ref<boolean>(false);
const search = ref<string>("");
const postData = shallowRef<PostData>({ notice: [], activity: [], news: [] });
const rawData = shallowRef<RawData>({
notice: { isLast: false, name: "公告", lastId: 0 },
activity: { isLast: false, name: "活动", lastId: 0 },
news: { isLast: false, name: "咨讯", lastId: 0 },
});
const tab = computed<NewsType>({
get: () => ((recentNewsType.value satisfies NewsType) ? recentNewsType.value : "notice"),
set: (v) => (recentNewsType.value = v),
});
onMounted(async () => await firstLoad(tab.value));
async function firstLoad(key: NewsType, refresh: boolean = false): Promise<void> {
if (rawData.value[key].lastId !== 0) {
if (!refresh) return;
postData.value[key] = [];
rawData.value[key].lastId = 0;
}
await showLoading.start(`正在获取${gameName}${rawData.value[key].name}数据`);
document.documentElement.scrollTo({ top: 0, behavior: "smooth" });
const getData = await Mys.Painter.getNewsList(gid, NewsTypeEnum[key]);
await showLoading.update(`数量:${getData.list.length},是否最后一页:${getData.is_last}`);
rawData.value[key].isLast = getData.is_last;
rawData.value[key].lastId = getData.list.length;
postData.value[key] = getData.list;
triggerRef(postData);
triggerRef(rawData);
await showLoading.end();
await TGLogger.Info(`[News][${gid}][firstLoad] 获取${rawData.value[key].name}数据成功`);
}
async function switchAnno(): Promise<void> {
await TGLogger.Info(`[News][${gid}][switchAnno] 切换公告`);
await router.push("/announcements");
}
// 加载更多
async function loadMore(key: NewsType): Promise<void> {
loading.value = true;
if (rawData.value[key].isLast) {
showSnackbar.warn("已经是最后一页了");
loading.value = false;
return;
}
await showLoading.start(`正在获取${gameName}${rawData.value[key].name}数据`);
const getData = await Mys.Painter.getNewsList(
gid,
NewsTypeEnum[key],
20,
rawData.value[key].lastId,
);
await showLoading.update(`数量:${getData.list.length},是否最后一页:${getData.is_last}`);
rawData.value[key].lastId = rawData.value[key].lastId + getData.list.length;
rawData.value[key].isLast = getData.is_last;
postData.value[key] = postData.value[key].concat(getData.list);
triggerRef(postData);
triggerRef(rawData);
if (rawData.value[key].isLast) {
await showLoading.end();
showSnackbar.warn("已经是最后一页了");
loading.value = false;
return;
}
await showLoading.end();
loading.value = false;
}
async function searchPost(): Promise<void> {
if (search.value === "") {
showSnackbar.warn("请输入搜索内容");
return;
}
const numCheck = Number(search.value);
if (isNaN(numCheck)) {
showSearch.value = true;
return;
}
await createPost(search.value);
showSearch.value = false;
}
</script>
<style lang="css" scoped>
.news-tab {
margin-bottom: 10px;
color: var(--common-text-title);
font-family: var(--font-title);
}
.post-news-btn {
height: 40px;
border-radius: 3px;
margin-left: 15px;
background: var(--btn-bg-1);
color: var(--btn-text-1);
font-family: var(--font-title);
}
.dark .post-news-btn {
border: 1px solid var(--common-shadow-2);
}
.news-search {
margin: 0 10px;
color: var(--box-text-1);
}
.news-grid {
display: grid;
padding: 5px;
font-family: var(--font-title);
grid-gap: 10px;
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
}
/* load more */
.load-news {
display: flex;
align-items: center;
justify-content: center;
margin: 10px;
font-family: var(--font-title);
transition: all 0.3s linear;
}
</style>