mirror of
https://github.com/BTMuli/TeyvatGuide.git
synced 2025-12-15 09:48:14 +08:00
fix(parser): 完善内容解析
This commit is contained in:
@@ -53,6 +53,14 @@
|
|||||||
height: 450px;
|
height: 450px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mys-post-iframe {
|
||||||
|
width: 800px;
|
||||||
|
height: 450px;
|
||||||
|
overflow: hidden;
|
||||||
|
border: 0;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
.mys-post-link-card {
|
.mys-post-link-card {
|
||||||
width: 800px;
|
width: 800px;
|
||||||
height: 200px;
|
height: 200px;
|
||||||
@@ -62,6 +70,17 @@
|
|||||||
background: #faf7e8;
|
background: #faf7e8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mys-post-unknown {
|
||||||
|
width: 800px;
|
||||||
|
background: #5b738f;
|
||||||
|
color: #faf7e8;
|
||||||
|
font-family: Consolas, monospace;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 10px;
|
||||||
|
margin: 10px auto;
|
||||||
|
border: 2px solid #485466;
|
||||||
|
}
|
||||||
|
|
||||||
.mys-post-link-card-cover {
|
.mys-post-link-card-cover {
|
||||||
width: auto;
|
width: auto;
|
||||||
height: 180px;
|
height: 180px;
|
||||||
@@ -69,6 +88,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.mys-post-link-card-cover img {
|
.mys-post-link-card-cover img {
|
||||||
|
max-width: 320px;
|
||||||
width: auto;
|
width: auto;
|
||||||
height: 180px;
|
height: 180px;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* @file plugins Mys interface post.ts
|
* @file plugins Mys interface post.ts
|
||||||
* @description Mys 插件帖子接口
|
* @description Mys 插件帖子接口
|
||||||
* @author BTMuli<bt-muli@outlook.com>
|
* @author BTMuli<bt-muli@outlook.com>
|
||||||
* @since Alpha
|
* @since Alpha v0.1.1
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { MysResponse } from "./base";
|
import { MysResponse } from "./base";
|
||||||
@@ -226,6 +226,20 @@ export interface PostStat {
|
|||||||
forward_num: number;
|
forward_num: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 帖子内容-结构化
|
||||||
|
* @description 当用户发帖时,解析内容用这个,为 post.content 的反序列化
|
||||||
|
* @since Alpha v0.1.1
|
||||||
|
* @interface PostContent
|
||||||
|
* @property {string} describe 描述
|
||||||
|
* @property {string[]} imgs 图片 URL
|
||||||
|
* @return {PostContent}
|
||||||
|
*/
|
||||||
|
export interface PostContent {
|
||||||
|
describe: string;
|
||||||
|
imgs?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description 帖子结构化内容
|
* @description 帖子结构化内容
|
||||||
* @since Alpha v0.1.1
|
* @since Alpha v0.1.1
|
||||||
@@ -233,6 +247,7 @@ export interface PostStat {
|
|||||||
* @property {string|object} insert 插入内容
|
* @property {string|object} insert 插入内容
|
||||||
* @property {string} insert.image 图片 URL
|
* @property {string} insert.image 图片 URL
|
||||||
* @property {PostStructuredContentVod} insert.vod 视频信息
|
* @property {PostStructuredContentVod} insert.vod 视频信息
|
||||||
|
* @property {string} insert.video 外部视频 URL
|
||||||
* @property {string} insert.backup_text 折叠文本
|
* @property {string} insert.backup_text 折叠文本
|
||||||
* @property {object} insert.lottery 抽奖,当 backup_text 为 [抽奖]
|
* @property {object} insert.lottery 抽奖,当 backup_text 为 [抽奖]
|
||||||
* @property {string} insert.lottery.id 抽奖 ID
|
* @property {string} insert.lottery.id 抽奖 ID
|
||||||
@@ -256,6 +271,7 @@ export interface PostStructuredContent {
|
|||||||
insert:
|
insert:
|
||||||
| {
|
| {
|
||||||
image?: string;
|
image?: string;
|
||||||
|
video?: string;
|
||||||
vod?: PostStructuredContentVod;
|
vod?: PostStructuredContentVod;
|
||||||
backup_text?: string;
|
backup_text?: string;
|
||||||
lottery?: {
|
lottery?: {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* @author BTMuli<bt-muli@outlook.com>
|
* @author BTMuli<bt-muli@outlook.com>
|
||||||
* @since Alpha v0.1.1
|
* @since Alpha v0.1.1
|
||||||
*/
|
*/
|
||||||
import { PostStructuredContent } from "../interface/post";
|
import { PostContent, PostData, PostStructuredContent } from "../interface/post";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description 检测链接是否是米游社帖子
|
* @description 检测链接是否是米游社帖子
|
||||||
@@ -19,16 +19,60 @@ export function IsMysPost(url: string): boolean {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 解析用户帖子,将其转换为 PostStructContent
|
||||||
|
* @since Alpha v0.1.1
|
||||||
|
* @see PostContent
|
||||||
|
* @param {string} content 帖子内容
|
||||||
|
* @returns {string} 解析后的内容
|
||||||
|
*/
|
||||||
|
export function contentParser(content: string): string {
|
||||||
|
const data = JSON.parse(content);
|
||||||
|
const result: PostStructuredContent[] = [];
|
||||||
|
// 遍历 data 属性,值
|
||||||
|
Object.keys(data).forEach(key => {
|
||||||
|
switch (key) {
|
||||||
|
case "describe":
|
||||||
|
result.push({
|
||||||
|
insert: data.describe,
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case "imgs":
|
||||||
|
for (const image of data.imgs) {
|
||||||
|
result.push({
|
||||||
|
insert: {
|
||||||
|
image: image,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// 如果是其他属性,就直接插入
|
||||||
|
result.push({
|
||||||
|
insert: JSON.stringify(data[key]),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return JSON.stringify(result);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description 解析Mys数据
|
* @description 解析Mys数据
|
||||||
* @since Alpha v0.1.1
|
* @since Alpha v0.1.1
|
||||||
* @param {string} data Mys数据
|
* @param {PostData} post Mys数据
|
||||||
* @description 为了安全考虑,不会解析所有的属性,只会解析几个常用的属性
|
* @description 为了安全考虑,不会解析所有的属性,只会解析几个常用的属性
|
||||||
* @returns {string} 解析后的HTML,可作为 v-html 使用
|
* @returns {string} 解析后的HTML,可作为 v-html 使用
|
||||||
*/
|
*/
|
||||||
export function PostParser(data: string): string {
|
export function PostParser(post: PostData): string {
|
||||||
|
const postContent = post.post.content;
|
||||||
|
let parserData;
|
||||||
|
if (postContent.startsWith("<")) {
|
||||||
|
parserData = post.post.structured_content;
|
||||||
|
} else {
|
||||||
|
parserData = contentParser(postContent);
|
||||||
|
}
|
||||||
// Json 化
|
// Json 化
|
||||||
let jsonData: PostStructuredContent[] = JSON.parse(data);
|
let jsonData: PostStructuredContent[] = JSON.parse(parserData);
|
||||||
// 创建 div
|
// 创建 div
|
||||||
const doc = document.createElement("div");
|
const doc = document.createElement("div");
|
||||||
// 遍历 Json 数据
|
// 遍历 Json 数据
|
||||||
@@ -54,6 +98,8 @@ function ParserTransfer(data: PostStructuredContent): HTMLDivElement | HTMLSpanE
|
|||||||
return ImageParser(data);
|
return ImageParser(data);
|
||||||
} else if (data.insert.vod) {
|
} else if (data.insert.vod) {
|
||||||
return VideoParser(data);
|
return VideoParser(data);
|
||||||
|
} else if (data.insert.video) {
|
||||||
|
return VideoParser(data);
|
||||||
} else if (data.insert.backup_text) {
|
} else if (data.insert.backup_text) {
|
||||||
return BackupTextParser(data);
|
return BackupTextParser(data);
|
||||||
} else if (data.insert.link_card) {
|
} else if (data.insert.link_card) {
|
||||||
@@ -61,11 +107,29 @@ function ParserTransfer(data: PostStructuredContent): HTMLDivElement | HTMLSpanE
|
|||||||
} else if (data.insert.divider) {
|
} else if (data.insert.divider) {
|
||||||
return DividerParser(data);
|
return DividerParser(data);
|
||||||
} else {
|
} else {
|
||||||
console.log(data);
|
return UnknownParser(data);
|
||||||
throw new Error("Unknown data.insert type");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 解析未知数据
|
||||||
|
* @since Alpha v0.1.1
|
||||||
|
* @param {PostStructuredContent} data Mys数据
|
||||||
|
* @returns {HTMLDivElement} 解析后的未知数据
|
||||||
|
*/
|
||||||
|
function UnknownParser(data: PostStructuredContent): HTMLDivElement {
|
||||||
|
// 创建 div
|
||||||
|
const div = document.createElement("div");
|
||||||
|
div.classList.add("mys-post-unknown");
|
||||||
|
// 创建 code,将数据放入 code
|
||||||
|
const code = document.createElement("code");
|
||||||
|
code.innerText = JSON.stringify(data);
|
||||||
|
// 插入 code
|
||||||
|
div.appendChild(code);
|
||||||
|
// 返回 div
|
||||||
|
return div;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description 解析文本
|
* @description 解析文本
|
||||||
* @since Alpha
|
* @since Alpha
|
||||||
@@ -222,34 +286,42 @@ function VideoParser(data: PostStructuredContent): HTMLDivElement {
|
|||||||
if (typeof data.insert === "string") {
|
if (typeof data.insert === "string") {
|
||||||
throw new Error("data.insert is a string");
|
throw new Error("data.insert is a string");
|
||||||
}
|
}
|
||||||
if (!data.insert.vod) {
|
if (!data.insert.vod && !data.insert.video) {
|
||||||
throw new Error("data.insert.vod is not defined");
|
throw new Error("data.insert.vod is not defined");
|
||||||
}
|
}
|
||||||
// 创建 div
|
// 创建 div
|
||||||
const div = document.createElement("div");
|
const div = document.createElement("div");
|
||||||
// 创建视频
|
|
||||||
const video = document.createElement("video");
|
|
||||||
// 获取 resolutions中size最大的视频
|
|
||||||
const resolution = data.insert.vod.resolutions.reduce((prev: any, curr: any) => {
|
|
||||||
if (prev.size > curr.size) return prev;
|
|
||||||
return curr;
|
|
||||||
});
|
|
||||||
// 设置视频属性
|
|
||||||
video.poster = data.insert.vod.cover; // 设置封面
|
|
||||||
video.controls = true; // 设置 controls
|
|
||||||
// 添加 class
|
|
||||||
video.classList.add("mys-post-vod");
|
|
||||||
// 添加 source
|
|
||||||
const source = document.createElement("source");
|
|
||||||
source.src = resolution.url;
|
|
||||||
source.type = resolution.format === ".mp4" ? "video/mp4" : "video/webm";
|
|
||||||
// 插入 source
|
|
||||||
video.appendChild(source);
|
|
||||||
// 插入 video
|
|
||||||
div.appendChild(video);
|
|
||||||
// 添加 class
|
|
||||||
div.classList.add("mys-post-div");
|
div.classList.add("mys-post-div");
|
||||||
// 返回 div
|
if (data.insert.vod) {
|
||||||
|
// 创建视频
|
||||||
|
const video = document.createElement("video");
|
||||||
|
video.classList.add("mys-post-vod");
|
||||||
|
// 获取 resolutions中size最大的视频
|
||||||
|
const resolution = data.insert.vod.resolutions.reduce((prev: any, curr: any) => {
|
||||||
|
if (prev.size > curr.size) return prev;
|
||||||
|
return curr;
|
||||||
|
});
|
||||||
|
video.poster = data.insert.vod.cover; // 设置封面
|
||||||
|
video.controls = true; // 设置 controls
|
||||||
|
// 添加 source
|
||||||
|
const source = document.createElement("source");
|
||||||
|
source.src = resolution.url;
|
||||||
|
source.type = resolution.format === ".mp4" ? "video/mp4" : "video/webm";
|
||||||
|
// 插入 source
|
||||||
|
video.appendChild(source);
|
||||||
|
// 插入 video
|
||||||
|
div.appendChild(video);
|
||||||
|
} else if (data.insert.video) {
|
||||||
|
// 创建 iframe
|
||||||
|
const video = document.createElement("iframe");
|
||||||
|
video.classList.add("mys-post-iframe");
|
||||||
|
// 设置 iframe 属性
|
||||||
|
video.src = data.insert.video;
|
||||||
|
video.allowFullscreen = true;
|
||||||
|
video.sandbox.add("allow-top-navigation", "allow-same-origin", "allow-forms", "allow-scripts");
|
||||||
|
// 插入 video
|
||||||
|
div.appendChild(video);
|
||||||
|
}
|
||||||
return div;
|
return div;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ onMounted(async () => {
|
|||||||
try {
|
try {
|
||||||
const postData = await MysOper.Post.get(post_id);
|
const postData = await MysOper.Post.get(post_id);
|
||||||
loadingTitle.value = "正在渲染数据...";
|
loadingTitle.value = "正在渲染数据...";
|
||||||
postHtml.value = MysOper.Post.parser(postData.post.structured_content);
|
postHtml.value = MysOper.Post.parser(postData);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
loadingEmpty.value = true;
|
loadingEmpty.value = true;
|
||||||
loadingTitle.value = "帖子不存在或解析失败";
|
loadingTitle.value = "帖子不存在或解析失败";
|
||||||
|
|||||||
Reference in New Issue
Block a user