♻️ 公告页样式重构

This commit is contained in:
BTMuli
2023-10-15 16:57:48 +08:00
parent cbce3eda60
commit 3691397cec
4 changed files with 158 additions and 152 deletions

View File

@@ -1,8 +1,9 @@
<template> <template>
<ToLoading v-model="loading" :title="loadingTitle" /> <ToLoading v-model="loading" :title="loadingTitle" />
<v-tabs v-model="tab" align-tabs="start" class="anno-tab"> <v-tabs v-model="tab" align-tabs="start" class="anno-tab">
<v-tab value="activity"> 活动公告 </v-tab> <v-tab v-for="(value, index) in tabValues" :key="index" :value="value">
<v-tab value="game"> 游戏公告 </v-tab> {{ AnnoType[value] }}
</v-tab>
<v-spacer /> <v-spacer />
<v-btn class="anno-switch-btn" @click="switchNews"> <v-btn class="anno-switch-btn" @click="switchNews">
<template #prepend> <template #prepend>
@@ -12,67 +13,24 @@
</v-btn> </v-btn>
</v-tabs> </v-tabs>
<v-window v-model="tab"> <v-window v-model="tab">
<v-window-item value="activity"> <v-window-item v-for="(value, index) in tabValues" :key="index" :value="value">
<div class="anno-grid"> <div class="anno-grid">
<v-card v-for="item in annoCards.activity" :key="item.id" class="anno-card"> <v-card v-for="item in annoCards[value]" :key="item.id" class="anno-card">
<div class="anno-cover" @click="toPost(item)"> <div class="anno-cover" @click="createAnno(item)" :title="item.title">
<img :src="item.banner" alt="cover" /> <img :src="item.banner" alt="cover" />
<div class="anno-info">
<div class="anno-id">ID:{{ item.id }}</div>
<div class="anno-time">
<v-icon>mdi-clock-time-four-outline</v-icon>
<span>{{ item.timeStr }}</span>
</div>
</div>
</div> </div>
<v-card-title class="anno-card-title" :title="item.title"> <v-card-title class="anno-title" :title="item.title">{{ item.subtitle }}</v-card-title>
{{ item.title }} <div class="anno-label">
</v-card-title> <img :src="item.tagIcon" alt="tag" />
<v-card-subtitle>{{ item.subtitle }}</v-card-subtitle> <span>{{ item.tagLabel }}</span>
<v-card-actions>
<v-btn class="anno-btn" variant="outlined" @click="toPost(item)">
<img :src="item.tagIcon || '../assets/icons/arrow-right.svg'" alt="right" />
<span>查看</span>
</v-btn>
<v-card-subtitle v-show="!appStore.devMode">
<v-icon>mdi-calendar</v-icon>
{{ item.startTime.split(" ")[0] }}~{{ item.endTime.split(" ")[0] }}
</v-card-subtitle>
<v-card-subtitle v-show="appStore.devMode"> id: {{ item.id }} </v-card-subtitle>
<v-btn
v-show="appStore.devMode"
variant="outlined"
class="anno-dev-btn"
@click="toJson(item)"
>
<img src="../../assets/icons/arrow-right.svg" alt="right" />
<span>查看数据</span>
</v-btn>
</v-card-actions>
</v-card>
</div>
</v-window-item>
<v-window-item value="game">
<div class="anno-grid">
<v-card v-for="item in annoCards.game" :key="item.id" class="anno-card">
<div class="anno-cover" @click="toPost(item)">
<img :src="item.banner" alt="cover" />
</div> </div>
<v-card-title class="anno-card-title" :title="item.title">{{ item.title }}</v-card-title>
<v-card-subtitle>{{ item.subtitle }}</v-card-subtitle>
<v-card-actions>
<v-btn class="anno-btn" variant="outlined" @click="toPost(item)">
<img :src="item.tagIcon || '../assets/icons/arrow-right.svg'" alt="right" />
<span>查看</span>
</v-btn>
<v-card-subtitle v-show="!appStore.devMode">
<v-icon>mdi-calendar</v-icon>
{{ item.startTime.split(" ")[0] }}~{{ item.endTime.split(" ")[0] }}
</v-card-subtitle>
<v-card-subtitle v-show="appStore.devMode"> id: {{ item.id }} </v-card-subtitle>
<v-btn
v-show="appStore.devMode"
variant="outlined"
class="anno-dev-btn"
@click="toJson(item)"
>
<img src="../../assets/icons/arrow-right.svg" alt="right" />
<span>查看数据</span>
</v-btn>
</v-card-actions>
</v-card> </v-card>
</div> </div>
</v-window-item> </v-window-item>
@@ -84,70 +42,54 @@ import { onMounted, ref } from "vue";
import { useRouter } from "vue-router"; import { useRouter } from "vue-router";
import ToLoading from "../../components/overlay/to-loading.vue"; import ToLoading from "../../components/overlay/to-loading.vue";
import { useAppStore } from "../../store/modules/app"; import { createAnno } from "../../utils/TGWindow";
import { createTGWindow } from "../../utils/TGWindow";
import TGRequest from "../../web/request/TGRequest"; import TGRequest from "../../web/request/TGRequest";
import TGUtils from "../../web/utils/TGUtils"; import TGUtils from "../../web/utils/TGUtils";
// store // 类型定义
const appStore = useAppStore(); enum AnnoType {
activity = "活动公告",
game = "游戏公告",
}
type AnnoKey = keyof typeof AnnoType;
type AnnoCard = {
[key in AnnoKey]: TGApp.App.Announcement.ListCard[];
};
// loading // loading
const loading = ref<boolean>(true); const loading = ref<boolean>(true);
const loadingTitle = ref<string>("正在加载"); const loadingTitle = ref<string>("正在加载");
// 路由 // 路由
const router = useRouter(); const router = useRouter();
// 数据 // 数据
const tab = ref<string>(""); const tab = ref<AnnoKey>("activity");
const annoCards = ref({ const tabValues = ref<Array<AnnoKey>>(["activity", "game"]);
activity: <TGApp.App.Announcement.ListCard[]>[], const annoCards = ref<AnnoCard>({
game: <TGApp.App.Announcement.ListCard[]>[], activity: [],
game: [],
}); });
const annoData = ref<TGApp.BBS.Announcement.ListData>(<TGApp.BBS.Announcement.ListData>{}); const annoData = ref<TGApp.BBS.Announcement.ListData>(<TGApp.BBS.Announcement.ListData>{});
onMounted(async () => { onMounted(async () => {
loadingTitle.value = "正在获取公告数据"; loadingTitle.value = "正在获取公告数据";
loading.value = true;
annoData.value = await TGRequest.Anno.getList(); annoData.value = await TGRequest.Anno.getList();
loadingTitle.value = "正在转换公告数据"; loadingTitle.value = "正在转换公告数据";
const listCards: TGApp.App.Announcement.ListCard[] = TGUtils.Anno.getCard(annoData.value); const listCards = TGUtils.Anno.getCard(annoData.value);
const activityCard: TGApp.App.Announcement.ListCard[] = listCards.filter(
(item) => item.typeLabel === "活动公告",
);
const newsCard: TGApp.App.Announcement.ListCard[] = listCards.filter(
(item) => item.typeLabel === "游戏公告",
);
annoCards.value = {
activity: activityCard,
game: newsCard,
};
tab.value = "activity"; tab.value = "activity";
annoCards.value = {
activity: listCards.filter((item) => item.typeLabel === AnnoType.activity),
game: listCards.filter((item) => item.typeLabel === AnnoType.game),
};
loading.value = false; loading.value = false;
}); });
async function switchNews(): Promise<void> { async function switchNews(): Promise<void> {
await router.push("/news/2"); await router.push("/news/2");
} }
async function toPost(item: TGApp.App.Announcement.ListCard): Promise<void> {
const path = router.resolve({
name: "游戏内公告",
params: {
anno_id: item.id,
},
}).href;
createTGWindow(path, "Sub_window", `Anno_${item.id} ${item.title}`, 960, 720, false, false);
}
async function toJson(item: TGApp.App.Announcement.ListCard): Promise<void> {
const path = router.resolve({
name: "游戏内公告JSON",
params: {
anno_id: item.id,
},
}).href;
createTGWindow(path, "Dev_JSON", `Anno_${item.id}_JSON ${item.title}`, 960, 720, false, false);
}
</script> </script>
<style lang="css" scoped> <style lang="css" scoped>
@@ -169,10 +111,11 @@ async function toJson(item: TGApp.App.Announcement.ListCard): Promise<void> {
padding: 5px; padding: 5px;
font-family: var(--font-title); font-family: var(--font-title);
grid-gap: 10px; grid-gap: 10px;
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); grid-template-columns: repeat(auto-fill, minmax(360px, 1fr));
} }
.anno-card { .anno-card {
border: 1px solid var(--common-shadow-2);
border-radius: 5px; border-radius: 5px;
background: var(--box-bg-1); background: var(--box-bg-1);
color: var(--box-text-1); color: var(--box-text-1);
@@ -183,9 +126,12 @@ async function toJson(item: TGApp.App.Announcement.ListCard): Promise<void> {
display: flex; display: flex;
overflow: hidden; overflow: hidden;
width: 100%; width: 100%;
height: 120px; height: 130px;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
border-bottom: 1px solid var(--common-shadow-2);
border-top-left-radius: 5px;
border-top-right-radius: 5px;
} }
.anno-cover img { .anno-cover img {
@@ -196,47 +142,88 @@ async function toJson(item: TGApp.App.Announcement.ListCard): Promise<void> {
transition: all 0.3s linear; transition: all 0.3s linear;
} }
.anno-cover :hover { .anno-title {
position: relative;
height: 50px;
text-align: right;
}
.anno-info {
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;
}
.anno-id {
position: relative;
display: flex;
align-items: center;
justify-content: flex-start;
padding: 5px 30px 5px 5px;
background: var(--app-page-bg);
clip-path: polygon(0 0, calc(100% - 15px) 0, 100% 50%, calc(100% - 15px) 100%, 0 100%);
color: var(--app-page-content);
&::after {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: var(--common-shadow-2);
clip-path: polygon(
calc(100% - 25px) 0,
100% 0,
100% 100%,
calc(100% - 25px) 100%,
calc(100% - 10px) 50%
);
content: "";
}
}
.anno-time {
display: flex;
align-items: center;
justify-content: flex-start;
margin: 5px;
color: var(--tgc-white-1);
gap: 5px;
opacity: 0.8;
}
.anno-label {
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);
}
.anno-label img {
width: 20px;
height: 20px;
margin-right: 5px;
}
.anno-cover img:hover {
cursor: pointer; cursor: pointer;
transform: scale(1.1); transform: scale(1.1);
transition: all 0.3s linear; transition: all 0.3s linear;
} }
.anno-card-title {
position: relative;
height: 50px;
}
.anno-btn {
display: flex;
height: 30px;
align-items: center;
justify-content: center;
border: 1px solid var(--common-shadow-4);
border-radius: 5px;
}
.anno-btn img {
width: 25px;
height: 25px;
margin-right: 5px;
object-fit: cover;
}
.anno-dev-btn {
border: 1px solid var(--common-shadow-4);
border-radius: 5px;
margin-left: auto;
font-family: var(--font-title);
}
.anno-dev-btn img {
width: 20px;
height: 20px;
padding: 3px;
border-radius: 50%;
margin-right: 5px;
background: var(--common-shadow-4);
object-fit: cover;
}
</style> </style>

View File

@@ -1,14 +1,13 @@
/** /**
* @file types App Announcement.d.ts * @file types App Announcement.d.ts
* @description 应用公告相关类型定义文件 * @description 应用公告相关类型定义文件
* @author BTMuli<bt-muli@outlook.com> * @since Beta v0.3.3
* @since Alpha v0.1.5
*/ */
declare namespace TGApp.App.Announcement { declare namespace TGApp.App.Announcement {
/** /**
* @description 渲染用的公告列表数据类型 * @description 渲染用的公告列表数据类型
* @since Alpha v0.1.5 * @since Beta v0.3.3
* @interface ListCard * @interface ListCard
* @property {number} id - 公告 ID * @property {number} id - 公告 ID
* @property {string} title - 公告标题 * @property {string} title - 公告标题
@@ -16,18 +15,18 @@ declare namespace TGApp.App.Announcement {
* @property {string} banner - 公告横幅 * @property {string} banner - 公告横幅
* @property {string} typeLabel - 公告类型标签 * @property {string} typeLabel - 公告类型标签
* @property {string} tagIcon - 公告标签图标 * @property {string} tagIcon - 公告标签图标
* @property {string} startTime - 公告开始时间 * @property {string} tagLabel - 公告标签文字
* @property {string} endTime - 公告结束时间 * @property {string} timeStr - 公告时间字符串
* @return ListCard * @return ListCard
*/ */
export interface ListCard { interface ListCard {
id: number; id: number;
title: string; title: string;
subtitle: string; subtitle: string;
banner: string; banner: string;
typeLabel: string; typeLabel: string;
tagIcon: string; tagIcon: string;
startTime: string; tagLabel: string;
endTime: string; timeStr: string;
} }
} }

View File

@@ -86,3 +86,21 @@ export function createPost(
} }
createTGWindow(postPath, "Sub_window", postTitle, 960, 720, false, false); createTGWindow(postPath, "Sub_window", postTitle, 960, 720, false, false);
} }
/**
* @description 打开公告
* @since Beta v0.3.3
* @param {TGApp.App.Announcement.ListCard} item 公告内容或ID
* @returns {void}
*/
export function createAnno(item: TGApp.App.Announcement.ListCard): void {
const annoPath = `/anno_detail/${item.id}`;
const annoJsonPath = `/anno_detail_json/${item.id}`;
const annoTitle = `Anno_${item.id} ${item.title}`;
const annoJsonTitle = `Anno_${item.id}_JSON ${item.title}`;
const isDev = useAppStore().devMode ?? false;
if (isDev) {
createTGWindow(annoJsonPath, "Dev_JSON", annoJsonTitle, 960, 720, false, false);
}
createTGWindow(annoPath, "Sub_window", annoTitle, 960, 720, false, false);
}

