公告支持切换服务器&语言

close #81
This commit is contained in:
目棃
2024-02-01 13:22:39 +08:00
parent 1cf29d0750
commit 02d875115f
7 changed files with 209 additions and 37 deletions

View File

@@ -4,6 +4,22 @@
<v-tab v-for="(value, index) in tabValues" :key="index" :value="value"> <v-tab v-for="(value, index) in tabValues" :key="index" :value="value">
{{ AnnoType[value] }} {{ AnnoType[value] }}
</v-tab> </v-tab>
<v-select
class="anno-select"
:items="annoServerList"
v-model="curRegionName"
label="服务器"
dense
outlined
/>
<v-select
class="anno-select"
:items="langList"
v-model="curLangName"
label="语言"
dense
outlined
/>
<v-spacer /> <v-spacer />
<v-btn class="anno-switch-btn" @click="switchNews"> <v-btn class="anno-switch-btn" @click="switchNews">
<template #prepend> <template #prepend>
@@ -38,15 +54,48 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { nextTick, onMounted, ref } from "vue"; import { nextTick, onMounted, ref, watch } 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 TGLogger from "../../utils/TGLogger"; import TGLogger from "../../utils/TGLogger";
import { createTGWindow } from "../../utils/TGWindow"; import { createTGWindow } from "../../utils/TGWindow";
import { SERVER } from "../../web/request/getAnno";
import TGRequest from "../../web/request/TGRequest"; import TGRequest from "../../web/request/TGRequest";
import TGUtils from "../../web/utils/TGUtils"; import TGUtils from "../../web/utils/TGUtils";
// 服务器名称-服务器对应
type AnnoServerMap = {
[key: string]: SERVER;
};
const annoServerList: string[] = [
"国服-官方服",
"国服-渠道服",
"国际服-亚服",
"国际服-欧服",
"国际服-美服",
"国际服-港澳台服",
];
const langList: string[] = ["简体中文", "繁体中文", "English", "日本語"];
// 服务器列表
const annoServerMap: AnnoServerMap = {
"国服-官方服": SERVER.CN_ISLAND,
"国服-渠道服": SERVER.CN_TREE,
"国际服-亚服": SERVER.OS_ASIA,
"国际服-欧服": SERVER.OS_EURO,
"国际服-美服": SERVER.OS_USA,
"国际服-港澳台服": SERVER.OS_CHT,
};
const langMap: Record<string, string> = {
简体中文: "zh-cn",
繁体中文: "zh-tw",
English: "en",
日本語: "ja",
};
// 类型定义 // 类型定义
enum AnnoType { enum AnnoType {
activity = "活动公告", activity = "活动公告",
@@ -62,8 +111,14 @@ type AnnoCard = {
const loading = ref<boolean>(true); const loading = ref<boolean>(true);
const loadingTitle = ref<string>("正在加载"); const loadingTitle = ref<string>("正在加载");
const appStore = useAppStore();
// 路由 // 路由
const router = useRouter(); const router = useRouter();
const curRegion = ref<SERVER>(appStore.server);
const curRegionName = ref<string>(annoServerList[0]);
const curLang = ref<string>(appStore.lang);
const curLangName = ref<string>(langList[0]);
// 数据 // 数据
const tab = ref<AnnoKey>("activity"); const tab = ref<AnnoKey>("activity");
@@ -74,13 +129,44 @@ const annoCards = ref<AnnoCard>({
}); });
const annoData = ref<TGApp.BBS.Announcement.ListData>(<TGApp.BBS.Announcement.ListData>{}); const annoData = ref<TGApp.BBS.Announcement.ListData>(<TGApp.BBS.Announcement.ListData>{});
watch(curRegionName, async (value) => {
appStore.server = annoServerMap[value] || SERVER.CN_ISLAND;
curRegion.value = annoServerMap[value] || SERVER.CN_ISLAND;
await TGLogger.Info(`[Announcements][watch][curRegionName] 切换服务器:${value}`);
await loadData();
});
watch(curLangName, async (value) => {
appStore.lang = langMap[value] || "zh-cn";
curLang.value = langMap[value] || "zh-cn";
await TGLogger.Info(`[Announcements][watch][curLang] 切换语言:${value}`);
await loadData();
});
onMounted(async () => { onMounted(async () => {
await TGLogger.Info("[Announcements][onMounted] 打开公告页面"); await TGLogger.Info("[Announcements][onMounted] 打开公告页面");
// 根据curRegion找到对应的curRegionName
for (const key in annoServerMap) {
if (annoServerMap[key] === curRegion.value) {
curRegionName.value = key;
break;
}
}
// 根据curLang找到对应的curLangName
for (const key in langMap) {
if (langMap[key] === curLang.value) {
curLangName.value = key;
break;
}
}
await loadData();
});
async function loadData(): Promise<void> {
loadingTitle.value = "正在获取公告数据"; loadingTitle.value = "正在获取公告数据";
loading.value = true; loading.value = true;
annoData.value = await TGRequest.Anno.getList(); annoData.value = await TGRequest.Anno.getList(curRegion.value, curLang.value);
const listCards = TGUtils.Anno.getCard(annoData.value); const listCards = TGUtils.Anno.getCard(annoData.value);
tab.value = "activity";
annoCards.value = { annoCards.value = {
activity: listCards.filter((item) => item.typeLabel === AnnoType.activity), activity: listCards.filter((item) => item.typeLabel === AnnoType.activity),
game: listCards.filter((item) => item.typeLabel === AnnoType.game), game: listCards.filter((item) => item.typeLabel === AnnoType.game),
@@ -89,7 +175,7 @@ onMounted(async () => {
await nextTick(async () => { await nextTick(async () => {
loading.value = false; loading.value = false;
}); });
}); }
function parseTitle(title: string): string { function parseTitle(title: string): string {
const div = document.createElement("div"); const div = document.createElement("div");
@@ -103,7 +189,7 @@ async function switchNews(): Promise<void> {
} }
function createAnno(item: TGApp.App.Announcement.ListCard): void { function createAnno(item: TGApp.App.Announcement.ListCard): void {
const annoPath = `/anno_detail/${item.id}`; const annoPath = `/anno_detail/${curRegion.value}/${item.id}/${curLang.value}`;
const annoTitle = `Anno_${item.id} ${item.title}`; const annoTitle = `Anno_${item.id} ${item.title}`;
TGLogger.Info(`[Announcements][createAnno][${item.id}] 打开公告窗口`).then(() => TGLogger.Info(`[Announcements][createAnno][${item.id}] 打开公告窗口`).then(() =>
createTGWindow(annoPath, "Sub_window", annoTitle, 960, 720, false, false), createTGWindow(annoPath, "Sub_window", annoTitle, 960, 720, false, false),
@@ -118,6 +204,13 @@ function createAnno(item: TGApp.App.Announcement.ListCard): void {
font-family: var(--font-title); font-family: var(--font-title);
} }
.anno-select {
width: 150px;
margin-left: 10px;
color: var(--common-text-title);
font-family: var(--font-title);
}
.anno-switch-btn { .anno-switch-btn {
height: 40px; height: 40px;
background: var(--btn-bg-1); background: var(--btn-bg-1);

View File

@@ -1,12 +1,13 @@
/** /**
* @file router modules sub.ts * @file router/modules/sub.ts
* @description 子路由模块,用于二级窗口 * @description 子路由模块,用于二级窗口
* @since Beta v0.3.3 * @since Beta v0.4.3
*/ */
const subRoutes = [ const subRoutes = [
{ {
path: "/anno_detail/:anno_id", // 传入参数公告ID-anno_id服务器-region语言-lang
path: "/anno_detail/:region/:anno_id/:lang",
name: "游戏内公告", name: "游戏内公告",
component: async () => await import("../../views/t-anno.vue"), component: async () => await import("../../views/t-anno.vue"),
}, },

View File

@@ -1,7 +1,7 @@
/** /**
* @file store/modules/app.ts * @file store/modules/app.ts
* @description App store module * @description App store module
* @since Beta v0.4.2 * @since Beta v0.4.3
*/ */
import { path } from "@tauri-apps/api"; import { path } from "@tauri-apps/api";
@@ -9,6 +9,7 @@ import { defineStore } from "pinia";
import { reactive, ref } from "vue"; import { reactive, ref } from "vue";
import { getInitDeviceInfo } from "../../utils/toolFunc"; import { getInitDeviceInfo } from "../../utils/toolFunc";
import { SERVER } from "../../web/request/getAnno";
// 用于存储用户数据的路径 // 用于存储用户数据的路径
const userDataDir = `${await path.appLocalDataDir()}userData`; const userDataDir = `${await path.appLocalDataDir()}userData`;
@@ -43,6 +44,10 @@ export const useAppStore = defineStore(
const logDir = ref(logDataDir); const logDir = ref(logDataDir);
// 设备信息 // 设备信息
const deviceInfo = ref<TGApp.App.Device.DeviceInfo>(getInitDeviceInfo()); const deviceInfo = ref<TGApp.App.Device.DeviceInfo>(getInitDeviceInfo());
// 服务器
const server = ref<SERVER>(SERVER.CN_ISLAND);
// 语言
const lang = ref<string>("zh-cn");
// 初始化 // 初始化
function init(): void { function init(): void {
@@ -50,6 +55,9 @@ export const useAppStore = defineStore(
devMode.value = false; devMode.value = false;
theme.value = "default"; theme.value = "default";
isLogin.value = false; isLogin.value = false;
sidebar.collapse = true;
server.value = SERVER.CN_ISLAND;
lang.value = "zh-cn";
initDevice(); initDevice();
} }
@@ -73,6 +81,8 @@ export const useAppStore = defineStore(
userDir, userDir,
dbPath, dbPath,
logDir, logDir,
server,
lang,
init, init,
changeTheme, changeTheme,
}; };
@@ -97,7 +107,7 @@ export const useAppStore = defineStore(
{ {
key: "theme", key: "theme",
storage: window.localStorage, storage: window.localStorage,
paths: ["theme"], paths: ["theme", "server", "lang"],
}, },
{ {
key: "deviceInfo", key: "deviceInfo",

View File

@@ -1,16 +1,41 @@
/** /**
* @file types/BBS/Announcement.d.ts * @file types/BBS/Announcement.d.ts
* @description 从 BBS 获取到的游戏内公告类型定义文件 * @description 从 BBS 获取到的游戏内公告类型定义文件
* @since Alpha v0.1.5 * @since Beta v0.4.3
*/ */
/** /**
* @description 游戏内公告类型定义 * @description 游戏内公告类型定义
* @since Alpha v0.1.5 * @since Beta v0.4.3
* @namespace TGApp.BBS.Announcement * @namespace TGApp.BBS.Announcement
* @memberof TGApp.BBS * @memberof TGApp.BBS
*/ */
declare namespace TGApp.BBS.Announcement { declare namespace TGApp.BBS.Announcement {
/**
* @description 需要的参数
* @interface Params
* @since Beta v0.4.3
* @property {string} game - 游戏名称
* @property {string} game_biz - 游戏业务名称
* @property {string} lang - 语言
* @property {string} bundle_id - 包 ID
* @property {string} platform - 平台
* @property {string} region - 区域
* @property {string} level - 等级
* @property {string} uid - 用户 ID
* @returns Params
*/
interface Params {
game: string;
game_biz: string;
lang: string;
bundle_id: string;
platform: string;
region: string;
level: string;
uid: string;
}
/** /**
* @description 公告列表返回响应类型 * @description 公告列表返回响应类型
* @interface ListResponse * @interface ListResponse

View File

@@ -32,6 +32,7 @@ import { useAppStore } from "../store/modules/app";
import TGLogger from "../utils/TGLogger"; import TGLogger from "../utils/TGLogger";
import { saveImgLocal } from "../utils/TGShare"; import { saveImgLocal } from "../utils/TGShare";
import { createTGWindow } from "../utils/TGWindow"; import { createTGWindow } from "../utils/TGWindow";
import { SERVER } from "../web/request/getAnno";
import TGRequest from "../web/request/TGRequest"; import TGRequest from "../web/request/TGRequest";
import TGUtils from "../web/utils/TGUtils"; import TGUtils from "../web/utils/TGUtils";
@@ -48,6 +49,8 @@ const annoTitle = ref<string>("");
// 数据 // 数据
const annoId = Number(useRoute().params.anno_id); const annoId = Number(useRoute().params.anno_id);
const region = <SERVER>useRoute().params.region;
const lang = ref<string>(<string>useRoute().params.lang);
const annoData = ref<TGApp.BBS.Announcement.ContentItem>(<TGApp.BBS.Announcement.ContentItem>{}); const annoData = ref<TGApp.BBS.Announcement.ContentItem>(<TGApp.BBS.Announcement.ContentItem>{});
const annoHtml = ref<string>(); const annoHtml = ref<string>();
const annoBanner = ref<string>(); const annoBanner = ref<string>();
@@ -55,7 +58,7 @@ const annoBanner = ref<string>();
onMounted(async () => { onMounted(async () => {
await appWindow.show(); await appWindow.show();
// 检查数据 // 检查数据
if (!annoId) { if (!annoId || !region) {
loadingEmpty.value = true; loadingEmpty.value = true;
loadingTitle.value = "未找到数据"; loadingTitle.value = "未找到数据";
await TGLogger.Error("[t-anno.vue] 未找到数据"); await TGLogger.Error("[t-anno.vue] 未找到数据");
@@ -64,7 +67,7 @@ onMounted(async () => {
// 获取数据 // 获取数据
loadingTitle.value = "正在获取数据..."; loadingTitle.value = "正在获取数据...";
try { try {
annoData.value = await TGRequest.Anno.getContent(annoId); annoData.value = await TGRequest.Anno.getContent(annoId, region, lang.value);
loadingTitle.value = "正在渲染数据..."; loadingTitle.value = "正在渲染数据...";
annoHtml.value = await TGUtils.Anno.parseContent(annoData.value.content); annoHtml.value = await TGUtils.Anno.parseContent(annoData.value.content);
if (annoData.value.banner !== "") annoBanner.value = await saveImgLocal(annoData.value.banner); if (annoData.value.banner !== "") annoBanner.value = await saveImgLocal(annoData.value.banner);

View File

@@ -1,31 +1,61 @@
/** /**
* @file web request getAnnouncement.ts * @file web/request/getAnnouncement.ts
* @description 获取游戏内公告 * @description 获取游戏内公告
* @author BTMuli <bt-muli@outlook.com> * @since Beta v0.4.3
* @since Beta v0.3.2
*/ */
// Tauri
import { http } from "@tauri-apps/api"; import { http } from "@tauri-apps/api";
const params = { export enum SERVER {
game: "hk4e", CN_ISLAND = "cn_gf01",
game_biz: "hk4e_cn", CN_TREE = "cn_qd01",
lang: "zh-cn", OS_USA = "os_usa",
bundle_id: "hk4e_cn", OS_EURO = "os_euro",
platform: "pc", OS_ASIA = "os_asia",
region: "cn_gf01", OS_CHT = "os_cht",
level: "60", }
uid: "500299765",
}; /**
* @description 获取游戏内公告参数
* @since Beta v0.4.3
* @param {SERVER} region 服务器
* @param {string} lang 语言
* @returns {TGApp.BBS.Announcement.Params}
*/
function getAnnoParams(region: SERVER, lang: string = "zh-cn"): TGApp.BBS.Announcement.Params {
const params: TGApp.BBS.Announcement.Params = {
game: "hk4e",
game_biz: "hk4e_cn",
lang,
bundle_id: "hk4e_cn",
platform: "pc",
region,
level: "55",
uid: "100000000",
};
if (region === SERVER.CN_ISLAND || region === SERVER.CN_TREE) {
return params;
}
params.game_biz = "hk4e_global";
params.bundle_id = "hk4e_global";
return params;
}
/** /**
* @description 获取游戏内公告列表 * @description 获取游戏内公告列表
* @since Beta v0.3.2 * @since Beta v0.4.3
* @param {string} region 服务器
* @param {string} lang 语言
* @returns {Promise<TGApp.BBS.Announcement.ListData>} * @returns {Promise<TGApp.BBS.Announcement.ListData>}
*/ */
export async function getAnnoList(): Promise<TGApp.BBS.Announcement.ListData> { export async function getAnnoList(
const url = "https://hk4e-api.mihoyo.com/common/hk4e_cn/announcement/api/getAnnList"; region: SERVER = SERVER.CN_ISLAND,
lang: string = "zh-cn",
): Promise<TGApp.BBS.Announcement.ListData> {
const params: TGApp.BBS.Announcement.Params = getAnnoParams(region, lang);
let url = "https://hk4e-api.mihoyo.com/common/hk4e_cn/announcement/api/getAnnList";
if (region !== SERVER.CN_ISLAND && region !== SERVER.CN_TREE) {
url = "https://hk4e-api-os.hoyoverse.com/common/hk4e_global/announcement/api/getAnnList";
}
return await http return await http
.fetch<TGApp.BBS.Announcement.ListResponse>(url, { .fetch<TGApp.BBS.Announcement.ListResponse>(url, {
method: "GET", method: "GET",
@@ -36,12 +66,22 @@ export async function getAnnoList(): Promise<TGApp.BBS.Announcement.ListData> {
/** /**
* @description 获取游戏内公告内容 * @description 获取游戏内公告内容
* @since Beta v0.3.2 * @since Beta v0.4.3
* @param {number} annId 公告 ID * @param {number} annId 公告 ID
* @param {SERVER} region 服务器
* @param {string} lang 语言
* @returns {Promise<TGApp.BBS.Announcement.ContentItem>} * @returns {Promise<TGApp.BBS.Announcement.ContentItem>}
*/ */
export async function getAnnoContent(annId: number): Promise<TGApp.BBS.Announcement.ContentItem> { export async function getAnnoContent(
const url = "https://hk4e-api.mihoyo.com/common/hk4e_cn/announcement/api/getAnnContent"; annId: number,
region: SERVER,
lang: string = "zh-cn",
): Promise<TGApp.BBS.Announcement.ContentItem> {
const params: TGApp.BBS.Announcement.Params = getAnnoParams(region, lang);
let url = "https://hk4e-api.mihoyo.com/common/hk4e_cn/announcement/api/getAnnContent";
if (region !== SERVER.CN_ISLAND && region !== SERVER.CN_TREE) {
url = "https://hk4e-api-os.hoyoverse.com/common/hk4e_global/announcement/api/getAnnContent";
}
const annoContents: TGApp.BBS.Announcement.ContentItem[] = await http const annoContents: TGApp.BBS.Announcement.ContentItem[] = await http
.fetch<TGApp.BBS.Announcement.ContentResponse>(url, { .fetch<TGApp.BBS.Announcement.ContentResponse>(url, {
method: "GET", method: "GET",

View File

@@ -9,7 +9,7 @@ const defaultCover = "/source/UI/defaultCover.webp";
/** /**
* @description 将获取到的数据转为渲染用的卡片 * @description 将获取到的数据转为渲染用的卡片
* @since Beta v0.3.3 * @since Beta v0.4.3
* @param {TGApp.BBS.Announcement.ListData[]} data 公告数据 * @param {TGApp.BBS.Announcement.ListData[]} data 公告数据
* @returns {TGApp.App.Announcement.ListCard[]} 渲染用的卡片 * @returns {TGApp.App.Announcement.ListCard[]} 渲染用的卡片
*/ */
@@ -27,7 +27,7 @@ export function getAnnoCard(
title: anno.title, title: anno.title,
subtitle: anno.subtitle, subtitle: anno.subtitle,
banner: anno.banner || defaultCover, banner: anno.banner || defaultCover,
typeLabel: anno.type_label, typeLabel: anno.type === 2 ? "游戏公告" : "活动公告",
tagIcon: anno.tag_icon, tagIcon: anno.tag_icon,
tagLabel: anno.tag_label, tagLabel: anno.tag_label,
timeStr: time, timeStr: time,