mirror of
https://github.com/BTMuli/TeyvatGuide.git
synced 2025-12-14 09:38:13 +08:00
feat(parser): 写了个解析
This commit is contained in:
12
src/assets/css/anno-parser.css
Normal file
12
src/assets/css/anno-parser.css
Normal 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 {
|
||||||
|
}
|
||||||
@@ -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 内的按钮 */
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
58
src/plugins/Genshin/utils/annoParser.ts
Normal file
58
src/plugins/Genshin/utils/annoParser.ts
Normal 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(/&/g, "&");
|
||||||
|
res = res.replace(/</g, "<");
|
||||||
|
res = res.replace(/>/g, ">");
|
||||||
|
res = res.replace(/ /g, " ");
|
||||||
|
res = res.replace(/'/g, "'");
|
||||||
|
res = res.replace(/"/g, '"');
|
||||||
|
res = res.replace(/'/g, "'");
|
||||||
|
return res;
|
||||||
|
}
|
||||||
@@ -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,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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
64
src/views/t-anno-json.vue
Normal 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>
|
||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user