mirror of
https://github.com/BTMuli/TeyvatGuide.git
synced 2025-12-12 09:18:14 +08:00
🔥 弃用旧渲染方式 #64
This commit is contained in:
@@ -16,7 +16,7 @@
|
|||||||
import { toRaw } from "vue";
|
import { toRaw } from "vue";
|
||||||
import { useRouter } from "vue-router";
|
import { useRouter } from "vue-router";
|
||||||
|
|
||||||
import { isMysPost } from "../../plugins/Mys/utils/parsePost";
|
import { isMysPost } from "../../utils/toolFunc";
|
||||||
|
|
||||||
interface TpLinkCard {
|
interface TpLinkCard {
|
||||||
insert: {
|
insert: {
|
||||||
|
|||||||
@@ -19,8 +19,8 @@ import { onMounted, ref, StyleValue, toRaw } from "vue";
|
|||||||
import { useRouter } from "vue-router";
|
import { useRouter } from "vue-router";
|
||||||
|
|
||||||
import { getEmojis } from "../../plugins/Mys/request/getEmojis";
|
import { getEmojis } from "../../plugins/Mys/request/getEmojis";
|
||||||
import { isColorSimilar, isMysPost } from "../../plugins/Mys/utils/parsePost";
|
|
||||||
import TGClient from "../../utils/TGClient";
|
import TGClient from "../../utils/TGClient";
|
||||||
|
import { isColorSimilar, isMysPost } from "../../utils/toolFunc";
|
||||||
import showConfirm from "../func/confirm";
|
import showConfirm from "../func/confirm";
|
||||||
import showSnackbar from "../func/snackbar";
|
import showSnackbar from "../func/snackbar";
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
* @file plugins Mys index.ts
|
* @file plugins/Mys/index.ts
|
||||||
* @description Mys plugin index
|
* @description Mys plugin index
|
||||||
* @since Beta v0.3.0
|
* @since Beta v0.3.7
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import MysApi from "./api";
|
import MysApi from "./api";
|
||||||
@@ -13,15 +13,13 @@ import getPositionData from "./request/getPositionData";
|
|||||||
import getPostData from "./request/getPostData";
|
import getPostData from "./request/getPostData";
|
||||||
import getGachaCard from "./utils/getGachaCard";
|
import getGachaCard from "./utils/getGachaCard";
|
||||||
import getLotteryCard from "./utils/getLotteryCard";
|
import getLotteryCard from "./utils/getLotteryCard";
|
||||||
import { getNoticeCard, getActivityCard, getNewsCard } from "./utils/getNewsCard";
|
import { getActivityCard, getNewsCard, getNoticeCard } from "./utils/getNewsCard";
|
||||||
import getPositionCard from "./utils/getPositionCard";
|
import getPositionCard from "./utils/getPositionCard";
|
||||||
import parsePost from "./utils/parsePost";
|
|
||||||
|
|
||||||
const Mys = {
|
const Mys = {
|
||||||
Api: MysApi,
|
Api: MysApi,
|
||||||
Post: {
|
Post: {
|
||||||
get: getPostData,
|
get: getPostData,
|
||||||
parser: parsePost,
|
|
||||||
},
|
},
|
||||||
Gacha: {
|
Gacha: {
|
||||||
get: getGachaData,
|
get: getGachaData,
|
||||||
|
|||||||
@@ -1,538 +0,0 @@
|
|||||||
/**
|
|
||||||
* @file plugins Mys utils parsePost.ts
|
|
||||||
* @description 用于解析Mys数据的工具
|
|
||||||
* @since Beta v0.3.7
|
|
||||||
*/
|
|
||||||
|
|
||||||
import * as colorConvert from "color-convert";
|
|
||||||
import type { KEYWORD } from "color-convert/conversions";
|
|
||||||
import { score } from "wcag-color";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @description 给定两个16进制颜色值,确认两者是否相近
|
|
||||||
* @since Beta v0.3.4
|
|
||||||
* @param {string} colorBg 背景颜色
|
|
||||||
* @param {string} colorFg 前景颜色
|
|
||||||
* @returns {boolean} 是否相近
|
|
||||||
*/
|
|
||||||
export function isColorSimilar(colorBg: string, colorFg: string): boolean {
|
|
||||||
let hexBg, hexFg;
|
|
||||||
if (colorBg.startsWith("#")) hexBg = colorBg;
|
|
||||||
else hexBg = colorConvert.keyword.hex(<KEYWORD>colorBg);
|
|
||||||
if (colorFg.startsWith("#")) hexFg = colorFg;
|
|
||||||
else hexFg = colorConvert.keyword.hex(<KEYWORD>colorFg);
|
|
||||||
const contrast = score(hexFg, hexBg);
|
|
||||||
return contrast === "Fail";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @description 检测链接是否是米游社帖子
|
|
||||||
* @since Alpha v0.1.2
|
|
||||||
* @param {string} url 链接
|
|
||||||
* @returns {boolean} 是否是米游社帖子
|
|
||||||
*/
|
|
||||||
export function isMysPost(url: string): boolean {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @description 根据 url 获取帖子 id
|
|
||||||
* @since Beta v0.3.0
|
|
||||||
* @param {string} url 链接
|
|
||||||
* @returns {string} 帖子 id
|
|
||||||
*/
|
|
||||||
function getPostId(url: string): string {
|
|
||||||
const postId: string | undefined = url.split("/").pop();
|
|
||||||
if (postId === undefined) {
|
|
||||||
throw new Error(`Can't get postId from ${url}`);
|
|
||||||
}
|
|
||||||
return postId;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @description 获取视频时长
|
|
||||||
* @since Beta v0.3.3
|
|
||||||
* @param {number} duration 视频时长
|
|
||||||
* @returns {string} 视频时长
|
|
||||||
*/
|
|
||||||
function getVodTime(duration: number): string {
|
|
||||||
const secTotal = Math.floor(duration / 1000);
|
|
||||||
const seconds = secTotal % 60;
|
|
||||||
const minutes = Math.floor(secTotal / 60) % 60;
|
|
||||||
const hours = Math.floor(secTotal / 3600);
|
|
||||||
let result = "";
|
|
||||||
if (hours > 0) {
|
|
||||||
result += `${hours.toString().padStart(2, "0")}:`;
|
|
||||||
}
|
|
||||||
result += `${minutes.toString().padStart(2, "0")}:`;
|
|
||||||
result += `${seconds.toString().padStart(2, "0")}`;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @description 解析用户帖子,将其转换为 StructContent
|
|
||||||
* @since Beta v0.3.4
|
|
||||||
* @see PostContent
|
|
||||||
* @param {string} content 帖子内容
|
|
||||||
* @returns {string} 解析后的内容
|
|
||||||
*/
|
|
||||||
export function parseContent(content: string): string {
|
|
||||||
const data: TGApp.Plugins.Mys.SctPost.Other = JSON.parse(content);
|
|
||||||
const result: TGApp.Plugins.Mys.SctPost.Common[] = [];
|
|
||||||
const keys = Object.keys(data);
|
|
||||||
keys.forEach((key) => {
|
|
||||||
switch (key) {
|
|
||||||
case "describe":
|
|
||||||
result.push({
|
|
||||||
insert: data.describe,
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case "imgs":
|
|
||||||
data.imgs.forEach((item) => {
|
|
||||||
result.push({
|
|
||||||
insert: {
|
|
||||||
image: item,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
console.warn(`[MysPostParser] Unknown key: ${key}`);
|
|
||||||
result.push({
|
|
||||||
insert: JSON.stringify(data[key]),
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return JSON.stringify(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @description 解析Mys数据
|
|
||||||
* @since Alpha v0.1.2
|
|
||||||
* @param {TGApp.Plugins.Mys.Post.FullData} post Mys数据
|
|
||||||
* @description 为了安全考虑,不会解析所有的属性,只会解析几个常用的属性
|
|
||||||
* @returns {string} 解析后的HTML,可作为 v-html 使用
|
|
||||||
*/
|
|
||||||
function parsePost(post: TGApp.Plugins.Mys.Post.FullData): string {
|
|
||||||
const postContent = post.post.content;
|
|
||||||
let parserData;
|
|
||||||
if (postContent.startsWith("<")) {
|
|
||||||
parserData = post.post.structured_content;
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
parserData = parseContent(post.post.content);
|
|
||||||
} catch (error) {
|
|
||||||
parserData = post.post.structured_content;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const jsonData: TGApp.Plugins.Mys.SctPost.Common[] = JSON.parse(parserData);
|
|
||||||
const doc = document.createElement("div");
|
|
||||||
jsonData.forEach((item: any) => {
|
|
||||||
const parsed = transferParser(item);
|
|
||||||
doc.appendChild(parsed);
|
|
||||||
});
|
|
||||||
return doc.innerHTML;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @description 解析中转
|
|
||||||
* @since Beta v0.3.3
|
|
||||||
* @param {TGApp.Plugins.Mys.SctPost.Common} data Mys数据
|
|
||||||
* @returns {HTMLDivElement | HTMLSpanElement} 解析后的中转
|
|
||||||
*/
|
|
||||||
function transferParser(data: TGApp.Plugins.Mys.SctPost.Common): HTMLDivElement | HTMLSpanElement {
|
|
||||||
if (typeof data.insert === "string") {
|
|
||||||
return parseText(<TGApp.Plugins.Mys.SctPost.Text | TGApp.Plugins.Mys.SctPost.Link>data);
|
|
||||||
} else if ("image" in data.insert) {
|
|
||||||
return parseImage(<TGApp.Plugins.Mys.SctPost.Image>data);
|
|
||||||
} else if ("vod" in data.insert) {
|
|
||||||
return parseVideo(<TGApp.Plugins.Mys.SctPost.Vod>data);
|
|
||||||
} else if ("video" in data.insert) {
|
|
||||||
return parseVideo(<TGApp.Plugins.Mys.SctPost.Video>data);
|
|
||||||
} else if ("backup_text" in data.insert) {
|
|
||||||
return parseBackup(<TGApp.Plugins.Mys.SctPost.Backup>data);
|
|
||||||
} else if ("link_card" in data.insert) {
|
|
||||||
return parseLinkCard(<TGApp.Plugins.Mys.SctPost.LinkCard>data);
|
|
||||||
} else if ("divider" in data.insert) {
|
|
||||||
return parseDivider(<TGApp.Plugins.Mys.SctPost.Divider>data);
|
|
||||||
} else if ("mention" in data.insert) {
|
|
||||||
return parseMention(<TGApp.Plugins.Mys.SctPost.Mention>data);
|
|
||||||
} else if ("villa_card" in data.insert) {
|
|
||||||
return parseVillaCard(<TGApp.Plugins.Mys.SctPost.VillaCard>data);
|
|
||||||
}
|
|
||||||
return parseUnknown(<TGApp.Plugins.Mys.SctPost.Empty>data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @description 解析未知数据
|
|
||||||
* @since Beta v0.3.4
|
|
||||||
* @param {TGApp.Plugins.Mys.SctPost.Empty} data Mys数据
|
|
||||||
* @returns {HTMLDivElement} 解析后的未知数据
|
|
||||||
*/
|
|
||||||
function parseUnknown(data: TGApp.Plugins.Mys.SctPost.Empty): HTMLDivElement {
|
|
||||||
const div = document.createElement("div");
|
|
||||||
div.classList.add("mys-post-unknown");
|
|
||||||
const code = document.createElement("code");
|
|
||||||
code.innerText = JSON.stringify(data, null, 2);
|
|
||||||
code.classList.add("mys-post-unknown-code");
|
|
||||||
div.appendChild(code);
|
|
||||||
return div;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @description 解析文本
|
|
||||||
* @since Beta v0.3.5
|
|
||||||
* @param {TGApp.Plugins.Mys.SctPost.Text |TGApp.Plugins.Mys.SctPost.Link} data Mys数据
|
|
||||||
* @returns {HTMLSpanElement} 解析后的文本
|
|
||||||
*/
|
|
||||||
function parseText(
|
|
||||||
data: TGApp.Plugins.Mys.SctPost.Text | TGApp.Plugins.Mys.SctPost.Link,
|
|
||||||
): HTMLSpanElement {
|
|
||||||
if (data.attributes && "link" in data.attributes) {
|
|
||||||
return LinkTextParser(<TGApp.Plugins.Mys.SctPost.Link>data);
|
|
||||||
}
|
|
||||||
const text = document.createElement("span");
|
|
||||||
if (data.attributes) {
|
|
||||||
if (data.attributes.bold) text.style.fontWeight = "bold";
|
|
||||||
if (data.attributes.color) {
|
|
||||||
let colorGet = data.attributes.color;
|
|
||||||
if (isColorSimilar("#000000", colorGet)) {
|
|
||||||
colorGet = "var(--app-page-content)";
|
|
||||||
}
|
|
||||||
text.style.color = colorGet;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (data.insert.startsWith("_(") && data.insert.endsWith(")")) {
|
|
||||||
return emojiParser(<TGApp.Plugins.Mys.SctPost.Text>data);
|
|
||||||
}
|
|
||||||
text.classList.add("mys-post-span");
|
|
||||||
text.innerText = data.insert;
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @description 解析链接
|
|
||||||
* @since Beta v0.3.4
|
|
||||||
* @param {TGApp.Plugins.Mys.SctPost.Link} data Mys数据
|
|
||||||
* @returns {HTMLSpanElement} 解析后的链接
|
|
||||||
*/
|
|
||||||
function LinkTextParser(data: TGApp.Plugins.Mys.SctPost.Link): HTMLSpanElement {
|
|
||||||
const icon = document.createElement("i");
|
|
||||||
icon.classList.add("mdi", "mdi-link-variant");
|
|
||||||
const link = document.createElement("a");
|
|
||||||
const linkUrl = data.attributes.link;
|
|
||||||
link.classList.add("mys-post-link");
|
|
||||||
if (isMysPost(linkUrl)) {
|
|
||||||
const postId = getPostId(linkUrl);
|
|
||||||
link.href = `/post_detail/${postId}`;
|
|
||||||
link.target = "_self";
|
|
||||||
} else {
|
|
||||||
link.href = linkUrl;
|
|
||||||
link.target = "view_window";
|
|
||||||
}
|
|
||||||
link.innerText = data.insert;
|
|
||||||
link.prepend(icon);
|
|
||||||
return link;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @description 解析分割线
|
|
||||||
* @since Beta v0.3.4
|
|
||||||
* @param {TGApp.Plugins.Mys.SctPost.Divider} data Mys数据
|
|
||||||
* @returns {HTMLDivElement} 解析后的分割线
|
|
||||||
*/
|
|
||||||
function parseDivider(data: TGApp.Plugins.Mys.SctPost.Divider): HTMLDivElement {
|
|
||||||
const div = document.createElement("div");
|
|
||||||
div.classList.add("mys-post-divider");
|
|
||||||
const img = document.createElement("img");
|
|
||||||
const dividerList = ["line_1", "line_2", "line_3", "line_4"];
|
|
||||||
if (!dividerList.includes(data.insert.divider)) {
|
|
||||||
console.error("Unknown divider type", data);
|
|
||||||
return parseUnknown(<TGApp.Plugins.Mys.SctPost.Empty>data);
|
|
||||||
}
|
|
||||||
img.src = `/source/post/divider_${data.insert.divider}.webp`;
|
|
||||||
div.appendChild(img);
|
|
||||||
return div;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @description 解析图片
|
|
||||||
* @since Beta v0.3.4
|
|
||||||
* @param {TGApp.Plugins.Mys.SctPost.Image} data Mys数据
|
|
||||||
* @returns {HTMLDivElement} 解析后的图片
|
|
||||||
*/
|
|
||||||
function parseImage(data: TGApp.Plugins.Mys.SctPost.Image): HTMLDivElement {
|
|
||||||
const div = document.createElement("div");
|
|
||||||
const img = document.createElement("img");
|
|
||||||
img.src = data.insert.image;
|
|
||||||
img.classList.add("mys-post-img");
|
|
||||||
div.appendChild(img);
|
|
||||||
div.classList.add("mys-post-div");
|
|
||||||
return div;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @description 解析视频
|
|
||||||
* @since Beta v0.3.3
|
|
||||||
* @todo 分开解析 Vod 和 Video
|
|
||||||
* @param {TGApp.Plugins.Mys.SctPost.Vod|TGApp.Plugins.Mys.SctPost.Video} data Mys数据
|
|
||||||
* @returns {HTMLDivElement} 解析后的视频
|
|
||||||
*/
|
|
||||||
function parseVideo(
|
|
||||||
data: TGApp.Plugins.Mys.SctPost.Vod | TGApp.Plugins.Mys.SctPost.Video,
|
|
||||||
): HTMLDivElement {
|
|
||||||
const div = document.createElement("div");
|
|
||||||
div.classList.add("mys-post-div");
|
|
||||||
if ("vod" in data.insert) {
|
|
||||||
const video = document.createElement("video");
|
|
||||||
video.classList.add("mys-post-vod");
|
|
||||||
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;
|
|
||||||
const source = document.createElement("source");
|
|
||||||
source.src = resolution.url;
|
|
||||||
source.type = resolution.format === ".mp4" ? "video/mp4" : "video/webm";
|
|
||||||
video.appendChild(source);
|
|
||||||
div.appendChild(video);
|
|
||||||
const coverDiv = document.createElement("div");
|
|
||||||
const cover = document.createElement("img");
|
|
||||||
cover.classList.add("mys-post-vod-cover");
|
|
||||||
cover.src = video.poster;
|
|
||||||
coverDiv.appendChild(cover);
|
|
||||||
const playIcon = document.createElement("img");
|
|
||||||
playIcon.classList.add("mys-post-vod-icon");
|
|
||||||
playIcon.src = "/source/UI/video_play.svg";
|
|
||||||
coverDiv.appendChild(playIcon);
|
|
||||||
const playTime = document.createElement("div");
|
|
||||||
playTime.classList.add("mys-post-vod-time");
|
|
||||||
playTime.innerText = getVodTime(data.insert.vod.duration);
|
|
||||||
coverDiv.appendChild(playTime);
|
|
||||||
coverDiv.classList.add("mys-post-vod-cover-div");
|
|
||||||
div.appendChild(coverDiv);
|
|
||||||
} else {
|
|
||||||
const video = document.createElement("iframe");
|
|
||||||
video.classList.add("mys-post-iframe");
|
|
||||||
video.src = data.insert.video;
|
|
||||||
video.allowFullscreen = true;
|
|
||||||
video.sandbox.add("allow-top-navigation", "allow-same-origin", "allow-forms", "allow-scripts");
|
|
||||||
div.appendChild(video);
|
|
||||||
}
|
|
||||||
return div;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @description 解析折叠内容
|
|
||||||
* @since Beta v0.3.4
|
|
||||||
* @param {TGApp.Plugins.Mys.SctPost.Backup} data Mys数据
|
|
||||||
* @returns {HTMLDivElement} 解析后的折叠内容
|
|
||||||
*/
|
|
||||||
function parseBackup(
|
|
||||||
data: TGApp.Plugins.Mys.SctPost.Backup | TGApp.Plugins.Mys.SctPost.Lottery,
|
|
||||||
): HTMLDivElement {
|
|
||||||
if ("lottery" in data.insert) {
|
|
||||||
return LotteryParser(<TGApp.Plugins.Mys.SctPost.Lottery>data);
|
|
||||||
}
|
|
||||||
const titleJson: TGApp.Plugins.Mys.SctPost.Base[] = JSON.parse(data.insert.fold.title);
|
|
||||||
const contentJson: TGApp.Plugins.Mys.SctPost.Base[] = JSON.parse(data.insert.fold.content);
|
|
||||||
const div = document.createElement("div");
|
|
||||||
div.classList.add("mys-post-div");
|
|
||||||
const details = document.createElement("details");
|
|
||||||
details.classList.add("mys-post-details");
|
|
||||||
const title = document.createElement("summary");
|
|
||||||
titleJson.forEach((item) => {
|
|
||||||
const parsed = transferParser(item);
|
|
||||||
title.appendChild(parsed);
|
|
||||||
});
|
|
||||||
const content = document.createElement("div");
|
|
||||||
contentJson.forEach((item) => {
|
|
||||||
const parsed = transferParser(item);
|
|
||||||
content.appendChild(parsed);
|
|
||||||
});
|
|
||||||
details.appendChild(title);
|
|
||||||
details.appendChild(content);
|
|
||||||
div.appendChild(details);
|
|
||||||
return div;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @description 解析抽奖
|
|
||||||
* @since Beta v0.3.4
|
|
||||||
* @param {TGApp.Plugins.Mys.SctPost.Lottery} data Mys数据
|
|
||||||
* @returns {HTMLDivElement} 解析后的抽奖
|
|
||||||
*/
|
|
||||||
function LotteryParser(data: TGApp.Plugins.Mys.SctPost.Lottery): HTMLDivElement {
|
|
||||||
const div = document.createElement("div");
|
|
||||||
const icon = document.createElement("i");
|
|
||||||
icon.classList.add("mdi", "mdi-gift");
|
|
||||||
const title = document.createElement("a");
|
|
||||||
title.classList.add("mys-post-link");
|
|
||||||
title.href = `/lottery/${data.insert.lottery.id}`;
|
|
||||||
title.innerText = data.insert.lottery.toast;
|
|
||||||
title.prepend(icon);
|
|
||||||
div.appendChild(title);
|
|
||||||
return div;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @description 解析链接卡片
|
|
||||||
* @since Beta v0.3.4
|
|
||||||
* @param {TGApp.Plugins.Mys.SctPost.LinkCard} data Mys数据
|
|
||||||
* @returns {HTMLDivElement} 解析后的链接卡片
|
|
||||||
*/
|
|
||||||
function parseLinkCard(data: TGApp.Plugins.Mys.SctPost.LinkCard): HTMLDivElement {
|
|
||||||
const div = document.createElement("div");
|
|
||||||
const cover = document.createElement("div");
|
|
||||||
cover.classList.add("mys-post-link-card-cover");
|
|
||||||
const img = document.createElement("img");
|
|
||||||
img.src = data.insert.link_card.cover;
|
|
||||||
cover.appendChild(img);
|
|
||||||
div.appendChild(cover);
|
|
||||||
const content = document.createElement("div");
|
|
||||||
content.classList.add("mys-post-link-card-content");
|
|
||||||
const title = document.createElement("div");
|
|
||||||
title.classList.add("mys-post-link-card-title");
|
|
||||||
title.innerHTML = data.insert.link_card.title;
|
|
||||||
content.appendChild(title);
|
|
||||||
if (data.insert.link_card.price) {
|
|
||||||
const price = document.createElement("div");
|
|
||||||
price.classList.add("mys-post-link-card-price");
|
|
||||||
price.innerHTML = data.insert.link_card.price;
|
|
||||||
content.appendChild(price);
|
|
||||||
}
|
|
||||||
const button = document.createElement("a");
|
|
||||||
button.classList.add("mys-post-link-card-btn");
|
|
||||||
button.innerHTML = (data.insert.link_card.button_text ?? "详情") + " >";
|
|
||||||
const linkUrl = data.insert.link_card.origin_url;
|
|
||||||
if (isMysPost(linkUrl)) {
|
|
||||||
const postId = getPostId(linkUrl);
|
|
||||||
button.href = `/post_detail/${postId}`;
|
|
||||||
button.target = "_self";
|
|
||||||
} else {
|
|
||||||
button.href = linkUrl;
|
|
||||||
button.target = "view_window";
|
|
||||||
}
|
|
||||||
content.appendChild(button);
|
|
||||||
div.appendChild(content);
|
|
||||||
div.classList.add("mys-post-link-card");
|
|
||||||
return div;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @description 解析 Mention
|
|
||||||
* @since Beta v0.3.4
|
|
||||||
* @param {TGApp.Plugins.Mys.SctPost.Mention} data Mys数据
|
|
||||||
* @returns {HTMLAnchorElement} 解析后的 Mention
|
|
||||||
*/
|
|
||||||
function parseMention(data: TGApp.Plugins.Mys.SctPost.Mention): HTMLAnchorElement {
|
|
||||||
const icon = document.createElement("i");
|
|
||||||
icon.classList.add("mdi", "mdi-account-circle-outline");
|
|
||||||
const link = document.createElement("a");
|
|
||||||
link.classList.add("mys-post-link");
|
|
||||||
link.href = `https://www.miyoushe.com/ys/accountCenter/postList?id=${data.insert.mention.uid}`;
|
|
||||||
link.target = "_blank";
|
|
||||||
link.innerText = data.insert.mention.nickname;
|
|
||||||
link.prepend(icon);
|
|
||||||
return link;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @description 解析 Emoji
|
|
||||||
* @since Beta v0.3.4
|
|
||||||
* @param {TGApp.Plugins.Mys.SctPost.Text} data Mys数据
|
|
||||||
* @returns {HTMLSpanElement} 解析后的 Emoji
|
|
||||||
*/
|
|
||||||
function emojiParser(data: TGApp.Plugins.Mys.SctPost.Text): HTMLImageElement {
|
|
||||||
const emojis = localStorage.getItem("emojis");
|
|
||||||
if (!emojis) {
|
|
||||||
throw new Error("[EmojiParser] emojis is not defined");
|
|
||||||
}
|
|
||||||
const emojiList: Record<string, string> = JSON.parse(emojis);
|
|
||||||
const emojiName = data.insert.slice(2, -1);
|
|
||||||
const emoji = emojiList[emojiName];
|
|
||||||
if (!emoji) {
|
|
||||||
throw new Error(`[EmojiParser] emoji is not defined: ${emojiName}`);
|
|
||||||
}
|
|
||||||
const img = document.createElement("img");
|
|
||||||
img.classList.add("mys-post-emoji");
|
|
||||||
img.src = emoji;
|
|
||||||
img.alt = emojiName;
|
|
||||||
img.title = emojiName;
|
|
||||||
return img;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @description 解析大别野房间的卡片
|
|
||||||
* @since Beta v0.3.7
|
|
||||||
* @param {TGApp.Plugins.Mys.SctPost.VillaCard} data Mys数据
|
|
||||||
* @returns {HTMLDivElement} 解析后的大别野房间的卡片
|
|
||||||
*/
|
|
||||||
function parseVillaCard(data: TGApp.Plugins.Mys.SctPost.VillaCard): HTMLDivElement {
|
|
||||||
const div = document.createElement("div");
|
|
||||||
div.classList.add("mys-post-div");
|
|
||||||
const villaCard = document.createElement("div");
|
|
||||||
villaCard.classList.add("mys-post-villa-card");
|
|
||||||
const bgImg = document.createElement("img");
|
|
||||||
bgImg.classList.add("mys-post-villa-card-bg");
|
|
||||||
bgImg.src = data.insert.villa_card.villa_cover;
|
|
||||||
villaCard.appendChild(bgImg);
|
|
||||||
const bgBefore = document.createElement("div");
|
|
||||||
bgBefore.classList.add("mys-post-villa-card-bg-before");
|
|
||||||
villaCard.appendChild(bgBefore);
|
|
||||||
const flexDiv = document.createElement("div");
|
|
||||||
flexDiv.classList.add("mys-post-villa-card-flex");
|
|
||||||
const topDiv = document.createElement("div");
|
|
||||||
topDiv.classList.add("mys-post-villa-card-top");
|
|
||||||
const topIcon = document.createElement("img");
|
|
||||||
topIcon.classList.add("mys-post-villa-card-icon");
|
|
||||||
topIcon.src = data.insert.villa_card.villa_avatar_url;
|
|
||||||
const topContent = document.createElement("div");
|
|
||||||
topContent.classList.add("mys-post-villa-card-content");
|
|
||||||
const topTitle = document.createElement("div");
|
|
||||||
topTitle.classList.add("mys-post-villa-card-title");
|
|
||||||
topTitle.innerText = data.insert.villa_card.villa_name;
|
|
||||||
topContent.appendChild(topTitle);
|
|
||||||
const topDesc = document.createElement("div");
|
|
||||||
topDesc.classList.add("mys-post-villa-card-desc");
|
|
||||||
const topDescIcon = document.createElement("img");
|
|
||||||
topDescIcon.src = data.insert.villa_card.owner_avatar_url;
|
|
||||||
topDescIcon.classList.add("mys-post-villa-card-desc-icon");
|
|
||||||
const topDescText = document.createElement("span");
|
|
||||||
topDescText.classList.add("mys-post-villa-card-desc-text");
|
|
||||||
topDescText.innerText = `${data.insert.villa_card.owner_nickname} 创建`;
|
|
||||||
topDesc.appendChild(topDescIcon);
|
|
||||||
topDesc.appendChild(topDescText);
|
|
||||||
topContent.appendChild(topDesc);
|
|
||||||
topDiv.appendChild(topIcon);
|
|
||||||
topDiv.appendChild(topContent);
|
|
||||||
flexDiv.appendChild(topDiv);
|
|
||||||
const midDiv = document.createElement("div");
|
|
||||||
midDiv.classList.add("mys-post-villa-card-mid");
|
|
||||||
const numberDiv = document.createElement("div");
|
|
||||||
numberDiv.classList.add("mys-post-villa-card-tag");
|
|
||||||
numberDiv.innerText = `${data.insert.villa_card.villa_member_num}人在聊`;
|
|
||||||
midDiv.appendChild(numberDiv);
|
|
||||||
if (data.insert.villa_card.tag_list !== undefined) {
|
|
||||||
data.insert.villa_card.tag_list.forEach((tag) => {
|
|
||||||
const tagDiv = document.createElement("div");
|
|
||||||
tagDiv.classList.add("mys-post-villa-card-tag");
|
|
||||||
tagDiv.innerText = tag;
|
|
||||||
midDiv.appendChild(tagDiv);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
flexDiv.appendChild(midDiv);
|
|
||||||
const bottomDiv = document.createElement("div");
|
|
||||||
bottomDiv.classList.add("mys-post-villa-card-bottom");
|
|
||||||
bottomDiv.innerText = data.insert.villa_card.villa_introduce;
|
|
||||||
flexDiv.appendChild(bottomDiv);
|
|
||||||
villaCard.appendChild(flexDiv);
|
|
||||||
div.appendChild(villaCard);
|
|
||||||
return div;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default parsePost;
|
|
||||||
@@ -5,7 +5,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { os, path } from "@tauri-apps/api";
|
import { os, path } from "@tauri-apps/api";
|
||||||
|
import colorConvert from "color-convert";
|
||||||
|
import type { KEYWORD } from "color-convert/conversions";
|
||||||
import { v4 } from "uuid";
|
import { v4 } from "uuid";
|
||||||
|
import { score } from "wcag-color";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description 时间戳转换为时间字符串
|
* @description 时间戳转换为时间字符串
|
||||||
@@ -159,3 +162,30 @@ export function getRandomString(length: number, type: string = "all"): string {
|
|||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 判断颜色是否相似
|
||||||
|
* @since Beta v0.3.7
|
||||||
|
* @param {string} colorBg - 背景颜色
|
||||||
|
* @param {string} colorText - 文本颜色
|
||||||
|
* @returns {boolean} 是否相似
|
||||||
|
*/
|
||||||
|
export function isColorSimilar(colorBg: string, colorText: string): boolean {
|
||||||
|
const hexBg = colorBg.startsWith("#") ? colorBg : colorConvert.keyword.hex(<KEYWORD>colorBg);
|
||||||
|
const hexText = colorText.startsWith("#")
|
||||||
|
? colorText
|
||||||
|
: colorConvert.keyword.hex(<KEYWORD>colorText);
|
||||||
|
return score(hexText, hexBg) === "Fail";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 判断是否为 Mys 帖子
|
||||||
|
* @since Beta v0.3.7
|
||||||
|
* @param {string} url - 网址
|
||||||
|
* @returns {boolean} 是否为 Mys 帖子
|
||||||
|
*/
|
||||||
|
export function isMysPost(url: string): boolean {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|||||||
@@ -57,7 +57,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mys-post-title" @click="switchRender()" style="cursor: pointer">
|
<div class="mys-post-title">
|
||||||
<span class="mpt-official" v-if="postRender.isOfficial">官</span>
|
<span class="mpt-official" v-if="postRender.isOfficial">官</span>
|
||||||
<span>{{ postRender.title }}</span>
|
<span>{{ postRender.title }}</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -65,9 +65,7 @@
|
|||||||
<span>创建时间:{{ postRender.created }} </span>
|
<span>创建时间:{{ postRender.created }} </span>
|
||||||
<span>更新时间:{{ postRender.updated }}</span>
|
<span>更新时间:{{ postRender.updated }}</span>
|
||||||
</div>
|
</div>
|
||||||
<!-- eslint-disable-nextline vue/no-v-html -->
|
<TpParser v-model:data="renderPost" />
|
||||||
<div class="mys-post-content" v-html="postHtml" v-if="!devRender" />
|
|
||||||
<TpParser v-else v-model:data="renderPost" />
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
@@ -77,12 +75,10 @@ import { nextTick, onMounted, ref, watch } from "vue";
|
|||||||
import { useRoute } from "vue-router";
|
import { useRoute } from "vue-router";
|
||||||
|
|
||||||
import TSwitchTheme from "../components/app/t-switchTheme.vue";
|
import TSwitchTheme from "../components/app/t-switchTheme.vue";
|
||||||
import showSnackbar from "../components/func/snackbar";
|
|
||||||
import TShareBtn from "../components/main/t-shareBtn.vue";
|
import TShareBtn from "../components/main/t-shareBtn.vue";
|
||||||
import ToLoading from "../components/overlay/to-loading.vue";
|
import ToLoading from "../components/overlay/to-loading.vue";
|
||||||
import TpParser from "../components/post/tp-parser.vue";
|
import TpParser from "../components/post/tp-parser.vue";
|
||||||
import Mys from "../plugins/Mys";
|
import Mys from "../plugins/Mys";
|
||||||
import { parseContent } from "../plugins/Mys/utils/parsePost";
|
|
||||||
|
|
||||||
interface PostRender {
|
interface PostRender {
|
||||||
title: string;
|
title: string;
|
||||||
@@ -108,8 +104,6 @@ const shareTitle = ref<string>("");
|
|||||||
// 数据
|
// 数据
|
||||||
const appVersion = ref<string>();
|
const appVersion = ref<string>();
|
||||||
const postId = Number(useRoute().params.post_id);
|
const postId = Number(useRoute().params.post_id);
|
||||||
const postHtml = ref<string>("");
|
|
||||||
const devRender = ref<boolean>(true);
|
|
||||||
const renderPost = ref<TGApp.Plugins.Mys.SctPost.Base[]>([]);
|
const renderPost = ref<TGApp.Plugins.Mys.SctPost.Base[]>([]);
|
||||||
const postRender = ref<PostRender>({
|
const postRender = ref<PostRender>({
|
||||||
title: "",
|
title: "",
|
||||||
@@ -151,7 +145,6 @@ onMounted(async () => {
|
|||||||
const postData = await Mys.Post.get(postId);
|
const postData = await Mys.Post.get(postId);
|
||||||
loadingTitle.value = "正在渲染数据...";
|
loadingTitle.value = "正在渲染数据...";
|
||||||
renderPost.value = getRenderPost(postData);
|
renderPost.value = getRenderPost(postData);
|
||||||
postHtml.value = Mys.Post.parser(postData);
|
|
||||||
postRender.value = {
|
postRender.value = {
|
||||||
title: postData.post.subject,
|
title: postData.post.subject,
|
||||||
isOfficial: postData.post.post_status.is_official,
|
isOfficial: postData.post.post_status.is_official,
|
||||||
@@ -203,12 +196,35 @@ function getRenderPost(data: TGApp.Plugins.Mys.Post.FullData): TGApp.Plugins.Mys
|
|||||||
return JSON.parse(jsonParse);
|
return JSON.parse(jsonParse);
|
||||||
}
|
}
|
||||||
|
|
||||||
function switchRender() {
|
function parseContent(content: string): string {
|
||||||
devRender.value = !devRender.value;
|
const data: TGApp.Plugins.Mys.SctPost.Other = JSON.parse(content);
|
||||||
showSnackbar({
|
const result: TGApp.Plugins.Mys.SctPost.Common[] = [];
|
||||||
text: devRender.value ? "已切换到测试渲染引擎" : "已切换到正常渲染引擎",
|
const keys = Object.keys(data);
|
||||||
color: "success",
|
keys.forEach((key) => {
|
||||||
|
switch (key) {
|
||||||
|
case "describe":
|
||||||
|
result.push({
|
||||||
|
insert: data.describe,
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case "imgs":
|
||||||
|
data.imgs.forEach((item) => {
|
||||||
|
result.push({
|
||||||
|
insert: {
|
||||||
|
image: item,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.warn(`[MysPostParser] Unknown key: ${key}`);
|
||||||
|
result.push({
|
||||||
|
insert: JSON.stringify(data[key]),
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
return JSON.stringify(result);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<style lang="css" scoped src="../assets/css/post-parser.css" />
|
<style lang="css" scoped src="../assets/css/post-parser.css" />
|
||||||
@@ -366,9 +382,4 @@ function switchRender() {
|
|||||||
column-gap: 2px;
|
column-gap: 2px;
|
||||||
opacity: 0.8;
|
opacity: 0.8;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* content */
|
|
||||||
.mys-post-content {
|
|
||||||
line-height: 2;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user