feat(parser): 写了个解析

This commit is contained in:
BTMuli
2023-04-02 16:34:53 +08:00
parent d3f7b4be13
commit 68c077326e
10 changed files with 214 additions and 31 deletions

View File

@@ -0,0 +1,12 @@
/*
* @description 游戏公告解析 css
* @since Alpha v0.1.1
*/
/* todo 样式美化 */
.anno-content {
font-size: 16px;
line-height: 2;
}
.anno-content p span {
}

View File

@@ -1,23 +1,13 @@
@import "fonts/index.css"; @import "fonts/index.css";
@import "css/mys-parser.css";
/* /*
* @description 米游社解析 json * @description 米游社解析 json
* @since Alpha v0.1.1 * @since Alpha v0.1.1
*/ */
.mys-post-json { .dev-json {
padding: 20px; padding: 20px;
border-radius: 20px; border-radius: 20px;
} font-family: Consolas, serif;
.mys-post-json * {
color: #faf7e8 !important;
background-color: #2b2b2b !important;
font-family: "Consolas", monospace;
}
.mys-post-json .jv-toggle {
background-color: #faf7e8 !important;
} }
/* card action 内的按钮 */ /* card action 内的按钮 */

View File

@@ -21,19 +21,29 @@
<div class="anno-cover" @click="toPost(item)"> <div class="anno-cover" @click="toPost(item)">
<img :src="item.banner" alt="cover" /> <img :src="item.banner" alt="cover" />
</div> </div>
<v-card-title>{{ item.title }}</v-card-title> <v-card-title>
{{ item.title }}
</v-card-title>
<v-card-subtitle>{{ item.subtitle }}</v-card-subtitle> <v-card-subtitle>{{ item.subtitle }}</v-card-subtitle>
<v-card-actions> <v-card-actions>
<v-btn @click="toPost(item)" class="card-btn"> <v-btn @click="toPost(item)" class="anno-btn">
<template v-slot:prepend> <template v-slot:prepend>
<img src="../assets/icons/arrow-right.svg" alt="right" /> <img :src="item.tag_icon || '../assets/icons/arrow-right.svg'" alt="right" />
</template> </template>
查看 查看
</v-btn> </v-btn>
<v-card-subtitle <v-card-subtitle v-show="!appStore.devMode">
><v-icon>mdi-calendar</v-icon> {{ item.start_time.split(" ")[0] }} - <v-icon>mdi-calendar</v-icon>
{{ item.start_time.split(" ")[0] }} -
{{ item.end_time.split(" ")[0] }}</v-card-subtitle {{ item.end_time.split(" ")[0] }}</v-card-subtitle
> >
<v-card-subtitle v-show="appStore.devMode">id: {{ item.id }}</v-card-subtitle>
<v-btn v-show="appStore.devMode" class="card-btn" @click="toJson(item)">
<template v-slot:prepend>
<img src="../assets/icons/arrow-right.svg" alt="right" />
</template>
查看数据
</v-btn>
</v-card-actions> </v-card-actions>
</v-card> </v-card>
</div> </div>
@@ -47,17 +57,24 @@
<v-card-title>{{ item.title }}</v-card-title> <v-card-title>{{ item.title }}</v-card-title>
<v-card-subtitle>{{ item.subtitle }}</v-card-subtitle> <v-card-subtitle>{{ item.subtitle }}</v-card-subtitle>
<v-card-actions> <v-card-actions>
<v-btn @click="toPost(item)" class="card-btn"> <v-btn @click="toPost(item)" class="anno-btn">
<template v-slot:prepend> <template v-slot:prepend>
<img src="../assets/icons/arrow-right.svg" alt="right" /> <img :src="item.tag_icon || '../assets/icons/arrow-right.svg'" alt="right" />
</template> </template>
查看 查看
</v-btn> </v-btn>
<v-card-subtitle> <v-card-subtitle v-show="!appStore.devMode">
<v-icon>mdi-calendar</v-icon> <v-icon>mdi-calendar</v-icon>
{{ item.start_time.split(" ")[0] }} - {{ item.start_time.split(" ")[0] }} -
{{ item.end_time.split(" ")[0] }}</v-card-subtitle {{ item.end_time.split(" ")[0] }}</v-card-subtitle
> >
<v-card-subtitle v-show="appStore.devMode">id: {{ item.id }}</v-card-subtitle>
<v-btn v-show="appStore.devMode" class="card-btn" @click="toJson(item)">
<template v-slot:prepend>
<img src="../assets/icons/arrow-right.svg" alt="right" />
</template>
查看数据
</v-btn>
</v-card-actions> </v-card-actions>
</v-card> </v-card>
</div> </div>
@@ -77,6 +94,10 @@ import GenshinOper from "../plugins/Genshin";
import { createTGWindow } from "../utils/TGWindow"; import { createTGWindow } from "../utils/TGWindow";
// interface // interface
import { AnnoListData, AnnoListCard } from "../plugins/Genshin/interface/announcement"; import { AnnoListData, AnnoListCard } from "../plugins/Genshin/interface/announcement";
import useAppStore from "../store/modules/app";
// store
const appStore = useAppStore();
// loading // loading
const loading = ref(true); const loading = ref(true);
@@ -94,7 +115,7 @@ const annoData = ref({} as AnnoListData);
onMounted(async () => { onMounted(async () => {
loadingTitle.value = "正在获取公告数据"; loadingTitle.value = "正在获取公告数据";
annoData.value = await GenshinOper.Announcement.get.list(); annoData.value = await GenshinOper.Announcement.getList();
loadingTitle.value = "正在转换公告数据"; loadingTitle.value = "正在转换公告数据";
const listCards = GenshinOper.Announcement.card(annoData.value); const listCards = GenshinOper.Announcement.card(annoData.value);
const activityCard = listCards.filter(item => item.type_label === "活动公告"); const activityCard = listCards.filter(item => item.type_label === "活动公告");
@@ -121,6 +142,16 @@ async function toPost(item: AnnoListCard) {
}).href; }).href;
createTGWindow(path, "游戏内公告", item.title, 960, 720, false); createTGWindow(path, "游戏内公告", item.title, 960, 720, false);
} }
async function toJson(item: AnnoListCard) {
const path = router.resolve({
name: "游戏内公告JSON",
params: {
anno_id: item.id,
},
}).href;
createTGWindow(path, "游戏内公告-JSON", item.title, 960, 720, false);
}
</script> </script>
<style lang="css" scoped> <style lang="css" scoped>
@@ -155,6 +186,16 @@ async function toPost(item: AnnoListCard) {
transition: all 0.3s linear; transition: all 0.3s linear;
} }
.anno-btn {
background: #546d8b;
color: #faf7e8;
}
.anno-btn img {
height: 30px;
width: 30px;
}
/* switch */ /* switch */
.switch-btn { .switch-btn {
font-family: Genshin, serif; font-family: Genshin, serif;

View File

@@ -8,14 +8,14 @@
// Announcement // Announcement
import { getAnnouncementList, getAnnouncementContent } from "./request/announcements"; import { getAnnouncementList, getAnnouncementContent } from "./request/announcements";
import { getAnnoCards } from "./utils/announcements"; import { getAnnoCards } from "./utils/announcements";
import { parseAnnoContent } from "./utils/annoParser";
const GenshinOper = { const GenshinOper = {
Announcement: { Announcement: {
get: { getList: getAnnouncementList,
list: getAnnouncementList, getContent: getAnnouncementContent,
content: getAnnouncementContent,
},
card: getAnnoCards, card: getAnnoCards,
parser: parseAnnoContent,
}, },
}; };

View File

@@ -194,6 +194,7 @@ export interface AnnoContentItem {
* @property {string} subtitle 公告副标题 * @property {string} subtitle 公告副标题
* @property {string} banner 公告图片 * @property {string} banner 公告图片
* @property {string} type_label 公告类型标签 * @property {string} type_label 公告类型标签
* @property {string} tag_icon 公告标签图标
* @property {string} start_time 公告开始时间 * @property {string} start_time 公告开始时间
* @property {string} end_time 公告结束时间 * @property {string} end_time 公告结束时间
* @return {AnnoListCard} * @return {AnnoListCard}
@@ -204,6 +205,7 @@ export interface AnnoListCard {
subtitle: string; subtitle: string;
banner: string; banner: string;
type_label: string; type_label: string;
tag_icon: string;
start_time: string; start_time: string;
end_time: string; end_time: string;
} }

View File

@@ -0,0 +1,58 @@
/**
* @file plugins Genshin utils annoParser.ts
* @description 原神游戏内公告解析工具
* @author BTMuli<bt-muli@outlook.com>
* @since Alpha v0.1.1
*/
/**
* @description 解析游戏内公告数据
* @since Alpha v0.1.1
* @param {string} data 游戏内公告数据
* @return {string} 解析后的数据
*/
export function parseAnnoContent(data: string): string {
const htmlBase = new DOMParser().parseFromString(data, "text/html");
// 遍历所有 span 标签
htmlBase.querySelectorAll("span").forEach(span => {
return (span.innerHTML = deleteRedundantTag(span.innerHTML));
});
// 遍历所有 p 标签
htmlBase.querySelectorAll("p").forEach(p => {
// 如果没有子元素
if (p.children.length === 0) {
return (p.innerHTML = deleteRedundantTag(p.innerHTML));
}
});
return htmlBase.body.innerHTML;
}
/**
* @description 删除冗余的标签
* @since Alpha v0.1.1
* @param {string} data 内容
* @return {string} 删除后的内容
*/
export function deleteRedundantTag(data: string): string {
// 先转义一下
return decodeRegExp(data);
}
/**
* @description 转义正则表达式
* @since Alpha v0.1.1
* @param {string} data 内容
* @return {string} 转义后的内容
*/
export function decodeRegExp(data: string): string {
let res = data;
if (res.length === 0) return res;
res = res.replace(/&amp;/g, "&");
res = res.replace(/&lt;/g, "<");
res = res.replace(/&gt;/g, ">");
res = res.replace(/&nbsp;/g, " ");
res = res.replace(/&#39;/g, "'");
res = res.replace(/&quot;/g, '"');
res = res.replace(/&apos;/g, "'");
return res;
}

View File

@@ -23,6 +23,7 @@ export function getAnnoCards(data: AnnoListData): AnnoListCard[] {
subtitle: anno.subtitle, subtitle: anno.subtitle,
banner: anno.banner, banner: anno.banner,
type_label: anno.type_label, type_label: anno.type_label,
tag_icon: anno.tag_icon,
start_time: anno.start_time, start_time: anno.start_time,
end_time: anno.end_time, end_time: anno.end_time,
}); });

