♻️ 重构公告模块,降低请求次数

This commit is contained in:
BTMuli
2025-06-01 19:31:40 +08:00
parent 33e9704f2d
commit 70a716842f
16 changed files with 419 additions and 234 deletions

View File

@@ -5,12 +5,12 @@
*/
@font-face {
font-family: "Genshin";
font-family: "Genshin"; /* stylelint-disable-line font-family-name-quotes */
src: url("@/assets/fonts/汉仪文黑-85W.ttf") format("truetype");
}
@font-face {
font-family: "Genshin-Light";
font-family: "Genshin-Light"; /* stylelint-disable-line font-family-name-quotes */
src: url("@/assets/fonts/汉仪文黑-55W.ttf") format("truetype");
}

View File

@@ -5,6 +5,7 @@
*/
/* dark mode */
html.dark {
/* app container */
--app-page-bg: #1e1e1eff; /* (0, 0, 0) */
@@ -17,7 +18,6 @@ html.dark {
--box-bg-2: #323844ff;
--box-bg-3: #282c34ff;
--box-bg-4: #3d424bff;
--box-bg-blue: var(--tgc-blue-1);
/* box bg transparent */
@@ -35,10 +35,10 @@ html.dark {
--btn-text: var(--tgc-yellow-1);
/* box-shadow */
--common-shadow-1: #FFFFFF1A;
--common-shadow-2: #FFFFFF33;
--common-shadow-4: #FFFFFF66;
--common-shadow-8: #FFFFFFCC;
--common-shadow-1: #ffffff1a;
--common-shadow-2: #ffffff33;
--common-shadow-4: #ffffff66;
--common-shadow-8: #ffffffcc;
/* box-shadow-transparent */
--common-shadow-t-1: #0000001a;

View File

@@ -5,6 +5,7 @@
*/
/* default(light) theme */
html.default {
/* app container */
--app-page-bg: #ffffffff;
@@ -17,7 +18,6 @@ html.default {
--box-bg-2: #f2f9f6ff;
--box-bg-3: #dee4e9ff;
--box-bg-4: #f5f5f5ff;
--box-bg-blue: #249ffdff;
/* box bg transparent */
@@ -41,10 +41,10 @@ html.default {
--common-shadow-8: #000000cc;
/* box-shadow-transparent */
--common-shadow-t-1: #FFFFFF1A;
--common-shadow-t-2: #FFFFFF33;
--common-shadow-t-4: #FFFFFF66;
--common-shadow-t-8: #FFFFFFCC;
--common-shadow-t-1: #ffffff1a;
--common-shadow-t-2: #ffffff33;
--common-shadow-t-4: #ffffff66;
--common-shadow-t-8: #ffffffcc;
/* common text color */
--common-text-title: var(--tgc-dark-8); /* title */

View File

@@ -142,9 +142,9 @@ defineExpose({ displayBox });
align-items: center;
justify-content: center;
padding: 10px;
border: #f4d8a8 1px solid;
border: #f4d8a8ff 1px solid;
border-radius: 5px;
color: #f4d8a8;
color: #f4d8a8ff;
}
.loading-title {
@@ -197,7 +197,7 @@ defineExpose({ displayBox });
display: block;
width: 32px;
height: 32px;
color: #f4d8a8;
color: #f4d8a8ff;
font-size: 0;
}

View File

@@ -23,7 +23,7 @@
<script lang="ts" setup>
import TMiImg from "@comp/app/t-mi-img.vue";
import showSnackbar from "@comp/func/snackbar.js";
import hk4eReq from "@req/hk4eReq.js";
import { AnnoTypeEnum } from "@enum/anno.js";
import useAppStore from "@store/app.js";
import TGLogger from "@utils/TGLogger.js";
import { generateShareImg } from "@utils/TGShare.js";
@@ -34,20 +34,15 @@ import { onMounted, ref, watch } from "vue";
import type { AnnoCard } from "@/pages/common/PageAnno.vue";
const { server } = storeToRefs(useAppStore());
type TAnnoCardProps = { region: string; lang: string };
const props = defineProps<TAnnoCardProps>();
const { server, lang } = storeToRefs(useAppStore());
const model = defineModel<AnnoCard>({ required: true });
const timeStr = ref<string>(model.value.timeStr);
onMounted(async () => await refreshAnnoTime());
onMounted(() => refreshAnnoTime());
watch(
() => model.value,
async (newVal, oldVal) => {
if (newVal.id !== oldVal.id) {
timeStr.value = newVal.timeStr;
await refreshAnnoTime();
}
(newVal, oldVal) => {
if (newVal.id !== oldVal.id) refreshAnnoTime();
},
);
@@ -57,7 +52,7 @@ function parseTitle(title: string): string {
}
async function createAnno(): Promise<void> {
const annoPath = `/anno_detail/${props.region}/${model.value.id}/${props.lang}`;
const annoPath = `/anno_detail/${server.value}/${model.value.id}/${lang.value}`;
const annoTitle = `Anno_${model.value.id} ${model.value.title}`;
await TGLogger.Info(`[Announcements][createAnno][${model.value.id}] 打开公告窗口`);
await createTGWindow(annoPath, "Sub_window", annoTitle, 960, 720, false, false);
@@ -74,8 +69,8 @@ async function shareAnno(): Promise<void> {
}
async function refreshAnnoTime(): Promise<void> {
const detail = await hk4eReq.anno.content(model.value.id, server.value, "zh-cn");
const strGet = getAnnoTime(detail.content);
if (model.value.typeLabel === AnnoTypeEnum.GAME) timeStr.value = model.value.timeStr;
const strGet = getAnnoTime(model.value.detail.content);
if (strGet !== false) timeStr.value = strGet;
}

View File

@@ -17,7 +17,7 @@ import type { Component } from "vue";
import TaTable from "./ta-table.vue";
type TaParserProps = { data: TGApp.BBS.Announcement.ContentItem };
type TaParserProps = { data: TGApp.BBS.Announcement.AnnoDetail };
const props = defineProps<TaParserProps>();
function getTaName(ta: TGApp.BBS.SctPost.Base): Component {

98
src/enum/anno.ts Normal file
View File

@@ -0,0 +1,98 @@
/**
* @file src/enum/anno.ts
* @description 游戏内公告相关枚举
* @since Beta v0.7.7
*/
/**
* @description 公告语言类型
* @since Beta v0.7.7
* @const AnnoLangEnum
*/
export const AnnoLangEnum: typeof TGApp.BBS.Announcement.AnnoLang = {
CHS: "zh-cn",
CHT: "zh-tw",
EN: "en",
JP: "ja",
};
/**
* @description 获取公告语音描述
* @since Beta v0.7.7
* @param {TGApp.BBS.Announcement.AnnoLangEnum} lang 公告语言
* @return {string} 公告语言描述
*/
export function getAnnoLangDesc(lang: TGApp.BBS.Announcement.AnnoLangEnum): string {
switch (lang) {
case AnnoLangEnum.CHS:
return "简体中文";
case AnnoLangEnum.CHT:
return "繁体中文";
case AnnoLangEnum.EN:
return "英语";
case AnnoLangEnum.JP:
return "日语";
}
}
/**
* @description 公告服务器类型
* @since Beta v0.7.7
* @const AnnoServerEnum
*/
export const AnnoServerEnum: typeof TGApp.BBS.Announcement.AnnoServer = {
CN_GF01: "cn_gf01",
CN_QD01: "cn_qd01",
OS_USA: "os_usa",
OS_EURO: "os_euro",
OS_ASIA: "os_asia",
OS_CHT: "os_cht",
};
/**
* @description 获取公告服务器描述
* @since Beta v0.7.7
* @param {TGApp.BBS.Announcement.AnnoServerEnum} server 公告服务器
* @return {string} 公告服务器描述
*/
export function getAnnoServerDesc(server: TGApp.BBS.Announcement.AnnoServerEnum): string {
switch (server) {
case AnnoServerEnum.CN_GF01:
return "国服-官方服";
case AnnoServerEnum.CN_QD01:
return "国服-渠道服";
case AnnoServerEnum.OS_USA:
return "国际服-美服";
case AnnoServerEnum.OS_EURO:
return "国际服-欧服";
case AnnoServerEnum.OS_ASIA:
return "国际服-亚服";
case AnnoServerEnum.OS_CHT:
return "国际服-港澳台服";
}
}
/**
* @description 公告类型
* @since Beta v0.7.7
* @const AnnoTypeEnum
*/
export const AnnoTypeEnum: typeof TGApp.BBS.Announcement.AnnoType = {
ACTIVITY: "activity",
GAME: "game",
};
/**
* @description 获取公告类型描述
* @since Beta v0.7.7
* @param {TGApp.BBS.Announcement.AnnoTypeEnum} type 公告类型
* @return {string} 公告类型描述
*/
export function getAnnoTypeDesc(type: TGApp.BBS.Announcement.AnnoTypeEnum): string {
switch (type) {
case AnnoTypeEnum.ACTIVITY:
return "活动公告";
case AnnoTypeEnum.GAME:
return "游戏公告";
}
}

View File

@@ -2,14 +2,14 @@
<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">
{{ annoMap[value] }}
<v-tab v-for="(tab, index) in tabList" :key="index" :value="tab.value">
{{ tab.text }}
</v-tab>
</v-tabs>
<div class="anno-selects">
<v-select
class="anno-select"
:items="annoServerList"
:items="serverList"
v-model="server"
item-title="text"
item-value="value"
@@ -20,7 +20,7 @@
/>
<v-select
class="anno-select"
:items="annoLangList"
:items="langList"
v-model="lang"
item-title="text"
item-value="value"
@@ -38,15 +38,9 @@
</template>
</v-app-bar>
<v-window v-model="tab">
<v-window-item v-for="(value, index) in tabValues" :key="index" :value="value">
<v-window-item v-for="(tab, index) in tabList" :key="index" :value="tab.value">
<div class="anno-grid">
<TaCard
v-for="item in annoCards[value]"
:key="item.id"
:model-value="item"
:region="server"
:lang="lang"
/>
<TaCard v-for="item in annoCards[tab.value]" :key="item.id" :model-value="item" />
</div>
</v-window-item>
</v-window>
@@ -56,7 +50,15 @@
import showLoading from "@comp/func/loading.js";
import showSnackbar from "@comp/func/snackbar.js";
import TaCard from "@comp/pageAnno/ta-card.vue";
import hk4eReq, { type AnnoLang, type AnnoServer } from "@req/hk4eReq.js";
import {
AnnoLangEnum,
AnnoServerEnum,
AnnoTypeEnum,
getAnnoLangDesc,
getAnnoServerDesc,
getAnnoTypeDesc,
} from "@enum/anno.js";
import hk4eReq from "@req/hk4eReq.js";
import useAppStore from "@store/app.js";
import TGLogger from "@utils/TGLogger.js";
import { storeToRefs } from "pinia";
@@ -64,46 +66,49 @@ import { onMounted, ref, shallowRef, watch } from "vue";
import { useRouter } from "vue-router";
type AnnoSelect<T = string> = { text: string; value: T };
type AnnoKey = "activity" | "game";
export type AnnoCard = {
id: number;
title: string;
subtitle: string;
banner: string;
typeLabel: string;
typeLabel: TGApp.BBS.Announcement.AnnoTypeEnum;
tagIcon: string;
tagLabel: string;
timeStr: string;
detail: TGApp.BBS.Announcement.AnnoDetail;
};
type AnnoList = { [key in AnnoKey]: Array<AnnoCard> };
type AnnoList = Record<TGApp.BBS.Announcement.AnnoTypeEnum, Array<AnnoCard>>;
const annoServerList: Array<AnnoSelect<AnnoServer>> = [
{ text: "国服-官方服", value: "cn_gf01" },
{ text: "国服-渠道服", value: "cn_qd01" },
{ text: "国际服-亚服", value: "os_asia" },
{ text: "国际服-欧服", value: "os_euro" },
{ text: "国际服-美服", value: "os_usa" },
{ text: "国际服-港澳台服", value: "os_cht" },
];
const annoLangList: Array<AnnoSelect> = [
{ text: "简体中文", value: "zh-cn" },
{ text: "繁体中文", value: "zh-tw" },
{ text: "English", value: "en" },
{ text: "日本語", value: "ja" },
];
const annoMap: Readonly<Record<AnnoKey, string>> = { activity: "活动公告", game: "游戏公告" };
const serverList: ReadonlyArray<AnnoSelect<TGApp.BBS.Announcement.AnnoServerEnum>> = [
AnnoServerEnum.CN_GF01,
AnnoServerEnum.CN_QD01,
AnnoServerEnum.OS_ASIA,
AnnoServerEnum.OS_EURO,
AnnoServerEnum.OS_USA,
AnnoServerEnum.OS_CHT,
].map((i) => ({ text: getAnnoServerDesc(i), value: i }));
const langList: ReadonlyArray<AnnoSelect<TGApp.BBS.Announcement.AnnoLangEnum>> = [
AnnoLangEnum.CHS,
AnnoLangEnum.CHT,
AnnoLangEnum.EN,
AnnoLangEnum.JP,
].map((i) => ({ text: getAnnoLangDesc(i), value: i }));
const tabList: ReadonlyArray<AnnoSelect<TGApp.BBS.Announcement.AnnoTypeEnum>> = [
AnnoTypeEnum.ACTIVITY,
AnnoTypeEnum.GAME,
].map((i) => ({ text: getAnnoTypeDesc(i), value: i }));
const { server, lang } = storeToRefs(useAppStore());
const router = useRouter();
const tabValues: Readonly<Array<AnnoKey>> = ["activity", "game"];
const tab = ref<AnnoKey>("activity");
const tab = ref<TGApp.BBS.Announcement.AnnoTypeEnum>(AnnoTypeEnum.ACTIVITY);
const annoCards = shallowRef<AnnoList>({ activity: [], game: [] });
const isReq = ref<boolean>(false);
watch(
() => server.value,
async () => {
const name = getRegionName(server.value);
const name = getAnnoServerDesc(server.value);
await TGLogger.Info(`[Announcements][watch][curRegionName] 切换服务器:${name}`);
await loadData();
showSnackbar.success(`服务器切换为:${name}`);
@@ -113,7 +118,7 @@ watch(
watch(
() => lang.value,
async () => {
const name = getLangName(lang.value);
const name = getAnnoLangDesc(lang.value);
await TGLogger.Info(`[Announcements][watch][curLangName] 切换语言:${name}`);
await loadData();
showSnackbar.success(`语言切换为:${name}`);
@@ -130,14 +135,28 @@ async function loadData(): Promise<void> {
isReq.value = true;
await showLoading.start(
"正在获取公告数据",
`服务器:${getRegionName(server.value)},语言:${getLangName(lang.value)}`,
`服务器:${getAnnoServerDesc(server.value)},语言:${getAnnoLangDesc(lang.value)}`,
);
const annoData = await hk4eReq.anno.list(server.value, lang.value);
const listCards = annoData.list.map((list) => list.list.map((anno) => getAnnoCard(anno))).flat();
annoCards.value = {
activity: listCards.filter((item) => item.typeLabel === "activity"),
game: listCards.filter((item) => item.typeLabel === "game"),
};
const listResp = await hk4eReq.anno.list(server.value, lang.value);
const detailResp = await hk4eReq.anno.detail(server.value, AnnoLangEnum.CHS);
const actCards: Array<AnnoCard> = [];
const gameCards: Array<AnnoCard> = [];
for (const list of listResp.list) {
for (const item of list.list) {
const detail = detailResp.find((i) => i.ann_id === item.ann_id);
if (detail) {
const card = getAnnoCard(item, detail);
if (card.typeLabel === "activity") {
actCards.push(card);
} else if (card.typeLabel === "game") {
gameCards.push(card);
}
} else {
await TGLogger.Warn(`[Announcements][loadData] 未找到公告详情:${item.ann_id}`);
}
}
}
annoCards.value = { activity: actCards, game: gameCards };
await showLoading.end();
isReq.value = false;
}
@@ -158,7 +177,10 @@ function getAnnoTag(tag: string): string {
}
}
function getAnnoCard(anno: TGApp.BBS.Announcement.AnnoSingle): AnnoCard {
function getAnnoCard(
anno: TGApp.BBS.Announcement.AnnoSingle,
detail: TGApp.BBS.Announcement.AnnoDetail,
): AnnoCard {
const timeStart = anno.start_time.split(" ")[0];
const timeEnd = anno.end_time.split(" ")[0];
const time = `${timeStart} ~ ${timeEnd}`;
@@ -171,17 +193,10 @@ function getAnnoCard(anno: TGApp.BBS.Announcement.AnnoSingle): AnnoCard {
tagIcon: anno.tag_icon,
tagLabel: getAnnoTag(anno.tag_label),
timeStr: time,
detail: detail,
};
}
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;
}
async function switchNews(): Promise<void> {
await TGLogger.Info("[Announcements][switchNews] 切换米游社咨讯");
await router.push("/news/2");

View File

@@ -4,12 +4,10 @@
* @since Beta v0.7.7
*/
import { AnnoLangEnum, AnnoServerEnum } from "@enum/anno.js";
import TGHttp from "@utils/TGHttp.js";
import { getDeviceInfo } from "@utils/toolFunc.js";
export type AnnoServer = "cn_gf01" | "cn_qd01" | "os_usa" | "os_euro" | "os_asia" | "os_cht";
export type AnnoLang = "zh-cn" | "zh-tw" | "en" | "ja";
const AnnoApi: Readonly<string> = "https://hk4e-ann-api.mihoyo.com/common/hk4e_cn/announcement/api";
const AnnoApiGlobal: Readonly<string> =
"https://sg-hk4e-api.hoyoverse.com/common/hk4e_global/announcement/api";
@@ -17,34 +15,40 @@ const SdkApi: Readonly<string> = "https://hk4e-sdk.mihoyo.com/hk4e_cn/";
/**
* @description 判断是否为国内服务器
* @since Beta v0.7.2
* @param {AnnoServer} region 服务器
* @since Beta v0.7.7
* @param {TGApp.BBS.Announcement.AnnoServerEnum} region 服务器
* @returns {boolean} 是否为国内服务器
*/
function isCN(region: AnnoServer): boolean {
return region.startsWith("cn");
function isCN(region: TGApp.BBS.Announcement.AnnoServerEnum): boolean {
switch (region) {
case AnnoServerEnum.CN_QD01:
case AnnoServerEnum.CN_GF01:
return true;
default:
return false;
}
}
/**
* @description 根据服务器获取公告地址
* @since Beta v0.6.5
* @param {AnnoServer} region 服务器
* @since Beta v0.7.7
* @param {TGApp.BBS.Announcement.AnnoServerEnum} region 服务器
* @returns {string} 公告地址
*/
function getAnnoApi(region: AnnoServer): string {
function getAnnoApi(region: TGApp.BBS.Announcement.AnnoServerEnum): string {
return isCN(region) ? AnnoApi : AnnoApiGlobal;
}
/**
* @description 获取游戏内公告参数
* @since Beta v0.7.2
* @param {AnnoServer} region 服务器
* @param {string} lang 语言
* @since Beta v0.7.7
* @param {TGApp.BBS.Announcement.AnnoServerEnum} region 服务器
* @param {TGApp.BBS.Announcement.AnnoLangEnum} lang 语言
* @returns {TGApp.BBS.Announcement.Params}
*/
function getAnnoParams(
region: AnnoServer = "cn_gf01",
lang: AnnoLang = "zh-cn",
region: TGApp.BBS.Announcement.AnnoServerEnum = AnnoServerEnum.CN_GF01,
lang: TGApp.BBS.Announcement.AnnoLangEnum = AnnoLangEnum.CHS,
): TGApp.BBS.Announcement.Params {
return {
game: "hk4e",
@@ -60,44 +64,38 @@ function getAnnoParams(
/**
* @description 获取游戏内公告列表
* @since Beta v0.5.5
* @param {string} region 服务器
* @param {AnnoLang} lang 语言
* @returns {Promise<TGApp.BBS.Announcement.ListData>}
* @since Beta v0.7.7
* @param {TGApp.BBS.Announcement.AnnoServerEnum} region 服务器
* @param {TGApp.BBS.Announcement.AnnoLangEnum} lang 语言
* @returns {Promise<TGApp.BBS.Announcement.ListRes>}
*/
async function getAnnoList(
region: AnnoServer = "cn_gf01",
lang: AnnoLang = "zh-cn",
): Promise<TGApp.BBS.Announcement.ListData> {
const resp = await TGHttp<TGApp.BBS.Announcement.ListResponse>(
`${getAnnoApi(region)}/getAnnList`,
{ method: "GET", query: getAnnoParams(region, lang) },
);
region: TGApp.BBS.Announcement.AnnoServerEnum = AnnoServerEnum.CN_GF01,
lang: TGApp.BBS.Announcement.AnnoLangEnum = AnnoLangEnum.CHS,
): Promise<TGApp.BBS.Announcement.ListRes> {
const resp = await TGHttp<TGApp.BBS.Announcement.ListResp>(`${getAnnoApi(region)}/getAnnList`, {
method: "GET",
query: getAnnoParams(region, lang),
});
return resp.data;
}
/**
* @description 获取游戏内公告内容
* @since Beta v0.5.5
* @param {number} annId 公告 ID
* @param {AnnoServer} region 服务器
* @param {AnnoLang} lang 语言
* @returns {Promise<TGApp.BBS.Announcement.ContentItem>}
* @since Beta v0.7.7
* @param {TGApp.BBS.Announcement.AnnoServerEnum} region 服务器
* @param {TGApp.BBS.Announcement.AnnoLangEnum} lang 语言
* @returns {Promise<Array<TGApp.BBS.Announcement.AnnoDetail>>}
*/
async function getAnnoContent(
annId: number,
region: AnnoServer = "cn_gf01",
lang: AnnoLang = "zh-cn",
): Promise<TGApp.BBS.Announcement.ContentItem> {
const annoResp = await TGHttp<TGApp.BBS.Announcement.ContentResponse>(
async function getAnnoDetail(
region: TGApp.BBS.Announcement.AnnoServerEnum = AnnoServerEnum.CN_GF01,
lang: TGApp.BBS.Announcement.AnnoLangEnum = AnnoLangEnum.CHS,
): Promise<Array<TGApp.BBS.Announcement.AnnoDetail>> {
const resp = await TGHttp<TGApp.BBS.Announcement.DetailResp>(
`${getAnnoApi(region)}/getAnnContent`,
{ method: "GET", query: getAnnoParams(region, lang) },
);
const annoContent = annoResp.data.list.find((item) => item.ann_id === annId);
if (annoContent === undefined) {
throw new Error("公告内容不存在");
}
return annoContent;
return resp.data.list;
}
/**
@@ -170,7 +168,7 @@ async function queryPandaQr(
}
const hk4eReq = {
anno: { list: getAnnoList, content: getAnnoContent },
anno: { list: getAnnoList, detail: getAnnoDetail },
gacha: getGachaLog,
loginQr: { create: fetchPandaQr, state: queryPandaQr },
};

View File

@@ -1,10 +1,10 @@
/**
* @file store/modules/app.ts
* @description App store module
* @since Beta v0.7.6
* @since Beta v0.7.7
*/
import type { AnnoLang, AnnoServer } from "@req/hk4eReq.js";
import { AnnoLangEnum, AnnoServerEnum } from "@enum/anno.js";
import { path } from "@tauri-apps/api";
import { getInitDeviceInfo } from "@utils/toolFunc.js";
import { defineStore } from "pinia";
@@ -43,9 +43,9 @@ const useAppStore = defineStore(
// 设备信息
const deviceInfo = ref<TGApp.App.Device.DeviceInfo>(getInitDeviceInfo());
// 服务器
const server = ref<AnnoServer>("cn_gf01");
const server = ref<TGApp.BBS.Announcement.AnnoServerEnum>(AnnoServerEnum.CN_QD01);
// 语言
const lang = ref<AnnoLang>("zh-cn");
const lang = ref<TGApp.BBS.Announcement.AnnoLangEnum>(AnnoLangEnum.CHS);
// 最近的咨讯类型
const recentNewsType = ref<NewsType>("notice");
// 是否开启分辨率回正

View File

@@ -1,16 +1,79 @@
/**
* @file types/BBS/Announcement.d.ts
* @description 从 BBS 获取到的游戏内公告类型定义文件
* @since Beta v0.4.3
* @since Beta v0.7.7
*/
/**
* @description 游戏内公告类型定义
* @since Beta v0.4.3
* @namespace TGApp.BBS.Announcement
* @memberof TGApp.BBS
*/
declare namespace TGApp.BBS.Announcement {
/**
* @description 公告语言类型
* @since Beta v0.7.7
* @const AnnoLang
* @property {string} "zh-cn" - 简体中文
* @property {string} "zh-tw" - 繁体中文
* @property {string} "en" - 英语
* @property {string} "ja" - 日语
*/
const AnnoLang = <const>{
CHS: "zh-cn",
CHT: "zh-tw",
EN: "en",
JP: "ja",
};
/**
* @description 公告语言类型枚举
* @since Beta v0.7.7
* @enum AnnoLangEnum
*/
type AnnoLangEnum = (typeof AnnoLang)[keyof typeof AnnoLang];
/**
* @description 公告服务器类型
* @since Beta v0.7.7
* @const AnnoServer
* @property {string} "cn_gf01" - 国内-国服
* @property {string} "cn_qd01" - 国内-渠道服
* @property {string} "os_usa" - 海外-美国
* @property {string} "os_euro" - 海外-欧洲
* @property {string} "os_asia" - 海外-亚洲
* @property {string} "os_cht" - 海外-繁体中文
*/
const AnnoServer = <const>{
CN_GF01: "cn_gf01",
CN_QD01: "cn_qd01",
OS_USA: "os_usa",
OS_EURO: "os_euro",
OS_ASIA: "os_asia",
OS_CHT: "os_cht",
};
/**
* @description 公告服务器类型枚举
* @since Beta v0.7.7
* @enum AnnoServerEnum
*/
type AnnoServerEnum = (typeof AnnoServer)[keyof typeof AnnoServer];
/**
* @description 公告类型
* @since Beta v0.7.7
* @const AnnoType
* @property {string} "activity" - 活动公告
* @property {string} "game" - 游戏公告
*/
const AnnoType = <const>{
ACTIVITY: "activity",
GAME: "game",
};
/**
* @description 公告类型枚举
* @since Beta v0.7.7
* @enum AnnoTypeEnum
*/
type AnnoTypeEnum = (typeof AnnoType)[keyof typeof AnnoType];
/**
* @description 需要的参数
* @interface Params
@@ -23,107 +86,72 @@ declare namespace TGApp.BBS.Announcement {
* @property {string} region - 区域
* @property {string} level - 等级
* @property {string} uid - 用户 ID
* @returns Params
*/
interface Params {
type Params = {
game: string;
game_biz: string;
lang: string;
lang: AnnoLangEnum;
bundle_id: string;
platform: string;
region: string;
region: AnnoServerEnum;
level: string;
uid: string;
}
};
/**
* @description 公告列表返回响应类型
* @interface ListResponse
* @interface ListResp
* @since Alpha v0.1.5
* @extends TGApp.BBS.Response.BaseWithData
* @property {ListData} data - 公告列表数据
* @return ListResponse
* @extends TGApp.BBS.Response.BaseWithData<ListRes>
* @return ListResp
*/
interface ListResponse extends TGApp.BBS.Response.BaseWithData {
data: ListData;
}
/**
* @description 公告内容返回响应类型
* @interface ContentResponse
* @since Alpha v0.1.5
* @extends TGApp.BBS.Response.BaseWithData
* @property {ContentData} data - 公告内容数据
* @return ContentResponse
*/
interface ContentResponse extends TGApp.BBS.Response.BaseWithData {
data: ContentData;
}
type ListResp = TGApp.BBS.Response.BaseWithData<ListRes>;
/**
* @description 公告列表数据
* @interface ListData
* @since Alpha v0.1.5
* @property {ListItem[]} list - 公告列表
* @interface ListRes
* @since Beta v0.7.7
* @property {Array<ListItem>} list - 公告列表
* @property {number} total - 公告总数
* @property {ListType[]} type_list - 公告类型列表
* @property {Array<ListType>} type_list - 公告类型列表
* @property {boolean} alert - 是否有紧急公告
* @property {number} time_zone - 时区
* @property {string} t - 时间戳,单位为秒
* @property {unknown[]} pic_list - 图片列表
* @property {Array<unknown>} pic_list - 图片列表
* @property {number} pic_total - 图片总数
* @property {unknown[]} pic_type_list - 图片类型列表
* @property {Array<unknown>} pic_type_list - 图片类型列表
* @property {boolean} pic_alert - 是否有紧急图片
* @property {number} pic_alert_id - 紧急图片 ID
* @property {unknown} static_sign - 静态签名
* @return ListData
* @return ListRes
*/
interface ListData {
list: ListItem[];
type ListRes = {
list: Array<ListItem>;
total: number;
type_list: ListType[];
type_list: Array<ListType>;
alert: boolean;
time_zone: number;
t: string;
pic_list: unknown[];
pic_list: Array<unknown>;
pic_total: number;
pic_type_list: unknown[];
pic_type_list: Array<unknown>;
pic_alert: boolean;
pic_alert_id: number;
static_sign: unknown;
}
/**
* @description 公告内容数据
* @interface ContentData
* @since Alpha v0.1.5
* @property {ContentItem[]} list - 公告内容列表
* @property {number} total - 公告内容总数
* @property {unknown[]} pic_list - 图片列表
* @property {number} pic_total - 图片总数
* @return ContentData
*/
interface ContentData {
list: ContentItem[];
total: number;
pic_list: unknown[];
pic_total: number;
}
banner: string;
calendar_type: ListCalendar;
};
/**
* @description 公告列表项
* @interface ListItem
* @since Alpha v0.1.5
* @property {AnnoSingle[]} list - 公告列表
* @property {Array<AnnoSingle>} list - 公告列表
* @property {number} type_id - 公告类型 ID
* @property {string} type_label - 公告类型标签
* @return ListItem
*/
interface ListItem {
list: AnnoSingle[];
type_id: number;
type_label: string;
}
type ListItem = { list: Array<AnnoSingle>; type_id: number; type_label: string };
/**
* @description 单个公告内容
@@ -149,9 +177,16 @@ declare namespace TGApp.BBS.Announcement {
* @property {number} remind_ver 公告提醒版本
* @property {boolean} has_content 是否有内容
* @property {boolean} extra_remind 是否有额外提醒
* @return AnnoSingle
* @property {string} tag_icon_hover 公告标签悬停图标
* @property {number} logout_remind 是否登出提醒
* @property {number} logout_remind_ver 登出提醒版本
* @property {string} country 公告国家
* @property {number} need_remind_text 是否需要提醒文本
* @property {string} remind_text 提醒文本
* @property {number} weak_remind 弱提醒
* @property {number} remind_consumption_type 提醒消费类型
*/
interface AnnoSingle {
type AnnoSingle = {
ann_id: number;
title: string;
subtitle: string;
@@ -172,7 +207,15 @@ declare namespace TGApp.BBS.Announcement {
remind_ver: number;
has_content: boolean;
extra_remind: boolean;
}
tag_icon_hover: string;
logout_remind: number;
logout_remind_ver: number;
country: string;
need_remind_text: number;
remind_text: string;
weak_remind: number;
remind_consumption_type: number;
};
/**
* @description 公告类型列表项
@@ -181,17 +224,46 @@ declare namespace TGApp.BBS.Announcement {
* @property {number} id 公告类型 ID
* @property {string} name 公告类型名称
* @property {string} mi18n_name 公告类型国际化名称
* @return ListType
*/
interface ListType {
id: number;
name: string;
mi18n_name: string;
}
type ListType = { id: number; name: string; mi18n_name: string };
/**
* @description 公告日历类型
* @interface ListCalendar
* @since Beta v0.7.7
* @property {string} mi18n_name 公告日历国际化名称
* @property {boolean} enabled 是否启用
* @property {boolean} remind 是否提醒
*/
type ListCalendar = { mi18n_name: string; enabled: boolean; remind: boolean };
/**
* @description 公告内容返回响应类型
* @interface DetailResp
* @since Alpha v0.1.5
* @extends TGApp.BBS.Response.BaseWithData<DetailRes>
*/
type DetailResp = TGApp.BBS.Response.BaseWithData<DetailRes>;
/**
* @description 公告内容数据
* @interface DetailRes
* @since Alpha v0.1.5
* @property {Array<AnnoDetail>} list - 公告内容列表
* @property {number} total - 公告内容总数
* @property {Array<unknown>} pic_list - 图片列表
* @property {number} pic_total - 图片总数
*/
type DetailRes = {
list: Array<AnnoDetail>;
total: number;
pic_list: Array<unknown>;
pic_total: number;
};
/**
* @description 公告内容列表项
* @interface ContentItem
* @interface AnnoDetail
* @since Alpha v0.1.5
* @property {number} ann_id 公告 ID
* @property {string} title 公告标题
@@ -199,14 +271,13 @@ declare namespace TGApp.BBS.Announcement {
* @property {string} banner 公告图片
* @property {string} content 公告内容,为 html
* @property {string} lang 公告语言
* @return ContentItem
*/
interface ContentItem {
type AnnoDetail = {
ann_id: number;
title: string;
subtitle: string;
banner: string;
content: string;
lang: string;
}
};
}

View File

@@ -40,10 +40,10 @@ function handleAnnoContent(data: string): string {
/**
* @description 解析公告内容,转换为结构化数据
* @since Beta v0.5.3
* @param {TGApp.BBS.Announcement.ContentItem} anno - 公告内容
* @param {TGApp.BBS.Announcement.AnnoDetail} anno - 公告内容
* @returns {TGApp.BBS.SctPost.Base[]} 结构化数据
*/
function parseAnnoContent(anno: TGApp.BBS.Announcement.ContentItem): Array<TGApp.BBS.SctPost.Base> {
function parseAnnoContent(anno: TGApp.BBS.Announcement.AnnoDetail): Array<TGApp.BBS.SctPost.Base> {
const parser = new DOMParser();
const first = handleAnnoContent(anno.content);
const doc = parser.parseFromString(first, "text/html");

View File

@@ -73,7 +73,8 @@
import TPinWin from "@comp/app/t-pinWin.vue";
import TSwitchTheme from "@comp/app/t-switchTheme.vue";
import showLoading from "@comp/func/loading.js";
import hk4eReq, { type AnnoLang, AnnoServer } from "@req/hk4eReq.js";
import showSnackbar from "@comp/func/snackbar.js";
import hk4eReq from "@req/hk4eReq.js";
import useAppStore from "@store/app.js";
import parseAnnoContent from "@utils/annoParser.js";
import { storeToRefs } from "pinia";
@@ -82,14 +83,15 @@ import VueJsonPretty from "vue-json-pretty";
import "vue-json-pretty/lib/styles.css";
import { useRoute } from "vue-router";
// 数据
const { theme } = storeToRefs(useAppStore());
const route = useRoute();
const annoId = Number(route.params.anno_id);
const region = <AnnoServer>route.params.region;
const lang = <AnnoLang>route.params.lang;
const { theme } = storeToRefs(useAppStore());
const region = <TGApp.BBS.Announcement.AnnoServerEnum>route.params.region;
const lang = <TGApp.BBS.Announcement.AnnoLangEnum>route.params.lang;
const jsonList = shallowRef<TGApp.BBS.Announcement.AnnoSingle>();
const jsonContent = shallowRef<TGApp.BBS.Announcement.ContentItem>();
const jsonContent = shallowRef<TGApp.BBS.Announcement.AnnoDetail>();
const parsedJson = shallowRef<Array<TGApp.BBS.SctPost.Base>>();
const jsonTheme = computed<"dark" | "light">(() => (theme.value === "dark" ? "dark" : "light"));
@@ -100,8 +102,8 @@ onMounted(async () => {
return;
}
await showLoading.update(`公告ID: ${annoId}`);
const listData = await hk4eReq.anno.list(region, lang);
for (const listItem of listData.list) {
const listResp = await hk4eReq.anno.list(region, lang);
for (const listItem of listResp.list) {
for (const single of listItem.list) {
if (single.ann_id === annoId) {
jsonList.value = single;
@@ -109,7 +111,13 @@ onMounted(async () => {
}
}
}
jsonContent.value = await hk4eReq.anno.content(annoId, region, lang);
const detailResp = await hk4eReq.anno.detail(region, lang);
const find = detailResp.find((item) => item.ann_id === annoId);
if (!find) {
showSnackbar.error("未找到公告数据");
return;
}
jsonContent.value = find;
parsedJson.value = parseAnnoContent(jsonContent.value);
await showLoading.end();
});

View File

@@ -19,7 +19,7 @@ import TShareBtn from "@comp/app/t-shareBtn.vue";
import TSwitchTheme from "@comp/app/t-switchTheme.vue";
import showLoading from "@comp/func/loading.js";
import TaParser from "@comp/pageAnno/ta-parser.vue";
import hk4eReq, { type AnnoLang, AnnoServer } from "@req/hk4eReq.js";
import hk4eReq from "@req/hk4eReq.js";
import useAppStore from "@store/app.js";
import { app, webviewWindow } from "@tauri-apps/api";
import TGLogger from "@utils/TGLogger.js";
@@ -29,10 +29,10 @@ import { useRoute } from "vue-router";
const route = useRoute();
const annoId = Number(route.params.anno_id);
const region = <AnnoServer>route.params.region;
const lang = <AnnoLang>route.params.lang;
const region = <TGApp.BBS.Announcement.AnnoServerEnum>route.params.region;
const lang = <TGApp.BBS.Announcement.AnnoLangEnum>route.params.lang;
const appVersion = ref<string>();
const annoData = shallowRef<TGApp.BBS.Announcement.ContentItem>();
const annoData = shallowRef<TGApp.BBS.Announcement.AnnoDetail>();
onMounted(async () => {
await showLoading.start("正在加载公告数据");
@@ -43,20 +43,18 @@ onMounted(async () => {
return;
}
await showLoading.update("正在获取数据");
try {
annoData.value = await hk4eReq.anno.content(annoId, region, lang);
await showLoading.update("正在渲染数据");
await webviewWindow
.getCurrentWebviewWindow()
.setTitle(`Anno_${annoId} ${annoData.value.title}`);
} catch (error) {
if (error instanceof Error)
await TGLogger.Error(`[t-anno.vue][${annoId}] ${error.name}${error.message}`);
else console.error(error);
const detailResp = await hk4eReq.anno.detail(region, lang);
await showLoading.update("正在渲染数据");
const find = detailResp.find((item) => item.ann_id === annoId);
if (!find) {
await showLoading.empty("未找到数据", "公告不存在或解析失败");
await webviewWindow.getCurrentWebviewWindow().setTitle(`Anno_${annoId} Parsing Error`);
await TGLogger.Error(`[t-anno.vue][${annoId}] 未找到公告`);
await webviewWindow.getCurrentWebviewWindow().setTitle(`Anno_${annoId} Not Found`);
return;
}
annoData.value = find;
await showLoading.update(`公告ID: ${annoId} - ${annoData.value.title}`);
await webviewWindow.getCurrentWebviewWindow().setTitle(`Anno_${annoId} ${annoData.value.title}`);
const isDev = useAppStore().devMode ?? false;
if (isDev) await createAnnoJson();
await showLoading.end();

View File

@@ -22,6 +22,7 @@
"@/*": ["src/*"],
"@styles/*": ["src/assets/styles/*"],
"@comp/*": ["src/components/*"],
"@enum/*": ["src/enum/*"],
"@Bili/*": ["src/plugins/Bili/*"],
"@Hutao/*": ["src/plugins/Hutao/*"],
"@Mys/*": ["src/plugins/Mys/*"],

View File

@@ -1,7 +1,7 @@
/**
* @file vite.config.ts
* @description vite 配置文件
* @since Beta v0.7.6
* @since Beta v0.7.7
*/
import vue from "@vitejs/plugin-vue";
@@ -21,6 +21,7 @@ export default defineConfig({
"@/": "/src/",
"@styles/": "/src/assets/styles/",
"@comp/": "/src/components/",
"@enum/": "/src/enum/",
"@Bili/": "/src/plugins/Bili/",
"@Hutao/": "/src/plugins/Hutao/",
"@Mys/": "/src/plugins/Mys/",