♻️ 帖子文本解析部分重构

This commit is contained in:
目棃
2024-04-07 14:31:43 +08:00
parent 74eb612d72
commit f50398f6e3
14 changed files with 121 additions and 89 deletions

View File

@@ -103,7 +103,6 @@ function onCancel() {
visible.value = false; visible.value = false;
} }
// todo 存在 bug点击成就标题时可能会没有效果
async function searchDirect(word: string): Promise<void> { async function searchDirect(word: string): Promise<void> {
await TGLogger.Info(`[ToAchiInfo][${props.data?.id}][Search] 查询 ${word}`); await TGLogger.Info(`[ToAchiInfo][${props.data?.id}][Search] 查询 ${word}`);
search.value = word; search.value = word;

View File

@@ -47,7 +47,6 @@ interface TpBackupText {
interface TpBackupTextProps { interface TpBackupTextProps {
data: TpBackupText; data: TpBackupText;
next: unknown;
} }
const props = defineProps<TpBackupTextProps>(); const props = defineProps<TpBackupTextProps>();

View File

@@ -21,7 +21,6 @@ interface TpDivider {
interface TpDividerProps { interface TpDividerProps {
data: TpDivider; data: TpDivider;
next: unknown;
} }
const props = defineProps<TpDividerProps>(); const props = defineProps<TpDividerProps>();

View File

@@ -29,7 +29,6 @@ export interface TpImage {
} }
interface TpImageProps { interface TpImageProps {
data: TpImage; data: TpImage;
next: unknown;
} }
const props = defineProps<TpImageProps>(); const props = defineProps<TpImageProps>();

View File

@@ -39,7 +39,6 @@ interface TpLinkCard {
interface TpLinkCardProps { interface TpLinkCardProps {
data: TpLinkCard; data: TpLinkCard;
next: unknown;
} }
const props = defineProps<TpLinkCardProps>(); const props = defineProps<TpLinkCardProps>();

View File

@@ -22,7 +22,6 @@ interface TpMention {
interface TpMentionProps { interface TpMentionProps {
data: TpMention; data: TpMention;
next: unknown;
} }
const props = defineProps<TpMentionProps>(); const props = defineProps<TpMentionProps>();

View File

@@ -1,10 +1,9 @@
<template> <template>
<component <component
v-for="(tp, index) in props.data" v-for="(tp, index) in getParsedData(props.data)"
:key="index" :key="index"
:is="getTpName(tp)" :is="getTpName(tp)"
:data="tp" :data="tp"
:next="getTpNext(index)"
/> />
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
@@ -13,7 +12,8 @@ import TpDivider from "./tp-divider.vue";
import TpImage from "./tp-image.vue"; import TpImage from "./tp-image.vue";
import TpLinkCard from "./tp-linkCard.vue"; import TpLinkCard from "./tp-linkCard.vue";
import TpMention from "./tp-mention.vue"; import TpMention from "./tp-mention.vue";
import TpText from "./tp-text.vue"; import TpText, { type TpText as TpTextType } from "./tp-text.vue";
import TpTexts from "./tp-texts.vue";
import TpUnknown from "./tp-unknown.vue"; import TpUnknown from "./tp-unknown.vue";
import TpVideo from "./tp-video.vue"; import TpVideo from "./tp-video.vue";
import TpVod from "./tp-vod.vue"; import TpVod from "./tp-vod.vue";
@@ -25,7 +25,65 @@ interface TpParserProps {
const props = defineProps<TpParserProps>(); const props = defineProps<TpParserProps>();
function getParsedData(data: TGApp.Plugins.Mys.SctPost.Base[]): TGApp.Plugins.Mys.SctPost.Base[] {
const res: TGApp.Plugins.Mys.SctPost.Base[] = [];
let child: TGApp.Plugins.Mys.SctPost.Base[] = [];
let cur: TGApp.Plugins.Mys.SctPost.Base | undefined;
for (const tp of data) {
const tpName = getTpName(tp);
if (tpName !== TpText) {
cur = tp;
child = [];
res.push(cur);
continue;
}
if (tp.insert === "\n") {
child.push(tp);
cur = {
insert: "",
attributes: tp.attributes,
children: child,
};
res.push(cur);
child = [];
continue;
}
const parsedText = getParsedText(tp);
for (const text of parsedText) {
child.push(text);
if (text.insert === "\n") {
cur = {
insert: "",
attributes: text.attributes,
children: child,
};
res.push(cur);
child = [];
}
}
}
return res;
}
function getParsedText(data: TpTextType): TpTextType[] {
if (data.insert.includes("\n") && data.insert !== "\n") {
const splits = data.insert.split("\n");
const res = [];
for (let i = 0; i < splits.length; i++) {
if (splits[i] !== "") {
res.push({ insert: splits[i], attributes: data.attributes });
}
if (i !== splits.length - 1) {
res.push({ insert: "\n", attributes: data.attributes });
}
}
return res;
}
return [data];
}
function getTpName(tp: TGApp.Plugins.Mys.SctPost.Base) { function getTpName(tp: TGApp.Plugins.Mys.SctPost.Base) {
if (tp.children) return TpTexts;
if (typeof tp.insert === "string") { if (typeof tp.insert === "string") {
return TpText; return TpText;
} else if ("image" in tp.insert) { } else if ("image" in tp.insert) {
@@ -47,11 +105,4 @@ function getTpName(tp: TGApp.Plugins.Mys.SctPost.Base) {
} }
return TpUnknown; return TpUnknown;
} }
function getTpNext(index: number) {
if (index + 1 >= props.data.length) {
return undefined;
}
return props.data[index + 1];
}
</script> </script>

View File

@@ -17,18 +17,10 @@
v-for="(emoji, indexE) in emojis" v-for="(emoji, indexE) in emojis"
:data="emoji" :data="emoji"
:key="indexE" :key="indexE"
:next="indexE === emojis.length - 1 ? undefined : next"
/> />
<TpText <span v-else :style="getTextStyle()">
v-else-if="mode == 'texts'"
v-for="(text, indexT) in texts"
:data="text"
:key="indexT"
:next="indexT === texts.length - 1 ? next : undefined"
/>
<div v-else :style="getTextStyle()" class="tp-text-span">
{{ props.data.insert }} {{ props.data.insert }}
</div> </span>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, ref, StyleValue, toRaw } from "vue"; import { onMounted, ref, StyleValue, toRaw } from "vue";
@@ -39,7 +31,7 @@ import { parseLink, parsePost } from "../../utils/linkParser";
import { isColorSimilar } from "../../utils/toolFunc"; import { isColorSimilar } from "../../utils/toolFunc";
import showSnackbar from "../func/snackbar"; import showSnackbar from "../func/snackbar";
interface TpText { export interface TpText {
insert: string; insert: string;
attributes?: { attributes?: {
header?: number; header?: number;
@@ -52,14 +44,12 @@ interface TpText {
interface TpTextProps { interface TpTextProps {
data: TpText; data: TpText;
next: TGApp.Plugins.Mys.SctPost.Base | undefined;
} }
const props = defineProps<TpTextProps>(); const props = defineProps<TpTextProps>();
const mode = ref<string>("text"); const mode = ref<string>("text");
const localEmojis = ref(localStorage.getItem("emojis")); const localEmojis = ref(localStorage.getItem("emojis"));
const emojis = ref<TpText[]>([]); const emojis = ref<TpText[]>([]);
const texts = ref<TpText[]>([]);
const router = useRouter(); const router = useRouter();
console.log("tpText", JSON.stringify(props.data.insert), toRaw(props.data)?.attributes); console.log("tpText", JSON.stringify(props.data.insert), toRaw(props.data)?.attributes);
@@ -81,45 +71,16 @@ onMounted(async () => {
emojis.value = res.map((i) => ({ insert: i, attributes: props.data.attributes })); emojis.value = res.map((i) => ({ insert: i, attributes: props.data.attributes }));
return; return;
} }
if (props.data.insert.includes("\n") && props.data.insert !== "\n") {
mode.value = "texts";
const splits = props.data.insert.split("\n");
const temp = [];
for (let i = 0; i < splits.length; i++) {
if (splits[i] !== "") {
temp.push({ insert: splits[i], attributes: props.data.attributes });
}
if (i !== splits.length - 1) {
temp.push({ insert: "\n", attributes: props.data.attributes });
}
}
texts.value = temp;
return;
}
mode.value = "text"; mode.value = "text";
}); });
// 解析文本样式 // 解析文本样式
function getTextStyle(): StyleValue { function getTextStyle(): StyleValue {
const style = <Array<StyleValue>>[]; const style = <Array<StyleValue>>[];
let data: TpText; const data: TpText = props.data;
if (props.data.insert === "\n") { style.push("white-space: pre-wrap");
return "display: inline";
}
if (typeof props.next?.insert === "string" && props.next.insert.includes("\n")) {
data = {
insert: props.data.insert,
attributes: {
...props.data.attributes,
...props.next.attributes,
},
};
} else {
data = props.data;
}
if (data.attributes) { if (data.attributes) {
const ruleBold: StyleValue = "fontFamily: var(--font-title)"; const ruleBold: StyleValue = "fontFamily: var(--font-title)";
const headerFontSizes = ["2rem", "1.75rem", "1.5rem", "1.25rem", "1rem", "0.75rem"];
if (data.attributes.bold) { if (data.attributes.bold) {
style.push(ruleBold); style.push(ruleBold);
} }
@@ -131,21 +92,6 @@ function getTextStyle(): StyleValue {
const ruleColor: StyleValue = `color: ${colorGet}`; const ruleColor: StyleValue = `color: ${colorGet}`;
style.push(ruleColor); style.push(ruleColor);
} }
if (data.attributes.align) {
const ruleAlign: StyleValue = `textAlign: ${data.attributes.align}`;
if (data.attributes.align !== "center") style.push("display: inline");
else style.push(ruleAlign);
}
if (data.attributes.header) {
const ruleHeader: StyleValue = `fontSize: ${headerFontSizes[data.attributes.header - 1]}`;
style.push(ruleHeader);
style.push(ruleBold);
}
if (!data.attributes.align && !data.attributes.header) {
style.push("display: inline");
}
} else {
style.push("display: inline");
} }
return style; return style;
} }
@@ -211,7 +157,7 @@ function getEmojiName() {
</script> </script>
<style lang="css" scoped> <style lang="css" scoped>
.tp-text-link { .tp-text-link {
display: flex; display: inline-flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
color: #00c3ff; color: #00c3ff;
@@ -231,10 +177,4 @@ function getEmojiName() {
height: 45px; height: 45px;
margin: 0 5px; margin: 0 5px;
} }
.tp-text-span {
overflow-wrap: break-word;
white-space: pre-wrap;
word-break: break-all;
}
</style> </style>

View File

@@ -0,0 +1,49 @@
<template>
<div :style="getLineStyle()" class="tp-texts">
<TpText v-for="(text, indexT) in props.data.children" :data="text" :key="indexT" />
</div>
</template>
<script lang="ts" setup>
import { onMounted, StyleValue, toRaw } from "vue";
import TpText, { type TpText as TpTextType } from "./tp-text.vue";
interface TpTexts extends TpTextType {
children: TpTextType[];
}
interface TpTextsProps {
data: TpTexts;
}
const props = defineProps<TpTextsProps>();
if (props.data.attributes) {
console.warn("TpTexts", props.data.attributes);
}
function getLineStyle(): StyleValue {
const style = <Array<StyleValue>>[];
if (props.data.attributes === undefined) {
return style;
}
const ruleBold: StyleValue = "fontFamily: var(--font-title)";
const headerFontSizes = ["2rem", "1.75rem", "1.5rem", "1.25rem", "1rem", "0.75rem"];
if (props.data.attributes.align) {
const ruleAlign: StyleValue = `textAlign: ${props.data.attributes.align}`;
if (props.data.attributes.align !== "center") style.push("display: inline");
else style.push(ruleAlign);
}
if (props.data.attributes.header) {
const ruleHeader: StyleValue = `fontSize: ${headerFontSizes[props.data.attributes.header - 1]}`;
style.push(ruleHeader);
style.push(ruleBold);
}
return style;
}
</script>
<style lang="css" scoped>
.tp-texts {
white-space: pre-wrap;
}
</style>

View File

@@ -10,7 +10,6 @@ import TGLogger from "../../utils/TGLogger";
interface TpUnknownProps { interface TpUnknownProps {
data: TGApp.Plugins.Mys.SctPost.Empty; data: TGApp.Plugins.Mys.SctPost.Empty;
next?: unknown;
} }
const props = defineProps<TpUnknownProps>(); const props = defineProps<TpUnknownProps>();

View File

@@ -38,7 +38,6 @@ interface TpVideo {
interface TpVideoProps { interface TpVideoProps {
data: TpVideo; data: TpVideo;
next: unknown;
} }
const props = defineProps<TpVideoProps>(); const props = defineProps<TpVideoProps>();

View File

@@ -43,7 +43,6 @@ interface TpVod {
interface TpVodProps { interface TpVodProps {
data: TpVod; data: TpVod;
next: unknown;
} }
const props = defineProps<TpVodProps>(); const props = defineProps<TpVodProps>();

View File

@@ -36,7 +36,6 @@ interface TpVote {
interface TpVoteProps { interface TpVoteProps {
data: TpVote; data: TpVote;
next: unknown;
} }
interface TpVoteInfo { interface TpVoteInfo {

View File

@@ -1,27 +1,29 @@
/** /**
* @file plugins/Mys/types/SctPost.d.ts * @file plugins/Mys/types/SctPost.d.ts
* @description Mys 插件 结构化帖子类型声明文件 * @description Mys 插件 结构化帖子类型声明文件
* @since Beta v0.3.7 * @since Beta v0.4.5
*/ */
/** /**
* @description 结构化帖子类型命名空间 * @description 结构化帖子类型命名空间
* @since Beta v0.3.7 * @since Beta v0.4.5
* @namespace TGApp.Plugins.Mys.SctPost * @namespace TGApp.Plugins.Mys.SctPost
* @memberof TGApp.Plugins.Mys * @memberof TGApp.Plugins.Mys
*/ */
declare namespace TGApp.Plugins.Mys.SctPost { declare namespace TGApp.Plugins.Mys.SctPost {
/** /**
* @description 帖子结构化数据-基础类型 * @description 帖子结构化数据-基础类型
* @since Beta v0.3.4 * @since Beta v0.4.5
* @interface Base * @interface Base
* @property {unknown} insert - 帖子内容 * @property {unknown} insert - 帖子内容
* @property {unknown} attributes - 帖子属性 * @property {unknown} attributes - 帖子属性
* @property {Base[]} children - 子帖子
* @return Base * @return Base
*/ */
interface Base { interface Base {
insert: any; insert: any;
attributes?: any; attributes?: any;
children?: Base[];
} }
/** /**