View File

@@ -11,6 +11,7 @@ import TPostJson from "../../views/t-post-json.vue";
import TLottery from "../../views/t-lottery.vue"; import TLottery from "../../views/t-lottery.vue";
// 游戏内公告 // 游戏内公告
import TAnno from "../../views/t-anno.vue"; import TAnno from "../../views/t-anno.vue";
import TAnnoJson from "../../views/t-anno-json.vue";
const subRoutes = [ const subRoutes = [
{ {
@@ -29,10 +30,15 @@ const subRoutes = [
component: TLottery, component: TLottery,
}, },
{ {
path: "/anno/:anno_id", path: "/anno_detail/:anno_id",
name: "游戏内公告", name: "游戏内公告",
component: TAnno, component: TAnno,
}, },
{
path: "/anno_detail_json/:anno_id",
name: "游戏内公告JSON",
component: TAnnoJson,
},
]; ];
export default subRoutes; export default subRoutes;

64
src/views/t-anno-json.vue Normal file
View File

@@ -0,0 +1,64 @@
<template>
<div v-if="loading">
<t-loading :empty="loadingEmpty" :title="loadingTitle" />
</div>
<div v-else class="dev-json">
<div class="anno-title">活动列表 JSON</div>
<json-viewer :value="jsonList" copyable boxed />
<div class="anno-title">活动内容 JSON</div>
<json-viewer :value="jsonContent" copyable boxed />
</div>
</template>
<script lang="ts" setup>
// vue
import { ref, onMounted, reactive } from "vue";
import { useRoute } from "vue-router";
import TLoading from "../components/t-loading.vue";
// plugins
import GenshinOper from "../plugins/Genshin";
// interface
import { AnnoListItem, Announcement } from "../plugins/Genshin/interface/announcement";
// loading
const loading = ref(true as boolean);
const loadingTitle = ref("正在加载");
const loadingEmpty = ref(false as boolean);
// 数据
const anno_id = Number(useRoute().params.anno_id);
let jsonList = reactive({});
let jsonContent = reactive({});
onMounted(async () => {
// 检查数据
if (!anno_id) {
loadingEmpty.value = true;
loadingTitle.value = "未找到数据";
return;
}
// 获取数据
loadingTitle.value = "正在获取数据...";
const listData = await GenshinOper.Announcement.getList();
listData.list.map((item: Announcement) => {
return item.list.map((single: AnnoListItem) => {
if (single.ann_id === anno_id) {
jsonList = single;
return;
}
});
});
jsonContent = await GenshinOper.Announcement.getContent(anno_id);
setInterval(() => {
loading.value = false;
}, 200);
});
</script>
<style lang="css" scoped>
.anno-title {
font-size: 20px;
color: #546d8b;
font-family: Genshin-Light, serif;
font-weight: 600;
margin: 20px 0;
}
</style>

View File

@@ -5,8 +5,8 @@
<div v-else class="anno-body"> <div v-else class="anno-body">
<div class="anno-title">{{ annoData.title }}</div> <div class="anno-title">{{ annoData.title }}</div>
<div class="anno-subtitle">{{ annoData.subtitle }}</div> <div class="anno-subtitle">{{ annoData.subtitle }}</div>
<img :src="annoData.banner" alt="cover" class="mys-post-img" /> <img :src="annoData.banner" alt="cover" class="anno-img" />
<div v-html="annoHtml" /> <div v-html="annoHtml" class="anno-content" />
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
@@ -39,9 +39,9 @@ onMounted(async () => {
// 获取数据 // 获取数据
loadingTitle.value = "正在获取数据..."; loadingTitle.value = "正在获取数据...";
try { try {
annoData.value = await GenshinOper.Announcement.get.content(anno_id); annoData.value = await GenshinOper.Announcement.getContent(anno_id);
loadingTitle.value = "正在渲染数据..."; loadingTitle.value = "正在渲染数据...";
annoHtml.value = annoData.value.content; annoHtml.value = GenshinOper.Announcement.parser(annoData.value.content);
} catch (error) { } catch (error) {
loadingEmpty.value = true; loadingEmpty.value = true;
loadingTitle.value = "公告不存在或解析失败"; loadingTitle.value = "公告不存在或解析失败";
@@ -53,7 +53,8 @@ onMounted(async () => {
}); });
</script> </script>
<style lang="css" scoped> <style lang="css" scoped>
/* todo 完善样式 */ @import "../assets/css/anno-parser.css";
.anno-body { .anno-body {
margin: 20px auto; margin: 20px auto;
width: 800px; width: 800px;
@@ -73,4 +74,12 @@ onMounted(async () => {
color: #a1aeb6; color: #a1aeb6;
margin-bottom: 16px; margin-bottom: 16px;
} }
.anno-img {
max-width: 100%;
width: 800px;
height: auto;
border-radius: 10px;
margin-bottom: 10px;
}
</style> </style>