feat(sidebar): 支持各种游戏咨讯获取

Signed-off-by: BTMuli <BT-Muli@outlook.com>
This commit is contained in:
BTMuli
2023-04-05 12:20:40 +08:00
parent f8a2343d28
commit 1fafbd4612
8 changed files with 173 additions and 90 deletions

View File

@@ -20,11 +20,57 @@
<img src="/source/UI/paimon.webp" alt="homeIcon" class="sideIcon" />
</template>
</v-list-item>
<v-list-item title="公告" value="news" link href="/news">
<v-list-item title="公告" value="announcements" link href="/announcements">
<template v-slot:prepend>
<img src="../assets/icons/board.svg" alt="annoIcon" class="sideIcon" />
</template>
</v-list-item>
<v-divider></v-divider>
<v-list-group value="mihoyo" fluid>
<template v-slot:activator="{ props }">
<v-list-item title="米游社" v-bind="props">
<template v-slot:prepend>
<img src="/platforms/mhy/mys.webp" alt="mihoyo" class="sideIcon" />
</template>
</v-list-item>
</template>
<v-list-item title="原神" value="mhy-ys" link href="/news/2">
<template v-slot:prepend>
<img src="/platforms/mhy/ys.webp" alt="ys" class="sideIcon" />
</template>
</v-list-item>
<v-list-item title="崩坏3" value="mhy-bh3" link href="/news/1">
<template v-slot:prepend>
<img src="/platforms/mhy/bh3.webp" alt="bh3" class="sideIcon" />
</template>
</v-list-item>
<v-list-item title="崩坏2" value="mhy-bh2" link href="/news/3">
<template v-slot:prepend>
<img src="/platforms/mhy/bh2.webp" alt="bh2" class="sideIcon" />
</template>
</v-list-item>
<v-list-item title="未定事件簿" value="mhy-wd" link href="/news/4">
<template v-slot:prepend>
<img src="/platforms/mhy/wd.webp" alt="wd" class="sideIcon" />
</template>
</v-list-item>
<v-list-item title="星穹铁道" value="mhy-sr" link href="/news/6">
<template v-slot:prepend>
<img src="/platforms/mhy/sr.webp" alt="sr" class="sideIcon" />
</template>
</v-list-item>
<v-list-item title="绝区零" value="mhy-zzz" link href="/news/8">
<template v-slot:prepend>
<img src="/platforms/mhy/zzz.webp" alt="zzz" class="sideIcon" />
</template>
</v-list-item>
<v-list-item title="大别野" value="mhy-dby" link href="/news/5">
<template v-slot:prepend>
<img src="/platforms/mhy/dby.webp" alt="dby" class="sideIcon" />
</template>
</v-list-item>
</v-list-group>
<v-divider></v-divider>
<v-list-item title="成就" value="achievements" link href="/achievements">
<template v-slot:prepend>
<img src="../assets/icons/achievements.svg" alt="achievementsIcon" class="sideIcon" />
@@ -33,13 +79,13 @@
<v-divider></v-divider>
<v-list-group value="database" fluid>
<template v-slot:activator="{ props }">
<v-list-item title="数据库" v-bind="props" @click="submenu('database')">
<v-list-item title="数据库" v-bind="props">
<template v-slot:prepend>
<v-icon color="rgb(205, 182, 145)">mdi-database</v-icon>
</template>
</v-list-item>
</template>
<v-list-item title="GCG" value="GCG" link href="/GCG">
<v-list-item title="GCG" value="db-GCG" link href="/GCG">
<template v-slot:prepend>
<img src="../assets/icons/GCG.svg" alt="gcgIcon" class="sideIcon" />
</template>
@@ -57,25 +103,27 @@
<script lang="ts" setup>
// vue
import { ref } from "vue";
import { computed, ref } from "vue";
// store
import useAppStore from "../store/modules/app";
const appStore = useAppStore();
const rail = ref(appStore.sidebar.collapse);
const open = ref(appStore.getSubmenu());
const open = computed({
get() {
return appStore.getSubmenu();
},
set(value: string[]) {
appStore.sidebar.submenu.mihoyo = value.includes("mihoyo");
appStore.sidebar.submenu.database = value.includes("database");
},
});
function collapse() {
rail.value = !rail.value;
appStore.sidebar.collapse = rail.value;
}
function submenu(value: string) {
if (value === "database") {
appStore.sidebar.submenu.database = open.value.includes(value);
}
}
</script>
<style lang="css" scoped>
@@ -87,5 +135,6 @@ function submenu(value: string) {
width: 24px;
height: 24px;
margin-right: 32px;
border-radius: 5px;
}
</style>

