mirror of
https://github.com/BTMuli/TeyvatGuide.git
synced 2025-12-15 09:48:14 +08:00
♻️ 咨讯页路由路径变更
This commit is contained in:
536
src/pages/common/News.vue
Normal file
536
src/pages/common/News.vue
Normal file
@@ -0,0 +1,536 @@
|
||||
<template>
|
||||
<ToLoading v-model="loading" :title="loadingTitle" />
|
||||
<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-text-field
|
||||
v-model="search"
|
||||
class="news-search"
|
||||
append-icon="mdi-magnify"
|
||||
label="请输入米游社帖子 ID"
|
||||
single-line
|
||||
hide-details
|
||||
@click:append="searchPost"
|
||||
@keyup.enter="searchPost"
|
||||
/>
|
||||
<v-spacer />
|
||||
<v-btn class="news-switch-btn" @click="switchAnno">
|
||||
<template #prepend>
|
||||
<v-icon>mdi-bullhorn</v-icon>
|
||||
</template>
|
||||
切换游戏内公告
|
||||
</v-btn>
|
||||
<v-btn class="news-switch-btn" @click="showList = true">
|
||||
<v-icon>mdi-view-list</v-icon>
|
||||
</v-btn>
|
||||
</v-tabs>
|
||||
<v-window v-model="tab">
|
||||
<v-window-item v-for="(value, index) in tabValues" :key="index" :value="value">
|
||||
<div class="news-grid">
|
||||
<v-card v-for="item in postData[value]" :key="item.postId" class="news-card">
|
||||
<div class="news-cover" @click="createPost(item)">
|
||||
<img :src="item.cover" alt="cover" />
|
||||
<div v-if="value === 'activity'" class="news-card-act">
|
||||
<div
|
||||
class="nca-status"
|
||||
:style="{
|
||||
background: item.status?.colorCss,
|
||||
}"
|
||||
>
|
||||
{{ item.status?.status }}
|
||||
</div>
|
||||
<div class="nca-time">
|
||||
<v-icon>mdi-clock-time-four-outline</v-icon>
|
||||
<span>{{ item.subtitle }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<v-card-title class="news-card-title" :title="item.title">{{ item.title }}</v-card-title>
|
||||
<div class="news-card-info">
|
||||
<div class="news-card-user">
|
||||
<div class="ncu-left">
|
||||
<div class="ncu-icon">
|
||||
<img :src="item.user.icon" alt="userIcon" />
|
||||
</div>
|
||||
<div v-if="item.user.pendant !== ''" class="ncu-pendant">
|
||||
<img :src="item.user.pendant" alt="userPendant" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="ncu-right">
|
||||
<span>{{ item.user.nickname }}</span>
|
||||
<span>{{ item.user.label }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<v-btn class="news-card-btn" variant="outlined" @click="createPost(item)">
|
||||
查看详情
|
||||
</v-btn>
|
||||
<div class="news-card-forum">
|
||||
<img :src="item.forum.icon" alt="forumIcon" />
|
||||
<span>{{ item.forum.name }}</span>
|
||||
</div>
|
||||
<div class="news-card-data">
|
||||
<div class="ncd-item">
|
||||
<v-icon>mdi-eye</v-icon>
|
||||
<span>{{ item.data.view }}</span>
|
||||
</div>
|
||||
<div class="ncd-item">
|
||||
<v-icon>mdi-star</v-icon>
|
||||
<span>{{ item.data.mark }}</span>
|
||||
</div>
|
||||
<div class="ncd-item">
|
||||
<v-icon>mdi-comment</v-icon>
|
||||
<span>{{ item.data.reply }}</span>
|
||||
</div>
|
||||
<div class="ncd-item">
|
||||
<v-icon>mdi-thumb-up</v-icon>
|
||||
<span>{{ item.data.like }}</span>
|
||||
</div>
|
||||
<div class="ncd-item">
|
||||
<v-icon>mdi-share-variant</v-icon>
|
||||
<span>{{ item.data.forward }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</v-card>
|
||||
</div>
|
||||
<div class="load-news">
|
||||
<v-btn :loading="loadingSub" @click="loadMore(value)">
|
||||
已加载:{{ rawData[value].lastId }},加载更多
|
||||
</v-btn>
|
||||
</div>
|
||||
</v-window-item>
|
||||
</v-window>
|
||||
<ToChannel v-model="showList" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onBeforeMount, onMounted, ref } from "vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
|
||||
import showSnackbar from "../../components/func/snackbar";
|
||||
import ToChannel from "../../components/overlay/to-channel.vue";
|
||||
import ToLoading from "../../components/overlay/to-loading.vue";
|
||||
import Mys from "../../plugins/Mys";
|
||||
import { createPost } from "../../utils/TGWindow";
|
||||
|
||||
// 类型定义
|
||||
enum NewsType {
|
||||
notice = "1",
|
||||
activity = "2",
|
||||
news = "3",
|
||||
}
|
||||
type NewsKey = keyof typeof NewsType;
|
||||
type PostData = {
|
||||
[key in NewsKey]: TGApp.Plugins.Mys.News.RenderCard[];
|
||||
};
|
||||
type RawData = {
|
||||
[key in NewsKey]: {
|
||||
isLast: boolean;
|
||||
name: string;
|
||||
lastId: number;
|
||||
};
|
||||
};
|
||||
|
||||
// 路由
|
||||
const router = useRouter();
|
||||
const gid = <string>useRoute().params.gid;
|
||||
|
||||
// loading
|
||||
const loading = ref<boolean>(true);
|
||||
const loadingTitle = ref<string>("正在加载");
|
||||
const loadingSub = ref<boolean>(false);
|
||||
|
||||
// UI 数据
|
||||
const tab = ref<string>("");
|
||||
const showList = ref<boolean>(false);
|
||||
const tabValues = ref<Array<NewsKey>>(["notice", "activity", "news"]);
|
||||
|
||||
// 渲染数据
|
||||
const search = ref<string>("");
|
||||
const postData = ref<PostData>({
|
||||
notice: [],
|
||||
activity: [],
|
||||
news: [],
|
||||
});
|
||||
const rawData = ref<RawData>({
|
||||
notice: {
|
||||
isLast: false,
|
||||
name: "公告",
|
||||
lastId: 0,
|
||||
},
|
||||
activity: {
|
||||
isLast: false,
|
||||
name: "活动",
|
||||
lastId: 0,
|
||||
},
|
||||
news: {
|
||||
isLast: false,
|
||||
name: "咨讯",
|
||||
lastId: 0,
|
||||
},
|
||||
});
|
||||
|
||||
onBeforeMount(() => {
|
||||
if (gid === "5") {
|
||||
tabValues.value = ["notice", "activity"];
|
||||
}
|
||||
});
|
||||
|
||||
onMounted(async () => {
|
||||
loadingTitle.value = "正在获取公告数据";
|
||||
const noticeData = await Mys.News.get(gid);
|
||||
rawData.value.notice.isLast = noticeData.is_last;
|
||||
rawData.value.notice.lastId = noticeData.list.length;
|
||||
postData.value.notice = Mys.News.card.notice(noticeData);
|
||||
tab.value = "notice";
|
||||
setTimeout(() => {
|
||||
loading.value = false;
|
||||
}, 1500);
|
||||
});
|
||||
|
||||
async function firstLoad(key: NewsKey): Promise<void> {
|
||||
if (rawData.value[key].lastId !== 0) {
|
||||
return;
|
||||
}
|
||||
if (rawData.value[key].lastId === 0) {
|
||||
loadingTitle.value = `正在获取${rawData.value[key].name}数据...;`;
|
||||
loading.value = true;
|
||||
const getData = await Mys.News.get(gid, NewsType[key]);
|
||||
rawData.value[key].isLast = getData.is_last;
|
||||
rawData.value[key].lastId = getData.list.length;
|
||||
postData.value[key] = Mys.News.card[key](getData);
|
||||
}
|
||||
setTimeout(() => {
|
||||
loading.value = false;
|
||||
}, 1500);
|
||||
}
|
||||
|
||||
async function switchAnno(): Promise<void> {
|
||||
await router.push("/announcements");
|
||||
}
|
||||
|
||||
// 加载更多
|
||||
async function loadMore(key: NewsKey): Promise<void> {
|
||||
loadingSub.value = true;
|
||||
if (rawData.value[key].isLast) {
|
||||
showSnackbar({
|
||||
text: "已经是最后一页了",
|
||||
color: "warn",
|
||||
});
|
||||
loadingSub.value = false;
|
||||
return;
|
||||
}
|
||||
loadingTitle.value = `正在获取${rawData.value[key].name}数据...`;
|
||||
loading.value = true;
|
||||
const getData = await Mys.News.get(gid, NewsType[key], 20, rawData.value[key].lastId);
|
||||
rawData.value[key].lastId = rawData.value[key].lastId + getData.list.length;
|
||||
rawData.value[key].isLast = getData.is_last;
|
||||
const getCard = Mys.News.card[key](getData);
|
||||
postData.value[key] = postData.value[key].concat(getCard);
|
||||
if (rawData.value[key].isLast) {
|
||||
showSnackbar({
|
||||
text: "已经是最后一页了",
|
||||
color: "warn",
|
||||
});
|
||||
loadingSub.value = false;
|
||||
loading.value = false;
|
||||
return;
|
||||
}
|
||||
setTimeout(() => {
|
||||
loading.value = false;
|
||||
loadingSub.value = false;
|
||||
}, 1500);
|
||||
}
|
||||
|
||||
function searchPost(): void {
|
||||
if (search.value === "") {
|
||||
showSnackbar({
|
||||
text: "请输入搜索内容",
|
||||
color: "error",
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (!isNaN(Number(search.value))) {
|
||||
createPost(search.value);
|
||||
} else {
|
||||
showSnackbar({
|
||||
text: "请输入搜索内容",
|
||||
color: "error",
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="css" scoped>
|
||||
.news-tab {
|
||||
margin-bottom: 10px;
|
||||
color: var(--common-text-title);
|
||||
font-family: var(--font-title);
|
||||
}
|
||||
|
||||
.news-switch-btn {
|
||||
height: 40px;
|
||||
margin-left: 15px;
|
||||
background: var(--btn-bg-1);
|
||||
color: var(--btn-text-1);
|
||||
font-family: var(--font-title);
|
||||
}
|
||||
|
||||
.news-search {
|
||||
margin-left: 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));
|
||||
}
|
||||
|
||||
.news-card {
|
||||
border-radius: 5px;
|
||||
background: var(--box-bg-1);
|
||||
color: var(--box-text-1);
|
||||
}
|
||||
|
||||
.news-cover {
|
||||
position: relative;
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
height: 150px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.news-cover img {
|
||||
min-width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
object-position: center;
|
||||
transition: all 0.3s linear;
|
||||
}
|
||||
|
||||
.news-card-title {
|
||||
position: relative;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
/* news item info */
|
||||
.news-card-info {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
justify-content: start;
|
||||
margin: 0 10px 10px;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.news-card-user {
|
||||
display: flex;
|
||||
max-width: 235px;
|
||||
height: 50px;
|
||||
align-items: center;
|
||||
color: var(--box-text-4);
|
||||
}
|
||||
|
||||
.ncu-left {
|
||||
position: relative;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
.ncu-icon {
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
left: 5px;
|
||||
overflow: hidden;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.ncu-icon img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.ncu-pendant {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
overflow: hidden;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.ncu-pendant img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.ncu-right {
|
||||
display: flex;
|
||||
height: 50px;
|
||||
flex-direction: column;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.ncu-right :nth-child(1) {
|
||||
display: flex;
|
||||
height: 30px;
|
||||
align-items: center;
|
||||
justify-content: start;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.ncu-right :nth-child(2) {
|
||||
display: flex;
|
||||
height: 20px;
|
||||
align-items: center;
|
||||
justify-content: start;
|
||||
border-top: 2px solid var(--common-shadow-2);
|
||||
font-size: 8px;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.news-card-forum {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
padding: 5px;
|
||||
backdrop-filter: blur(20px);
|
||||
background: rgb(0 0 0/20%);
|
||||
border-bottom-left-radius: 5px;
|
||||
border-top-right-radius: 5px;
|
||||
box-shadow: 0 0 10px var(--tgc-dark-1);
|
||||
color: var(--tgc-white-1);
|
||||
}
|
||||
|
||||
.news-card-forum img {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.news-card-btn {
|
||||
border-radius: 5px;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.news-dev-btn img {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
padding: 5px;
|
||||
border-radius: 50%;
|
||||
margin-right: 5px;
|
||||
background: var(--common-shadow-2);
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.news-cover img:hover {
|
||||
cursor: pointer;
|
||||
transform: scale(1.1);
|
||||
transition: all 0.3s linear;
|
||||
}
|
||||
|
||||
.news-card-data {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 20px;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
padding: 5px;
|
||||
column-gap: 10px;
|
||||
}
|
||||
|
||||
.ncd-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
color: var(--box-text-7);
|
||||
font-size: 12px;
|
||||
gap: 5px;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
/* 活动页 */
|
||||
.news-card-act {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
backdrop-filter: blur(20px);
|
||||
background: rgb(0 0 0/50%);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.nca-status {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
padding: 5px 30px 5px 5px;
|
||||
clip-path: polygon(0 0, calc(100% - 15px) 0, 100% 50%, calc(100% - 15px) 100%, 0 100%);
|
||||
color: var(--tgc-white-1);
|
||||
|
||||
&::after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgb(255 255 255/40%);
|
||||
clip-path: polygon(
|
||||
calc(100% - 25px) 0,
|
||||
100% 0,
|
||||
100% 100%,
|
||||
calc(100% - 25px) 100%,
|
||||
calc(100% - 10px) 50%
|
||||
);
|
||||
content: "";
|
||||
}
|
||||
}
|
||||
|
||||
.nca-time {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
margin: 5px;
|
||||
color: var(--tgc-white-1);
|
||||
gap: 5px;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
/* load more */
|
||||
.load-news {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 10px;
|
||||
font-family: var(--font-title);
|
||||
transition: all 0.3s linear;
|
||||
}
|
||||
|
||||
.load-news button {
|
||||
border-radius: 5px;
|
||||
background: var(--tgc-btn-1);
|
||||
color: var(--btn-text);
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user