mirror of
https://github.com/BTMuli/TeyvatGuide.git
synced 2025-12-19 10:23:21 +08:00
♻️ 调整目录结构
This commit is contained in:
272
src/pages/common/PageAnno.vue
Normal file
272
src/pages/common/PageAnno.vue
Normal file
@@ -0,0 +1,272 @@
|
||||
<template>
|
||||
<v-app-bar>
|
||||
<template #prepend>
|
||||
<v-tabs v-model="tab" align-tabs="start" class="anno-tab">
|
||||
<v-tab v-for="(value, index) in tabValues" :key="index" :value="value">
|
||||
{{ AnnoType[value] }}
|
||||
</v-tab>
|
||||
</v-tabs>
|
||||
<div class="anno-selects">
|
||||
<v-select
|
||||
class="anno-select"
|
||||
:items="annoServerList"
|
||||
v-model="curRegion"
|
||||
item-title="text"
|
||||
item-value="value"
|
||||
label="服务器"
|
||||
width="200px"
|
||||
density="compact"
|
||||
/>
|
||||
<v-select
|
||||
class="anno-select"
|
||||
:items="annoLangList"
|
||||
v-model="curLang"
|
||||
item-title="text"
|
||||
item-value="value"
|
||||
label="语言"
|
||||
width="200px"
|
||||
density="compact"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<template #append>
|
||||
<v-btn class="anno-switch-btn" @click="switchNews">
|
||||
<template #prepend>
|
||||
<v-icon>mdi-bullhorn</v-icon>
|
||||
</template>
|
||||
切换米游社咨讯
|
||||
</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="anno-grid">
|
||||
<TaCard
|
||||
v-for="item in annoCards[value]"
|
||||
:key="item.id"
|
||||
:model-value="item"
|
||||
:region="curRegion"
|
||||
:lang="curLang"
|
||||
/>
|
||||
</div>
|
||||
</v-window-item>
|
||||
</v-window>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { nextTick, onMounted, ref, watch } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
|
||||
import showLoading from "../../components/func/loading.js";
|
||||
import showSnackbar from "../../components/func/snackbar.js";
|
||||
import TaCard from "../../components/pageAnno/ta-card.vue";
|
||||
import { useAppStore } from "../../store/modules/app.js";
|
||||
import TGLogger from "../../utils/TGLogger.js";
|
||||
import Hk4eApi, { AnnoLang, AnnoServer } from "../../web/request/hk4eReq.js";
|
||||
import { getAnnoCard } from "../../web/utils/getAnnoCard.js";
|
||||
import { decodeRegExp } from "../../web/utils/tools.js";
|
||||
|
||||
type AnnoSelect = { text: string; value: string };
|
||||
|
||||
const annoServerList: AnnoSelect[] = [
|
||||
{ text: "国服-官方服", value: AnnoServer.CN_ISLAND },
|
||||
{ text: "国服-渠道服", value: AnnoServer.CN_TREE },
|
||||
{ text: "国际服-亚服", value: AnnoServer.OS_ASIA },
|
||||
{ text: "国际服-欧服", value: AnnoServer.OS_EURO },
|
||||
{ text: "国际服-美服", value: AnnoServer.OS_USA },
|
||||
{ text: "国际服-港澳台服", value: AnnoServer.OS_CHT },
|
||||
];
|
||||
const annoLangList: AnnoSelect[] = [
|
||||
{ text: "简体中文", value: "zh-cn" },
|
||||
{ text: "繁体中文", value: "zh-tw" },
|
||||
{ text: "English", value: "en" },
|
||||
{ text: "日本語", value: "ja" },
|
||||
];
|
||||
|
||||
// 类型定义
|
||||
enum AnnoType {
|
||||
activity = "活动公告",
|
||||
game = "游戏公告",
|
||||
}
|
||||
|
||||
type AnnoKey = keyof typeof AnnoType;
|
||||
type AnnoCard = {
|
||||
[key in AnnoKey]: TGApp.App.Announcement.ListCard[];
|
||||
};
|
||||
|
||||
const appStore = useAppStore();
|
||||
|
||||
// 路由
|
||||
const router = useRouter();
|
||||
const curRegion = ref<AnnoServer>(appStore.server);
|
||||
const curLang = ref<AnnoLang>(appStore.lang);
|
||||
|
||||
// 数据
|
||||
const tab = ref<AnnoKey>("activity");
|
||||
const tabValues = ref<Array<AnnoKey>>(["activity", "game"]);
|
||||
const annoCards = ref<AnnoCard>({
|
||||
activity: [],
|
||||
game: [],
|
||||
});
|
||||
|
||||
watch(
|
||||
() => curRegion.value,
|
||||
async () => {
|
||||
appStore.server = curRegion.value;
|
||||
const name = getRegionName(curRegion.value);
|
||||
await TGLogger.Info(`[Announcements][watch][curRegionName] 切换服务器:${name}`);
|
||||
await loadData();
|
||||
showSnackbar.success(`服务器切换为:${name}`);
|
||||
},
|
||||
);
|
||||
|
||||
watch(
|
||||
() => curLang.value,
|
||||
async () => {
|
||||
appStore.lang = curLang.value;
|
||||
const name = getLangName(curLang.value);
|
||||
await TGLogger.Info(`[Announcements][watch][curLangName] 切换语言:${name}`);
|
||||
await loadData();
|
||||
showSnackbar.success(`语言切换为:${name}`);
|
||||
},
|
||||
);
|
||||
|
||||
onMounted(async () => {
|
||||
await TGLogger.Info("[Announcements][onMounted] 打开公告页面");
|
||||
curRegion.value = appStore.server;
|
||||
curLang.value = appStore.lang;
|
||||
await loadData();
|
||||
});
|
||||
|
||||
async function loadData(): Promise<void> {
|
||||
showLoading.start(
|
||||
"正在获取公告数据",
|
||||
`服务器:${getRegionName(curRegion.value)},语言:${getLangName(curLang.value)}`,
|
||||
);
|
||||
const annoData = await Hk4eApi.anno.list(curRegion.value, curLang.value);
|
||||
const listCards = getAnnoCard(annoData);
|
||||
await Promise.all(
|
||||
listCards.map(async (item) => {
|
||||
if (item.typeLabel === AnnoType.game) return;
|
||||
const detail = await Hk4eApi.anno.content(item.id, curRegion.value, "zh-cn");
|
||||
const timeStr = getAnnoTime(detail.content);
|
||||
if (timeStr !== false) item.timeStr = timeStr;
|
||||
}),
|
||||
);
|
||||
annoCards.value = {
|
||||
activity: listCards.filter((item) => item.typeLabel === AnnoType.activity),
|
||||
game: listCards.filter((item) => item.typeLabel === AnnoType.game),
|
||||
};
|
||||
showLoading.update("正在渲染公告数据");
|
||||
await nextTick(() => showLoading.end());
|
||||
}
|
||||
|
||||
function getRegionName(value: AnnoServer): string {
|
||||
return annoServerList.find((item) => item.value === value)?.text ?? annoServerList[0].text;
|
||||
}
|
||||
|
||||
function getLangName(value: AnnoLang): string {
|
||||
return annoLangList.find((item) => item.value === value)?.text ?? annoLangList[0].text;
|
||||
}
|
||||
|
||||
function getAnnoTime(content: string): string | false {
|
||||
const regexes = [
|
||||
/〓活动时间〓.*?\d\.\d版本期间持续开放/,
|
||||
/(?:〓活动时间〓|〓任务开放时间〓).*?(?:(\d\.\d)版本更新(?:完成|)|<t class="t_(?:gl|lc)".*?>(.*?)<\/t> *?)后永久开放/s,
|
||||
/(?:〓活动时间〓|祈愿时间|【上架时间】|〓折扣时间〓|〓获取奖励时限〓).*?(\d\.\d)版本更新后.*?~.*?<t class="t_(?:gl|lc)".*?>(.*?)<\/t>/s,
|
||||
/(?:〓(?:活动|折扣)时间〓|祈愿时间|【上架时间】).*?<t class="t_(?:gl|lc)".*?>(.*?)<\/t>.*?~.*?<t class="t_(?:gl|lc)".*?>(.*?)<\/t>/s,
|
||||
// /〓活动时间〓.*?(\d{4}\/\d{2}\/\d{2} \d{2}:\d{2}:\d{2}).*?(\d\.\d版本结束)/
|
||||
/〓活动时间〓.*?(\d{4}\/\d{2}\/\d{2} \d{2}:\d{2}).*?(\d\.\d版本结束)/s,
|
||||
];
|
||||
if (content.match(regexes[0])) {
|
||||
const res = content.match(regexes[0]);
|
||||
return res?.[0].replace(/.*?(\d\.\d版本期间持续开放)/, "$1") ?? false;
|
||||
}
|
||||
if (content.match(regexes[1])) {
|
||||
const res = content.match(regexes[1]);
|
||||
if (res === null) return false;
|
||||
const regex2 = /\d\.\d版本更新(?:完成|)后永久开放/;
|
||||
const regex3 = /\d{4}\/\d{2}\/\d{2} \d{2}:\d{2}:\d{2}/;
|
||||
const res2 = res[0].match(regex2);
|
||||
if (res2 !== null) return res2[0];
|
||||
const res3 = res[0].match(regex3);
|
||||
return res3 === null ? false : `${res3[0]} 后永久开放`;
|
||||
}
|
||||
if (content.match(regexes[2])) {
|
||||
const res = content.match(regexes[2]);
|
||||
if (res?.[1]?.match(/\d\.\d/)) {
|
||||
const parser = new DOMParser().parseFromString(decodeRegExp(res[2]), "text/html");
|
||||
return `${res?.[1]}版本更新后 ~ ${parser.body.innerText}`;
|
||||
}
|
||||
return `${res?.[1]} ~ ${res?.[2]}`;
|
||||
}
|
||||
if (content.match(regexes[3])) {
|
||||
const res = content.match(regexes[3]);
|
||||
try {
|
||||
const span1 = document.createElement("span");
|
||||
span1.innerHTML = res?.[1] ?? "";
|
||||
const span2 = document.createElement("span");
|
||||
span2.innerHTML = res?.[2] ?? "";
|
||||
return `${span1.innerText} ~ ${span2.innerText}`;
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
return `${res?.[1]} ~ ${res?.[2]}`;
|
||||
}
|
||||
if (content.match(regexes[4])) {
|
||||
const res = content.match(regexes[4]);
|
||||
if (res !== null) {
|
||||
const cnt = res[0].match(/〓/g);
|
||||
if (cnt && cnt.length > 2) return false;
|
||||
}
|
||||
return `${res?.[1]} ~ ${res?.[2]}`;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
async function switchNews(): Promise<void> {
|
||||
await TGLogger.Info("[Announcements][switchNews] 切换米游社咨讯");
|
||||
await router.push("/news/2");
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="css" scoped>
|
||||
.anno-tab {
|
||||
color: var(--common-text-title);
|
||||
font-family: var(--font-title);
|
||||
}
|
||||
|
||||
.anno-selects {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding-top: 25px;
|
||||
margin: 0 10px;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.anno-select {
|
||||
width: 150px;
|
||||
color: var(--common-text-title);
|
||||
font-family: var(--font-title);
|
||||
}
|
||||
|
||||
.anno-switch-btn {
|
||||
height: 40px;
|
||||
background: var(--btn-bg-1);
|
||||
color: var(--btn-text-1);
|
||||
font-family: var(--font-title);
|
||||
}
|
||||
|
||||
.dark .anno-switch-btn {
|
||||
border: 1px solid var(--common-shadow-2);
|
||||
}
|
||||
|
||||
.anno-grid {
|
||||
display: grid;
|
||||
padding: 5px;
|
||||
font-family: var(--font-title);
|
||||
grid-gap: 10px;
|
||||
grid-template-columns: repeat(auto-fill, minmax(360px, 1fr));
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user