View File

@@ -2,7 +2,7 @@
* @file plugins Mys request news.ts
* @description Mys 插件咨讯请求
* @author BTMuli<bt-muli@outlook.com>
* @since Alpha v0.1.1
* @since Alpha v0.1.2
*/
import { http } from "@tauri-apps/api";
@@ -10,7 +10,7 @@ import { NewsData, NewsResponse } from "../interface/news";
// 咨讯 API
const NEWS_LIST_API =
"https://bbs-api.mihoyo.com/post/wapi/getNewsList?gids=2&page_size={page_size}&type={news_type}&last_id={last_id}";
"https://bbs-api.mihoyo.com/post/wapi/getNewsList?gids={gid}&page_size={page_size}&type={news_type}&last_id={last_id}";
/**
* @description 咨讯类型
@@ -29,16 +29,19 @@ enum NewsType {
/**
* @description 获取 Notice 列表
* @since Alpha v0.1.1
* @since Alpha v0.1.2
* @param {string} gid gid: 1 为崩坏3 2 为原神3 为崩坏24 为未定事件簿5 为大别野6 为崩坏星穹铁道8 为绝区零
* @param {number} page_size 返回数量
* @param {number} last_id 上一次请求的最后一条数据的 id
* @return {Promise<NewsData>}
*/
export async function getNoticeList(
gid: string = "2",
page_size: number = 20,
last_id: number = 0
): Promise<NewsData> {
const url = NEWS_LIST_API.replace("{page_size}", page_size.toString())
.replace("{gid}", gid)
.replace("{news_type}", NewsType.NOTICE)
.replace("{last_id}", last_id.toString());
return await http.fetch<NewsResponse>(url).then(res => res.data.data);
@@ -46,16 +49,19 @@ export async function getNoticeList(
/**
* @description 获取 Activity 列表
* @since Alpha v0.1.1
* @since Alpha v0.1.2
* @param {string} gid gid: 1 为崩坏3 2 为原神3 为崩坏24 为未定事件簿5 为大别野6 为崩坏星穹铁道8 为绝区零
* @param {number} page_size 返回数量
* @param {number} last_id 上一次请求的最后一条数据的 id
* @return {Promise<NewsData>}
*/
export async function getActivityList(
gid: string = "2",
page_size: number = 20,
last_id: number = 0
): Promise<NewsData> {
const url = NEWS_LIST_API.replace("{page_size}", page_size.toString())
.replace("{gid}", gid)
.replace("{news_type}", NewsType.ACTIVITY)
.replace("{last_id}", last_id.toString());
return await http.fetch<NewsResponse>(url).then(res => res.data.data);
@@ -63,13 +69,19 @@ export async function getActivityList(
/**
* @description 获取 News 列表
* @since Alpha v0.1.1
* @since Alpha v0.1.2
* @param {string} gid gid: 1 为崩坏3 2 为原神3 为崩坏24 为未定事件簿5 为大别野6 为崩坏星穹铁道8 为绝区零
* @param {number} page_size 返回数量
* @param {number} last_id 上一次请求的最后一条数据的 id
* @return {Promise<NewsData>}
*/
export async function getNewsList(page_size: number = 20, last_id: number = 0): Promise<NewsData> {
export async function getNewsList(
gid: string = "2",
page_size: number = 20,
last_id: number = 0
): Promise<NewsData> {
const url = NEWS_LIST_API.replace("{page_size}", page_size.toString())
.replace("{gid}", gid)
.replace("{news_type}", NewsType.NEWS)
.replace("{last_id}", last_id.toString());
return await http.fetch<NewsResponse>(url).then(res => res.data.data);

View File

@@ -2,19 +2,19 @@
* @file plugins Mys request post.ts
* @description Mys帖子请求
* @author BTMuli<bt-muli@outlook.com>
* @since Alpha
* @since Alpha v0.1.2
*/
import { http } from "@tauri-apps/api";
import { PostResponse, PostData } from "../interface/post";
// 帖子 API
const POST_API = "https://bbs-api.mihoyo.com/post/wapi/getPostFull?gids=2&post_id={post_id}";
const POST_REFERER = "https://bbs.mihoyo.com/ys/article/{post_id}";
const POST_API = "https://bbs-api.mihoyo.com/post/wapi/getPostFull?post_id={post_id}";
const POST_REFERER = "https://bbs.mihoyo.com/";
/**
* @description 获取帖子信息
* @since Alpha
* @since Alpha v0.1.2
* @param {number} post_id 帖子 ID
* @return {Promise<PostData>}
*/
@@ -24,7 +24,7 @@ export async function getPostData(post_id: number): Promise<PostData> {
method: "GET",
headers: {
"Content-Type": "application/json",
Referer: POST_REFERER.replace("{post_id}", post_id.toString()),
Referer: POST_REFERER,
},
})
.then(res => {

View File

@@ -2,21 +2,20 @@
* @file plugins Mys utils PostParser.ts
* @description 用于解析Mys数据的工具
* @author BTMuli<bt-muli@outlook.com>
* @since Alpha v0.1.1
* @since Alpha v0.1.2
*/
import { PostContent, PostData, PostStructuredContent } from "../interface/post";
/**
* @description 检测链接是否是米游社帖子
* @since Alpha v0.1.1
* @since Alpha v0.1.2
* @param {string} url 链接
* @returns {boolean} 是否是米游社帖子
*/
export function IsMysPost(url: string): boolean {
return (
url.startsWith("https://bbs.mihoyo.com/ys/article/") ||
url.startsWith("https://www.miyoushe.com/ys/article/")
);
const regBBS = /^https:\/\/bbs\.mihoyo\.com\/\w+\/article\/\d+$/;
const regMYS = /^https:\/\/www\.miyoushe\.com\/\w+\/article\/\d+$/;
return regBBS.test(url) || regMYS.test(url);
}
/**
@@ -58,7 +57,7 @@ export function contentParser(content: string): string {
/**
* @description 解析Mys数据
* @since Alpha v0.1.1
* @since Alpha v0.1.2
* @param {PostData} post Mys数据
* @description 为了安全考虑,不会解析所有的属性,只会解析几个常用的属性
* @returns {string} 解析后的HTML可作为 v-html 使用
@@ -69,17 +68,16 @@ export function PostParser(post: PostData): string {
if (postContent.startsWith("<")) {
parserData = post.post.structured_content;
} else {
parserData = contentParser(postContent);
try {
parserData = contentParser(post.post.content);
} catch (error) {
parserData = post.post.structured_content;
}
}
// Json 化
let jsonData: PostStructuredContent[] = JSON.parse(parserData);
// 创建 div
const doc = document.createElement("div");
// 遍历 Json 数据
jsonData.forEach((item: any) => {
// 解析
const parsed = ParserTransfer(item);
// 插入
doc.appendChild(parsed);
});
return doc.innerHTML;

View File

@@ -2,13 +2,12 @@
* @file router modules main.ts
* @description 主路由模块
* @author BTMuli<bt-muli@outlook.com>
* @since Alpha v0.1.1
* @since Alpha v0.1.2
*/
// 信息展示
import Announcements from "../../pages/Announcements.vue";
import Home from "../../pages/Home.vue";
import News from "../../pages/News.vue";
import GCG from "../../pages/GCG.vue";
// 数据交互
import Achievements from "../../pages/Achievements.vue";
@@ -45,11 +44,6 @@ const mainRoutes = [
path: "/home",
redirect: "/",
},
{
path: "/news",
name: "咨讯",
component: News,
},
];
export default mainRoutes;

View File

@@ -2,18 +2,35 @@
* @file router modules sub.ts
* @description 子路由模块,用于二级窗口
* @author BTMuli<bt-muli@outlook.com>
* @since Alpha v0.1.1
* @since Alpha v0.1.2
*/
// 游戏内公告
import TAnno from "../../views/t-anno.vue";
import TAnnoJson from "../../views/t-anno-json.vue";
// 咨讯
import TNews from "../../views/t-news.vue";
// 帖子相关
import TPost from "../../views/t-post.vue";
import TPostJson from "../../views/t-post-json.vue";
// 抽奖
import TLottery from "../../views/t-lottery.vue";
// 游戏内公告
import TAnno from "../../views/t-anno.vue";
import TAnnoJson from "../../views/t-anno-json.vue";
const subRoutes = [
{
path: "/anno_detail/:anno_id",
name: "游戏内公告",
component: TAnno,
},
{
path: "/anno_detail_json/:anno_id",
name: "游戏内公告JSON",
component: TAnnoJson,
},
{
path: "/news/:gid",
name: "咨讯",
component: TNews,
},
{
path: "/post_detail/:post_id",
name: "帖子详情",
@@ -29,16 +46,6 @@ const subRoutes = [
name: "抽奖详情",
component: TLottery,
},
{
path: "/anno_detail/:anno_id",
name: "游戏内公告",
component: TAnno,
},
{
path: "/anno_detail_json/:anno_id",
name: "游戏内公告JSON",
component: TAnnoJson,
},
];
export default subRoutes;

View File

@@ -27,6 +27,9 @@ const useAppStore = defineStore({
collapse: true,
// 是否显示
submenu: {
// 米游社
mihoyo: false,
// 数据库
database: false,
},
},
@@ -57,13 +60,16 @@ const useAppStore = defineStore({
this.sidebar = {
collapse: true,
submenu: {
mihoyo: false,
database: false,
},
};
} else {
if (this.sidebar.collapse === undefined) this.sidebar.collapse = false;
if (this.sidebar.submenu === undefined) this.sidebar.submenu = { database: false };
if (this.sidebar.submenu === undefined)
this.sidebar.submenu = { database: false, mihoyo: false };
if (this.sidebar.submenu.database === undefined) this.sidebar.submenu.database = false;
if (this.sidebar.submenu.mihoyo === undefined) this.sidebar.submenu.mihoyo = false;
}
},
// 初始化配置
@@ -72,6 +78,7 @@ const useAppStore = defineStore({
this.sidebar = {
collapse: false,
submenu: {
mihoyo: false,
database: false,
},
};
@@ -88,6 +95,7 @@ const useAppStore = defineStore({
getSubmenu() {
let open = [];
if (this.sidebar.submenu.database) open.push("database");
if (this.sidebar.submenu.mihoyo) open.push("mihoyo");
return open;
},
},

View File

@@ -6,9 +6,9 @@
<v-tabs v-model="tab" align-tabs="start" class="news-tabs">
<v-tab value="notice" title="公告" />
<v-tab value="activity" title="活动" />
<v-tab value="news" title="新闻" />
<v-tab value="news" title="新闻" v-if="showNews" />
<v-spacer></v-spacer>
<v-btn class="switch-btn" @click="switchAnno">
<v-btn class="switch-btn" @click="switchAnno" v-if="showSwitch">
<template v-slot:prepend>
<v-icon>mdi-bullhorn</v-icon>
</template>
@@ -100,7 +100,7 @@
</v-btn>
</div>
</v-window-item>
<v-window-item value="news">
<v-window-item value="news" v-if="showNews">
<div class="news-grid">
<v-card class="news-card" v-for="item in postData.news" width="340">
<div class="news-cover" @click="toPost(item)">
@@ -142,7 +142,7 @@
<script lang="ts" setup>
// vue
import { onMounted, ref } from "vue";
import { useRouter } from "vue-router";
import { useRoute, useRouter } from "vue-router";
import TLoading from "../components/t-loading.vue";
// store
import useAppStore from "../store/modules/app";
@@ -153,24 +153,29 @@ import { createTGWindow } from "../utils/TGWindow";
// interface
import { NewsCard } from "../plugins/Mys/interface/news";
//
const router = useRouter();
const gid = useRoute().params.gid as string;
const showNews = ref((gid !== "5") as boolean);
const showSwitch = ref((gid === "2") as boolean);
// Store
const appStore = useAppStore();
// loading
const loading = ref(true);
const loadingTitle = ref("正在加载");
const loadingSub = ref(false);
const loading = ref(true as boolean);
const loadingTitle = ref("正在加载" as string);
const loadingSub = ref(false as boolean);
// snackbar
const snackbar = ref(false);
const snackbarText = ref("");
const snackbarColor = ref("success");
//
const router = useRouter();
const snackbar = ref(false as boolean);
const snackbarText = ref("" as string);
const snackbarColor = ref("success" as string);
// search
const search = ref("");
const search = ref("" as string);
//
const tab = ref("");
const tab = ref("" as string);
const postData = ref({
notice: [] as NewsCard[],
activity: [] as NewsCard[],
@@ -193,23 +198,31 @@ const rawData = ref({
onMounted(async () => {
loadingTitle.value = "正在获取公告数据...";
const noticeData = await MysOper.News.get.notice();
const noticeData = await MysOper.News.get.notice(gid);
rawData.value.notice.is_last = noticeData.is_last;
rawData.value.notice.last_id = noticeData.last_id;
rawData.value.notice.last_id = noticeData.list.length;
loadingTitle.value = "正在获取活动数据...";
const activityData = await MysOper.News.get.activity();
const activityData = await MysOper.News.get.activity(gid);
rawData.value.activity.is_last = activityData.is_last;
rawData.value.activity.last_id = activityData.last_id;
loadingTitle.value = "正在获取新闻数据...";
const newsData = await MysOper.News.get.news();
rawData.value.news.is_last = newsData.is_last;
rawData.value.news.last_id = newsData.last_id;
loadingTitle.value = "正在渲染数据...";
postData.value = {
notice: MysOper.News.card.notice(noticeData),
activity: MysOper.News.card.activity(activityData),
news: MysOper.News.card.news(newsData),
};
rawData.value.activity.last_id = activityData.list.length;
if (showNews) {
loadingTitle.value = "正在获取新闻数据...";
const newsData = await MysOper.News.get.news(gid);
console.log(newsData);
rawData.value.news!.is_last = newsData.is_last;
rawData.value.news!.last_id = newsData.list.length;
postData.value = {
notice: MysOper.News.card.notice(noticeData),
activity: MysOper.News.card.activity(activityData),
news: MysOper.News.card.news(newsData),
};
} else {
postData.value = {
notice: MysOper.News.card.notice(noticeData),
activity: MysOper.News.card.activity(activityData),
news: [],
};
}
tab.value = "notice";
loading.value = false;
});
@@ -230,8 +243,8 @@ async function loadMore(data: string) {
loadingSub.value = false;
return;
}
const getNotice = await MysOper.News.get.notice(20, rawData.value.notice.last_id);
rawData.value.notice.last_id = getNotice.last_id;
const getNotice = await MysOper.News.get.notice(gid, 20, rawData.value.notice.last_id);
rawData.value.notice.last_id = rawData.value.notice.last_id + getNotice.list.length;
rawData.value.notice.is_last = getNotice.is_last;
const noticeCard = MysOper.News.card.notice(getNotice);
postData.value.notice = postData.value.notice.concat(noticeCard);
@@ -245,8 +258,8 @@ async function loadMore(data: string) {
loadingSub.value = false;
return;
}
const getActivity = await MysOper.News.get.activity(20, rawData.value.activity.last_id);
rawData.value.activity.last_id = getActivity.last_id;
const getActivity = await MysOper.News.get.activity(gid, 20, rawData.value.activity.last_id);
rawData.value.activity.last_id = rawData.value.activity.last_id + getActivity.list.length;
rawData.value.activity.is_last = getActivity.is_last;
const activityCard = MysOper.News.card.activity(getActivity);
postData.value.activity = postData.value.activity.concat(activityCard);
@@ -260,13 +273,15 @@ async function loadMore(data: string) {
loadingSub.value = false;
return;
}
const getNews = await MysOper.News.get.news(20, rawData.value.news.last_id);
const getNews = await MysOper.News.get.news(gid, 20, rawData.value.news.last_id);
rawData.value.news.last_id = getNews.last_id;
rawData.value.news.is_last = getNews.is_last;
const newsCard = MysOper.News.card.news(getNews);
postData.value.news = postData.value.news.concat(newsCard);
loadingSub.value = false;
break;
default:
break;
}
}