Files
TeyvatGuide/src/components/viewPost/tp-text.vue
2024-11-19 14:45:29 +08:00

162 lines
4.3 KiB
Vue

<template>
<span
v-if="mode == 'link'"
class="tp-text-link"
@click="toLink()"
:title="props.data.attributes?.link"
:style="getTextStyle()"
>
{{ props.data.insert }}
</span>
<span v-else-if="mode == 'emoji'" class="tp-text-emoji">
<img :src="getEmojiUrl()" :alt="getEmojiName()" :title="getEmojiName()" />
</span>
<TpText
v-else-if="mode == 'emojis'"
v-for="(emoji, indexE) in emojis"
:data="emoji"
:key="indexE"
/>
<span v-else :style="getTextStyle()">{{ props.data.insert }}</span>
</template>
<script lang="ts" setup>
import { onMounted, ref, StyleValue, toRaw } from "vue";
import { getEmojis } from "../../plugins/Mys/request/getEmojis.js";
import { parseLink, parsePost } from "../../utils/linkParser.js";
import { isColorSimilar } from "../../utils/toolFunc.js";
import showSnackbar from "../func/snackbar.js";
export interface TpText {
insert: string;
attributes?: {
header?: number;
link?: string;
bold?: boolean;
color?: string;
align?: string;
italic?: boolean;
};
}
interface TpTextProps {
data: TpText;
}
const props = defineProps<TpTextProps>();
const mode = ref<string>("text");
const localEmojis = ref<string | null>(localStorage.getItem("emojis"));
const emojis = ref<TpText[]>([]);
console.log("tpText", JSON.stringify(props.data.insert), toRaw(props.data)?.attributes);
onMounted(async () => {
if (props.data.attributes && "link" in props.data.attributes) {
mode.value = "link";
return;
}
// thanks, @Lightczx & webstorm
const reg = /(?<=\n|.+?|^)(_\(.+?\)(?=\n|.+?|$))/g;
const res = props.data.insert.split(reg);
if (res.length > 2) {
if (res.length === 3 && res[0] === "" && res[2] === "") {
mode.value = "emoji";
return;
}
mode.value = "emojis";
emojis.value = res.map((i) => ({ insert: i, attributes: props.data.attributes }));
return;
}
mode.value = "text";
});
// 解析文本样式
function getTextStyle(): StyleValue {
const style = <Array<StyleValue>>[];
const data: TpText = props.data;
style.push("white-space: pre-wrap");
style.push("line-break: anywhere");
style.push("word-break: break-all");
if (data.attributes) {
const ruleBold: StyleValue = "fontFamily: var(--font-title)";
const ruleItalic: StyleValue = "fontStyle: italic";
if (data.attributes.bold) style.push(ruleBold);
if (data.attributes.italic) style.push(ruleItalic);
if (data.attributes.color) {
let colorGet = data.attributes.color;
if (isColorSimilar("#000000", data.attributes.color)) {
colorGet = "var(--app-page-content)";
}
const ruleColor: StyleValue = `color: ${colorGet}`;
style.push(ruleColor);
}
}
return style;
}
// 解析链接目标
async function toLink() {
if (!props.data.attributes) return;
if (!props.data.attributes.link) return;
const link = props.data.attributes.link;
const isPost = await parsePost(link);
if (isPost !== false) {
location.href = `/post_detail/${isPost}`;
return;
}
const res = await parseLink(link);
if (res === true) return;
if (res === false) {
showSnackbar.error(`未知链接:${link}`, 3000);
return;
}
window.open(res);
}
// 解析表情链接
function getEmojiUrl(): string {
if (localEmojis.value === null || !JSON.parse(localEmojis.value)[getEmojiName()]) {
console.warn("tpEmoji unknown", getEmojiName());
getEmojis().then((res) => {
if ("retcode" in res) {
console.error(res);
showSnackbar.error("获取表情包失败!");
mode.value = "text";
return "";
}
localEmojis.value = JSON.stringify(res);
localStorage.setItem("emojis", localEmojis.value);
});
}
const emojiName = getEmojiName();
const emojiMap: Record<string, string> = JSON.parse(<string>localEmojis.value);
if (!Object.keys(emojiMap).includes(emojiName)) mode.value = "text";
return JSON.parse(<string>localEmojis.value)[emojiName];
}
function getEmojiName() {
return props.data.insert.slice(2, -1);
}
</script>
<style lang="css" scoped>
.tp-text-link {
color: #00c3ff;
cursor: pointer;
text-decoration: underline solid #00c3ff;
text-underline-position: under;
}
.tp-text-emoji {
display: inline-flex;
align-items: flex-end;
justify-content: center;
transform: translateY(5px);
}
.tp-text-emoji img {
width: 45px;
height: 45px;
margin: 0 5px;
}
</style>