From 3cc71a89210a85efc08d1c54bcfc8111cd049092 Mon Sep 17 00:00:00 2001 From: BTMuli Date: Sat, 1 Apr 2023 15:32:39 +0800 Subject: [PATCH] =?UTF-8?q?fix(parser):=20=E5=AE=8C=E5=96=84=E5=86=85?= =?UTF-8?q?=E5=AE=B9=E8=A7=A3=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/assets/css/mys-parser.css | 20 +++++ src/plugins/Mys/interface/post.ts | 18 ++++- src/plugins/Mys/utils/parser.ts | 130 +++++++++++++++++++++++------- src/views/t-post.vue | 2 +- 4 files changed, 139 insertions(+), 31 deletions(-) diff --git a/src/assets/css/mys-parser.css b/src/assets/css/mys-parser.css index 68ac4716..749ad59c 100644 --- a/src/assets/css/mys-parser.css +++ b/src/assets/css/mys-parser.css @@ -53,6 +53,14 @@ height: 450px; } +.mys-post-iframe { + width: 800px; + height: 450px; + overflow: hidden; + border: 0; + border-radius: 10px; +} + .mys-post-link-card { width: 800px; height: 200px; @@ -62,6 +70,17 @@ 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 { width: auto; height: 180px; @@ -69,6 +88,7 @@ } .mys-post-link-card-cover img { + max-width: 320px; width: auto; height: 180px; border-radius: 10px; diff --git a/src/plugins/Mys/interface/post.ts b/src/plugins/Mys/interface/post.ts index 533d53e0..0064548a 100644 --- a/src/plugins/Mys/interface/post.ts +++ b/src/plugins/Mys/interface/post.ts @@ -2,7 +2,7 @@ * @file plugins Mys interface post.ts * @description Mys 插件帖子接口 * @author BTMuli - * @since Alpha + * @since Alpha v0.1.1 */ import { MysResponse } from "./base"; @@ -226,6 +226,20 @@ export interface PostStat { 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 帖子结构化内容 * @since Alpha v0.1.1 @@ -233,6 +247,7 @@ export interface PostStat { * @property {string|object} insert 插入内容 * @property {string} insert.image 图片 URL * @property {PostStructuredContentVod} insert.vod 视频信息 + * @property {string} insert.video 外部视频 URL * @property {string} insert.backup_text 折叠文本 * @property {object} insert.lottery 抽奖,当 backup_text 为 [抽奖] * @property {string} insert.lottery.id 抽奖 ID @@ -256,6 +271,7 @@ export interface PostStructuredContent { insert: | { image?: string; + video?: string; vod?: PostStructuredContentVod; backup_text?: string; lottery?: { diff --git a/src/plugins/Mys/utils/parser.ts b/src/plugins/Mys/utils/parser.ts index e435871f..948fa522 100644 --- a/src/plugins/Mys/utils/parser.ts +++ b/src/plugins/Mys/utils/parser.ts @@ -4,7 +4,7 @@ * @author BTMuli * @since Alpha v0.1.1 */ -import { PostStructuredContent } from "../interface/post"; +import { PostContent, PostData, PostStructuredContent } from "../interface/post"; /** * @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数据 * @since Alpha v0.1.1 - * @param {string} data Mys数据 + * @param {PostData} post Mys数据 * @description 为了安全考虑,不会解析所有的属性,只会解析几个常用的属性 * @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 化 - let jsonData: PostStructuredContent[] = JSON.parse(data); + let jsonData: PostStructuredContent[] = JSON.parse(parserData); // 创建 div const doc = document.createElement("div"); // 遍历 Json 数据 @@ -54,6 +98,8 @@ function ParserTransfer(data: PostStructuredContent): HTMLDivElement | HTMLSpanE return ImageParser(data); } else if (data.insert.vod) { return VideoParser(data); + } else if (data.insert.video) { + return VideoParser(data); } else if (data.insert.backup_text) { return BackupTextParser(data); } else if (data.insert.link_card) { @@ -61,11 +107,29 @@ function ParserTransfer(data: PostStructuredContent): HTMLDivElement | HTMLSpanE } else if (data.insert.divider) { return DividerParser(data); } else { - console.log(data); - throw new Error("Unknown data.insert type"); + return UnknownParser(data); } } +/** + * @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 解析文本 * @since Alpha @@ -222,34 +286,42 @@ function VideoParser(data: PostStructuredContent): HTMLDivElement { if (typeof data.insert === "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"); } // 创建 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 + 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; } diff --git a/src/views/t-post.vue b/src/views/t-post.vue index 19772264..ed59bfae 100644 --- a/src/views/t-post.vue +++ b/src/views/t-post.vue @@ -33,7 +33,7 @@ onMounted(async () => { try { const postData = await MysOper.Post.get(post_id); loadingTitle.value = "正在渲染数据..."; - postHtml.value = MysOper.Post.parser(postData.post.structured_content); + postHtml.value = MysOper.Post.parser(postData); } catch (error) { loadingEmpty.value = true; loadingTitle.value = "帖子不存在或解析失败";