View File

@@ -1,8 +1,7 @@
/** /**
* @file web utils transAnno.ts * @file web utils transAnno.ts
* @description 公告数据转换工具 * @description 公告数据转换工具
* @author BTMuli<bt-muli@outlook.com> * @since Beta v0.3.3
* @since Alpha v0.1.2
*/ */
// 默认封面图 // 默认封面图
@@ -10,7 +9,7 @@ const defaultCover = "/source/UI/defaultCover.webp";
/** /**
* @description 将获取到的数据转为渲染用的卡片 * @description 将获取到的数据转为渲染用的卡片
* @since Alpha v0.1.2 * @since Beta v0.3.3
* @param {TGApp.BBS.Announcement.ListData[]} data 公告数据 * @param {TGApp.BBS.Announcement.ListData[]} data 公告数据
* @returns {TGApp.App.Announcement.ListCard[]} 渲染用的卡片 * @returns {TGApp.App.Announcement.ListCard[]} 渲染用的卡片
*/ */
@@ -20,6 +19,9 @@ export function getAnnoCard(
const cards: TGApp.App.Announcement.ListCard[] = []; const cards: TGApp.App.Announcement.ListCard[] = [];
data.list.map((annoList: TGApp.BBS.Announcement.ListItem) => { data.list.map((annoList: TGApp.BBS.Announcement.ListItem) => {
return annoList.list.map((anno: TGApp.BBS.Announcement.AnnoSingle) => { return annoList.list.map((anno: TGApp.BBS.Announcement.AnnoSingle) => {
const timeStart = anno.start_time.split(" ")[0];
const timeEnd = anno.end_time.split(" ")[0];
const time = `${timeStart} ~ ${timeEnd}`;
return cards.push({ return cards.push({
id: anno.ann_id, id: anno.ann_id,
title: anno.title, title: anno.title,
@@ -27,8 +29,8 @@ export function getAnnoCard(
banner: anno.banner || defaultCover, banner: anno.banner || defaultCover,
typeLabel: anno.type_label, typeLabel: anno.type_label,
tagIcon: anno.tag_icon, tagIcon: anno.tag_icon,
startTime: anno.start_time, tagLabel: anno.tag_label,
endTime: anno.end_time, timeStr: time,
}); });
}); });
}); });